Skip to content

Commit

Permalink
fix: initialized class attribute being overridden
Browse files Browse the repository at this point in the history
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).
  • Loading branch information
lucas-labs committed Nov 11, 2023
1 parent 0633a7d commit 8e7aa61
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 4 deletions.
9 changes: 5 additions & 4 deletions rodi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
37 changes: 37 additions & 0 deletions tests/test_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit 8e7aa61

Please sign in to comment.