Skip to content

Commit ccccef3

Browse files
authored
Merge pull request #3 from nocarryr/asyncio
asyncio support
2 parents 42bb750 + f9d29e2 commit ccccef3

File tree

13 files changed

+1174
-23
lines changed

13 files changed

+1174
-23
lines changed

.coveragerc-py2

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ exclude_lines =
77
pragma: no cover
88
if not PY2:
99
if AIO_AVAILABLE:
10+
def bind_async

.coveragerc-py34

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ exclude_lines =
77
pragma: no cover
88
if PY2:
99
if AIO_AVAILABLE:
10+
def bind_async

doc/source/async.rst

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
asyncio support
2+
===============
3+
4+
.. versionadded:: 0.1.0
5+
6+
Callbacks
7+
---------
8+
9+
Callbacks can also be :term:`coroutine functions <coroutine function>`
10+
(defined using :keyword:`async def` or decorated with
11+
:func:`@asyncio.coroutine <asyncio.coroutine>`). For this to work properly,
12+
the :class:`event loop <~asyncio.BaseEventLoop>` they belong to **must**
13+
be specified. This requirement is in place to prevent issues when multiple event
14+
loops are in use (via threads, etc).
15+
16+
The loop can be specified using the :any:`Dispatcher.bind_async` method,
17+
or passed as a keyword argument to :any:`Dispatcher.bind`.
18+
19+
Examples
20+
^^^^^^^^
21+
22+
bind_async method
23+
"""""""""""""""""
24+
25+
::
26+
27+
import asyncio
28+
from pydispatch import Dispatcher
29+
30+
class MyEmitter(Dispatcher):
31+
events = ['on_state']
32+
33+
class MyAsyncListener(object):
34+
def __init__(self):
35+
self.event_received = asyncio.Event()
36+
async def on_emitter_state(self, *args, **kwargs):
37+
self.event_received.set()
38+
39+
loop = asyncio.get_event_loop()
40+
emitter = MyEmitter()
41+
listener = MyAsyncListener()
42+
43+
# Pass the event loop as first argument to "bind_async"
44+
emitter.bind_async(loop, on_state=listener.on_emitter_state)
45+
46+
async def trigger_and_wait_for_event():
47+
await asyncio.sleep(1)
48+
49+
emitter.emit('on_state')
50+
51+
# listener.event_received should be set from "on_emitter_state"
52+
await listener.event_received.wait()
53+
54+
loop.run_until_complete(trigger_and_wait_for_event())
55+
56+
bind (with keyword argument)
57+
""""""""""""""""""""""""""""
58+
59+
::
60+
61+
import asyncio
62+
from pydispatch import Dispatcher
63+
64+
class MyEmitter(Dispatcher):
65+
events = ['on_state']
66+
67+
class MyAsyncListener(object):
68+
def __init__(self):
69+
self.event_received = asyncio.Event()
70+
async def on_emitter_state(self, *args, **kwargs):
71+
self.event_received.set()
72+
73+
loop = asyncio.get_event_loop()
74+
emitter = MyEmitter()
75+
listener = MyAsyncListener()
76+
77+
# Pass the event loop using __aio_loop__
78+
emitter.bind(on_state=listener.on_emitter_state, __aio_loop__=loop)
79+
80+
async def trigger_and_wait_for_event():
81+
await asyncio.sleep(1)
82+
83+
emitter.emit('on_state')
84+
85+
# listener.event_received should be set from "on_emitter_state"
86+
await listener.event_received.wait()
87+
88+
loop.run_until_complete(trigger_and_wait_for_event())
89+
90+
Async (awaitable) Events
91+
------------------------
92+
93+
Event (and :any:`Property`) objects are :term:`awaitable`. This allows event
94+
subscription without callbacks in an async environment. The :any:`Event` instance
95+
itself must first be obtained using the :any:`Dispatcher.get_dispatcher_event`
96+
method. Any positional and keyword arguments from the event are returned as a
97+
two-tuple::
98+
99+
async def wait_for_event(event_name):
100+
event = emitter.get_dispatcher_event(event_name)
101+
args, kwargs = await event
102+
return args, kwargs
103+
104+
loop.run_until_complete(wait_for_event('on_state'))
105+
106+
This can also be done with :any:`Property` objects::
107+
108+
import asyncio
109+
from pydispatch import Dispatcher, Property
110+
111+
112+
class MyEmitter(Dispatcher):
113+
value = Property()
114+
async def change_values(self):
115+
for i in range(10):
116+
await asyncio.sleep(.1)
117+
self.value = i
118+
119+
class MyAsyncListener(object):
120+
async def wait_for_value(self, emitter):
121+
event = emitter.get_dispatcher_event('value')
122+
while True:
123+
args, kwargs = await event
124+
instance, value = args
125+
print(value)
126+
if value >= 9:
127+
break
128+
129+
loop = asyncio.get_event_loop()
130+
emitter = MyEmitter()
131+
listener = MyAsyncListener()
132+
133+
# Make the emitter value iterate from 0-9
134+
asyncio.ensure_future(emitter.change_values())
135+
136+
# Listens for changes, then exits after value reaches 9
137+
loop.run_until_complete(listener.wait_for_value(emitter))

doc/source/overview.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ Overview
66

77
dispatcher
88
properties
9+
async

doc/source/reference/aioutils.rst

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
pydispatch.aioutils module
2+
==========================
3+
4+
.. automodule:: pydispatch.aioutils
5+
6+
AioWeakMethodContainer class
7+
----------------------------
8+
9+
.. autoclass:: pydispatch.aioutils.AioWeakMethodContainer
10+
:members:
11+
:show-inheritance:
12+
13+
AioEventWaiters class
14+
---------------------
15+
16+
.. autoclass:: pydispatch.aioutils.AioEventWaiters
17+
:members:
18+
19+
AioEventWaiter class
20+
--------------------
21+
22+
.. autoclass:: pydispatch.aioutils.AioEventWaiter
23+
:members:
24+
25+
AioSimpleLock class
26+
-------------------
27+
28+
.. autoclass:: pydispatch.aioutils.AioSimpleLock
29+
:members:

doc/source/reference/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ Reference
77
dispatch
88
properties
99
utils
10+
aioutils

0 commit comments

Comments
 (0)