Skip to content

Commit a0ee068

Browse files
committed
Slightly refactor the last two changes
- Split the asyncio and 'regular' io changes. - Reformat docstrings and remove trailing whitespace. - Rename _need_write() back to need_write().
1 parent 27dc2c3 commit a0ee068

3 files changed

Lines changed: 110 additions & 93 deletions

File tree

evdev/device.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,18 @@
22

33
import os
44
import fcntl
5-
import functools
65
import select
6+
import functools
77
import collections
88

99
from evdev import _input, ecodes, util
1010
from evdev.events import InputEvent
11-
from evdev.eventio import EventIO
11+
12+
try:
13+
import asyncio
14+
from evdev.eventio_async import EventIO
15+
except ImportError:
16+
from evdev.eventio import EventIO
1217

1318

1419
#--------------------------------------------------------------------------

evdev/eventio.py

Lines changed: 22 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -9,106 +9,39 @@
99
from evdev import _input, _uinput, ecodes, util
1010
from evdev.events import InputEvent
1111

12-
try:
13-
import asyncio
14-
except ImportError:
15-
asyncio = None
1612

1713
class EventIO(object):
1814
'''
19-
Class capable of read and write input events.
20-
21-
This is used as as a base class for both device and uinput:
22-
23-
- On ``device``, you read user-generated events (keys pressed, mouse moved, etc) and write feedback events (leds, beeps)
24-
25-
- On ``uinput``, you write user-generated events (keys pressed, mouse moved, etc) and read feedback events (leds, beeps)
15+
Base class for reading and writing input events.
16+
17+
This class is used by :class:`InputDevice` and :class:`UInput`.
18+
19+
- On, :class:`InputDevice` it used for reading user-generated events (e.g.
20+
key presses, mouse movements) and writing feedback events (e.g. leds,
21+
beeps).
22+
23+
- On, :class:`UInput` it used for writing user-generated events (e.g.
24+
key presses, mouse movements) and reading feedback events (e.g. leds,
25+
beeps).
2626
'''
2727

2828
def fileno(self):
2929
'''
3030
Return the file descriptor to the open event device. This makes
31-
it possible to pass instances directly to :func:`select.select()` and
31+
it possible to pass instances directly to :func:`select.select()` and
3232
:class:`asyncore.file_dispatcher`.
3333
'''
3434
return self.fd
3535

3636
def read_loop(self):
3737
'''
38-
Enter an endless loop that yields input events.
39-
40-
The returned iterator is compatible with `async for` syntax
38+
Enter an endless :func:`select.select()` loop that yields input events.
4139
'''
42-
class ReadIterator:
43-
def __init__(self, device):
44-
self.current_batch = iter(())
45-
self.device = device
46-
47-
# Standard iterator
48-
def __iter__(self):
49-
return self
50-
51-
def next(self): # Python 2.x
52-
return self.__next__()
53-
54-
def __next__(self): # Python 3.x
55-
try:
56-
# Try to read from previous batch
57-
return next(self.current_batch)
58-
except StopIteration:
59-
# Fetch next batch
60-
r, w, x = select.select([self.device.fd], [], [])
61-
self.current_batch = self.device.read()
62-
return next(self.current_batch)
63-
64-
# Async iteration - Python 3.5
65-
if asyncio is not None:
66-
@asyncio.coroutine
67-
def __aiter__(self):
68-
return self
69-
70-
@asyncio.coroutine
71-
def __anext__(self):
72-
future = asyncio.Future()
73-
try:
74-
# Try to read from previous batch
75-
future.set_result(next(self.current_batch))
76-
except StopIteration:
77-
# Fetch next batch
78-
def next_batch_ready(batch):
79-
self.current_batch = batch.result()
80-
future.set_result(next(self.current_batch))
81-
self.device.async_read().add_done_callback(next_batch_ready)
82-
return future
83-
84-
return ReadIterator(self)
85-
86-
if asyncio is not None:
87-
def _do_when_readable(self, callback):
88-
loop = asyncio.get_event_loop()
89-
def ready():
90-
loop.remove_reader(self.fileno())
91-
callback()
92-
loop.add_reader(self.fileno(), ready)
93-
94-
def async_read_one(self):
95-
'''
96-
asyncio coroutine to read and return a single input event as an instance of
97-
:class:`InputEvent <evdev.events.InputEvent>`.
98-
'''
99-
future = asyncio.Future()
100-
self._do_when_readable(lambda: future.set_result(self.read_one()))
101-
return future
102-
103-
def async_read(self):
104-
'''
105-
asyncio coroutine to read multiple input events from device. Return a generator object that
106-
yields :class:`InputEvent <evdev.events.InputEvent>` instances.
107-
'''
108-
future = asyncio.Future()
109-
self._do_when_readable(lambda: future.set_result(self.read()))
110-
return future
11140

41+
while True:
42+
r, w, x = select([self.fd], [], [])
43+
for event in self.read():
44+
yield event
11245

11346
def read_one(self):
11447
'''
@@ -123,7 +56,7 @@ def read_one(self):
12356

12457
if event:
12558
return InputEvent(*event)
126-
59+
12760
def read(self):
12861
'''
12962
Read multiple input events from device. Return a generator object that
@@ -134,12 +67,10 @@ def read(self):
13467
# events -> [(sec, usec, type, code, val), ...]
13568
events = _input.device_read_many(self.fd)
13669

137-
for i in events:
138-
yield InputEvent(*i)
139-
140-
70+
for event in events:
71+
yield InputEvent(*event)
14172

142-
def _need_write(func):
73+
def need_write(func):
14374
'''
14475
Decorator that raises :class:`EvdevError` if there is no write access to the
14576
input device.
@@ -176,7 +107,7 @@ def write_event(self, event):
176107

177108
self.write(event.type, event.code, event.value)
178109

179-
@_need_write
110+
@need_write
180111
def write(self, etype, code, value):
181112
'''
182113
Inject an input event into the input subsystem. Events are

evdev/eventio_async.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# encoding: utf-8
2+
3+
import asyncio
4+
5+
from evdev import eventio
6+
7+
8+
class EventIO(eventio.EventIO):
9+
def _do_when_readable(self, callback):
10+
loop = asyncio.get_event_loop()
11+
def ready():
12+
loop.remove_reader(self.fileno())
13+
callback()
14+
loop.add_reader(self.fileno(), ready)
15+
16+
def async_read_one(self):
17+
'''
18+
Asyncio coroutine to read and return a single input event as
19+
an instance of :class:`InputEvent <evdev.events.InputEvent>`.
20+
'''
21+
future = asyncio.Future()
22+
self._do_when_readable(lambda: future.set_result(self.read_one()))
23+
return future
24+
25+
def async_read(self):
26+
'''
27+
Asyncio coroutine to read multiple input events from device. Return
28+
a generator object that yields :class:`InputEvent <evdev.events.InputEvent>`
29+
instances.
30+
'''
31+
future = asyncio.Future()
32+
self._do_when_readable(lambda: future.set_result(self.read()))
33+
return future
34+
35+
def read_loop(self):
36+
'''
37+
Enter an endless loop that yields input events.
38+
39+
The returned iterator is compatible with the ``async for`` syntax
40+
'''
41+
return ReadItertor(self)
42+
43+
44+
class ReadIterator(object):
45+
def __init__(self, device):
46+
self.current_batch = iter(())
47+
self.device = device
48+
49+
# Standard iterator protocol.
50+
def __iter__(self):
51+
return self
52+
53+
# Python 2.x compatibility.
54+
def next(self):
55+
return self.__next__()
56+
57+
def __next__(self):
58+
try:
59+
# Read from the previous batch of events.
60+
return next(self.current_batch)
61+
except StopIteration:
62+
r, w, x = select.select([self.device.fd], [], [])
63+
self.current_batch = self.device.read()
64+
return next(self.current_batch)
65+
66+
@asyncio.coroutine
67+
def __aiter__(self):
68+
return self
69+
70+
@asyncio.coroutine
71+
def __anext__(self):
72+
future = asyncio.Future()
73+
try:
74+
# Read from the previous batch of events.
75+
future.set_result(next(self.current_batch))
76+
except StopIteration:
77+
def next_batch_ready(batch):
78+
self.current_batch = batch.result()
79+
future.set_result(next(self.current_batch))
80+
self.device.async_read().add_done_callback(next_batch_ready)
81+
return future

0 commit comments

Comments
 (0)