Skip to content

Commit 33aafaa

Browse files
authored
Merge pull request #21 from nocarryr/exists-errors
Add `DoesNotExistError` and `ExistsError`
2 parents 222591f + c463814 commit 33aafaa

File tree

4 files changed

+166
-8
lines changed

4 files changed

+166
-8
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/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,5 @@
1515
'After version 0.1.x, `python-dispatch` will only support Python 3.6 or greater.',
1616
UserWarning)
1717

18-
from pydispatch.dispatch import Dispatcher, Event
18+
from pydispatch.dispatch import *
1919
from pydispatch.properties import *

pydispatch/dispatch.py

Lines changed: 99 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,51 @@
99
import asyncio
1010
from pydispatch.aioutils import AioWeakMethodContainer, AioEventWaiters
1111

12+
__all__ = (
13+
'DoesNotExistError', 'ExistsError', 'EventExistsError',
14+
'PropertyExistsError', 'Event', 'Dispatcher',
15+
)
16+
17+
18+
class DoesNotExistError(KeyError):
19+
"""Raised when binding to an :class:`Event` or :class:`~.properties.Property`
20+
that does not exist
21+
22+
.. versionadded:: 0.2.2
23+
"""
24+
def __init__(self, name):
25+
self.name = name
26+
27+
def __str__(self):
28+
return f'Event "{self.name}" not registered'
29+
30+
31+
class ExistsError(RuntimeError):
32+
"""Raised when registering an event name that already exists
33+
as either a normal :class:`Event` or :class:`~.properies.Property`
34+
35+
.. versionadded:: 0.2.2
36+
"""
37+
def __init__(self, name):
38+
self.name = name
39+
40+
def __str__(self):
41+
return f'"{self.name}" already exists'
42+
43+
class EventExistsError(ExistsError):
44+
"""Raised when registering an event name that already exists
45+
as an :class:`Event`
46+
47+
.. versionadded:: 0.2.2
48+
"""
49+
50+
51+
class PropertyExistsError(ExistsError):
52+
"""Raised when registering an event name that already exists
53+
as a :class:`~.properties.Property`
54+
55+
.. versionadded:: 0.2.2
56+
"""
1257

1358

1459
class Event(object):
@@ -108,10 +153,20 @@ def register_event(self, *names):
108153
109154
Args:
110155
*names (str): Name or names of the events to register
156+
157+
Raises:
158+
EventExistsError: If an event with the given name already exists
159+
PropertyExistsError: If a property with the given name already exists
160+
161+
.. versionchanged:: 0.2.2
162+
:class:`ExistsError` exceptions are raised when attempting to
163+
register an event or property that already exists
111164
"""
112165
for name in names:
113166
if name in self.__events:
114-
continue
167+
raise EventExistsError(name)
168+
elif name in self.__property_events:
169+
raise PropertyExistsError(name)
115170
self.__events[name] = Event(name)
116171
def bind(self, **kwargs):
117172
"""Subscribes to events or to :class:`~pydispatch.properties.Property` updates
@@ -164,6 +219,14 @@ class Foo(Dispatcher):
164219
165220
This can also be done using :meth:`bind_async`.
166221
222+
Raises:
223+
DoesNotExistError: If attempting to bind to an event or
224+
property that has not been registered
225+
226+
.. versionchanged:: 0.2.2
227+
:class:`DoesNotExistError` is now raised when binding to
228+
non-existent events or properties
229+
167230
.. versionadded:: 0.1.0
168231
169232
"""
@@ -174,7 +237,10 @@ class Foo(Dispatcher):
174237
if name in props:
175238
e = props[name]
176239
else:
177-
e = events[name]
240+
try:
241+
e = events[name]
242+
except KeyError:
243+
raise DoesNotExistError(name)
178244
e.add_listener(cb, __aio_loop__=aio_loop)
179245
def unbind(self, *args):
180246
"""Unsubscribes from events or :class:`~pydispatch.properties.Property` updates
@@ -224,10 +290,21 @@ def emit(self, name, *args, **kwargs):
224290
name (str): The name of the :class:`Event` to dispatch
225291
*args (Optional): Positional arguments to be sent to listeners
226292
**kwargs (Optional): Keyword arguments to be sent to listeners
293+
294+
Raises:
295+
DoesNotExistError: If attempting to emit an event or
296+
property that has not been registered
297+
298+
.. versionchanged:: 0.2.2
299+
:class:`DoesNotExistError` is now raised if the event or property
300+
does not exist
227301
"""
228302
e = self.__property_events.get(name)
229303
if e is None:
230-
e = self.__events[name]
304+
try:
305+
e = self.__events[name]
306+
except KeyError:
307+
raise DoesNotExistError(name)
231308
return e(*args, **kwargs)
232309
def get_dispatcher_event(self, name):
233310
"""Retrieves an Event object by name
@@ -239,11 +316,21 @@ def get_dispatcher_event(self, name):
239316
Returns:
240317
The :class:`Event` instance for the event or property definition
241318
319+
Raises:
320+
DoesNotExistError: If no event or property with the given name exists
321+
322+
.. versionchanged:: 0.2.2
323+
:class:`DoesNotExistError` is now raised if the event or property
324+
does not exist
325+
242326
.. versionadded:: 0.1.0
243327
"""
244328
e = self.__property_events.get(name)
245329
if e is None:
246-
e = self.__events[name]
330+
try:
331+
e = self.__events[name]
332+
except KeyError:
333+
raise DoesNotExistError(name)
247334
return e
248335
def emission_lock(self, name):
249336
"""Holds emission of events and dispatches the last event on release
@@ -279,9 +366,14 @@ def emission_lock(self, name):
279366
The context manager is re-entrant, meaning that multiple calls to
280367
this method within nested context scopes are possible.
281368
369+
Raises:
370+
DoesNotExistError: If no event or property with the given name exists
371+
372+
.. versionchanged:: 0.2.2
373+
:class:`DoesNotExistError` is now raised if the event or property
374+
does not exist
375+
282376
.. _PEP 492: https://www.python.org/dev/peps/pep-0492/#asynchronous-context-managers-and-async-with
283377
"""
284-
e = self.__property_events.get(name)
285-
if e is None:
286-
e = self.__events[name]
378+
e = self.get_dispatcher_event(name)
287379
return e.emission_lock

tests/test_events.py

Lines changed: 50 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,52 @@ 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+
with pytest.raises(DoesNotExistError) as excinfo:
204+
e = sender.get_dispatcher_event('foo')
205+
assert '"foo"' in str(excinfo.value)
206+
207+
with pytest.raises(DoesNotExistError) as excinfo:
208+
lock = sender.emission_lock('foo')
209+
assert '"foo"' in str(excinfo.value)
210+
211+
def test_register_existing_event():
212+
from pydispatch import Dispatcher, EventExistsError
213+
214+
class Sender(Dispatcher):
215+
_events_ = ['on_foo']
216+
217+
sender = Sender()
218+
with pytest.raises(EventExistsError) as excinfo:
219+
sender.register_event('on_foo')
220+
assert '"on_foo"' in str(excinfo.value)
221+
222+
def test_register_existing_property():
223+
from pydispatch import Dispatcher, Property, PropertyExistsError
224+
225+
class Sender(Dispatcher):
226+
foo = Property()
227+
228+
sender = Sender()
229+
with pytest.raises(PropertyExistsError) as excinfo:
230+
sender.register_event('foo')
231+
assert '"foo"' in str(excinfo.value)

0 commit comments

Comments
 (0)