python-injector / python-injector/injector

Nonresolvable Type Hint on Non-Injected Argument Causes Error

Open
#258 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
1.5k
Forks
94
PR merge metrics
No merged PRs in 30d

Description

Steps To Reproduce

test.py:

#!/usr/bin/env python3
import injector as _injector
import test2

@_injector.inject
@_injector.noninjectable('body')
def patch(body: test2.RecursiveDict) -> None:
    ...

injector = _injector.Injector()
injector.call_with_injection(patch, kwargs={"body": {}})

test2.py:

#!/usr/bin/env python3
import typing as _tp
RecursiveDict: _tp.TypeAlias = dict[str, _tp.Optional["RecursiveDict"]]

Run test.py

Expected Results

Call proceeds without error

Actual Results

Traceback (most recent call last):
  File "/usr/local/lib/python3.11/site-packages/injector/__init__.py", line 1213, in _infer_injected_bindings
    bindings = get_type_hints(cast(Callable, _NoReturnAnnotationProxy(callable)), include_extras=True)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/typing.py", line 2342, in get_type_hints
    hints[name] = _eval_type(value, globalns, localns)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/typing.py", line 373, in _eval_type
    ev_args = tuple(_eval_type(a, globalns, localns, recursive_guard) for a in t.__args__)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/typing.py", line 373, in <genexpr>
    ev_args = tuple(_eval_type(a, globalns, localns, recursive_guard) for a in t.__args__)
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/typing.py", line 373, in _eval_type
    ev_args = tuple(_eval_type(a, globalns, localns, recursive_guard) for a in t.__args__)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/typing.py", line 373, in <genexpr>
    ev_args = tuple(_eval_type(a, globalns, localns, recursive_guard) for a in t.__args__)
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/typing.py", line 359, in _eval_type
    return t._evaluate(globalns, localns, recursive_guard)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/typing.py", line 857, in _evaluate
    eval(self.__forward_code__, globalns, localns),
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<string>", line 1, in <module>
NameError: name 'RecursiveDict' is not defined

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/src/app/test.py", line 11, in <module>
    injector.call_with_injection(patch, kwargs={"body": {}})
  File "/usr/local/lib/python3.11/site-packages/injector/__init__.py", line 1020, in call_with_injection
    bindings = get_bindings(callable)
               ^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/injector/__init__.py", line 1173, in get_bindings
    callable, _infer_injected_bindings(callable, only_explicit_bindings=look_for_explicit_bindings)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/injector/__init__.py", line 1215, in _infer_injected_bindings
    raise _BindingNotYetAvailable(e)
injector._BindingNotYetAvailable: name 'RecursiveDict' is not defined

Notes

The error raised in get_type_hints() is not Injector's fault; that is a bug in Python itself. However, the fact that Injector fails due to an issue with the type hint of a parameter I explicitly told it not to even try to inject is an Injector issue.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in injector/init.py at _infer_injected_bindings, especially the get_type_hints call shown in the traceback. Reproduce the case using test.py and test2.py, then add a regression test covering a noninjectable argument with the recursive type hint. Done means call_with_injection(patch, kwargs={"body": {}}) completes without the type-hint error.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
backend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.