Skip to content

Commit 1d4a410

Browse files
authored
Add DoesNotExistError and ExistsError
Raised in `register_event`, `bind` and `emit` for pre-existing / non-existing events
1 parent 222591f commit 1d4a410

File tree

3 files changed

+134
-3
lines changed

3 files changed

+134
-3
lines changed

doc/source/reference/dispatch.rst

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,19 @@ Event class
1414

1515
.. autoclass:: pydispatch.dispatch.Event
1616
:members:
17+
18+
19+
Exceptions
20+
----------
21+
22+
.. autoclass:: pydispatch.dispatch.DoesNotExistError
23+
:members:
24+
25+
.. autoclass:: pydispatch.dispatch.ExistsError
26+
:members:
27+
28+
.. autoclass:: pydispatch.dispatch.EventExistsError
29+
:members:
30+
31+
.. autoclass:: pydispatch.dispatch.PropertyExistsError
32+
:members:

pydispatch/dispatch.py

Lines changed: 76 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,47 @@
1111

1212

1313

14+
class DoesNotExistError(KeyError):
15+
"""Raised when binding to an :class:`Event` or :class:`~.properties.Property`
16+
that does not exist
17+
18+
.. versionadded:: 0.2.2
19+
"""
20+
def __init__(self, name):
21+
self.name = name
22+
23+
def __str__(self):
24+
return f'Event "{self.name}" not registered'
25+
26+
27+
class ExistsError(RuntimeError):
28+
"""Raised when registering an event name that already exists
29+
as either a normal :class:`Event` or :class:`~.properies.Property`
30+
31+
.. versionadded:: 0.2.2
32+
"""
33+
def __init__(self, name):
34+
self.name = name
35+
36+
def __str__(self):
37+
return f'"{self.name}" already exists'
38+
39+
class EventExistsError(ExistsError):
40+
"""Raised when registering an event name that already exists
41+
as an :class:`Event`
42+
43+
.. versionadded:: 0.2.2
44+
"""
45+
46+
47+
class PropertyExistsError(ExistsError):
48+
"""Raised when registering an event name that already exists
49+
as a :class:`~.properies.Property`
50+
51+
.. versionadded:: 0.2.2
52+
"""
53+
54+
1455
class Event(object):
1556
"""Holds references to event names and subscribed listeners
1657
@@ -108,10 +149,20 @@ def register_event(self, *names):
108149
109150
Args:
110151
*names (str): Name or names of the events to register
152+
153+
Raises:
154+
EventExistsError: If an event with the given name already exists
155+
PropertyExistsError: If a property with the given name already exists
156+
157+
.. versionchanged:: 0.2.2
158+
:class:`ExistsError` exceptions are raised when attempting to
159+
register an event or property that already exists
111160
"""
112161
for name in names:
113162
if name in self.__events:
114-
continue
163+
raise EventExistsError(name)
164+
elif name in self.__property_events:
165+
raise PropertyExistsError(name)
115166
self.__events[name] = Event(name)
116167
def bind(self, **kwargs):
117168
"""Subscribes to events or to :class:`~pydispatch.properties.Property` updates
@@ -164,6 +215,14 @@ class Foo(Dispatcher):
164215
165216
This can also be done using :meth:`bind_async`.
166217
218+
Raises:
219+
DoesNotExistError: If attempting to bind to an event or
220+
property that has not been registered
221+
222+
.. versionchanged:: 0.2.2
223+
:class:`DoesNotExistError` is now raised when binding to
224+
non-existent events or properties
225+
167226
.. versionadded:: 0.1.0
168227
169228
"""
@@ -174,7 +233,10 @@ class Foo(Dispatcher):
174233
if name in props:
175234
e = props[name]
176235
else:
177-
e = events[name]
236+
try:
237+
e = events[name]
238+
except KeyError:
239+
raise DoesNotExistError(name)
178240
e.add_listener(cb, __aio_loop__=aio_loop)
179241
def unbind(self, *args):
180242
"""Unsubscribes from events or :class:`~pydispatch.properties.Property` updates
@@ -224,10 +286,21 @@ def emit(self, name, *args, **kwargs):
224286
name (str): The name of the :class:`Event` to dispatch
225287
*args (Optional): Positional arguments to be sent to listeners
226288
**kwargs (Optional): Keyword arguments to be sent to listeners
289+
290+
Raises:
291+
DoesNotExistError: If attempting to emit an event or
292+
property that has not been registered
293+
294+
.. versionchanged:: 0.2.2
295+
:class:`DoesNotExistError` is now raised if the event or property
296+
does not exist
227297
"""
228298
e = self.__property_events.get(name)
229299
if e is None:
230-
e = self.__events[name]
300+
try:
301+
e = self.__events[name]
302+
except KeyError:
303+
raise DoesNotExistError(name)
231304
return e(*args, **kwargs)
232305
def get_dispatcher_event(self, name):
233306
"""Retrieves an Event object by name

tests/test_events.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import pytest
12

23
def test_basic(listener, sender):
34
sender.register_event('on_test_a')
@@ -179,3 +180,44 @@ def test_emission_lock(listener, sender):
179180
sender.emit('on_test', 'inner')
180181
assert len(listener.received_event_data) == 1
181182
assert listener.received_event_data[0]['args'] == ('inner', )
183+
184+
185+
def test_bind_and_emit_unregistered():
186+
from pydispatch import Dispatcher, DoesNotExistError
187+
188+
class Sender(Dispatcher):
189+
pass
190+
191+
def callback(*args, **kwargs):
192+
pass
193+
194+
sender = Sender()
195+
with pytest.raises(DoesNotExistError) as excinfo:
196+
sender.bind(foo=callback)
197+
assert '"foo"' in str(excinfo.value)
198+
199+
with pytest.raises(DoesNotExistError) as excinfo:
200+
sender.emit('foo')
201+
assert '"foo"' in str(excinfo.value)
202+
203+
def test_register_existing_event():
204+
from pydispatch import Dispatcher, EventExistsError
205+
206+
class Sender(Dispatcher):
207+
_events_ = ['on_foo']
208+
209+
sender = Sender()
210+
with pytest.raises(EventExistsError) as excinfo:
211+
sender.register_event('on_foo')
212+
assert '"on_foo"' in str(excinfo.value)
213+
214+
def test_register_existing_property():
215+
from pydispatch import Dispatcher, Property, PropertyExistsError
216+
217+
class Sender(Dispatcher):
218+
foo = Property()
219+
220+
sender = Sender()
221+
with pytest.raises(PropertyExistsError) as excinfo:
222+
sender.register_event('foo')
223+
assert '"foo"' in str(excinfo.value)

0 commit comments

Comments
 (0)