Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit d74900e

Browse files
committed
Committing Py3k version of changelist 64080 and 64257, along with updated tests
for smtpd, which required updating with the new semantics.
1 parent d51ee54 commit d74900e

8 files changed

Lines changed: 268 additions & 133 deletions

File tree

Doc/library/asynchat.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,12 @@ connection requests.
8181
:exc:`NotImplementedError` exception.
8282

8383

84+
.. method:: async_chat._collect_incoming_data(data)
85+
86+
Sample implementation of a data collection rutine to be used in conjunction
87+
with :meth:`_get_data` in a user-specified :meth:`found_terminator`.
88+
89+
8490
.. method:: async_chat.discard_buffers()
8591

8692
In emergencies this method will discard any data held in the input and/or
@@ -95,6 +101,12 @@ connection requests.
95101
should be available via an instance attribute.
96102

97103

104+
.. method:: async_chat._get_data()
105+
106+
Will return and clear the data received with the sample
107+
:meth:`_collect_incoming_data` implementation.
108+
109+
98110
.. method:: async_chat.get_terminator()
99111

100112
Returns the current terminator for the channel.

Doc/library/asyncore.rst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,21 @@ any that have been added to the map during asynchronous service) is closed.
222222
flushed). Sockets are automatically closed when they are
223223
garbage-collected.
224224

225+
.. class:: file_dispatcher()
226+
227+
A file_dispatcher takes a file descriptor or file object along with an
228+
optional map argument and wraps it for use with the :cfunc:`poll` or
229+
:cfunc:`loop` functions. If provided a file object or anything with a
230+
:cfunc:`fileno` method, that method will be called and passed to the
231+
:class:`file_wrapper` constructor. Availability: UNIX.
232+
233+
.. class:: file_wrapper()
234+
235+
A file_wrapper takes an integer file descriptor and calls :func:`os.dup` to
236+
duplicate the handle so that the original handle may be closed independently
237+
of the file_wrapper. This class implements sufficient methods to emulate a
238+
socket for use by the :class:`file_dispatcher` class. Availability: UNIX.
239+
225240

226241
.. _asyncore-example:
227242

Lib/asynchat.py

Lines changed: 98 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,23 @@
4545
method) up to the terminator, and then control will be returned to
4646
you - by calling your self.found_terminator() method.
4747
"""
48-
49-
import sys
5048
import socket
5149
import asyncore
5250
from 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+
5465
class 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

244276
class simple_producer:
245277

0 commit comments

Comments
 (0)