Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add explicit Pingee connect and disconnect methods #255

Merged
merged 4 commits into from
Nov 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions traits_futures/message_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,15 @@ def connect(self):
"""
Prepare router for routing.
"""
pass
self._pingee = Pingee(on_ping=self._route_message)
self._pingee.connect()

def disconnect(self):
"""
Undo any connections made by the ``connect`` call.
"""
pass
self._pingee.disconnect()
self._pingee = None

# Private traits ##########################################################

Expand Down Expand Up @@ -110,6 +112,3 @@ def __message_queue_default(self):

def __connection_ids_default(self):
return itertools.count()

def __pingee_default(self):
return Pingee(on_ping=self._route_message)
29 changes: 24 additions & 5 deletions traits_futures/null/pinger.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,26 +22,45 @@ class Pingee:
"""
Receiver for pings.

Whenever a ping is received from a linked Pingee, the receiver
calls the given fixed parameterless callable.

The ping receiver must be connected (using the ``connect``) method
before use, and should call ``disconnect`` when it's no longer
expected to receive pings.

Parameters
----------
on_ping : callable
Zero-argument callable that's executed on the main thread as a
result of each ping.
Zero-argument callable that's called on the main thread
every time a ping is received.
"""

def __init__(self, on_ping):
self._event_loop = asyncio.get_event_loop()
self._on_ping = on_ping

def connect(self):
"""
Prepare Pingee to receive pings.
"""
self._event_loop = asyncio.get_event_loop()

def disconnect(self):
"""
Undo any connections made in the connect method.
"""
del self._event_loop
kitchoi marked this conversation as resolved.
Show resolved Hide resolved


class Pinger:
"""
Ping emitter, which can emit pings in a thread-safe manner.
Ping emitter, which can send pings to a receiver in a thread-safe manner.

Parameters
----------
pingee : Pingee
The corresponding ping receiver.
The target receiver for the pings. The receiver must already be
connected.
"""

def __init__(self, pingee):
Expand Down
28 changes: 24 additions & 4 deletions traits_futures/qt/pinger.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,18 @@ class Pingee(QObject):
"""
Receiver for pings.

Whenever a ping is received from a linked Pingee, the receiver
calls the given fixed parameterless callable.

The ping receiver must be connected (using the ``connect``) method
before use, and should call ``disconnect`` when it's no longer
expected to receive pings.

Parameters
----------
on_ping : callable
Zero-argument callable that's executed on the main thread as a
result of each ping.
Zero-argument callable that's called on the main thread
every time a ping is received.
"""

def __init__(self, on_ping):
Expand All @@ -45,15 +52,28 @@ def __init__(self, on_ping):
def _execute_ping_callback(self):
self._on_ping()

def connect(self):
"""
Prepare Pingee to receive pings.
"""
pass

def disconnect(self):
"""
Undo any connections made in the connect method.
"""
pass


class Pinger:
"""
Ping emitter, which can emit pings in a thread-safe manner.
Ping emitter, which can send pings to a receiver in a thread-safe manner.

Parameters
----------
pingee : Pingee
The corresponding ping receiver.
The target receiver for the pings. The receiver must already be
connected.
"""

def __init__(self, pingee):
Expand Down
45 changes: 28 additions & 17 deletions traits_futures/tests/test_pinger.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,14 +102,24 @@ class PingListener(HasStrictTraits):
#: Total number of pings received.
ping_count = Int(0)

def __enter__(self):
self.connect()
return self

def __exit__(self, *exc_info):
self.disconnect()

def connect(self):
self.pingee = Pingee(on_ping=lambda: setattr(self, "ping", True))
self.pingee.connect()

def disconnect(self):
self.pingee.disconnect()
self.pingee = None

def _ping_fired(self):
self.ping_count += 1

def _pingee_default(self):
return Pingee(
on_ping=lambda: setattr(self, "ping", True),
)


class MultipleListeners(HasStrictTraits):
"""
Expand Down Expand Up @@ -137,8 +147,10 @@ class TestPinger(GuiTestAssistant, unittest.TestCase):
def setUp(self):
GuiTestAssistant.setUp(self)
self.listener = PingListener()
self.listener.connect()

def tearDown(self):
self.listener.disconnect()
del self.listener
GuiTestAssistant.tearDown(self)

Expand Down Expand Up @@ -178,18 +190,17 @@ def test_multiple_background_pingers(self):
self.assertEqual(self.listener.ping_count, 15)

def test_multiple_pingees(self):
listener1 = PingListener()
listener2 = PingListener()
listeners = MultipleListeners(listeners=[listener1, listener2])

with BackgroundPinger(listener1.pingee) as pinger1:
with BackgroundPinger(listener2.pingee) as pinger2:
pinger1.ping(3)
pinger2.ping(4)

self.run_until(
listeners, "ping", lambda obj: obj.ping_count >= 7
)
with PingListener() as listener1:
with PingListener() as listener2:
listeners = MultipleListeners(listeners=[listener1, listener2])
with BackgroundPinger(listener1.pingee) as pinger1:
with BackgroundPinger(listener2.pingee) as pinger2:
pinger1.ping(3)
pinger2.ping(4)

self.run_until(
listeners, "ping", lambda obj: obj.ping_count >= 7
)

self.assertEqual(listener1.ping_count, 3)
self.assertEqual(listener2.ping_count, 4)
Expand Down
27 changes: 20 additions & 7 deletions traits_futures/wx/pinger.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,13 @@

class Pinger:
"""
Ping emitter, which can emit pings in a thread-safe manner.
Ping emitter, which can send pings to a receiver in a thread-safe manner.

Parameters
----------
pingee : Pingee
The corresponding ping receiver.
The target receiver for the pings. The receiver must already be
connected.
"""

def __init__(self, pingee):
Expand Down Expand Up @@ -60,6 +61,13 @@ class Pingee(wx.EvtHandler):
"""
Receiver for pings.

Whenever a ping is received from a linked Pingee, the receiver
calls the given fixed parameterless callable.

The ping receiver must be connected (using the ``connect``) method
before use, and should call ``disconnect`` when it's no longer
expected to receive pings.

Parameters
----------
on_ping : callable
Expand All @@ -69,11 +77,16 @@ class Pingee(wx.EvtHandler):

def __init__(self, on_ping):
wx.EvtHandler.__init__(self)
self._on_ping = on_ping
self.Bind(_PingEventBinder, self._on_ping_event)
self._on_ping = lambda event: on_ping()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels like an orthogonal change - changing self._on_pingand replacingself._on_ping_eventwithself._on_pingin theself.Bind` call. I'm not sure why this is needed.

Is this just refactoring/making the code cleaner?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, yes, it's mostly orthogonal, except that with this change I can be sure that I'm binding and unbinding the exact same handler. (That would probably have been true with the method lookups too, assuming that wx is working with Python equality rather than identity, but with this change I don't have to make that assumption.) Call it superstition again, but this PR is mostly superstition anyway (which is what makes the changes hard to test).


def _on_ping_event(self, event):
def connect(self):
"""
Prepare Pingee to receive pings.
"""
self.Bind(_PingEventBinder, handler=self._on_ping)

def disconnect(self):
"""
Handler for events of type _PING_EVENT_TYPE.
Undo any connections made in the connect method.
"""
self._on_ping()
self.Unbind(_PingEventBinder, handler=self._on_ping)