Skip to content

Commit

Permalink
BREAKING_CHANGE: disallow using a dependency with multiple scopes (#61)
Browse files Browse the repository at this point in the history
  • Loading branch information
adriangb authored Feb 6, 2022
1 parent c988f5c commit b658443
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 15 deletions.
8 changes: 7 additions & 1 deletion di/container.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
from di.api.providers import DependencyProvider
from di.api.scopes import Scope
from di.api.solved import SolvedDependant
from di.exceptions import WiringError
from di.exceptions import SolvingError, WiringError

__all__ = ("BaseContainer", "Container")

Expand Down Expand Up @@ -217,6 +217,12 @@ def get_params(
seen.add(dep)
cache_key = dep.cache_key
if cache_key in dependants:
other = dependants[cache_key]
if other.scope != dep.scope:
raise SolvingError(
f"The dependency {dep.call} is used with multiple scopes"
f" ({dep.scope} and {other.scope}); this is not allowed."
)
continue
dependants[cache_key] = dep
params = get_params(dep)
Expand Down
2 changes: 1 addition & 1 deletion di/dependant.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ def __init__(
def cache_key(self) -> CacheKey:
if self.use_cache is False or self.call is None:
return (self.__class__, id(self))
return (self.__class__, self.call, self.scope)
return (self.__class__, self.call)

def register_parameter(self, param: inspect.Parameter) -> DependantBase[Any]:
"""Hook to register the parameter this Dependant corresponds to.
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "di"
version = "0.54.2"
version = "0.55.0"
description = "Autowiring dependency injection"
authors = ["Adrian Garcia Badaracco <[email protected]>"]
readme = "README.md"
Expand Down
1 change: 0 additions & 1 deletion tests/test_dependant.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ class DependantSubclass(Dependant[T]):
),
(Dependant(func), DependantSubclass(func), False, False),
(Dependant(func), Dependant(other_func), False, False),
(Dependant(func, scope=0), Dependant(func, scope=1), False, False),
(Dependant(None), Dependant(None), False, False),
],
)
Expand Down
41 changes: 30 additions & 11 deletions tests/test_solving.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@
from di import Container, Dependant, SyncExecutor
from di.api.dependencies import DependencyParameter
from di.dependant import JoinedDependant
from di.exceptions import ScopeViolationError, UnknownScopeError, WiringError
from di.exceptions import (
ScopeViolationError,
SolvingError,
UnknownScopeError,
WiringError,
)
from di.typing import Annotated


Expand Down Expand Up @@ -67,23 +72,18 @@ def B(a: Annotated[None, Dependant(A, scope="inner")]):


def test_dependency_with_multiple_scopes():

values = iter(range(2))

def A() -> int:
return next(values)
def A() -> None:
...

def B(
a1: Annotated[None, Dependant(A, scope="app")],
a2: Annotated[None, Dependant(A, scope="request")],
) -> None:
assert a1 != a2
...

container = Container(scopes=("app", "request"))
solved = container.solve(Dependant(B, scope="request"))
with container.enter_scope("app"):
with container.enter_scope("request"):
container.execute_sync(solved, executor=SyncExecutor())
with pytest.raises(SolvingError, match="used with multiple scopes"):
container.solve(Dependant(B, scope="request"))


def test_siblings() -> None:
Expand Down Expand Up @@ -191,3 +191,22 @@ def bad_dep(v: Annotated[int, Dependant(lambda: 1, scope="app")]) -> int:
container = Container()
with pytest.raises(UnknownScopeError):
container.solve(Dependant(bad_dep))


def test_re_used_dependant() -> None:
def dep1() -> None:
...

Dep1 = Annotated[None, Dependant(dep1)]

def dep2(one: Dep1) -> None:
...

def dep3(
one: Dep1,
two: Annotated[None, Dependant(dep2)],
) -> None:
...

container = Container(scopes=(None,))
container.solve(Dependant(dep3, scope=None))

0 comments on commit b658443

Please sign in to comment.