11
11
)
12
12
13
13
from time import time
14
+ import threading
14
15
import sys
15
16
16
- __all__ = ('Channel' , 'SerialChannel' , 'Writer' , 'CallbackWriter' , 'Reader' ,
17
- 'CallbackReader' , 'mkchannel' , 'ReadOnly' )
17
+ __all__ = ('Channel' , 'SerialChannel' , 'Writer' , 'ChannelWriter' , 'CallbackChannelWriter' ,
18
+ 'Reader' , 'ChannelReader' , 'CallbackChannelReader' , 'mkchannel' , 'ReadOnly' ,
19
+ 'IteratorReader' )
18
20
19
21
#{ Classes
20
22
class Channel (object ):
@@ -43,15 +45,50 @@ class SerialChannel(Channel):
43
45
44
46
45
47
class Writer (object ):
48
+ """A writer is an object providing write access to a possibly blocking reading device"""
49
+ __slots__ = tuple ()
50
+
51
+ #{ Interface
52
+
53
+ def __init__ (self , device ):
54
+ """Initialize the instance with the device to write to"""
55
+
56
+ def write (self , item , block = True , timeout = None ):
57
+ """Write the given item into the device
58
+ :param block: True if the device may block until space for the item is available
59
+ :param timeout: The time in seconds to wait for the device to become ready
60
+ in blocking mode"""
61
+ raise NotImplementedError ()
62
+
63
+ def size (self ):
64
+ """:return: number of items already in the device, they could be read with a reader"""
65
+ raise NotImplementedError ()
66
+
67
+ def close (self ):
68
+ """Close the channel. Multiple close calls on a closed channel are no
69
+ an error"""
70
+ raise NotImplementedError ()
71
+
72
+ def closed (self ):
73
+ """:return: True if the channel was closed"""
74
+ raise NotImplementedError ()
75
+
76
+ #} END interface
77
+
78
+
79
+ class ChannelWriter (Writer ):
46
80
"""The write end of a channel, a file-like interface for a channel"""
47
- __slots__ = ('write ' , 'channel ' )
81
+ __slots__ = ('channel ' , '_put ' )
48
82
49
83
def __init__ (self , channel ):
50
84
"""Initialize the writer to use the given channel"""
51
85
self .channel = channel
52
- self .write = channel .queue .put
86
+ self ._put = self . channel .queue .put
53
87
54
88
#{ Interface
89
+ def write (self , item , block = False , timeout = None ):
90
+ return self ._put (item , block , timeout )
91
+
55
92
def size (self ):
56
93
return self .channel .queue .qsize ()
57
94
@@ -66,15 +103,14 @@ def closed(self):
66
103
#} END interface
67
104
68
105
69
- class CallbackWriter ( Writer ):
106
+ class CallbackChannelWriter ( ChannelWriter ):
70
107
"""The write end of a channel which allows you to setup a callback to be
71
108
called after an item was written to the channel"""
72
109
__slots__ = ('_pre_cb' )
73
110
74
111
def __init__ (self , channel ):
75
- Writer . __init__ ( self , channel )
112
+ super ( CallbackChannelWriter , self ). __init__ ( channel )
76
113
self ._pre_cb = None
77
- self .write = self ._write
78
114
79
115
def set_pre_cb (self , fun = lambda item : item ):
80
116
"""Install a callback to be called before the given item is written.
@@ -87,25 +123,22 @@ def set_pre_cb(self, fun = lambda item: item):
87
123
self ._pre_cb = fun
88
124
return prev
89
125
90
- def _write (self , item , block = True , timeout = None ):
126
+ def write (self , item , block = True , timeout = None ):
91
127
if self ._pre_cb :
92
128
item = self ._pre_cb (item )
93
- self . channel . queue . put (item , block , timeout )
129
+ super ( CallbackChannelWriter , self ). write (item , block , timeout )
94
130
95
131
96
132
class Reader (object ):
97
- """Allows reading from a channel """
98
- __slots__ = 'channel'
133
+ """Allows reading from a device """
134
+ __slots__ = tuple ()
99
135
100
- def __init__ (self , channel ):
101
- """Initialize this instance from its parent write channel"""
102
- self .channel = channel
103
-
104
-
105
136
#{ Interface
106
-
137
+ def __init__ (self , device ):
138
+ """Initialize the instance with the device to read from"""
139
+
107
140
def read (self , count = 0 , block = True , timeout = None ):
108
- """read a list of items read from the channel . The list, as a sequence
141
+ """read a list of items read from the device . The list, as a sequence
109
142
of items, is similar to the string of characters returned when reading from
110
143
file like objects.
111
144
:param count: given amount of items to read. If < 1, all items will be read
@@ -114,11 +147,25 @@ def read(self, count=0, block=True, timeout=None):
114
147
given amount of seconds, returning the items it received so far.
115
148
The timeout is applied to each read item, not for the whole operation.
116
149
:return: single item in a list if count is 1, or a list of count items.
117
- If the channel was empty and count was 1, an empty list will be returned.
150
+ If the device was empty and count was 1, an empty list will be returned.
118
151
If count was greater 1, a list with less than count items will be
119
152
returned.
120
153
If count was < 1, a list with all items that could be read will be
121
154
returned."""
155
+ raise NotImplementedError ()
156
+
157
+
158
+ class ChannelReader (Reader ):
159
+ """Allows reading from a channel. The reader is thread-safe if the channel is as well"""
160
+ __slots__ = 'channel'
161
+
162
+ def __init__ (self , channel ):
163
+ """Initialize this instance from its parent write channel"""
164
+ self .channel = channel
165
+
166
+ #{ Interface
167
+
168
+ def read (self , count = 0 , block = True , timeout = None ):
122
169
# if the channel is closed for writing, we never block
123
170
# NOTE: is handled by the queue
124
171
# We don't check for a closed state here has it costs time - most of
@@ -191,12 +238,12 @@ def read(self, count=0, block=True, timeout=None):
191
238
192
239
#} END interface
193
240
194
- class CallbackReader ( Reader ):
241
+ class CallbackChannelReader ( ChannelReader ):
195
242
"""A channel which sends a callback before items are read from the channel"""
196
243
__slots__ = "_pre_cb"
197
244
198
245
def __init__ (self , channel ):
199
- Reader . __init__ ( self , channel )
246
+ super ( CallbackChannelReader , self ). __init__ ( channel )
200
247
self ._pre_cb = None
201
248
202
249
def set_pre_cb (self , fun = lambda count : None ):
@@ -213,13 +260,59 @@ def set_pre_cb(self, fun = lambda count: None):
213
260
def read (self , count = 0 , block = True , timeout = None ):
214
261
if self ._pre_cb :
215
262
self ._pre_cb (count )
216
- return Reader .read (self , count , block , timeout )
263
+ return super (CallbackChannelReader , self ).read (count , block , timeout )
264
+
217
265
266
+ class IteratorReader (Reader ):
267
+ """A Reader allowing to read items from an iterator, instead of a channel.
268
+ Reads will never block. Its thread-safe"""
269
+ __slots__ = ("_empty" , '_iter' , '_lock' )
270
+
271
+ # the type of the lock to use when reading from the iterator
272
+ lock_type = threading .Lock
273
+
274
+ def __init__ (self , iterator ):
275
+ self ._empty = False
276
+ if not hasattr (iterator , 'next' ):
277
+ raise ValueError ("Iterator %r needs a next() function" % iterator )
278
+ self ._iter = iterator
279
+ self ._lock = self .lock_type ()
280
+
281
+ def read (self , count = 0 , block = True , timeout = None ):
282
+ """Non-Blocking implementation of read"""
283
+ # not threadsafe, but worst thing that could happen is that
284
+ # we try to get items one more time
285
+ if self ._empty :
286
+ return list ()
287
+ # END early abort
288
+
289
+ self ._lock .acquire ()
290
+ try :
291
+ if count == 0 :
292
+ self ._empty = True
293
+ return list (self ._iter )
294
+ else :
295
+ out = list ()
296
+ it = self ._iter
297
+ for i in xrange (count ):
298
+ try :
299
+ out .append (it .next ())
300
+ except StopIteration :
301
+ self ._empty = True
302
+ break
303
+ # END handle empty iterator
304
+ # END for each item to take
305
+ return out
306
+ # END handle count
307
+ finally :
308
+ self ._lock .release ()
309
+ # END handle locking
310
+
218
311
219
312
#} END classes
220
313
221
314
#{ Constructors
222
- def mkchannel (ctype = Channel , wtype = Writer , rtype = Reader ):
315
+ def mkchannel (ctype = Channel , wtype = ChannelWriter , rtype = ChannelReader ):
223
316
"""Create a channel, with a reader and a writer
224
317
:return: tuple(reader, writer)
225
318
:param ctype: Channel to instantiate
0 commit comments