From f5aa2a0d5dbba038b1c9576212e0d8df7d4cc0d8 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Mon, 9 Aug 2021 08:59:30 +0100 Subject: [PATCH 1/4] Prep for allowing listener to be fully created by the ListenerParser --- traits/has_traits.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/traits/has_traits.py b/traits/has_traits.py index b7453ff7d..431136814 100644 --- a/traits/has_traits.py +++ b/traits/has_traits.py @@ -2658,11 +2658,14 @@ def on_trait_change( if wrapper.equals(handler): break else: + # The listener notify wrapper needs a reference to the + # listener, and the listener needs a (weak) reference to the + # wrapper. We first construct the wrapper with a listener of + # `None`, then construct the listener with its reference to the + # wrapper, then we replace the `None` listener with the correct + # one. + lnw = ListenerNotifyWrapper(handler, self, name, None, target) listener = ListenerParser(name).listener - lnw = ListenerNotifyWrapper( - handler, self, name, listener, target - ) - listeners.append(lnw) listener.trait_set( handler=ListenerHandler(handler), wrapped_handler_ref=weakref.ref(lnw), @@ -2671,7 +2674,9 @@ def on_trait_change( priority=priority, deferred=deferred, ) + lnw.listener = listener listener.register(self) + listeners.append(lnw) # A synonym for 'on_trait_change' on_trait_event = on_trait_change From ddc72da60a49549ca4afba048420e7c183228448 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Mon, 9 Aug 2021 09:48:00 +0100 Subject: [PATCH 2/4] Set handler directly on all ListenerItems --- traits/has_traits.py | 6 ++++-- traits/traits_listener.py | 34 +++++++++++++--------------------- 2 files changed, 17 insertions(+), 23 deletions(-) diff --git a/traits/has_traits.py b/traits/has_traits.py index 431136814..eda0790c4 100644 --- a/traits/has_traits.py +++ b/traits/has_traits.py @@ -2665,9 +2665,11 @@ def on_trait_change( # wrapper, then we replace the `None` listener with the correct # one. lnw = ListenerNotifyWrapper(handler, self, name, None, target) - listener = ListenerParser(name).listener - listener.trait_set( + listener = ListenerParser( + name, handler=ListenerHandler(handler), + ).listener + listener.trait_set( wrapped_handler_ref=weakref.ref(lnw), type=lnw.type, dispatch=dispatch, diff --git a/traits/traits_listener.py b/traits/traits_listener.py index 7c81494d8..4b0998fe7 100644 --- a/traits/traits_listener.py +++ b/traits/traits_listener.py @@ -126,9 +126,6 @@ def not_event(value): class ListenerBase(HasPrivateTraits): - # The handler to be called when any listened to trait is changed: - # handler = Any - # The dispatch mechanism to use when invoking the handler: # dispatch = Str @@ -498,12 +495,6 @@ def handle_error(self, obj, name, old, new): # -- Event Handlers ------------------------------------------------------- - def _handler_changed(self, handler): - """ Handles the **handler** trait being changed. - """ - if self.next is not None: - self.next.handler = handler - def _wrapped_handler_ref_changed(self, wrapped_handler_ref): """ Handles the 'wrapped_handler_ref' trait being changed. """ @@ -861,9 +852,6 @@ def _get_value(self, name): class ListenerGroup(ListenerBase): - #: The handler to be called when any listened-to trait is changed - handler = Property - #: A weakref 'wrapped' version of 'handler': wrapped_handler_ref = Property @@ -893,12 +881,6 @@ class ListenerGroup(ListenerBase): # -- Property Implementations --------------------------------------------- - def _set_handler(self, handler): - if self._handler is None: - self._handler = handler - for item in self.items: - item.handler = handler - def _set_wrapped_handler_ref(self, wrapped_handler_ref): if self._wrapped_handler_ref is None: self._wrapped_handler_ref = wrapped_handler_ref @@ -993,7 +975,7 @@ def name(self): # -- object Method Overrides ---------------------------------------------- - def __init__(self, text): + def __init__(self, text, handler=None): #: The text being parsed. self.text = text @@ -1003,6 +985,9 @@ def __init__(self, text): #: The current parse index within the string. self.index = 0 + #: The handler to be called when any listened-to trait is changed. + self.handler = handler + #: The parsed listener. self.listener = self.parse() @@ -1027,7 +1012,11 @@ def parse(self): return ListenerItem( name=match.group(1), notify=match.group(2) == ".", - next=ListenerItem(name=match.group(3)), + next=ListenerItem( + name=match.group(3), + handler=self.handler, + ), + handler=self.handler, ) return self.parse_group(EOS) @@ -1066,7 +1055,10 @@ def parse_item(self, terminator): if name != "": c = self.next - result = ListenerItem(name=name) + result = ListenerItem( + name=name, + handler=self.handler, + ) if c in "+-": result.name += "*" From 39f792e6a9dcab5b39dccdb9b6c6f075c2e87cd8 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Mon, 9 Aug 2021 09:57:39 +0100 Subject: [PATCH 3/4] Set wrapped handler directly on all ListenerItems --- traits/has_traits.py | 2 +- traits/traits_listener.py | 23 +++++++---------------- 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/traits/has_traits.py b/traits/has_traits.py index eda0790c4..2a0711458 100644 --- a/traits/has_traits.py +++ b/traits/has_traits.py @@ -2668,9 +2668,9 @@ def on_trait_change( listener = ListenerParser( name, handler=ListenerHandler(handler), + wrapped_handler_ref=weakref.ref(lnw), ).listener listener.trait_set( - wrapped_handler_ref=weakref.ref(lnw), type=lnw.type, dispatch=dispatch, priority=priority, diff --git a/traits/traits_listener.py b/traits/traits_listener.py index 4b0998fe7..3c729aadb 100644 --- a/traits/traits_listener.py +++ b/traits/traits_listener.py @@ -495,12 +495,6 @@ def handle_error(self, obj, name, old, new): # -- Event Handlers ------------------------------------------------------- - def _wrapped_handler_ref_changed(self, wrapped_handler_ref): - """ Handles the 'wrapped_handler_ref' trait being changed. - """ - if self.next is not None: - self.next.wrapped_handler_ref = wrapped_handler_ref - def _dispatch_changed(self, dispatch): """ Handles the **dispatch** trait being changed. """ @@ -852,9 +846,6 @@ def _get_value(self, name): class ListenerGroup(ListenerBase): - #: A weakref 'wrapped' version of 'handler': - wrapped_handler_ref = Property - #: The dispatch mechanism to use when invoking the handler: dispatch = Property @@ -881,12 +872,6 @@ class ListenerGroup(ListenerBase): # -- Property Implementations --------------------------------------------- - def _set_wrapped_handler_ref(self, wrapped_handler_ref): - if self._wrapped_handler_ref is None: - self._wrapped_handler_ref = wrapped_handler_ref - for item in self.items: - item.wrapped_handler_ref = wrapped_handler_ref - def _set_dispatch(self, dispatch): if self._dispatch is None: self._dispatch = dispatch @@ -975,7 +960,7 @@ def name(self): # -- object Method Overrides ---------------------------------------------- - def __init__(self, text, handler=None): + def __init__(self, text, handler=None, wrapped_handler_ref=None): #: The text being parsed. self.text = text @@ -988,6 +973,9 @@ def __init__(self, text, handler=None): #: The handler to be called when any listened-to trait is changed. self.handler = handler + #: A weakref 'wrapped' version of 'handler': + self.wrapped_handler_ref = wrapped_handler_ref + #: The parsed listener. self.listener = self.parse() @@ -1015,8 +1003,10 @@ def parse(self): next=ListenerItem( name=match.group(3), handler=self.handler, + wrapped_handler_ref=self.wrapped_handler_ref, ), handler=self.handler, + wrapped_handler_ref=self.wrapped_handler_ref, ) return self.parse_group(EOS) @@ -1058,6 +1048,7 @@ def parse_item(self, terminator): result = ListenerItem( name=name, handler=self.handler, + wrapped_handler_ref=self.wrapped_handler_ref, ) if c in "+-": From 4a7498f5d166d8589e2dc7f208cb9bbba0bfd7f0 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Mon, 9 Aug 2021 10:06:35 +0100 Subject: [PATCH 4/4] Set dispatch type directly on all ListenerItems --- traits/has_traits.py | 2 +- traits/traits_listener.py | 32 ++++++++++---------------------- 2 files changed, 11 insertions(+), 23 deletions(-) diff --git a/traits/has_traits.py b/traits/has_traits.py index 2a0711458..2cd9d6094 100644 --- a/traits/has_traits.py +++ b/traits/has_traits.py @@ -2669,10 +2669,10 @@ def on_trait_change( name, handler=ListenerHandler(handler), wrapped_handler_ref=weakref.ref(lnw), + dispatch=dispatch, ).listener listener.trait_set( type=lnw.type, - dispatch=dispatch, priority=priority, deferred=deferred, ) diff --git a/traits/traits_listener.py b/traits/traits_listener.py index 3c729aadb..96bcd4593 100644 --- a/traits/traits_listener.py +++ b/traits/traits_listener.py @@ -126,9 +126,6 @@ def not_event(value): class ListenerBase(HasPrivateTraits): - # The dispatch mechanism to use when invoking the handler: - # dispatch = Str - # Does the handler go at the beginning (True) or end (False) of the # notification handlers list? # priority = Bool( False ) @@ -495,12 +492,6 @@ def handle_error(self, obj, name, old, new): # -- Event Handlers ------------------------------------------------------- - def _dispatch_changed(self, dispatch): - """ Handles the **dispatch** trait being changed. - """ - if self.next is not None: - self.next.dispatch = dispatch - def _priority_changed(self, priority): """ Handles the **priority** trait being changed. """ @@ -846,9 +837,6 @@ def _get_value(self, name): class ListenerGroup(ListenerBase): - #: The dispatch mechanism to use when invoking the handler: - dispatch = Property - #: Does the handler go at the beginning (True) or end (False) of the #: notification handlers list? priority = ListProperty @@ -870,14 +858,6 @@ class ListenerGroup(ListenerBase): # The list of ListenerBase objects in the group items = List(ListenerBase) - # -- Property Implementations --------------------------------------------- - - def _set_dispatch(self, dispatch): - if self._dispatch is None: - self._dispatch = dispatch - for item in self.items: - item.dispatch = dispatch - # -- 'ListenerBase' Class Method Implementations -------------------------- def __repr__(self, seen=None): @@ -960,7 +940,9 @@ def name(self): # -- object Method Overrides ---------------------------------------------- - def __init__(self, text, handler=None, wrapped_handler_ref=None): + def __init__( + self, text, handler=None, wrapped_handler_ref=None, dispatch="" + ): #: The text being parsed. self.text = text @@ -973,9 +955,12 @@ def __init__(self, text, handler=None, wrapped_handler_ref=None): #: The handler to be called when any listened-to trait is changed. self.handler = handler - #: A weakref 'wrapped' version of 'handler': + #: A weakref 'wrapped' version of 'handler'. self.wrapped_handler_ref = wrapped_handler_ref + #: The dispatch mechanism to use when invoking the handler. + self.dispatch = dispatch + #: The parsed listener. self.listener = self.parse() @@ -1004,9 +989,11 @@ def parse(self): name=match.group(3), handler=self.handler, wrapped_handler_ref=self.wrapped_handler_ref, + dispatch=self.dispatch, ), handler=self.handler, wrapped_handler_ref=self.wrapped_handler_ref, + dispatch=self.dispatch, ) return self.parse_group(EOS) @@ -1049,6 +1036,7 @@ def parse_item(self, terminator): name=name, handler=self.handler, wrapped_handler_ref=self.wrapped_handler_ref, + dispatch=self.dispatch, ) if c in "+-":