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

Skip to content

Commit 977c707

Browse files
committed
Fix issue 6706: adds new handle_accepted() method to asyncore.dispatcher
1 parent 4c94c53 commit 977c707

10 files changed

Lines changed: 126 additions & 42 deletions

File tree

Doc/library/asyncore.rst

Lines changed: 66 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ any that have been added to the map during asynchronous service) is closed.
8686
| ``handle_close()`` | Implied by a read event with no data |
8787
| | available |
8888
+----------------------+----------------------------------------+
89-
| ``handle_accept()`` | Implied by a read event on a listening |
89+
| ``handle_accepted()``| Implied by a read event on a listening |
9090
| | socket |
9191
+----------------------+----------------------------------------+
9292

@@ -144,8 +144,20 @@ any that have been added to the map during asynchronous service) is closed.
144144

145145
Called on listening channels (passive openers) when a connection can be
146146
established with a new remote endpoint that has issued a :meth:`connect`
147-
call for the local endpoint.
147+
call for the local endpoint. Deprecated in version 3.2; use
148+
:meth:`handle_accepted` instead.
148149

150+
.. deprecated:: 3.2
151+
152+
.. method:: handle_accepted(sock, addr)
153+
154+
Called on listening channels (passive openers) when a connection has been
155+
established with a new remote endpoint that has issued a :meth:`connect`
156+
call for the local endpoint. *conn* is a *new* socket object usable to
157+
send and receive data on the connection, and *address* is the address
158+
bound to the socket on the other end of the connection.
159+
160+
.. versionadded:: 3.2
149161

150162
.. method:: readable()
151163

@@ -210,10 +222,13 @@ any that have been added to the map during asynchronous service) is closed.
210222
.. method:: accept()
211223

212224
Accept a connection. The socket must be bound to an address and listening
213-
for connections. The return value is a pair ``(conn, address)`` where
214-
*conn* is a *new* socket object usable to send and receive data on the
215-
connection, and *address* is the address bound to the socket on the other
216-
end of the connection.
225+
for connections. The return value can be either ``None`` or a pair
226+
``(conn, address)`` where *conn* is a *new* socket object usable to send
227+
and receive data on the connection, and *address* is the address bound to
228+
the socket on the other end of the connection.
229+
When ``None`` is returned it means the connection didn't take place, in
230+
which case the server should just ignore this event and keep listening
231+
for further incoming connections.
217232

218233

219234
.. method:: close()
@@ -223,6 +238,13 @@ any that have been added to the map during asynchronous service) is closed.
223238
flushed). Sockets are automatically closed when they are
224239
garbage-collected.
225240

241+
242+
.. class:: dispatcher_with_send()
243+
244+
A :class:`dispatcher` subclass which adds simple buffered output capability,
245+
useful for simple clients. For more sophisticated usage use
246+
:class:`asynchat.async_chat`.
247+
226248
.. class:: file_dispatcher()
227249

228250
A file_dispatcher takes a file descriptor or :term:`file object` along
@@ -239,7 +261,7 @@ any that have been added to the map during asynchronous service) is closed.
239261
socket for use by the :class:`file_dispatcher` class. Availability: UNIX.
240262

241263

242-
.. _asyncore-example:
264+
.. _asyncore-example-1:
243265

244266
asyncore Example basic HTTP client
245267
----------------------------------
@@ -249,7 +271,7 @@ implement its socket handling::
249271

250272
import asyncore, socket
251273

252-
class http_client(asyncore.dispatcher):
274+
class HTTPClient(asyncore.dispatcher):
253275

254276
def __init__(self, host, path):
255277
asyncore.dispatcher.__init__(self)
@@ -273,6 +295,40 @@ implement its socket handling::
273295
sent = self.send(self.buffer)
274296
self.buffer = self.buffer[sent:]
275297

276-
c = http_client('www.python.org', '/')
277298

278-
asyncore.loop()
299+
client = HTTPClient('www.python.org', '/')
300+
asyncore.loop()
301+
302+
.. _asyncore-example-2:
303+
304+
asyncore Example basic echo server
305+
----------------------------------
306+
307+
Here is abasic echo server that uses the :class:`dispatcher` class to accept
308+
connections and dispatches the incoming connections to a handler::
309+
310+
import asyncore
311+
import socket
312+
313+
class EchoHandler(asyncore.dispatcher_with_send):
314+
315+
def handle_read(self):
316+
data = self.recv(8192)
317+
self.send(data)
318+
319+
class EchoServer(asyncore.dispatcher):
320+
321+
def __init__(self, host, port):
322+
asyncore.dispatcher.__init__(self)
323+
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
324+
self.set_reuse_addr()
325+
self.bind((host, port))
326+
self.listen(5)
327+
328+
def handle_accepted(self, sock, addr):
329+
print('Incoming connection from %s' % repr(addr))
330+
handler = EchoHandler(sock)
331+
332+
server = EchoServer('localhost', 8080)
333+
asyncore.loop()
334+

Doc/whatsnew/3.2.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,14 @@ New, Improved, and Deprecated Modules
434434

435435
(Contributed by Giampaolo Rodolà; :issue:`9794`.)
436436

437+
* :class:`asyncore.dispatcher` now provides a
438+
:meth:`~asyncore.dispatcher.handle_accepted()` method
439+
returning a `(sock, addr)` pair which is called when a connection has actually
440+
been established with a new remote endpoint. This is supposed to be used as a
441+
replacement for old :meth:`~asyncore.dispatcher.handle_accept()` and avoids
442+
the user to call :meth:`~asyncore.dispatcher.accept()` directly.
443+
444+
(Contributed by Giampaolo Rodolà; :issue:`6706`.)
437445

438446
Multi-threading
439447
===============

Lib/asyncore.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -352,12 +352,15 @@ def accept(self):
352352
# XXX can return either an address pair or None
353353
try:
354354
conn, addr = self.socket.accept()
355-
return conn, addr
355+
except TypeError:
356+
return None
356357
except socket.error as why:
357-
if why.args[0] == EWOULDBLOCK:
358-
pass
358+
if why.args[0] in (EWOULDBLOCK, ECONNABORTED):
359+
return None
359360
else:
360361
raise
362+
else:
363+
return conn, addr
361364

362365
def send(self, data):
363366
try:
@@ -506,7 +509,13 @@ def handle_connect(self):
506509
self.log_info('unhandled connect event', 'warning')
507510

508511
def handle_accept(self):
509-
self.log_info('unhandled accept event', 'warning')
512+
pair = self.accept()
513+
if pair is not None:
514+
self.handle_accepted(*pair)
515+
516+
def handle_accepted(self, sock, addr):
517+
sock.close()
518+
self.log_info('unhandled accepted event', 'warning')
510519

511520
def handle_close(self):
512521
self.log_info('unhandled close event', 'warning')

Lib/smtpd.py

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -421,21 +421,7 @@ def __init__(self, localaddr, remoteaddr):
421421
self.__class__.__name__, time.ctime(time.time()),
422422
localaddr, remoteaddr), file=DEBUGSTREAM)
423423

424-
def handle_accept(self):
425-
try:
426-
conn, addr = self.accept()
427-
except TypeError:
428-
# sometimes accept() might return None
429-
return
430-
except socket.error as err:
431-
# ECONNABORTED might be thrown
432-
if err.args[0] != errno.ECONNABORTED:
433-
raise
434-
return
435-
else:
436-
# sometimes addr == None instead of (ip, port)
437-
if addr == None:
438-
return
424+
def handle_accepted(self, conn, addr):
439425
print('Incoming connection from %s' % repr(addr), file=DEBUGSTREAM)
440426
channel = self.channel_class(self, conn, addr)
441427

Lib/test/test_asyncore.py

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -296,16 +296,14 @@ def test_unhandled(self):
296296
d.handle_read()
297297
d.handle_write()
298298
d.handle_connect()
299-
d.handle_accept()
300299
finally:
301300
sys.stdout = stdout
302301

303302
lines = fp.getvalue().splitlines()
304303
expected = ['warning: unhandled incoming priority event',
305304
'warning: unhandled read event',
306305
'warning: unhandled write event',
307-
'warning: unhandled connect event',
308-
'warning: unhandled accept event']
306+
'warning: unhandled connect event']
309307
self.assertEqual(lines, expected)
310308

311309
def test_issue_8594(self):
@@ -451,6 +449,9 @@ def __init__(self, sock=None):
451449
def handle_accept(self):
452450
raise Exception("handle_accept not supposed to be called")
453451

452+
def handle_accepted(self):
453+
raise Exception("handle_accepted not supposed to be called")
454+
454455
def handle_connect(self):
455456
raise Exception("handle_connect not supposed to be called")
456457

@@ -481,8 +482,7 @@ def __init__(self, handler=BaseTestHandler, host=HOST, port=0):
481482
def address(self):
482483
return self.socket.getsockname()[:2]
483484

484-
def handle_accept(self):
485-
sock, addr = self.accept()
485+
def handle_accepted(self, sock, addr):
486486
self.handler(sock)
487487

488488
def handle_error(self):
@@ -546,6 +546,29 @@ def handle_accept(self):
546546
client = BaseClient(server.address)
547547
self.loop_waiting_for_flag(server)
548548

549+
def test_handle_accepted(self):
550+
# make sure handle_accepted() is called when a client connects
551+
552+
class TestListener(BaseTestHandler):
553+
554+
def __init__(self):
555+
BaseTestHandler.__init__(self)
556+
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
557+
self.bind((HOST, 0))
558+
self.listen(5)
559+
self.address = self.socket.getsockname()[:2]
560+
561+
def handle_accept(self):
562+
asyncore.dispatcher.handle_accept(self)
563+
564+
def handle_accepted(self, sock, addr):
565+
self.flag = True
566+
567+
server = TestListener()
568+
client = BaseClient(server.address)
569+
self.loop_waiting_for_flag(server)
570+
571+
549572
def test_handle_read(self):
550573
# make sure handle_read is called on data received
551574

Lib/test/test_ftplib.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -244,8 +244,7 @@ def stop(self):
244244
self.active = False
245245
self.join()
246246

247-
def handle_accept(self):
248-
conn, addr = self.accept()
247+
def handle_accepted(self, conn, addr):
249248
self.handler_instance = self.handler(conn)
250249

251250
def handle_connect(self):

Lib/test/test_poplib.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,7 @@ def stop(self):
144144
self.active = False
145145
self.join()
146146

147-
def handle_accept(self):
148-
conn, addr = self.accept()
147+
def handle_accepted(self, conn, addr):
149148
self.handler_instance = self.handler(conn)
150149

151150
def handle_connect(self):

Lib/test/test_smtplib.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -379,8 +379,7 @@ def __init__(self, *args, **kw):
379379
self._extra_features = []
380380
smtpd.SMTPServer.__init__(self, *args, **kw)
381381

382-
def handle_accept(self):
383-
conn, addr = self.accept()
382+
def handle_accepted(self, conn, addr):
384383
self._SMTPchannel = SimSMTPChannel(self._extra_features,
385384
self, conn, addr)
386385

Lib/test/test_ssl.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -838,8 +838,7 @@ def __init__(self, certfile):
838838
asyncore.dispatcher.__init__(self, sock)
839839
self.listen(5)
840840

841-
def handle_accept(self):
842-
sock_obj, addr = self.accept()
841+
def handle_accepted(self, sock_obj, addr):
843842
if support.verbose:
844843
sys.stdout.write(" server: new connection from %s:%s\n" %addr)
845844
self.ConnectionHandler(sock_obj, self.certfile)

Misc/NEWS

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,12 @@ Core and Builtins
8888
Library
8989
-------
9090

91+
- Issue 6706: asyncore.dispatcher now provides a handle_accepted() method
92+
returning a (sock, addr) pair which is called when a connection has been
93+
established with a new remote endpoint. This is supposed to be used as a
94+
replacement for old handle_accept() and avoids the user to call accept()
95+
directly.
96+
9197
- Issue #9065: tarfile no longer uses "root" as the default for the uname and
9298
gname field.
9399

0 commit comments

Comments
 (0)