From 8e7aa61b3e337d69b893303b66041a3aec4d1845 Mon Sep 17 00:00:00 2001 From: Lucas Colombo Date: Sat, 11 Nov 2023 03:19:54 -0300 Subject: [PATCH] fix: initialized class attribute being overridden changed the "ignore attributes" logic so that if a class variable has already been initialized externally, rodi doesn't attempt to reinitialize it (and to also prevent overriding it if the initialized class variable is also a registered object). --- rodi/__init__.py | 9 +++++---- tests/test_services.py | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/rodi/__init__.py b/rodi/__init__.py index 11c35b7..d5947c9 100644 --- a/rodi/__init__.py +++ b/rodi/__init__.py @@ -572,11 +572,12 @@ def _ignore_class_attribute(self, key: str, value) -> bool: """ Returns a value indicating whether a class attribute should be ignored for dependency resolution, by name and value. + It's ignored if it's a ClassVar or if it's already initialized explicitly. """ - try: - return value.__origin__ is ClassVar - except AttributeError: - return False + is_classvar = getattr(value, "__origin__", None) is ClassVar + is_initialized = getattr(self.concrete_type, key, None) is not None + + return is_classvar or is_initialized def _resolve_by_annotations( self, context: ResolutionContext, annotations: Dict[str, Type] diff --git a/tests/test_services.py b/tests/test_services.py index b9bc1aa..9430813 100644 --- a/tests/test_services.py +++ b/tests/test_services.py @@ -2591,3 +2591,40 @@ class Bar2: # check that singletons are always the same instance assert bar.foo is bar2.foo is foo + + +def test_ignore_class_variable_if_already_initialized(): + """ + if a class variable is already initialized, it should not be overridden by + resolving a new instance nor fail if rodi can't resolve it. + """ + + foo_instance = Foo() + + class A: + foo: Foo = foo_instance + + class B: + example: ClassVar[str] = "example" + dependency: A + + container = Container() + + container.register(A) + container.register(B) + container._add_exact_singleton(Foo) + + b = container.resolve(B) + a = container.resolve(A) + foo = container.resolve(Foo) + + assert isinstance(a, A) + assert isinstance(a.foo, Foo) + assert foo_instance is a.foo + + assert isinstance(b, B) + assert b.example == "example" + assert b.dependency.foo is foo_instance + + # check that is not being overridden by resolving a new instance + assert foo is not a.foo