Lancetnik / FastDepends

FastDepends - FastAPI Dependency Injection system extracted from FastAPI and cleared of all HTTP logic. Async and sync modes are both supported.
https://lancetnik.github.io/FastDepends/
MIT License
254 stars 9 forks source link

CustomField error with class-based dependency #37

Closed chrisgoddard closed 7 months ago

chrisgoddard commented 7 months ago

Seeing this error:

class TestProvider(CustomField):
    def __init__(self):
        print('init')
        super().__init__(cast=True)

    def use(self, **kwargs) -> typing.Dict[str, typing.Any]:
        kwargs = super().use(**kwargs)
        if self.param_name:
            kwargs[self.param_name] = 'test'
        return kwargs

class Tester:
    @inject
    def __init__(self, test: str = TestProvider()):
        print(test)

Tester()

Raises this error:

TypeError                                 Traceback (most recent call last)
Cell In[32], [line 17](vscode-notebook-cell:?execution_count=32&line=17)
     [13](vscode-notebook-cell:?execution_count=32&line=13)     @inject
     [14](vscode-notebook-cell:?execution_count=32&line=14)     def __init__(self, test: str = TestProvider()):
     [15](vscode-notebook-cell:?execution_count=32&line=15)         print(test)
---> [17](vscode-notebook-cell:?execution_count=32&line=17) Tester()

File [/lib/python3.11/site-packages/fast_depends/use.py:156](.../lib/python3.11/site-packages/fast_depends/use.py:156), in _wrap_inject.<locals>.func_wrapper.<locals>.injected_wrapper(*args, **kwargs)
    [153](.../lib/python3.11/site-packages/fast_depends/use.py:153) @wraps(func)
    [154](.../lib/python3.11/site-packages/fast_depends/use.py:154) def injected_wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
    [155](.../lib/python3.11/site-packages/fast_depends/use.py:155)     with ExitStack() as stack:
--> [156](.../lib/python3.11/site-packages/fast_depends/use.py:156)         r = real_model.solve(
    [157](.../lib/python3.11/site-packages/fast_depends/use.py:157)             *args,
    [158](.../lib/python3.11/site-packages/fast_depends/use.py:158)             stack=stack,
    [159](.../lib/python3.11/site-packages/fast_depends/use.py:159)             dependency_overrides=overrides,
    [160](.../lib/python3.11/site-packages/fast_depends/use.py:160)             cache_dependencies={},
    [161](.../lib/python3.11/site-packages/fast_depends/use.py:161)             nested=False,
    [162](.../lib/python3.11/site-packages/fast_depends/use.py:162)             **kwargs,
    [163](.../lib/python3.11/site-packages/fast_depends/use.py:163)         )
    [164](.../lib/python3.11/site-packages/fast_depends/use.py:164)         return r
    [165](.../lib/python3.11/site-packages/fast_depends/use.py:165)     raise AssertionError("unreachable")

File [/lib/python3.11/site-packages/fast_depends/core/model.py:309](.../lib/python3.11/site-packages/fast_depends/core/model.py:309), in CallModel.solve(self__, stack, cache_dependencies, dependency_overrides, nested, *args, **kwargs)
    [300](.../lib/python3.11/site-packages/fast_depends/core/model.py:300)     kwargs[dep_arg] = dep.solve(
    [301](.../lib/python3.11/site-packages/fast_depends/core/model.py:301)         stack=stack,
    [302](.../lib/python3.11/site-packages/fast_depends/core/model.py:302)         cache_dependencies=cache_dependencies,
   (...)
    [305](.../lib/python3.11/site-packages/fast_depends/core/model.py:305)         **kwargs,
    [306](.../lib/python3.11/site-packages/fast_depends/core/model.py:306)     )
    [308](.../lib/python3.11/site-packages/fast_depends/core/model.py:308) for custom in self__.custom_fields.values():
--> [309](.../lib/python3.11/site-packages/fast_depends/core/model.py:309)     kwargs = custom.use(**kwargs)
    [311](.../lib/python3.11/site-packages/fast_depends/core/model.py:311) final_args, final_kwargs = cast_gen.send(kwargs)
    [313](.../lib/python3.11/site-packages/fast_depends/core/model.py:313) if self__.is_generator and nested:

TypeError: TestProvider.use() got multiple values for argument 'self'
Lancetnik commented 7 months ago

I'll take a look, what I can do with it, but for now you should rename your class 'self' argument to smth else to not conflict with original 'self'. I used 'self__' inside lib

Lancetnik commented 7 months ago

@chrisgoddard I found a solution, but now you should use CustomField in a new way (to support classes injection):

class Header(CustomField):
    def use(
        self,
        /,  # <- new thing
        **kwargs: AnyDict
    ) -> AnyDict:
        kwargs = super().use(**kwargs)
        kwargs[self.param_name] = kwargs["headers"][self.param_name]
        return kwargs

Old syntax works fine too, but doesn't support class case. All documentation was updated as well.

chrisgoddard commented 7 months ago

I've seen that positional-only syntax before and never really knew what it was for - so good to know! Thanks for responding quickly on this.