Example #1
0
def check_varargs(name, varargs, kwargs):

    if varargs and kwargs:
        message = "{0} have arbitrary argument list and keyword arguments"
        raise DependencyError(message.format(name))
    elif varargs:
        message = "{0} have arbitrary argument list"
        raise DependencyError(message.format(name))
    elif kwargs:
        message = "{0} have arbitrary keyword arguments"
        raise DependencyError(message.format(name))
Example #2
0
def _check_argument_default(argument, value, owner):
    expect_class = argument.endswith("_class")
    is_class = isclass(value)
    if expect_class and not is_class:
        message = "{0!r} default value should be a class"
        raise DependencyError(message.format(argument))
    if not expect_class and is_class:
        message = default_class_value_template.format(
            owner=owner, argument=argument, value=value.__name__
        )
        raise DependencyError(message)
Example #3
0
def check_cls_arguments(argnames, defaults, owner_message):

    for argument, value in zip(reversed(argnames), reversed(defaults)):
        expect_class = argument.endswith("_class")
        is_class = inspect.isclass(value)
        if expect_class and not is_class:
            message = "{0!r} default value should be a class"
            raise DependencyError(message.format(argument))
        if not expect_class and is_class:
            message = default_class_value_template.format(
                owner_message=owner_message,
                argument=argument,
                value=value.__name__)
            raise DependencyError(message)
Example #4
0
def _args(func, funcname, owner):
    args = []
    for name, param in signature(func).parameters.items():
        have_default = param.default is not param.empty
        args.append((name, have_default))
        if have_default:
            _check_argument_default(name, param.default, owner)
        if param.kind is param.VAR_POSITIONAL:
            raise DependencyError(
                f"{funcname!r} have variable-length positional arguments"
            )
        if param.kind is param.VAR_KEYWORD:
            raise DependencyError(
                f"{funcname!r} have variable-length keyword arguments"
            )
    return args
Example #5
0
def _check_expression(expression):
    if not any(
        symbol
        for operator, symbol in expression
        if operator == "." and symbol != "__parent__"
    ):
        raise DependencyError("You can not use 'this' directly in the 'Injector'")
Example #6
0
def _check_expression(dependency):

    if not any(
        symbol
        for kind, symbol in dependency.__expression__
        if kind == "." and symbol != "__parent__"
    ):
        raise DependencyError("You can not use 'this' directly in the 'Injector'")
Example #7
0
def _get_attribute(instance, name):
    try:
        return getattr(instance, name)
    except DependencyError:
        if name == "__parent__":
            raise DependencyError(
                "You tried to shift this more times than Injector has levels"
            )
        else:
            raise
Example #8
0
    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]
Example #9
0
def _check_circles_for(dependencies, attrname, origin):

    try:
        argspec = dependencies[attrname]
    except KeyError:
        return

    if argspec[0] is injectable:
        args = argspec[2]
        if origin in args:
            message = "{0!r} is a circular dependency in the {1!r} constructor"
            raise DependencyError(message.format(origin, argspec[1].__name__))
        for name in args:
            _check_circles_for(dependencies, name, origin)
Example #10
0
 def is_optional(self, spec):
     if spec is not None:
         return False
     if self.state.have_default:
         self.state.pop()
         return True
     if self.state.full():
         message = "{!r} can not resolve attribute {!r} while building {!r}".format(
             self.injector.__name__, self.state.current,
             self.state.stack.pop()[0])
     else:
         message = "{!r} can not resolve attribute {!r}".format(
             self.injector.__name__, self.state.current)
     raise DependencyError(message)
Example #11
0
    def __call__(self, __self__):

        result = __self__

        for kind, symbol in self.dependency.__expression__:
            if kind == ".":
                try:
                    result = getattr(result, symbol)
                except DependencyError:
                    message = (
                        "You tried to shift this more times than Injector has levels"
                    )
                    if symbol == "__parent__":
                        raise DependencyError(message)
                    else:
                        raise
            elif kind == "[]":
                result = result[symbol]

        return result
Example #12
0
def check_loops_for(class_name, attribute_name, dependencies, origin,
                    expression):

    try:
        attrname = next(expression)
    except StopIteration:
        return

    try:
        spec = dependencies[attrname]
    except KeyError:
        return

    if spec[0] is nested_injector:
        check_loops_for(
            class_name,
            attribute_name,
            nested_dependencies(dependencies, spec),
            origin,
            expression,
        )
    elif attrname == "__parent__":
        from weakref import ReferenceType

        if isinstance(spec[1], ReferenceType):
            # FIXME: This is an ad-hoc solution for the broken
            # `Replace` problem.  See `dependencies._injector` comment
            # for more info.
            resolved_parent = spec[1]().__dependencies__
        else:
            resolved_parent = spec[1]
        check_loops_for(class_name, attribute_name, resolved_parent, origin,
                        expression)
    elif spec is origin:
        message = "{0!r} is a circle link in the {1!r} injector"
        raise DependencyError(message.format(attribute_name, class_name))
    elif spec[0] is this:
        check_loops_for(class_name, attribute_name, dependencies, origin,
                        filter_expression(spec))
Example #13
0
def _check_loops_for(class_name, attribute_name, dependencies, origin, expression):

    try:
        attrname = next(expression)
    except StopIteration:
        return

    try:
        spec = dependencies[attrname]
    except KeyError:
        return

    if spec[0] is nested_injector:
        _check_loops_for(
            class_name,
            attribute_name,
            _nested_dependencies(dependencies, spec),
            origin,
            expression,
        )
    elif attrname == "__parent__":
        from weakref import ReferenceType

        if isinstance(spec[1], ReferenceType):
            resolved_parent = spec[1]().__dependencies__
        else:
            resolved_parent = spec[1]
        _check_loops_for(
            class_name, attribute_name, resolved_parent, origin, expression
        )
    elif spec is origin:
        message = "{0!r} is a circle link in the {1!r} injector"
        raise DependencyError(message.format(attribute_name, class_name))
    elif spec[0] is this:
        _check_loops_for(
            class_name, attribute_name, dependencies, origin, _filter_expression(spec)
        )
Example #14
0
def check_method(arguments):

    if "self" in arguments:
        raise DependencyError(
            "'operation' decorator can not be used on methods")
Example #15
0
 def __method(self, form):
     raise DependencyError(
         "Add {!r} to the {!r} injector".format(method, injector.__name__)
     )
Example #16
0
    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:
                    # FIXME: If first dependency have this name as
                    # default and the second one have this name
                    # without default, we will see a very strange
                    # KeyError about `cache` access.
                    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)
                    # FIXME:
                    #
                    # We'll probably resolve weakref.  This happens because nested
                    # injector decide to replace a lazy import.  This nested injector
                    # is already resolved by its parent, so it contain a weakref in
                    # it.  Ideally we should not have parent in the scope at all.
                    #
                    # Also, `Replace` doesn't change dependencies dict of its parent,
                    # so lazy import will be evaluated again and again.  This kills
                    # the whole point of `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]
Example #17
0
def check_inheritance(bases, injector):

    for base in bases:
        if not issubclass(base, injector):
            message = "Multiple inheritance is allowed for Injector subclasses only"
            raise DependencyError(message)
Example #18
0
def check_attrs_redefinition(name):

    if name == "let":
        raise DependencyError("'let' redefinition is not allowed")
Example #19
0
def check_dunder_name(name):

    if name.startswith("__") and name.endswith("__"):
        raise DependencyError("Magic methods are not allowed")
Example #20
0
    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]
Example #21
0
def _is_descriptor(name, dependency):
    if ismethoddescriptor(dependency) or isdatadescriptor(dependency):
        message = descriptor_template.format(name=name)
        raise DependencyError(message)
Example #22
0
def _check_class(function):

    if isclass(function):
        raise DependencyError("'value' decorator can not be used on classes")
Example #23
0
def _check_method(arguments):

    if "self" in arguments:
        raise DependencyError("'value' decorator can not be used on methods")
Example #24
0
def _check_extension_scope(bases, namespace):
    if len(bases) == 1 and not namespace:
        raise DependencyError("Extension scope can not be empty")
Example #25
0
def __init__(self, *args, **kwargs):

    raise DependencyError("Do not instantiate Injector")
Example #26
0
def check_class(function):

    if inspect.isclass(function):
        raise DependencyError(
            "'operation' decorator can not be used on classes")
Example #27
0
    def __delattr__(cls, attrname):

        raise DependencyError("'Injector' modification is not allowed")
Example #28
0
def _is_enum(name, dependency):
    if (not name.endswith("_class") and isclass(dependency)
            and issubclass(dependency, Enum)):
        message = enum_template.format(name=name)
        raise DependencyError(message)