Skip to content

Injection on a decorator #454

Open
Open
@platipo

Description

@platipo

Hi, I really like this package and am using it in production. I was tying to mess around with decorators because I want to add side effects to a function, adding a secret_number in the example. I was expecting decorated_function_1 to work but it didn't and I can't wrap my head around it. Is this an expected behavior? Could injection be supported like in my_decorator_1?

I also added few examples of things I tried and only decorated_function_4 actually works.

from functools import wraps

from dependency_injector import containers, providers
from dependency_injector.wiring import Provide, inject


class Container(containers.DeclarativeContainer):
    config = providers.Configuration()


@inject
def my_decorator_1(func, secret_number: int = Provide[Container.config.secret_number]):
    @wraps(func)
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        return result + secret_number
    return wrapper

@inject
def my_decorator_2(secret_number: int = Provide[Container.config.secret_number]):
    def inner_decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            result = func(*args, **kwargs)
            return result + secret_number
        return wrapper
    return inner_decorator

def my_decorator_3():
    @inject
    def inner_decorator(func, secret_number: int = Provide[Container.config.secret_number]):
        @wraps(func)
        def wrapper(*args, **kwargs):
            result = func(*args, **kwargs)
            return result + secret_number
        return wrapper
    return inner_decorator

def my_decorator_4(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        secret_number = kwargs['secret_number']
        return result + secret_number
    return wrapper


@my_decorator_1
def decorated_function_1():
    return 42

@inject
@my_decorator_1
def decorated_function_1a():
    return 42

@my_decorator_2()
def decorated_function_2():
    return 42

@my_decorator_3()
def decorated_function_3():
    return 42

@inject
@my_decorator_3()
def decorated_function_3a():
    return 42

@inject
@my_decorator_4
def decorated_function_4(secret_number: int = Provide[Container.config.secret_number]):
    return 42

@my_decorator_4
@inject
def decorated_function_4a(secret_number: int = Provide[Container.config.secret_number]):
    return 42


def main():
    test_funcs = [
        decorated_function_1,
        decorated_function_1a,
        decorated_function_2, 
        decorated_function_3, 
        decorated_function_3a,
        decorated_function_4, 
        decorated_function_4a, 
    ]
    for test_f in test_funcs:
        try:
            result = test_f()
            print(f"Function {test_f} returned {result}")
        except Exception as exc:
            print(f"Function {test_f} raised {exc.__class__.__name__} '{exc}'")

if __name__ == '__main__':
    import sys

    container = Container()
    container.init_resources()
    container.config.secret_number.from_env("SECRET_INT", 24)
    container.wire(modules=[sys.modules[__name__]])

    main()

The output is:

Function <function decorated_function_1 at 0x7f93d8a4d310> raised TypeError 'unsupported operand type(s) for +: 'int' and 'Provide''
Function <function decorated_function_1a at 0x7f93d8a4d4c0> raised TypeError 'unsupported operand type(s) for +: 'int' and 'Provide''
Function <function decorated_function_2 at 0x7f93d8a4d670> raised TypeError 'unsupported operand type(s) for +: 'int' and 'Provide''
Function <function decorated_function_3 at 0x7f93d8a4d820> raised TypeError 'unsupported operand type(s) for +: 'int' and 'Provide''
Function <function decorated_function_3a at 0x7f93d8a4daf0> raised TypeError 'unsupported operand type(s) for +: 'int' and 'Provide''
Function <function decorated_function_4 at 0x7f93d8a4dca0> returned 66
Function <function decorated_function_4a at 0x7f93d8a4de50> raised KeyError ''secret_number''

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions