python-injector / python-injector/injector

@noninjectable seems to not work with dataclasses and auto_bind

Open
#169 2 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

The interaction between @dataclass, @noninjectable, and auto_bind=True seems to be broken. I would not expect the following to work, because foo shouldn't be injected due to the decorator,

from dataclasses import dataclass
from injector import Injector, inject

class Persistence:
    pass

@inject
@noninjectable('foo')
@dataclass
class Parent:
    p: Persistence
    foo: int

injector = Injector()
parent = injector.get(Parent)
print(type(parent))
print(parent.foo)

but it actually runs and produces

<class '__main__.Parent'>
0

The same implementation without @dataclass

from injector import Injector, inject

class Persistence:
    pass

class Parent:
    @inject
    @noninjectable('foo')
    def __init__(self, persistence: Persistence, foo: int):
        self.p = persistence
        self.foo = foo

injector = Injector()
parent = injector.get(Parent)
print(type(parent))
print(parent.foo)

throws an exception, as expected

Traceback (most recent call last):
  File "examples\injector_test.py", line 116, in <module>
    parent = injector.get(Parent)
  File "[USER_DIR]\AppData\Local\pypoetry\Cache\virtualenvs\[VENV]\lib\site-packages\injector\__init__.py", line 963, in get
    result = scope_instance.get(interface, binding.provider).get(self)
  File "[USER_DIR]\AppData\Local\pypoetry\Cache\virtualenvs\[VENV]\lib\site-packages\injector\__init__.py", line 291, in get
    return injector.create_object(self._cls)
  File "[USER_DIR]\AppData\Local\pypoetry\Cache\virtualenvs\[VENV]\lib\site-packages\injector\__init__.py", line 990, in create_object
    self.call_with_injection(cls.__init__, self_=instance, kwargs=additional_kwargs)
  File "[USER_DIR]\AppData\Local\pypoetry\Cache\virtualenvs\[VENV]\lib\site-packages\injector\__init__.py", line 1032, in call_with_injection
    reraise(e, CallError(self_, callable, args, dependencies, e, self._stack))
  File "[USER_DIR]\AppData\Local\pypoetry\Cache\virtualenvs\[VENV]\lib\site-packages\injector\__init__.py", line 211, in reraise
    raise exception.with_traceback(tb)
  File "[USER_DIR]\AppData\Local\pypoetry\Cache\virtualenvs\[VENV]\lib\site-packages\injector\__init__.py", line 1030, in call_with_injection
    return callable(*full_args, **dependencies)
injector.CallError: Call to Parent.__init__(persistence=<__main__.Persistence object at 0x0000023D47334940>) failed: __init__() missing 1 required positional argument: 'foo' (injection stack: [])

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 by running the dataclass reproduction and compare its path through Injector.get, create_object, and call_with_injection with the non-dataclass example. Check how Parent.init is inspected when @dataclass generates it and how @noninjectable('foo') is applied. Done means foo is not injected and the dataclass case behaves consistently with the explicit constructor case.

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
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.