Skip to content

Commit a4469e7

Browse files
committed
Add check to check if slots is defined.
Slots prevents the operation of Event and Callback descriptors, this change adds a check for presents of __slots__ and raises an error
1 parent ed6a958 commit a4469e7

File tree

3 files changed

+48
-4
lines changed

3 files changed

+48
-4
lines changed

pyapp/events.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,19 @@
22
Events
33
~~~~~~
44
5-
A simple framework publish events or callbacks from a class to subscribed listener(s).
5+
A simple framework to publish events or callbacks to subscribed listener(s).
66
77
Async events/callbacks are also supported via the `AsyncEvent` and `AsyncCallback`
88
descriptors.
99
10-
Event can have multiple listening functions where callbacks can only have a single
11-
function bound (assigning a second will remove the previous).
10+
An event can have multiple listening functions where as callbacks can only have
11+
a single function bound (assigning a second will remove the previous binding).
12+
13+
.. note::
14+
15+
Event and Callback descriptors do not work when ``__slots__`` are defined.
16+
If slots are defined a ``InstanceHasNoDictError`` will be raised on access
17+
1218
1319
Example::
1420
@@ -49,8 +55,9 @@ async def on_new_message(message: str):
4955
from typing import TypeVar
5056
from typing import Union
5157

52-
__all__ = ("Event", "AsyncEvent", "listen_to", "Callback", "AsyncCallback", "bind_to")
58+
from pyapp.exceptions import UnsupportedObject
5359

60+
__all__ = ("Event", "AsyncEvent", "listen_to", "Callback", "AsyncCallback", "bind_to")
5461

5562
_CT = TypeVar("_CT")
5663

@@ -148,6 +155,10 @@ def __get__(self, instance, owner) -> ListenerSet[_CT]:
148155
return listeners
149156

150157
def __set_name__(self, owner, name):
158+
if hasattr(owner, "__slots__"):
159+
raise UnsupportedObject(
160+
"An Event cannot be used on an object with __slots__ defined."
161+
)
151162
self.name = name # pylint: disable=attribute-defined-outside-init
152163

153164

@@ -260,6 +271,10 @@ def __get__(self, instance, owner) -> CallbackBinding[_CT]:
260271
return wrapper
261272

262273
def __set_name__(self, owner, name):
274+
if hasattr(owner, "__slots__"):
275+
raise UnsupportedObject(
276+
"A Callback cannot be used on an object with __slots__ defined."
277+
)
263278
self.name = name # pylint: disable=attribute-defined-outside-init
264279

265280

pyapp/exceptions.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,15 @@ class UnsupportedContentType(Exception):
7171
"""
7272
Content type of the file is not supported
7373
"""
74+
75+
76+
class EventException(Exception):
77+
"""
78+
Exception caused by event/callback definition
79+
"""
80+
81+
82+
class UnsupportedObject(EventException, TypeError):
83+
"""
84+
Instance does not have a dict that descriptor can use to store callbacks
85+
"""

tests/test_events.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import pytest
55

66
from pyapp import events
7+
from pyapp.exceptions import UnsupportedObject
78

89

910
class TestListenerSet:
@@ -102,6 +103,14 @@ def on_target():
102103

103104
assert len(instance.target) == 1
104105

106+
def test__when_object_has_slots(self):
107+
with pytest.raises(RuntimeError):
108+
109+
class MyObject:
110+
__slots__ = ("foo",)
111+
112+
target = events.Event[Callable[[], None]]()
113+
105114

106115
class TestAsyncListenerSet:
107116
def test_call(self, event_loop):
@@ -197,6 +206,14 @@ def on_target():
197206
target = instance.target
198207
assert target._callback is on_target
199208

209+
def test__when_object_has_slots(self):
210+
with pytest.raises(RuntimeError):
211+
212+
class MyObject:
213+
__slots__ = ("foo",)
214+
215+
target = events.Callback[Callable[[], None]]()
216+
200217

201218
class TestAsyncCallbackBinding:
202219
def test_call__bound(self, event_loop):

0 commit comments

Comments
 (0)