Skip to content

Commit

Permalink
Add DoesNotExistError and ExistsError
Browse files Browse the repository at this point in the history
Raised in `register_event`, `bind` and `emit`
for pre-existing / non-existing events
  • Loading branch information
nocarryr authored May 30, 2023
1 parent 222591f commit 1d4a410
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 3 deletions.
16 changes: 16 additions & 0 deletions doc/source/reference/dispatch.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,19 @@ Event class

.. autoclass:: pydispatch.dispatch.Event
:members:


Exceptions
----------

.. autoclass:: pydispatch.dispatch.DoesNotExistError
:members:

.. autoclass:: pydispatch.dispatch.ExistsError
:members:

.. autoclass:: pydispatch.dispatch.EventExistsError
:members:

.. autoclass:: pydispatch.dispatch.PropertyExistsError
:members:
79 changes: 76 additions & 3 deletions pydispatch/dispatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,47 @@



class DoesNotExistError(KeyError):
"""Raised when binding to an :class:`Event` or :class:`~.properties.Property`
that does not exist
.. versionadded:: 0.2.2
"""
def __init__(self, name):
self.name = name

def __str__(self):
return f'Event "{self.name}" not registered'


class ExistsError(RuntimeError):
"""Raised when registering an event name that already exists
as either a normal :class:`Event` or :class:`~.properies.Property`
.. versionadded:: 0.2.2
"""
def __init__(self, name):
self.name = name

def __str__(self):
return f'"{self.name}" already exists'

class EventExistsError(ExistsError):
"""Raised when registering an event name that already exists
as an :class:`Event`
.. versionadded:: 0.2.2
"""


class PropertyExistsError(ExistsError):
"""Raised when registering an event name that already exists
as a :class:`~.properies.Property`
.. versionadded:: 0.2.2
"""


class Event(object):
"""Holds references to event names and subscribed listeners
Expand Down Expand Up @@ -108,10 +149,20 @@ def register_event(self, *names):
Args:
*names (str): Name or names of the events to register
Raises:
EventExistsError: If an event with the given name already exists
PropertyExistsError: If a property with the given name already exists
.. versionchanged:: 0.2.2
:class:`ExistsError` exceptions are raised when attempting to
register an event or property that already exists
"""
for name in names:
if name in self.__events:
continue
raise EventExistsError(name)
elif name in self.__property_events:
raise PropertyExistsError(name)
self.__events[name] = Event(name)
def bind(self, **kwargs):
"""Subscribes to events or to :class:`~pydispatch.properties.Property` updates
Expand Down Expand Up @@ -164,6 +215,14 @@ class Foo(Dispatcher):
This can also be done using :meth:`bind_async`.
Raises:
DoesNotExistError: If attempting to bind to an event or
property that has not been registered
.. versionchanged:: 0.2.2
:class:`DoesNotExistError` is now raised when binding to
non-existent events or properties
.. versionadded:: 0.1.0
"""
Expand All @@ -174,7 +233,10 @@ class Foo(Dispatcher):
if name in props:
e = props[name]
else:
e = events[name]
try:
e = events[name]
except KeyError:
raise DoesNotExistError(name)
e.add_listener(cb, __aio_loop__=aio_loop)
def unbind(self, *args):
"""Unsubscribes from events or :class:`~pydispatch.properties.Property` updates
Expand Down Expand Up @@ -224,10 +286,21 @@ def emit(self, name, *args, **kwargs):
name (str): The name of the :class:`Event` to dispatch
*args (Optional): Positional arguments to be sent to listeners
**kwargs (Optional): Keyword arguments to be sent to listeners
Raises:
DoesNotExistError: If attempting to emit an event or
property that has not been registered
.. versionchanged:: 0.2.2
:class:`DoesNotExistError` is now raised if the event or property
does not exist
"""
e = self.__property_events.get(name)
if e is None:
e = self.__events[name]
try:
e = self.__events[name]
except KeyError:
raise DoesNotExistError(name)
return e(*args, **kwargs)
def get_dispatcher_event(self, name):
"""Retrieves an Event object by name
Expand Down
42 changes: 42 additions & 0 deletions tests/test_events.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import pytest

def test_basic(listener, sender):
sender.register_event('on_test_a')
Expand Down Expand Up @@ -179,3 +180,44 @@ def test_emission_lock(listener, sender):
sender.emit('on_test', 'inner')
assert len(listener.received_event_data) == 1
assert listener.received_event_data[0]['args'] == ('inner', )


def test_bind_and_emit_unregistered():
from pydispatch import Dispatcher, DoesNotExistError

class Sender(Dispatcher):
pass

def callback(*args, **kwargs):
pass

sender = Sender()
with pytest.raises(DoesNotExistError) as excinfo:
sender.bind(foo=callback)
assert '"foo"' in str(excinfo.value)

with pytest.raises(DoesNotExistError) as excinfo:
sender.emit('foo')
assert '"foo"' in str(excinfo.value)

def test_register_existing_event():
from pydispatch import Dispatcher, EventExistsError

class Sender(Dispatcher):
_events_ = ['on_foo']

sender = Sender()
with pytest.raises(EventExistsError) as excinfo:
sender.register_event('on_foo')
assert '"on_foo"' in str(excinfo.value)

def test_register_existing_property():
from pydispatch import Dispatcher, Property, PropertyExistsError

class Sender(Dispatcher):
foo = Property()

sender = Sender()
with pytest.raises(PropertyExistsError) as excinfo:
sender.register_event('foo')
assert '"foo"' in str(excinfo.value)

0 comments on commit 1d4a410

Please sign in to comment.