python-dependency-injector icon indicating copy to clipboard operation
python-dependency-injector copied to clipboard

Making injections into class attributes can't work with `Resource` in nested container.

Open YogiLiu opened this issue 9 months ago • 2 comments

Reproducible project: https://github.com/YogiLiu/pdi_issue

When I run python -m pdi_issue.main, an error was raised:

Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "/home/yogiliu/Workspace/YogiLiu/pdi_issue/src/pdi_issue/main.py", line 4, in <module>
    container = SrvContainer()
                ^^^^^^^^^^^^^^
  File "src/dependency_injector/containers.pyx", line 727, in dependency_injector.containers.DeclarativeContainer.__new__
  File "src/dependency_injector/providers.pyx", line 4916, in dependency_injector.providers.deepcopy
  File "src/dependency_injector/providers.pyx", line 4923, in dependency_injector.providers.deepcopy
  File "/home/yogiliu/.local/share/uv/python/cpython-3.11.10-linux-x86_64-gnu/lib/python3.11/copy.py", line 146, in deepcopy
    y = copier(x, memo)
        ^^^^^^^^^^^^^^^
  File "/home/yogiliu/.local/share/uv/python/cpython-3.11.10-linux-x86_64-gnu/lib/python3.11/copy.py", line 231, in _deepcopy_dict
    y[deepcopy(key, memo)] = deepcopy(value, memo)
                             ^^^^^^^^^^^^^^^^^^^^^
  File "/home/yogiliu/.local/share/uv/python/cpython-3.11.10-linux-x86_64-gnu/lib/python3.11/copy.py", line 153, in deepcopy
    y = copier(memo)
        ^^^^^^^^^^^^
  File "src/dependency_injector/providers.pyx", line 4024, in dependency_injector.providers.Container.__deepcopy__
  File "src/dependency_injector/providers.pyx", line 4923, in dependency_injector.providers.deepcopy
  File "/home/yogiliu/.local/share/uv/python/cpython-3.11.10-linux-x86_64-gnu/lib/python3.11/copy.py", line 153, in deepcopy
    y = copier(memo)
        ^^^^^^^^^^^^
  File "src/dependency_injector/containers.pyx", line 125, in dependency_injector.containers.DynamicContainer.__deepcopy__
  File "src/dependency_injector/providers.pyx", line 4916, in dependency_injector.providers.deepcopy
  File "src/dependency_injector/providers.pyx", line 4923, in dependency_injector.providers.deepcopy
  File "/home/yogiliu/.local/share/uv/python/cpython-3.11.10-linux-x86_64-gnu/lib/python3.11/copy.py", line 146, in deepcopy
    y = copier(x, memo)
        ^^^^^^^^^^^^^^^
  File "/home/yogiliu/.local/share/uv/python/cpython-3.11.10-linux-x86_64-gnu/lib/python3.11/copy.py", line 231, in _deepcopy_dict
    y[deepcopy(key, memo)] = deepcopy(value, memo)
                             ^^^^^^^^^^^^^^^^^^^^^
  File "/home/yogiliu/.local/share/uv/python/cpython-3.11.10-linux-x86_64-gnu/lib/python3.11/copy.py", line 153, in deepcopy
    y = copier(memo)
        ^^^^^^^^^^^^
  File "src/dependency_injector/providers.pyx", line 3669, in dependency_injector.providers.Resource.__deepcopy__
dependency_injector.errors.Error: Can not copy initialized resource

YogiLiu avatar Mar 23 '25 12:03 YogiLiu

Thanks for the report. Nested containers and wiring is an area we can improve. No ETA when we'll work on it, but in the mean time, try doing so:

diff
diff --git a/src/pdi_issue/container.py b/src/pdi_issue/container.py
index e4485ec..47f75ef 100644
--- a/src/pdi_issue/container.py
+++ b/src/pdi_issue/container.py
@@ -6,8 +6,6 @@ def create_engine():
 
 
 class RepoContainer(containers.DeclarativeContainer):
-    wiring_config = containers.WiringConfiguration(modules=['.repo'])
-
     engine = providers.Resource(create_engine)
     # engine = providers.Factory(lambda: 'database engine')
 
@@ -15,4 +13,6 @@ class RepoContainer(containers.DeclarativeContainer):
 
 
 class SrvContainer(containers.DeclarativeContainer):
+    wiring_config = containers.WiringConfiguration(modules=['.repo'])
+
     repo = providers.Container(RepoContainer)
diff --git a/src/pdi_issue/repo.py b/src/pdi_issue/repo.py
index e34a497..e78df19 100644
--- a/src/pdi_issue/repo.py
+++ b/src/pdi_issue/repo.py
@@ -2,7 +2,7 @@ from dependency_injector.wiring import Provide
 
 
 class UserRepo:
-    engine = Provide['engine']
+    engine = Provide['repo.engine']
 
     def do_something(self):
         print(self.engine)

ZipFile avatar Mar 23 '25 13:03 ZipFile

Thanks for reply.

To be honest, there are many solutions for this case, like injecting as params of __init__ method. So, I think this is a quite special case. It must be great if it's supported.

Thanks for your contributions.

YogiLiu avatar Mar 23 '25 13:03 YogiLiu