Skip to content

reverse: Bidirectional links between modules create inconsistent behavior#298

Merged
remimd merged 1 commit intodevfrom
bi
Nov 24, 2025
Merged

reverse: Bidirectional links between modules create inconsistent behavior#298
remimd merged 1 commit intodevfrom
bi

Conversation

@remimd
Copy link
Copy Markdown
Member

@remimd remimd commented Nov 24, 2025

No description provided.

@remimd remimd self-assigned this Nov 24, 2025
@remimd remimd requested a review from Copilot November 24, 2025 18:23
@remimd remimd merged commit 2d699e4 into dev Nov 24, 2025
10 checks passed
@remimd remimd deleted the bi branch November 24, 2025 18:24
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses inconsistent behavior caused by bidirectional module dependencies by removing support for such relationships. The core change is to the module initialization order in ProfileLoader, which prevents modules from being marked as initialized before their subsets are fully processed, thereby avoiding the scenario where one module uses another before it's fully initialized. However, the PR removes cycle detection mechanisms without adding validation to prevent cycles from being created, which introduces potential infinite recursion issues.

Key Changes

  • Modified ProfileLoader.__init_subsets_for to mark modules as initialized after (rather than before) processing their subsets, preventing inconsistent state with bidirectional dependencies
  • Removed duplicate event detection from ModuleEventProxy and simplified Module.on_event to always return a context manager
  • Removed cycle detection from Module._iter_brokers, simplifying the iteration logic
  • Removed tests that verified bidirectional module relationships worked correctly

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
tests/loaders/test_profile_loader.py Removed test for bidirectional profile loading to align with no longer supporting bidirectional dependencies
tests/core/test_module.py Removed test for bidirectional module usage to align with no longer supporting circular module relationships
injection/loaders.py Changed initialization order to mark modules as initialized after subset initialization, preventing inconsistent state but introducing potential for infinite recursion with circular dependencies
injection/_core/module.py Removed cycle detection from broker iteration and event duplicate checking, simplifying code but creating infinite recursion risk if bidirectional relationships are created

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread injection/_core/module.py
Comment on lines +883 to +885
def _iter_brokers(self) -> Iterator[Broker]:
for module in self.__modules:
yield from module._iter_brokers(visited)
yield from module._iter_brokers()
Copy link

Copilot AI Nov 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing cycle detection from _iter_brokers() introduces a risk of infinite recursion. If bidirectional module relationships exist (module A uses module B, and module B uses module A), this method will infinitely recurse when called by unsafe_unlocking() or all_ready().

Consider adding validation in the use() method to detect and prevent cycles when modules are added, or restore the cycle detection mechanism here.

Copilot uses AI. Check for mistakes.
Comment thread injection/loaders.py
Comment on lines 177 to +184
def __init_subsets_for(self, module: Module) -> Module:
if not self.__is_empty and not self.__is_initialized(module):
self.__mark_initialized(module)
target_modules = tuple(
self.__init_subsets_for(mod(name))
for name in self.module_subsets.get(module.name, ())
)
module.init_modules(*target_modules)
self.__mark_initialized(module)
Copy link

Copilot AI Nov 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moving __mark_initialized(module) after module.init_modules() prevents the inconsistent behavior that occurred when bidirectional dependencies existed (where module B would use module A before A's initialization was complete). However, this change means that circular dependencies in the module_subsets configuration will now cause infinite recursion instead of producing inconsistent state.

While failing fast is better than inconsistent behavior, it would be more user-friendly to detect circular dependencies explicitly and raise a clear error message (e.g., CircularDependencyError) rather than allowing a stack overflow. Consider adding cycle detection that raises a descriptive error when a circular dependency is detected.

Copilot uses AI. Check for mistakes.
Comment thread injection/_core/module.py
Comment on lines +868 to 870
def on_event(self, event: Event, /) -> ContextManager[None]:
self_event = ModuleEventProxy(self, event)

if self_event.is_duplicate:
return None

return self.dispatch(self_event)
Copy link

Copilot AI Nov 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The removal of the duplicate event check changes the behavior of event propagation. Previously, is_duplicate prevented the same event from being processed multiple times in circular module relationships. Now, without this check, if modules have bidirectional relationships, events could propagate indefinitely in a loop.

Since the PR removes support for bidirectional module relationships, this change is only safe if validation is added elsewhere to prevent such relationships from being created. Without that validation, this could lead to infinite event propagation.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants