@@ -120,15 +120,24 @@ def __str__(self) -> str:
120120 return f"`{ self .module } ` has propagated an event: { self .origin } "
121121
122122 @property
123- def history (self ) -> Iterator [ Event ] :
124- if isinstance ( self . event , ModuleEventProxy ):
125- yield from self .event .history
126-
127- yield self . event
123+ def is_duplicate (self ) -> bool :
124+ return any (
125+ self . module is event . module and self .origin is event .origin
126+ for event in self . proxy_history
127+ )
128128
129129 @property
130130 def origin (self ) -> Event :
131- return next (self .history )
131+ reversed_proxy_history = reversed (tuple (self .proxy_history ))
132+ return next (reversed_proxy_history , self ).event
133+
134+ @property
135+ def proxy_history (self ) -> Iterator [ModuleEventProxy ]:
136+ event = self .event
137+
138+ if isinstance (event , ModuleEventProxy ):
139+ yield event
140+ yield from event .proxy_history
132141
133142
134143@dataclass (frozen = True , slots = True )
@@ -162,8 +171,10 @@ def __str__(self) -> str:
162171
163172@dataclass (frozen = True , slots = True )
164173class UnlockCalled (Event ):
174+ module : Module
175+
165176 def __str__ (self ) -> str :
166- return "An ` unlock` method has been called."
177+ return f"` { self . module } . unlock` has been called."
167178
168179
169180"""
@@ -420,23 +431,18 @@ def __post_init__(self) -> None:
420431 self .__locator .add_listener (self )
421432
422433 def __getitem__ [T ](self , cls : InputType [T ], / ) -> Injectable [T ]:
423- for broker in self .__brokers :
434+ for broker in self ._iter_brokers () :
424435 with suppress (KeyError ):
425436 return broker [cls ]
426437
427438 raise NoInjectable (cls )
428439
429440 def __contains__ (self , cls : InputType [Any ], / ) -> bool :
430- return any (cls in broker for broker in self .__brokers )
441+ return any (cls in broker for broker in self ._iter_brokers () )
431442
432443 @property
433444 def is_locked (self ) -> bool :
434- return any (broker .is_locked for broker in self .__brokers )
435-
436- @property
437- def __brokers (self ) -> Iterator [Broker ]:
438- yield from self .__modules
439- yield self .__locator
445+ return any (broker .is_locked for broker in self ._iter_brokers ())
440446
441447 def injectable [** P , T ](
442448 self ,
@@ -857,19 +863,19 @@ def change_priority(self, module: Module, priority: Priority | PriorityStr) -> S
857863 return self
858864
859865 def unlock (self ) -> Self :
860- event = UnlockCalled ()
866+ event = UnlockCalled (self )
861867
862868 with self .dispatch (event , lock_bypass = True ):
863869 self .unsafe_unlocking ()
864870
865871 return self
866872
867873 def unsafe_unlocking (self ) -> None :
868- for broker in self .__brokers :
874+ for broker in self ._iter_brokers () :
869875 broker .unsafe_unlocking ()
870876
871877 async def all_ready (self ) -> None :
872- for broker in self .__brokers :
878+ for broker in self ._iter_brokers () :
873879 await broker .all_ready ()
874880
875881 def add_logger (self , logger : Logger ) -> Self :
@@ -884,8 +890,12 @@ def remove_listener(self, listener: EventListener) -> Self:
884890 self .__channel .remove_listener (listener )
885891 return self
886892
887- def on_event (self , event : Event , / ) -> ContextManager [None ]:
893+ def on_event (self , event : Event , / ) -> ContextManager [None ] | None :
888894 self_event = ModuleEventProxy (self , event )
895+
896+ if self_event .is_duplicate :
897+ return None
898+
889899 return self .dispatch (self_event )
890900
891901 @contextmanager
@@ -899,6 +909,20 @@ def dispatch(self, event: Event, *, lock_bypass: bool = False) -> Iterator[None]
899909 finally :
900910 self .__debug (event )
901911
912+ def _iter_brokers (self , visited : set [Module ] | None = None , / ) -> Iterator [Broker ]:
913+ if visited is None :
914+ visited = set ()
915+
916+ if self in visited :
917+ return
918+
919+ visited .add (self )
920+
921+ for module in self .__modules :
922+ yield from module ._iter_brokers (visited )
923+
924+ yield self .__locator
925+
902926 def __debug (self , message : object ) -> None :
903927 for logger in self .__loggers :
904928 logger .debug (message )
0 commit comments