Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 20 additions & 10 deletions injection/core/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,12 @@

from injection.common.event import Event, EventChannel, EventListener
from injection.common.lazy import LazyMapping
from injection.exceptions import ModuleError, NoInjectable
from injection.exceptions import (
ModuleCircularUseError,
ModuleError,
ModuleNotUsedError,
NoInjectable,
)

__all__ = ("Injectable", "Module", "ModulePriorities")

Expand Down Expand Up @@ -73,24 +78,29 @@ class ModuleEvent(Event, ABC):
on_module: Module


@dataclass(frozen=True)
@dataclass(frozen=True, slots=True)
class ModuleEventProxy(ModuleEvent):
event: Event

def __str__(self) -> str:
return f"`{self.on_module}` has propagated an event: {self.origin}"

@cached_property
def origin(self) -> Event:
def __iter__(self) -> Iterator[Event]:
if isinstance(self.event, ModuleEventProxy):
return self.event.origin
yield from self.event

return self.event
yield self.event

@property
def origin(self) -> Event:
return next(self.__iter__())

@property
def is_circular(self) -> bool:
origin = self.origin
return isinstance(origin, ModuleEvent) and origin.on_module is self.on_module
return any(
isinstance(event, ModuleEvent) and event.on_module is self.on_module
for event in self
)

@property
def previous_module(self) -> Module | None:
Expand Down Expand Up @@ -381,7 +391,7 @@ def on_event(self, event: Event, /):
self_event = ModuleEventProxy(self, event)

if self_event.is_circular:
raise ModuleError(
raise ModuleCircularUseError(
"Circular dependency between two modules: "
f"`{self_event.previous_module}` and `{self}`."
)
Expand All @@ -399,7 +409,7 @@ def __move_module(self, module: Module, priority: ModulePriorities):
try:
self.__modules.move_to_end(module, last=last)
except KeyError as exc:
raise ModuleError(
raise ModuleNotUsedError(
f"`{module}` can't be found in the modules used by `{self}`."
) from exc

Expand Down
11 changes: 8 additions & 3 deletions injection/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
__all__ = ("InjectionError", "ModuleError", "NoInjectable")


class InjectionError(Exception):
...

Expand All @@ -11,3 +8,11 @@ class NoInjectable(KeyError, InjectionError):

class ModuleError(InjectionError):
...


class ModuleCircularUseError(ModuleError):
...


class ModuleNotUsedError(KeyError, ModuleError):
...
78 changes: 39 additions & 39 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 20 additions & 4 deletions tests/core/test_module.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from contextlib import suppress
from typing import Any

import pytest

from injection import Module, ModulePriorities
from injection.core import Injectable
from injection.exceptions import ModuleError
from injection.exceptions import ModuleCircularUseError, ModuleError, ModuleNotUsedError


class SomeClass:
Expand Down Expand Up @@ -86,13 +87,28 @@ def test_use_with_self_raise_module_error(self, module, event_history):

event_history.assert_length(0)

def test_use_with_circular_dependency_raise_module_error(self, module):
def test_use_with_circular_dependency_raise_module_circular_use_error(self, module):
second_module = Module()
module.use(second_module)

with pytest.raises(ModuleError):
with pytest.raises(ModuleCircularUseError):
second_module.use(module)

def test_use_with_deep_circular_dependency_raise_module_circular_use_error(
self,
module,
):
second_module = Module()
third_module = Module()

module.use(second_module)

with suppress(ModuleCircularUseError):
second_module.use(module)

with pytest.raises(ModuleCircularUseError):
module.use(third_module)

def test_use_with_module_already_in_use_raise_module_error(
self,
module,
Expand Down Expand Up @@ -178,5 +194,5 @@ def test_change_priority_with_success(self, module, event_history):
def test_change_priority_with_module_not_found(self, module, event_history):
second_module = Module()

with pytest.raises(ModuleError):
with pytest.raises(ModuleNotUsedError):
module.change_priority(second_module, ModulePriorities.HIGH)
2 changes: 0 additions & 2 deletions tests/helpers/event_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@

from injection.common.event import Event, EventListener

__all__ = ("EventHistory",)


@dataclass(repr=False, eq=False, frozen=True, slots=True)
class EventHistory(EventListener):
Expand Down