Skip to content

Commit

Permalink
Added type hints to HasTraits.observe() and friends. (#834
Browse files Browse the repository at this point in the history
* Added type hints to HasTraits.observe() and friends.
- unobserve()
- unobserve_all()
- parse_notifier_name()

* Fixed type hints to satisfy mypy:
- Added return type-hints.
- Added handler type-hint.
- Using t.Iterable instead of t.List, c.f. test_observe_iterables().
parse_notifier_name(): Added check in case names is unrecognized
Sentinel value (i.e. not `All`). Added type-name to TypeError message.
  • Loading branch information
scholer committed Feb 23, 2023
1 parent 1f4dc18 commit 58ee9fb
Showing 1 changed file with 23 additions and 11 deletions.
34 changes: 23 additions & 11 deletions traitlets/traitlets.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,9 @@ def is_trait(t):
return isinstance(t, TraitType) or (isinstance(t, type) and issubclass(t, TraitType))


def parse_notifier_name(names):
def parse_notifier_name(
names: t.Union[Sentinel, str, t.Iterable[t.Union[Sentinel, str]]]
) -> t.Iterable[t.Any]:
"""Convert the name argument to a list of names.
Examples
Expand All @@ -250,12 +252,14 @@ def parse_notifier_name(names):
"""
if names is All or isinstance(names, str):
return [names]
elif isinstance(names, Sentinel):
raise TypeError("`names` must be either `All`, a str, or a list of strs.")
else:
if not names or All in names:
return [All]
for n in names:
if not isinstance(n, str):
raise TypeError("names must be strings, not %r" % n)
raise TypeError(f"names must be strings, not {type(n).__name__}({n!r})")
return names


Expand Down Expand Up @@ -1625,7 +1629,12 @@ def on_trait_change(self, handler=None, name=None, remove=False):
else:
self.observe(_callback_wrapper(handler), names=name)

def observe(self, handler, names=All, type="change"):
def observe(
self,
handler: t.Callable[..., t.Any],
names: t.Union[Sentinel, str, t.Iterable[t.Union[Sentinel, str]]] = All,
type: t.Union[Sentinel, str] = "change",
) -> None:
"""Setup a handler to be called when a trait changes.
This is used to setup dynamic notifications of trait changes.
Expand All @@ -1651,11 +1660,15 @@ def observe(self, handler, names=All, type="change"):
The type of notification to filter by. If equal to All, then all
notifications are passed to the observe handler.
"""
names = parse_notifier_name(names)
for n in names:
self._add_notifiers(handler, n, type)
for name in parse_notifier_name(names):
self._add_notifiers(handler, name, type)

def unobserve(self, handler, names=All, type="change"):
def unobserve(
self,
handler: t.Callable[..., t.Any],
names: t.Union[Sentinel, str, t.Iterable[t.Union[Sentinel, str]]] = All,
type: t.Union[Sentinel, str] = "change",
) -> None:
"""Remove a trait change handler.
This is used to unregister handlers to trait change notifications.
Expand All @@ -1672,11 +1685,10 @@ def unobserve(self, handler, names=All, type="change"):
The type of notification to filter by. If All, the specified handler
is uninstalled from the list of notifiers corresponding to all types.
"""
names = parse_notifier_name(names)
for n in names:
self._remove_notifiers(handler, n, type)
for name in parse_notifier_name(names):
self._remove_notifiers(handler, name, type)

def unobserve_all(self, name=All):
def unobserve_all(self, name: t.Union[str, t.Any] = All) -> None:
"""Remove trait change handlers of any type for the specified name.
If name is not specified, removes all trait notifiers."""
if name is All:
Expand Down

0 comments on commit 58ee9fb

Please sign in to comment.