def __new__(cls, class_name, bases, namespace): if not bases: namespace["__dependencies__"] = {} namespace["__wrapped__"] = None # Doctest module compatibility. namespace["_subs_tree"] = None # Typing module compatibility. return type.__new__(cls, class_name, bases, namespace) _check_inheritance(bases, Injector) ns = {} for attr in ("__module__", "__doc__", "__weakref__", "__qualname__"): try: ns[attr] = namespace.pop(attr) except KeyError: pass for name in namespace: _check_dunder_name(name) _check_attrs_redefinition(name) dependencies = {} for base in reversed(bases): dependencies.update(base.__dependencies__) for name, dep in namespace.items(): dependencies[name] = _make_dependency_spec(name, dep) _check_loops(class_name, dependencies) _check_circles(dependencies) ns["__dependencies__"] = dependencies return type.__new__(cls, class_name, bases, ns)
def __getattr__(cls, attrname): __tracebackhide__ = True cache, cached = {"__self__": cls}, {"__self__"} current_attr, attrs_stack = attrname, [attrname] have_default = False while attrname not in cache: spec = cls.__dependencies__.get(current_attr) if spec is None: if have_default: cached.add(current_attr) current_attr = attrs_stack.pop() have_default = False continue if len(attrs_stack) > 1: message = "{!r} can not resolve attribute {!r} while building {!r}".format( # noqa: E501 cls.__name__, current_attr, attrs_stack.pop()) else: message = "{!r} can not resolve attribute {!r}".format( cls.__name__, current_attr) raise DependencyError(message) marker, attribute, args, have_defaults = spec if set(args).issubset(cached): kwargs = {k: cache[k] for k in args if k in cache} try: cache[current_attr] = attribute(**kwargs) except _Replace as replace: _deep_replace_dependency(cls, current_attr, replace) _check_loops(cls.__name__, cls.__dependencies__) _check_circles(cls.__dependencies__) continue cached.add(current_attr) current_attr = attrs_stack.pop() have_default = False continue for n, arg in enumerate(args, 1): if arg not in cached: attrs_stack.append(current_attr) current_attr = arg have_default = False if n < have_defaults else True break return cache[attrname]
def __getattr__(cls, attrname): __tracebackhide__ = True cache, cached = {"__self__": cls}, {"__self__"} current_attr, attrs_stack = attrname, [attrname] have_default = False replaced_dependencies = {} while attrname not in cache: spec = cls.__dependencies__.get(current_attr) if spec is None: if have_default: cached.add(current_attr) current_attr = attrs_stack.pop() have_default = False continue if len(attrs_stack) > 1: message = "{!r} can not resolve attribute {!r} while building {!r}".format( # noqa: E501 cls.__name__, current_attr, attrs_stack.pop() ) else: message = "{!r} can not resolve attribute {!r}".format( cls.__name__, current_attr ) raise DependencyError(message) marker, attribute, args, have_defaults = spec if set(args).issubset(cached): kwargs = {k: cache[k] for k in args if k in cache} try: dependency = attribute(**kwargs) if ('nested' not in marker and inspect.isclass(dependency) and not current_attr.endswith("_class")): spec = _make_init_spec(dependency) replaced_dependency = _replace_dependency(cls, current_attr, spec) replaced_dependencies[current_attr] = replaced_dependency continue elif isinstance(dependency, This): spec = _make_this_spec(dependency) replaced_dependency = _replace_dependency(cls, current_attr, spec) replaced_dependencies[current_attr] = replaced_dependency continue else: cache[current_attr] = dependency except _Replace as replace: _deep_replace_dependency(cls, current_attr, replace) _check_loops(cls.__name__, cls.__dependencies__) _check_circles(cls.__dependencies__) continue cached.add(current_attr) current_attr = attrs_stack.pop() have_default = False continue for n, arg in enumerate(args, 1): if arg not in cached: attrs_stack.append(current_attr) current_attr = arg have_default = False if n < have_defaults else True break # Restore @value dependencies that returned a class from the result class # to their defining function for attr, dep in replaced_dependencies.items(): cls.__dependencies__[attr] = dep return cache[attrname]
def _replace_dependency(injector, current_attr, spec): replaced_dependency = injector.__dependencies__[current_attr] injector.__dependencies__[current_attr] = spec _check_loops(injector.__name__, injector.__dependencies__) _check_circles(injector.__dependencies__) return replaced_dependency