From d078e04978aa898e86fca8c236ba6fa7c9b02d70 Mon Sep 17 00:00:00 2001 From: Zach Sailer Date: Thu, 14 Mar 2024 11:41:42 -0700 Subject: [PATCH 1/3] Enable adding listeners to event before the event is registered --- jupyter_events/logger.py | 29 ++++++++++++++++++++++------- tests/test_listeners.py | 36 +++++++++++++++++++++++++++++++++++- 2 files changed, 57 insertions(+), 8 deletions(-) diff --git a/jupyter_events/logger.py b/jupyter_events/logger.py index 79b4fba..96a3bf4 100644 --- a/jupyter_events/logger.py +++ b/jupyter_events/logger.py @@ -142,12 +142,16 @@ def register_event_schema(self, schema: SchemaType) -> None: Get this registered schema using the EventLogger.schema.get() method. """ - event_schema = self.schemas.register(schema) # type:ignore[arg-type] key = event_schema.id - self._modifiers[key] = set() - self._modified_listeners[key] = set() - self._unmodified_listeners[key] = set() + # It's possible that listeners and modifiers have been added for this + # schema before the schema is registered. + if key not in self._modifiers: + self._modifiers[key] = set() + if key not in self._modified_listeners: + self._modified_listeners[key] = set() + if key not in self._unmodified_listeners: + self._unmodified_listeners[key] = set() def register_handler(self, handler: logging.Handler) -> None: """Register a new logging handler to the Event Logger. @@ -205,7 +209,11 @@ def add_modifier( # If the schema ID and version is given, only add # this modifier to that schema if schema_id: - self._modifiers[schema_id].add(modifier) + # If the schema hasn't been added yet, + # start a placeholder set. + modifiers = self._modifiers.get(schema_id, set()) + modifiers.add(modifier) + self._modifiers[schema_id] = modifiers return for id_ in self._modifiers: if schema_id is None or id_ == schema_id: @@ -264,9 +272,16 @@ def add_listener( # this modifier to that schema if schema_id: if modified: - self._modified_listeners[schema_id].add(listener) + # If the schema hasn't been added yet, + # start a placeholder set. + listeners = self._modified_listeners.get(schema_id, set()) + listeners.add(listener) + self._modified_listeners[schema_id] = listeners return - self._unmodified_listeners[schema_id].add(listener) + listeners = self._unmodified_listeners.get(schema_id, set()) + listeners.add(listener) + self._unmodified_listeners[schema_id] = listeners + return for id_ in self.schemas.schema_ids: if schema_id is None or id_ == schema_id: if modified: diff --git a/tests/test_listeners.py b/tests/test_listeners.py index 2e167a1..97035cd 100644 --- a/tests/test_listeners.py +++ b/tests/test_listeners.py @@ -5,7 +5,7 @@ import pytest -from jupyter_events.logger import EventLogger +from jupyter_events.logger import EventLogger, SchemaNotRegistered from jupyter_events.schema import EventSchema from .utils import SCHEMA_PATH @@ -138,3 +138,37 @@ async def my_listener(logger: EventLogger, schema_id: str, data: dict) -> None: assert listener_was_called # Check that the active listeners are cleaned up. assert len(event_logger._active_listeners) == 0 + + +@pytest.mark.parametrize( + # Make sure no schemas are added at the start of this test. + "jp_event_schemas", ([],) +) +async def test_listener_added_before_schemas_passes(jp_event_logger, schema): + # Ensure there are no schemas listed. + assert len(jp_event_logger.schemas.schema_ids) == 0 + + listener_was_called = False + + async def my_listener(logger: EventLogger, schema_id: str, data: dict) -> None: + nonlocal listener_was_called + listener_was_called = True + + # Add the listener without any schemas + jp_event_logger.add_listener(schema_id=schema.id, listener=my_listener) + + # Proof that emitting the event won't success + with pytest.raises(SchemaNotRegistered): + jp_event_logger.emit(schema_id=schema.id, data={"prop": "hello, world"}) + + assert not listener_was_called + + # Now register the event and emit. + jp_event_logger.register_event_schema(schema) + + # Try emitting the event again and ensure the listener saw it. + jp_event_logger.emit(schema_id=schema.id, data={"prop": "hello, world"}) + await jp_event_logger.gather_listeners() + assert listener_was_called + # Check that the active listeners are cleaned up. + assert len(jp_event_logger._active_listeners) == 0 From 7bbacaa8f34b39b4aff739612155c9714d5b8927 Mon Sep 17 00:00:00 2001 From: Zach Sailer Date: Thu, 14 Mar 2024 11:55:16 -0700 Subject: [PATCH 2/3] pre-commit issues --- jupyter_events/logger.py | 10 +++++----- tests/test_listeners.py | 8 ++++++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/jupyter_events/logger.py b/jupyter_events/logger.py index 96a3bf4..63838e0 100644 --- a/jupyter_events/logger.py +++ b/jupyter_events/logger.py @@ -144,7 +144,7 @@ def register_event_schema(self, schema: SchemaType) -> None: """ event_schema = self.schemas.register(schema) # type:ignore[arg-type] key = event_schema.id - # It's possible that listeners and modifiers have been added for this + # It's possible that listeners and modifiers have been added for this # schema before the schema is registered. if key not in self._modifiers: self._modifiers[key] = set() @@ -209,7 +209,7 @@ def add_modifier( # If the schema ID and version is given, only add # this modifier to that schema if schema_id: - # If the schema hasn't been added yet, + # If the schema hasn't been added yet, # start a placeholder set. modifiers = self._modifiers.get(schema_id, set()) modifiers.add(modifier) @@ -272,15 +272,15 @@ def add_listener( # this modifier to that schema if schema_id: if modified: - # If the schema hasn't been added yet, + # If the schema hasn't been added yet, # start a placeholder set. listeners = self._modified_listeners.get(schema_id, set()) listeners.add(listener) - self._modified_listeners[schema_id] = listeners + self._modified_listeners[schema_id] = listeners return listeners = self._unmodified_listeners.get(schema_id, set()) listeners.add(listener) - self._unmodified_listeners[schema_id] = listeners + self._unmodified_listeners[schema_id] = listeners return for id_ in self.schemas.schema_ids: if schema_id is None or id_ == schema_id: diff --git a/tests/test_listeners.py b/tests/test_listeners.py index 97035cd..3651fbe 100644 --- a/tests/test_listeners.py +++ b/tests/test_listeners.py @@ -142,7 +142,11 @@ async def my_listener(logger: EventLogger, schema_id: str, data: dict) -> None: @pytest.mark.parametrize( # Make sure no schemas are added at the start of this test. - "jp_event_schemas", ([],) + "jp_event_schemas", + [ + # Empty events list. + [] + ], ) async def test_listener_added_before_schemas_passes(jp_event_logger, schema): # Ensure there are no schemas listed. @@ -164,7 +168,7 @@ async def my_listener(logger: EventLogger, schema_id: str, data: dict) -> None: assert not listener_was_called # Now register the event and emit. - jp_event_logger.register_event_schema(schema) + jp_event_logger.register_event_schema(schema) # Try emitting the event again and ensure the listener saw it. jp_event_logger.emit(schema_id=schema.id, data={"prop": "hello, world"}) From 3fbc24628c76a47fbf069edbd988e363078b9663 Mon Sep 17 00:00:00 2001 From: Zach Sailer Date: Fri, 15 Mar 2024 05:22:51 -0700 Subject: [PATCH 3/3] use pytest.warns when schema is not registered --- tests/test_listeners.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_listeners.py b/tests/test_listeners.py index 3651fbe..684a51b 100644 --- a/tests/test_listeners.py +++ b/tests/test_listeners.py @@ -162,7 +162,7 @@ async def my_listener(logger: EventLogger, schema_id: str, data: dict) -> None: jp_event_logger.add_listener(schema_id=schema.id, listener=my_listener) # Proof that emitting the event won't success - with pytest.raises(SchemaNotRegistered): + with pytest.warns(SchemaNotRegistered): jp_event_logger.emit(schema_id=schema.id, data={"prop": "hello, world"}) assert not listener_was_called