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
52 changes: 35 additions & 17 deletions injection/core/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,12 @@ def _format_type(cls: type) -> str:
"""


@dataclass(slots=True)
@dataclass(frozen=True, slots=True)
class ContainerEvent(Event, ABC):
on_container: Container


@dataclass(slots=True)
@dataclass(frozen=True, slots=True)
class ContainerDependenciesUpdated(ContainerEvent):
references: set[type]

Expand All @@ -68,43 +68,55 @@ def __str__(self) -> str:
)


@dataclass(slots=True)
@dataclass(frozen=True, slots=True)
class ModuleEvent(Event, ABC):
on_module: Module


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

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

@property
@cached_property
def origin(self) -> Event:
if isinstance(self.event, ModuleEventProxy):
return self.event.origin

return self.event

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

@property
def previous_module(self) -> Module | None:
if isinstance(self.event, ModuleEvent):
return self.event.on_module

@dataclass(slots=True)
return None


@dataclass(frozen=True, slots=True)
class ModuleAdded(ModuleEvent):
module_added: Module

def __str__(self) -> str:
return f"`{self.on_module}` now uses `{self.module_added}`."


@dataclass(slots=True)
@dataclass(frozen=True, slots=True)
class ModuleRemoved(ModuleEvent):
module_removed: Module

def __str__(self) -> str:
return f"`{self.on_module}` no longer uses `{self.module_removed}`."


@dataclass(slots=True)
@dataclass(frozen=True, slots=True)
class ModulePriorityUpdated(ModuleEvent):
module_updated: Module
priority: ModulePriorities
Expand Down Expand Up @@ -352,10 +364,9 @@ def change_priority(self, module: Module, priority: ModulePriorities):
* **HIGH**: The module concerned becomes the most important of the modules used.
"""

if self.__move_module(module, priority):
event = ModulePriorityUpdated(self, module, priority)
self.notify(event)

self.__move_module(module, priority)
event = ModulePriorityUpdated(self, module, priority)
self.notify(event)
return self

def add_listener(self, listener: EventListener):
Expand All @@ -368,22 +379,29 @@ def remove_listener(self, listener: EventListener):

def on_event(self, event: Event, /):
self_event = ModuleEventProxy(self, event)

if self_event.is_circular:
raise ModuleError(
"Circular dependency between two modules: "
f"`{self_event.previous_module}` and `{self}`."
)

self.notify(self_event)

def notify(self, event: Event):
_logger.debug(f"{event}")
self.__channel.dispatch(event)
return self

def __move_module(self, module: Module, priority: ModulePriorities) -> bool:
def __move_module(self, module: Module, priority: ModulePriorities):
last = priority == ModulePriorities.LOW

try:
self.__modules.move_to_end(module, last=last)
except KeyError:
return False

return True
except KeyError as exc:
raise ModuleError(
f"`{module}` can't be found in the modules used by `{self}`."
) from exc


"""
Expand Down
12 changes: 10 additions & 2 deletions tests/core/test_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,13 @@ 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):
second_module = Module()
module.use(second_module)

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

def test_use_with_module_already_in_use_raise_module_error(
self,
module,
Expand Down Expand Up @@ -170,5 +177,6 @@ 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()
module.change_priority(second_module, ModulePriorities.HIGH)
event_history.assert_length(0)

with pytest.raises(ModuleError):
module.change_priority(second_module, ModulePriorities.HIGH)