4545method) up to the terminator, and then control will be returned to
4646you - by calling your self.found_terminator() method.
4747"""
48-
49- import sys
5048import socket
5149import asyncore
5250from collections import deque
5351
52+ def buffer (obj , start = None , stop = None ):
53+ # if memoryview objects gain slicing semantics,
54+ # this function will change for the better
55+ # memoryview used for the TypeError
56+ memoryview (obj )
57+ if start == None :
58+ start = 0
59+ if stop == None :
60+ stop = len (obj )
61+ x = obj [start :stop ]
62+ ## print("buffer type is: %s"%(type(x),))
63+ return x
64+
5465class async_chat (asyncore .dispatcher ):
5566 """This is an abstract class. You must derive from this class, and add
5667 the two methods collect_incoming_data() and found_terminator()"""
@@ -60,20 +71,47 @@ class async_chat (asyncore.dispatcher):
6071 ac_in_buffer_size = 4096
6172 ac_out_buffer_size = 4096
6273
74+ # we don't want to enable the use of encoding by default, because that is a
75+ # sign of an application bug that we don't want to pass silently
76+
77+ use_encoding = 0
78+ encoding = 'latin1'
79+
6380 def __init__ (self , conn = None ):
81+ # for string terminator matching
6482 self .ac_in_buffer = b''
65- self .ac_out_buffer = b''
66- self .producer_fifo = fifo ()
83+
84+ # we use a list here rather than cStringIO for a few reasons...
85+ # del lst[:] is faster than sio.truncate(0)
86+ # lst = [] is faster than sio.truncate(0)
87+ # cStringIO will be gaining unicode support in py3k, which
88+ # will negatively affect the performance of bytes compared to
89+ # a ''.join() equivalent
90+ self .incoming = []
91+
92+ # we toss the use of the "simple producer" and replace it with
93+ # a pure deque, which the original fifo was a wrapping of
94+ self .producer_fifo = deque ()
6795 asyncore .dispatcher .__init__ (self , conn )
6896
6997 def collect_incoming_data (self , data ):
7098 raise NotImplementedError ("must be implemented in subclass" )
7199
100+ def _collect_incoming_data (self , data ):
101+ self .incoming .append (data )
102+
103+ def _get_data (self ):
104+ d = b'' .join (self .incoming )
105+ del self .incoming [:]
106+ return d
107+
72108 def found_terminator (self ):
73109 raise NotImplementedError ("must be implemented in subclass" )
74110
75111 def set_terminator (self , term ):
76112 "Set the input delimiter. Can be a fixed string of any length, an integer, or None"
113+ if isinstance (term , str ) and self .use_encoding :
114+ term = bytes (term , self .encoding )
77115 self .terminator = term
78116
79117 def get_terminator (self ):
@@ -92,14 +130,14 @@ def handle_read (self):
92130 self .handle_error ()
93131 return
94132
95- if isinstance (data , str ):
96- data = data . encode ( 'ascii' )
97- self .ac_in_buffer = self .ac_in_buffer + bytes ( data )
133+ if isinstance (data , str ) and self . use_encoding :
134+ data = bytes ( str , self . encoding )
135+ self .ac_in_buffer = self .ac_in_buffer + data
98136
99137 # Continue to search for self.terminator in self.ac_in_buffer,
100138 # while calling self.collect_incoming_data. The while loop
101139 # is necessary because we might read several data+terminator
102- # combos with a single recv(1024 ).
140+ # combos with a single recv(4096 ).
103141
104142 while self .ac_in_buffer :
105143 lb = len (self .ac_in_buffer )
@@ -108,7 +146,7 @@ def handle_read (self):
108146 # no terminator, collect it all
109147 self .collect_incoming_data (self .ac_in_buffer )
110148 self .ac_in_buffer = b''
111- elif isinstance (terminator , int ) or isinstance ( terminator , int ) :
149+ elif isinstance (terminator , int ):
112150 # numeric terminator
113151 n = terminator
114152 if lb < n :
@@ -129,8 +167,6 @@ def handle_read (self):
129167 # 3) end of buffer does not match any prefix:
130168 # collect data
131169 terminator_len = len (terminator )
132- if isinstance (terminator , str ):
133- terminator = terminator .encode ('ascii' )
134170 index = self .ac_in_buffer .find (terminator )
135171 if index != - 1 :
136172 # we found the terminator
@@ -155,91 +191,87 @@ def handle_read (self):
155191 self .ac_in_buffer = b''
156192
157193 def handle_write (self ):
158- self .initiate_send ()
194+ self .initiate_send ()
159195
160196 def handle_close (self ):
161197 self .close ()
162198
163199 def push (self , data ):
164- self .producer_fifo .push (simple_producer (data ))
200+ sabs = self .ac_out_buffer_size
201+ if len (data ) > sabs :
202+ for i in range (0 , len (data ), sabs ):
203+ self .producer_fifo .append (data [i :i + sabs ])
204+ else :
205+ self .producer_fifo .append (data )
165206 self .initiate_send ()
166207
167208 def push_with_producer (self , producer ):
168- self .producer_fifo .push (producer )
209+ self .producer_fifo .append (producer )
169210 self .initiate_send ()
170211
171212 def readable (self ):
172213 "predicate for inclusion in the readable for select()"
173- return (len (self .ac_in_buffer ) <= self .ac_in_buffer_size )
214+ # cannot use the old predicate, it violates the claim of the
215+ # set_terminator method.
216+
217+ # return (len(self.ac_in_buffer) <= self.ac_in_buffer_size)
218+ return 1
174219
175220 def writable (self ):
176221 "predicate for inclusion in the writable for select()"
177- # return len(self.ac_out_buffer) or len(self.producer_fifo) or (not self.connected)
178- # this is about twice as fast, though not as clear.
179- return not (
180- (self .ac_out_buffer == b'' ) and
181- self .producer_fifo .is_empty () and
182- self .connected
183- )
222+ return self .producer_fifo or (not self .connected )
184223
185224 def close_when_done (self ):
186225 "automatically close this channel once the outgoing queue is empty"
187- self .producer_fifo .push (None )
188-
189- # refill the outgoing buffer by calling the more() method
190- # of the first producer in the queue
191- def refill_buffer (self ):
192- while 1 :
193- if len (self .producer_fifo ):
194- p = self .producer_fifo .first ()
195- # a 'None' in the producer fifo is a sentinel,
196- # telling us to close the channel.
197- if p is None :
198- if not self .ac_out_buffer :
199- self .producer_fifo .pop ()
200- self .close ()
201- return
202- elif isinstance (p , str ) or isinstance (p , bytes ):
203- if isinstance (p , str ):
204- p = p .encode ('ascii' )
205- self .producer_fifo .pop ()
206- self .ac_out_buffer = self .ac_out_buffer + p
226+ self .producer_fifo .append (None )
227+
228+ def initiate_send (self ):
229+ while self .producer_fifo and self .connected :
230+ first = self .producer_fifo [0 ]
231+ # handle empty string/buffer or None entry
232+ if not first :
233+ del self .producer_fifo [0 ]
234+ if first is None :
235+ ## print("first is None")
236+ self .handle_close ()
207237 return
208- data = p .more ()
238+ ## print("first is not None")
239+
240+ # handle classic producer behavior
241+ obs = self .ac_out_buffer_size
242+ try :
243+ data = buffer (first , 0 , obs )
244+ except TypeError :
245+ data = first .more ()
209246 if data :
210- if isinstance (data , str ):
211- data = data .encode ('ascii' )
212- self .ac_out_buffer = self .ac_out_buffer + bytes (data )
213- return
247+ self .producer_fifo .appendleft (data )
214248 else :
215- self .producer_fifo .pop ()
216- else :
217- return
249+ del self .producer_fifo [0 ]
250+ continue
218251
219- def initiate_send (self ):
220- obs = self .ac_out_buffer_size
221- # try to refill the buffer
222- if (len (self .ac_out_buffer ) < obs ):
223- self .refill_buffer ()
252+ if isinstance (data , str ) and self .use_encoding :
253+ data = bytes (data , self .encoding )
224254
225- if self .ac_out_buffer and self .connected :
226- # try to send the buffer
255+ # send the data
227256 try :
228- num_sent = self .send (self .ac_out_buffer [:obs ])
229- if num_sent :
230- self .ac_out_buffer = self .ac_out_buffer [num_sent :]
231-
232- except socket .error as why :
257+ num_sent = self .send (data )
258+ except socket .error :
233259 self .handle_error ()
234260 return
235261
262+ if num_sent :
263+ if num_sent < len (data ) or obs < len (first ):
264+ self .producer_fifo [0 ] = first [num_sent :]
265+ else :
266+ del self .producer_fifo [0 ]
267+ # we tried to send some actual data
268+ return
269+
236270 def discard_buffers (self ):
237271 # Emergencies only!
238272 self .ac_in_buffer = b''
239- self .ac_out_buffer = b''
240- while self .producer_fifo :
241- self .producer_fifo .pop ()
242-
273+ del self .incoming [:]
274+ self .producer_fifo .clear ()
243275
244276class simple_producer :
245277
0 commit comments