gh-72719: Remove asyncore and asynchat modules (#96580)

Remove modules asyncore and asynchat, which were deprecated by PEP 594.

Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
This commit is contained in:
Nikita Sobolev 2022-11-08 18:48:58 +03:00 committed by GitHub
parent c43714fbcd
commit e56e33d271
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 32 additions and 1762 deletions

2
.github/CODEOWNERS vendored
View File

@ -137,8 +137,6 @@ Lib/ast.py @isidentical
**/*typing* @gvanrossum @Fidget-Spinner @JelleZijlstra @AlexWaygood
**/*asyncore @giampaolo
**/*asynchat @giampaolo
**/*ftplib @giampaolo
**/*shutil @giampaolo

View File

@ -1,217 +0,0 @@
:mod:`asynchat` --- Asynchronous socket command/response handler
================================================================
.. module:: asynchat
:synopsis: Support for asynchronous command/response protocols.
:deprecated:
.. moduleauthor:: Sam Rushing <rushing@nightmare.com>
.. sectionauthor:: Steve Holden <sholden@holdenweb.com>
**Source code:** :source:`Lib/asynchat.py`
.. deprecated-removed:: 3.6 3.12
The :mod:`asynchat` module is deprecated
(see :pep:`PEP 594 <594#asynchat>` for details).
Please use :mod:`asyncio` instead.
--------------
.. note::
This module exists for backwards compatibility only. For new code we
recommend using :mod:`asyncio`.
This module builds on the :mod:`asyncore` infrastructure, simplifying
asynchronous clients and servers and making it easier to handle protocols
whose elements are terminated by arbitrary strings, or are of variable length.
:mod:`asynchat` defines the abstract class :class:`async_chat` that you
subclass, providing implementations of the :meth:`collect_incoming_data` and
:meth:`found_terminator` methods. It uses the same asynchronous loop as
:mod:`asyncore`, and the two types of channel, :class:`asyncore.dispatcher`
and :class:`asynchat.async_chat`, can freely be mixed in the channel map.
Typically an :class:`asyncore.dispatcher` server channel generates new
:class:`asynchat.async_chat` channel objects as it receives incoming
connection requests.
.. include:: ../includes/wasm-notavail.rst
.. class:: async_chat()
This class is an abstract subclass of :class:`asyncore.dispatcher`. To make
practical use of the code you must subclass :class:`async_chat`, providing
meaningful :meth:`collect_incoming_data` and :meth:`found_terminator`
methods.
The :class:`asyncore.dispatcher` methods can be used, although not all make
sense in a message/response context.
Like :class:`asyncore.dispatcher`, :class:`async_chat` defines a set of
events that are generated by an analysis of socket conditions after a
:c:func:`select` call. Once the polling loop has been started the
:class:`async_chat` object's methods are called by the event-processing
framework with no action on the part of the programmer.
Two class attributes can be modified, to improve performance, or possibly
even to conserve memory.
.. data:: ac_in_buffer_size
The asynchronous input buffer size (default ``4096``).
.. data:: ac_out_buffer_size
The asynchronous output buffer size (default ``4096``).
Unlike :class:`asyncore.dispatcher`, :class:`async_chat` allows you to
define a :abbr:`FIFO (first-in, first-out)` queue of *producers*. A producer need
have only one method, :meth:`more`, which should return data to be
transmitted on the channel.
The producer indicates exhaustion (*i.e.* that it contains no more data) by
having its :meth:`more` method return the empty bytes object. At this point
the :class:`async_chat` object removes the producer from the queue and starts
using the next producer, if any. When the producer queue is empty the
:meth:`handle_write` method does nothing. You use the channel object's
:meth:`set_terminator` method to describe how to recognize the end of, or
an important breakpoint in, an incoming transmission from the remote
endpoint.
To build a functioning :class:`async_chat` subclass your input methods
:meth:`collect_incoming_data` and :meth:`found_terminator` must handle the
data that the channel receives asynchronously. The methods are described
below.
.. method:: async_chat.close_when_done()
Pushes a ``None`` on to the producer queue. When this producer is popped off
the queue it causes the channel to be closed.
.. method:: async_chat.collect_incoming_data(data)
Called with *data* holding an arbitrary amount of received data. The
default method, which must be overridden, raises a
:exc:`NotImplementedError` exception.
.. method:: async_chat.discard_buffers()
In emergencies this method will discard any data held in the input and/or
output buffers and the producer queue.
.. method:: async_chat.found_terminator()
Called when the incoming data stream matches the termination condition set
by :meth:`set_terminator`. The default method, which must be overridden,
raises a :exc:`NotImplementedError` exception. The buffered input data
should be available via an instance attribute.
.. method:: async_chat.get_terminator()
Returns the current terminator for the channel.
.. method:: async_chat.push(data)
Pushes data on to the channel's queue to ensure its transmission.
This is all you need to do to have the channel write the data out to the
network, although it is possible to use your own producers in more complex
schemes to implement encryption and chunking, for example.
.. method:: async_chat.push_with_producer(producer)
Takes a producer object and adds it to the producer queue associated with
the channel. When all currently pushed producers have been exhausted the
channel will consume this producer's data by calling its :meth:`more`
method and send the data to the remote endpoint.
.. method:: async_chat.set_terminator(term)
Sets the terminating condition to be recognized on the channel. ``term``
may be any of three types of value, corresponding to three different ways
to handle incoming protocol data.
+-----------+---------------------------------------------+
| term | Description |
+===========+=============================================+
| *string* | Will call :meth:`found_terminator` when the |
| | string is found in the input stream |
+-----------+---------------------------------------------+
| *integer* | Will call :meth:`found_terminator` when the |
| | indicated number of characters have been |
| | received |
+-----------+---------------------------------------------+
| ``None`` | The channel continues to collect data |
| | forever |
+-----------+---------------------------------------------+
Note that any data following the terminator will be available for reading
by the channel after :meth:`found_terminator` is called.
.. _asynchat-example:
asynchat Example
----------------
The following partial example shows how HTTP requests can be read with
:class:`async_chat`. A web server might create an
:class:`http_request_handler` object for each incoming client connection.
Notice that initially the channel terminator is set to match the blank line at
the end of the HTTP headers, and a flag indicates that the headers are being
read.
Once the headers have been read, if the request is of type POST (indicating
that further data are present in the input stream) then the
``Content-Length:`` header is used to set a numeric terminator to read the
right amount of data from the channel.
The :meth:`handle_request` method is called once all relevant input has been
marshalled, after setting the channel terminator to ``None`` to ensure that
any extraneous data sent by the web client are ignored. ::
import asynchat
class http_request_handler(asynchat.async_chat):
def __init__(self, sock, addr, sessions, log):
asynchat.async_chat.__init__(self, sock=sock)
self.addr = addr
self.sessions = sessions
self.ibuffer = []
self.obuffer = b""
self.set_terminator(b"\r\n\r\n")
self.reading_headers = True
self.handling = False
self.cgi_data = None
self.log = log
def collect_incoming_data(self, data):
"""Buffer the data"""
self.ibuffer.append(data)
def found_terminator(self):
if self.reading_headers:
self.reading_headers = False
self.parse_headers(b"".join(self.ibuffer))
self.ibuffer = []
if self.op.upper() == b"POST":
clen = self.headers.getheader("content-length")
self.set_terminator(int(clen))
else:
self.handling = True
self.set_terminator(None)
self.handle_request()
elif not self.handling:
self.set_terminator(None) # browsers sometimes over-send
self.cgi_data = parse(self.headers, b"".join(self.ibuffer))
self.handling = True
self.ibuffer = []
self.handle_request()

View File

@ -1,365 +0,0 @@
:mod:`asyncore` --- Asynchronous socket handler
===============================================
.. module:: asyncore
:synopsis: A base class for developing asynchronous socket handling
services.
:deprecated:
.. moduleauthor:: Sam Rushing <rushing@nightmare.com>
.. sectionauthor:: Christopher Petrilli <petrilli@amber.org>
.. sectionauthor:: Steve Holden <sholden@holdenweb.com>
.. heavily adapted from original documentation by Sam Rushing
**Source code:** :source:`Lib/asyncore.py`
.. deprecated-removed:: 3.6 3.12
The :mod:`asyncore` module is deprecated
(see :pep:`PEP 594 <594#asyncore>` for details).
Please use :mod:`asyncio` instead.
--------------
.. note::
This module exists for backwards compatibility only. For new code we
recommend using :mod:`asyncio`.
This module provides the basic infrastructure for writing asynchronous socket
service clients and servers.
.. include:: ../includes/wasm-notavail.rst
There are only two ways to have a program on a single processor do "more than
one thing at a time." Multi-threaded programming is the simplest and most
popular way to do it, but there is another very different technique, that lets
you have nearly all the advantages of multi-threading, without actually using
multiple threads. It's really only practical if your program is largely I/O
bound. If your program is processor bound, then pre-emptive scheduled threads
are probably what you really need. Network servers are rarely processor
bound, however.
If your operating system supports the :c:func:`select` system call in its I/O
library (and nearly all do), then you can use it to juggle multiple
communication channels at once; doing other work while your I/O is taking
place in the "background." Although this strategy can seem strange and
complex, especially at first, it is in many ways easier to understand and
control than multi-threaded programming. The :mod:`asyncore` module solves
many of the difficult problems for you, making the task of building
sophisticated high-performance network servers and clients a snap. For
"conversational" applications and protocols the companion :mod:`asynchat`
module is invaluable.
The basic idea behind both modules is to create one or more network
*channels*, instances of class :class:`asyncore.dispatcher` and
:class:`asynchat.async_chat`. Creating the channels adds them to a global
map, used by the :func:`loop` function if you do not provide it with your own
*map*.
Once the initial channel(s) is(are) created, calling the :func:`loop` function
activates channel service, which continues until the last channel (including
any that have been added to the map during asynchronous service) is closed.
.. function:: loop([timeout[, use_poll[, map[,count]]]])
Enter a polling loop that terminates after count passes or all open
channels have been closed. All arguments are optional. The *count*
parameter defaults to ``None``, resulting in the loop terminating only when all
channels have been closed. The *timeout* argument sets the timeout
parameter for the appropriate :func:`~select.select` or :func:`~select.poll`
call, measured in seconds; the default is 30 seconds. The *use_poll*
parameter, if true, indicates that :func:`~select.poll` should be used in
preference to :func:`~select.select` (the default is ``False``).
The *map* parameter is a dictionary whose items are the channels to watch.
As channels are closed they are deleted from their map. If *map* is
omitted, a global map is used. Channels (instances of
:class:`asyncore.dispatcher`, :class:`asynchat.async_chat` and subclasses
thereof) can freely be mixed in the map.
.. class:: dispatcher()
The :class:`dispatcher` class is a thin wrapper around a low-level socket
object. To make it more useful, it has a few methods for event-handling
which are called from the asynchronous loop. Otherwise, it can be treated
as a normal non-blocking socket object.
The firing of low-level events at certain times or in certain connection
states tells the asynchronous loop that certain higher-level events have
taken place. For example, if we have asked for a socket to connect to
another host, we know that the connection has been made when the socket
becomes writable for the first time (at this point you know that you may
write to it with the expectation of success). The implied higher-level
events are:
+----------------------+----------------------------------------+
| Event | Description |
+======================+========================================+
| ``handle_connect()`` | Implied by the first read or write |
| | event |
+----------------------+----------------------------------------+
| ``handle_close()`` | Implied by a read event with no data |
| | available |
+----------------------+----------------------------------------+
| ``handle_accepted()``| Implied by a read event on a listening |
| | socket |
+----------------------+----------------------------------------+
During asynchronous processing, each mapped channel's :meth:`readable` and
:meth:`writable` methods are used to determine whether the channel's socket
should be added to the list of channels :c:func:`select`\ ed or
:c:func:`poll`\ ed for read and write events.
Thus, the set of channel events is larger than the basic socket events. The
full set of methods that can be overridden in your subclass follows:
.. method:: handle_read()
Called when the asynchronous loop detects that a :meth:`read` call on the
channel's socket will succeed.
.. method:: handle_write()
Called when the asynchronous loop detects that a writable socket can be
written. Often this method will implement the necessary buffering for
performance. For example::
def handle_write(self):
sent = self.send(self.buffer)
self.buffer = self.buffer[sent:]
.. method:: handle_expt()
Called when there is out of band (OOB) data for a socket connection. This
will almost never happen, as OOB is tenuously supported and rarely used.
.. method:: handle_connect()
Called when the active opener's socket actually makes a connection. Might
send a "welcome" banner, or initiate a protocol negotiation with the
remote endpoint, for example.
.. method:: handle_close()
Called when the socket is closed.
.. method:: handle_error()
Called when an exception is raised and not otherwise handled. The default
version prints a condensed traceback.
.. method:: handle_accept()
Called on listening channels (passive openers) when a connection can be
established with a new remote endpoint that has issued a :meth:`connect`
call for the local endpoint. Deprecated in version 3.2; use
:meth:`handle_accepted` instead.
.. deprecated:: 3.2
.. method:: handle_accepted(sock, addr)
Called on listening channels (passive openers) when a connection has been
established with a new remote endpoint that has issued a :meth:`connect`
call for the local endpoint. *sock* is a *new* socket object usable to
send and receive data on the connection, and *addr* is the address
bound to the socket on the other end of the connection.
.. versionadded:: 3.2
.. method:: readable()
Called each time around the asynchronous loop to determine whether a
channel's socket should be added to the list on which read events can
occur. The default method simply returns ``True``, indicating that by
default, all channels will be interested in read events.
.. method:: writable()
Called each time around the asynchronous loop to determine whether a
channel's socket should be added to the list on which write events can
occur. The default method simply returns ``True``, indicating that by
default, all channels will be interested in write events.
In addition, each channel delegates or extends many of the socket methods.
Most of these are nearly identical to their socket partners.
.. method:: create_socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
This is identical to the creation of a normal socket, and will use the
same options for creation. Refer to the :mod:`socket` documentation for
information on creating sockets.
.. versionchanged:: 3.3
*family* and *type* arguments can be omitted.
.. method:: connect(address)
As with the normal socket object, *address* is a tuple with the first
element the host to connect to, and the second the port number.
.. method:: send(data)
Send *data* to the remote end-point of the socket.
.. method:: recv(buffer_size)
Read at most *buffer_size* bytes from the socket's remote end-point. An
empty bytes object implies that the channel has been closed from the
other end.
Note that :meth:`recv` may raise :exc:`BlockingIOError` , even though
:func:`select.select` or :func:`select.poll` has reported the socket
ready for reading.
.. method:: listen(backlog)
Listen for connections made to the socket. The *backlog* argument
specifies the maximum number of queued connections and should be at least
1; the maximum value is system-dependent (usually 5).
.. method:: bind(address)
Bind the socket to *address*. The socket must not already be bound. (The
format of *address* depends on the address family --- refer to the
:mod:`socket` documentation for more information.) To mark
the socket as re-usable (setting the :const:`SO_REUSEADDR` option), call
the :class:`dispatcher` object's :meth:`set_reuse_addr` method.
.. method:: accept()
Accept a connection. The socket must be bound to an address and listening
for connections. The return value can be either ``None`` or a pair
``(conn, address)`` where *conn* is a *new* socket object usable to send
and receive data on the connection, and *address* is the address bound to
the socket on the other end of the connection.
When ``None`` is returned it means the connection didn't take place, in
which case the server should just ignore this event and keep listening
for further incoming connections.
.. method:: close()
Close the socket. All future operations on the socket object will fail.
The remote end-point will receive no more data (after queued data is
flushed). Sockets are automatically closed when they are
garbage-collected.
.. class:: dispatcher_with_send()
A :class:`dispatcher` subclass which adds simple buffered output capability,
useful for simple clients. For more sophisticated usage use
:class:`asynchat.async_chat`.
.. class:: file_dispatcher()
A file_dispatcher takes a file descriptor or :term:`file object` along
with an optional map argument and wraps it for use with the :c:func:`poll`
or :c:func:`loop` functions. If provided a file object or anything with a
:c:func:`fileno` method, that method will be called and passed to the
:class:`file_wrapper` constructor.
.. availability:: Unix.
.. class:: file_wrapper()
A file_wrapper takes an integer file descriptor and calls :func:`os.dup` to
duplicate the handle so that the original handle may be closed independently
of the file_wrapper. This class implements sufficient methods to emulate a
socket for use by the :class:`file_dispatcher` class.
.. availability:: Unix.
.. _asyncore-example-1:
asyncore Example basic HTTP client
----------------------------------
Here is a very basic HTTP client that uses the :class:`dispatcher` class to
implement its socket handling::
import asyncore
class HTTPClient(asyncore.dispatcher):
def __init__(self, host, path):
asyncore.dispatcher.__init__(self)
self.create_socket()
self.connect( (host, 80) )
self.buffer = bytes('GET %s HTTP/1.0\r\nHost: %s\r\n\r\n' %
(path, host), 'ascii')
def handle_connect(self):
pass
def handle_close(self):
self.close()
def handle_read(self):
print(self.recv(8192))
def writable(self):
return (len(self.buffer) > 0)
def handle_write(self):
sent = self.send(self.buffer)
self.buffer = self.buffer[sent:]
client = HTTPClient('www.python.org', '/')
asyncore.loop()
.. _asyncore-example-2:
asyncore Example basic echo server
----------------------------------
Here is a basic echo server that uses the :class:`dispatcher` class to accept
connections and dispatches the incoming connections to a handler::
import asyncore
class EchoHandler(asyncore.dispatcher_with_send):
def handle_read(self):
data = self.recv(8192)
if data:
self.send(data)
class EchoServer(asyncore.dispatcher):
def __init__(self, host, port):
asyncore.dispatcher.__init__(self)
self.create_socket()
self.set_reuse_addr()
self.bind((host, port))
self.listen(5)
def handle_accepted(self, sock, addr):
print('Incoming connection from %s' % repr(addr))
handler = EchoHandler(sock)
server = EchoServer('localhost', 8080)
asyncore.loop()

View File

@ -177,8 +177,7 @@ expensive or inappropriate for the service) is to maintain an explicit table of
partially finished requests and to use :mod:`selectors` to decide which
request to work on next (or whether to handle a new incoming request). This is
particularly important for stream services where each client can potentially be
connected for a long time (if threads or subprocesses cannot be used). See
:mod:`asyncore` for another way to manage this.
connected for a long time (if threads or subprocesses cannot be used).
.. XXX should data and methods be intermingled, or separate?
how should the distinction between class and instance variables be drawn?

View File

@ -11,8 +11,6 @@ backwards compatibility. They have been superseded by other modules.
.. toctree::
aifc.rst
asynchat.rst
asyncore.rst
audioop.rst
cgi.rst
cgitb.rst

View File

@ -383,11 +383,11 @@ Project, https://www.wide.ad.jp/. ::
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
Asynchronous socket services
----------------------------
The :mod:`asynchat` and :mod:`asyncore` modules contain the following notice::
The :mod:`test.support.asynchat` and :mod:`test.support.asyncore`
modules contain the following notice::
Copyright 1996 by Sam Rushing

View File

@ -522,6 +522,12 @@ Removed
.. _aiosmtpd: https://pypi.org/project/aiosmtpd/
* ``asynchat`` and ``asyncore`` have been removed
according to the schedule in :pep:`594`,
having been deprecated in Python 3.6.
Use :mod:`asyncio` instead.
(Contributed by Nikita Sobolev in :gh:`96580`.)
* Remove ``io.OpenWrapper`` and ``_pyio.OpenWrapper``, deprecated in Python
3.10: just use :func:`open` instead. The :func:`open` (:func:`io.open`)
function is a built-in function. Since Python 3.10, :func:`_pyio.open` is

View File

@ -161,11 +161,11 @@ class saved_test_environment:
warnings.filters[:] = saved_filters[2]
def get_asyncore_socket_map(self):
asyncore = sys.modules.get('asyncore')
asyncore = sys.modules.get('test.support.asyncore')
# XXX Making a copy keeps objects alive until __exit__ gets called.
return asyncore and asyncore.socket_map.copy() or {}
def restore_asyncore_socket_map(self, saved_map):
asyncore = sys.modules.get('asyncore')
asyncore = sys.modules.get('test.support.asyncore')
if asyncore is not None:
asyncore.close_all(ignore_all=True)
asyncore.socket_map.update(saved_map)

View File

@ -77,7 +77,7 @@ import getopt
import time
import socket
import collections
from test.support.import_helper import import_module
from test.support import asyncore, asynchat
from warnings import warn
from email._header_value_parser import get_addr_spec, get_angle_addr
@ -85,9 +85,6 @@ __all__ = [
"SMTPChannel", "SMTPServer", "DebuggingServer", "PureProxy",
]
asyncore = import_module('asyncore', deprecated=True)
asynchat = import_module('asynchat', deprecated=True)
program = sys.argv[0]
__version__ = 'Python SMTP proxy version 0.3'

View File

@ -1,3 +1,8 @@
# TODO: This module was deprecated and removed from CPython 3.12
# Now it is a test-only helper. Any attempts to rewrite exising tests that
# are using this module and remove it completely are appreciated!
# See: https://github.com/python/cpython/issues/72719
# -*- Mode: Python; tab-width: 4 -*-
# Id: asynchat.py,v 2.26 2000/09/07 22:29:26 rushing Exp
# Author: Sam Rushing <rushing@nightmare.com>
@ -45,15 +50,10 @@ command will be accumulated (using your own 'collect_incoming_data'
method) up to the terminator, and then control will be returned to
you - by calling your self.found_terminator() method.
"""
import asyncore
from collections import deque
from warnings import _deprecated
_DEPRECATION_MSG = ('The {name} module is deprecated and will be removed in '
'Python {remove}. The recommended replacement is asyncio')
_deprecated(__name__, _DEPRECATION_MSG, remove=(3, 12))
from test.support import asyncore
class async_chat(asyncore.dispatcher):

View File

@ -1,3 +1,8 @@
# TODO: This module was deprecated and removed from CPython 3.12
# Now it is a test-only helper. Any attempts to rewrite exising tests that
# are using this module and remove it completely are appreciated!
# See: https://github.com/python/cpython/issues/72719
# -*- Mode: Python -*-
# Id: asyncore.py,v 2.51 2000/09/07 22:29:26 rushing Exp
# Author: Sam Rushing <rushing@nightmare.com>
@ -57,10 +62,6 @@ from errno import EALREADY, EINPROGRESS, EWOULDBLOCK, ECONNRESET, EINVAL, \
ENOTCONN, ESHUTDOWN, EISCONN, EBADF, ECONNABORTED, EPIPE, EAGAIN, \
errorcode
_DEPRECATION_MSG = ('The {name} module is deprecated and will be removed in '
'Python {remove}. The recommended replacement is asyncio')
warnings._deprecated(__name__, _DEPRECATION_MSG, remove=(3, 12))
_DISCONNECTED = frozenset({ECONNRESET, ENOTCONN, ESHUTDOWN, ECONNABORTED, EPIPE,
EBADF})

View File

@ -1,293 +0,0 @@
# test asynchat
from test import support
from test.support import socket_helper
from test.support import threading_helper
from test.support import warnings_helper
import errno
import socket
import sys
import threading
import time
import unittest
import unittest.mock
asynchat = warnings_helper.import_deprecated('asynchat')
asyncore = warnings_helper.import_deprecated('asyncore')
support.requires_working_socket(module=True)
HOST = socket_helper.HOST
SERVER_QUIT = b'QUIT\n'
class echo_server(threading.Thread):
# parameter to determine the number of bytes passed back to the
# client each send
chunk_size = 1
def __init__(self, event):
threading.Thread.__init__(self)
self.event = event
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.port = socket_helper.bind_port(self.sock)
# This will be set if the client wants us to wait before echoing
# data back.
self.start_resend_event = None
def run(self):
self.sock.listen()
self.event.set()
conn, client = self.sock.accept()
self.buffer = b""
# collect data until quit message is seen
while SERVER_QUIT not in self.buffer:
data = conn.recv(1)
if not data:
break
self.buffer = self.buffer + data
# remove the SERVER_QUIT message
self.buffer = self.buffer.replace(SERVER_QUIT, b'')
if self.start_resend_event:
self.start_resend_event.wait()
# re-send entire set of collected data
try:
# this may fail on some tests, such as test_close_when_done,
# since the client closes the channel when it's done sending
while self.buffer:
n = conn.send(self.buffer[:self.chunk_size])
time.sleep(0.001)
self.buffer = self.buffer[n:]
except:
pass
conn.close()
self.sock.close()
class echo_client(asynchat.async_chat):
def __init__(self, terminator, server_port):
asynchat.async_chat.__init__(self)
self.contents = []
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.connect((HOST, server_port))
self.set_terminator(terminator)
self.buffer = b""
def handle_connect(self):
pass
if sys.platform == 'darwin':
# select.poll returns a select.POLLHUP at the end of the tests
# on darwin, so just ignore it
def handle_expt(self):
pass
def collect_incoming_data(self, data):
self.buffer += data
def found_terminator(self):
self.contents.append(self.buffer)
self.buffer = b""
def start_echo_server():
event = threading.Event()
s = echo_server(event)
s.start()
event.wait()
event.clear()
time.sleep(0.01) # Give server time to start accepting.
return s, event
class TestAsynchat(unittest.TestCase):
usepoll = False
def setUp(self):
self._threads = threading_helper.threading_setup()
def tearDown(self):
threading_helper.threading_cleanup(*self._threads)
def line_terminator_check(self, term, server_chunk):
event = threading.Event()
s = echo_server(event)
s.chunk_size = server_chunk
s.start()
event.wait()
event.clear()
time.sleep(0.01) # Give server time to start accepting.
c = echo_client(term, s.port)
c.push(b"hello ")
c.push(b"world" + term)
c.push(b"I'm not dead yet!" + term)
c.push(SERVER_QUIT)
asyncore.loop(use_poll=self.usepoll, count=300, timeout=.01)
threading_helper.join_thread(s)
self.assertEqual(c.contents, [b"hello world", b"I'm not dead yet!"])
# the line terminator tests below check receiving variously-sized
# chunks back from the server in order to exercise all branches of
# async_chat.handle_read
def test_line_terminator1(self):
# test one-character terminator
for l in (1, 2, 3):
self.line_terminator_check(b'\n', l)
def test_line_terminator2(self):
# test two-character terminator
for l in (1, 2, 3):
self.line_terminator_check(b'\r\n', l)
def test_line_terminator3(self):
# test three-character terminator
for l in (1, 2, 3):
self.line_terminator_check(b'qqq', l)
def numeric_terminator_check(self, termlen):
# Try reading a fixed number of bytes
s, event = start_echo_server()
c = echo_client(termlen, s.port)
data = b"hello world, I'm not dead yet!\n"
c.push(data)
c.push(SERVER_QUIT)
asyncore.loop(use_poll=self.usepoll, count=300, timeout=.01)
threading_helper.join_thread(s)
self.assertEqual(c.contents, [data[:termlen]])
def test_numeric_terminator1(self):
# check that ints & longs both work (since type is
# explicitly checked in async_chat.handle_read)
self.numeric_terminator_check(1)
def test_numeric_terminator2(self):
self.numeric_terminator_check(6)
def test_none_terminator(self):
# Try reading a fixed number of bytes
s, event = start_echo_server()
c = echo_client(None, s.port)
data = b"hello world, I'm not dead yet!\n"
c.push(data)
c.push(SERVER_QUIT)
asyncore.loop(use_poll=self.usepoll, count=300, timeout=.01)
threading_helper.join_thread(s)
self.assertEqual(c.contents, [])
self.assertEqual(c.buffer, data)
def test_simple_producer(self):
s, event = start_echo_server()
c = echo_client(b'\n', s.port)
data = b"hello world\nI'm not dead yet!\n"
p = asynchat.simple_producer(data+SERVER_QUIT, buffer_size=8)
c.push_with_producer(p)
asyncore.loop(use_poll=self.usepoll, count=300, timeout=.01)
threading_helper.join_thread(s)
self.assertEqual(c.contents, [b"hello world", b"I'm not dead yet!"])
def test_string_producer(self):
s, event = start_echo_server()
c = echo_client(b'\n', s.port)
data = b"hello world\nI'm not dead yet!\n"
c.push_with_producer(data+SERVER_QUIT)
asyncore.loop(use_poll=self.usepoll, count=300, timeout=.01)
threading_helper.join_thread(s)
self.assertEqual(c.contents, [b"hello world", b"I'm not dead yet!"])
def test_empty_line(self):
# checks that empty lines are handled correctly
s, event = start_echo_server()
c = echo_client(b'\n', s.port)
c.push(b"hello world\n\nI'm not dead yet!\n")
c.push(SERVER_QUIT)
asyncore.loop(use_poll=self.usepoll, count=300, timeout=.01)
threading_helper.join_thread(s)
self.assertEqual(c.contents,
[b"hello world", b"", b"I'm not dead yet!"])
def test_close_when_done(self):
s, event = start_echo_server()
s.start_resend_event = threading.Event()
c = echo_client(b'\n', s.port)
c.push(b"hello world\nI'm not dead yet!\n")
c.push(SERVER_QUIT)
c.close_when_done()
asyncore.loop(use_poll=self.usepoll, count=300, timeout=.01)
# Only allow the server to start echoing data back to the client after
# the client has closed its connection. This prevents a race condition
# where the server echoes all of its data before we can check that it
# got any down below.
s.start_resend_event.set()
threading_helper.join_thread(s)
self.assertEqual(c.contents, [])
# the server might have been able to send a byte or two back, but this
# at least checks that it received something and didn't just fail
# (which could still result in the client not having received anything)
self.assertGreater(len(s.buffer), 0)
def test_push(self):
# Issue #12523: push() should raise a TypeError if it doesn't get
# a bytes string
s, event = start_echo_server()
c = echo_client(b'\n', s.port)
data = b'bytes\n'
c.push(data)
c.push(bytearray(data))
c.push(memoryview(data))
self.assertRaises(TypeError, c.push, 10)
self.assertRaises(TypeError, c.push, 'unicode')
c.push(SERVER_QUIT)
asyncore.loop(use_poll=self.usepoll, count=300, timeout=.01)
threading_helper.join_thread(s)
self.assertEqual(c.contents, [b'bytes', b'bytes', b'bytes'])
class TestAsynchat_WithPoll(TestAsynchat):
usepoll = True
class TestAsynchatMocked(unittest.TestCase):
def test_blockingioerror(self):
# Issue #16133: handle_read() must ignore BlockingIOError
sock = unittest.mock.Mock()
sock.recv.side_effect = BlockingIOError(errno.EAGAIN)
dispatcher = asynchat.async_chat()
dispatcher.set_socket(sock)
self.addCleanup(dispatcher.del_channel)
with unittest.mock.patch.object(dispatcher, 'handle_error') as error:
dispatcher.handle_read()
self.assertFalse(error.called)
class TestHelperFunctions(unittest.TestCase):
def test_find_prefix_at_end(self):
self.assertEqual(asynchat.find_prefix_at_end("qwerty\r", "\r\n"), 1)
self.assertEqual(asynchat.find_prefix_at_end("qwertydkjf", "\r\n"), 0)
class TestNotConnected(unittest.TestCase):
def test_disallow_negative_terminator(self):
# Issue #11259
client = asynchat.async_chat()
self.assertRaises(ValueError, client.set_terminator, -1)
if __name__ == "__main__":
unittest.main()

View File

@ -1,840 +0,0 @@
import unittest
import select
import os
import socket
import sys
import time
import errno
import struct
import threading
from test import support
from test.support import os_helper
from test.support import socket_helper
from test.support import threading_helper
from test.support import warnings_helper
from io import BytesIO
if support.PGO:
raise unittest.SkipTest("test is not helpful for PGO")
support.requires_working_socket(module=True)
asyncore = warnings_helper.import_deprecated('asyncore')
HAS_UNIX_SOCKETS = hasattr(socket, 'AF_UNIX')
class dummysocket:
def __init__(self):
self.closed = False
def close(self):
self.closed = True
def fileno(self):
return 42
class dummychannel:
def __init__(self):
self.socket = dummysocket()
def close(self):
self.socket.close()
class exitingdummy:
def __init__(self):
pass
def handle_read_event(self):
raise asyncore.ExitNow()
handle_write_event = handle_read_event
handle_close = handle_read_event
handle_expt_event = handle_read_event
class crashingdummy:
def __init__(self):
self.error_handled = False
def handle_read_event(self):
raise Exception()
handle_write_event = handle_read_event
handle_close = handle_read_event
handle_expt_event = handle_read_event
def handle_error(self):
self.error_handled = True
# used when testing senders; just collects what it gets until newline is sent
def capture_server(evt, buf, serv):
try:
serv.listen()
conn, addr = serv.accept()
except TimeoutError:
pass
else:
n = 200
for _ in support.busy_retry(support.SHORT_TIMEOUT):
r, w, e = select.select([conn], [], [], 0.1)
if r:
n -= 1
data = conn.recv(10)
# keep everything except for the newline terminator
buf.write(data.replace(b'\n', b''))
if b'\n' in data:
break
if n <= 0:
break
conn.close()
finally:
serv.close()
evt.set()
def bind_af_aware(sock, addr):
"""Helper function to bind a socket according to its family."""
if HAS_UNIX_SOCKETS and sock.family == socket.AF_UNIX:
# Make sure the path doesn't exist.
os_helper.unlink(addr)
socket_helper.bind_unix_socket(sock, addr)
else:
sock.bind(addr)
class HelperFunctionTests(unittest.TestCase):
def test_readwriteexc(self):
# Check exception handling behavior of read, write and _exception
# check that ExitNow exceptions in the object handler method
# bubbles all the way up through asyncore read/write/_exception calls
tr1 = exitingdummy()
self.assertRaises(asyncore.ExitNow, asyncore.read, tr1)
self.assertRaises(asyncore.ExitNow, asyncore.write, tr1)
self.assertRaises(asyncore.ExitNow, asyncore._exception, tr1)
# check that an exception other than ExitNow in the object handler
# method causes the handle_error method to get called
tr2 = crashingdummy()
asyncore.read(tr2)
self.assertEqual(tr2.error_handled, True)
tr2 = crashingdummy()
asyncore.write(tr2)
self.assertEqual(tr2.error_handled, True)
tr2 = crashingdummy()
asyncore._exception(tr2)
self.assertEqual(tr2.error_handled, True)
# asyncore.readwrite uses constants in the select module that
# are not present in Windows systems (see this thread:
# http://mail.python.org/pipermail/python-list/2001-October/109973.html)
# These constants should be present as long as poll is available
@unittest.skipUnless(hasattr(select, 'poll'), 'select.poll required')
def test_readwrite(self):
# Check that correct methods are called by readwrite()
attributes = ('read', 'expt', 'write', 'closed', 'error_handled')
expected = (
(select.POLLIN, 'read'),
(select.POLLPRI, 'expt'),
(select.POLLOUT, 'write'),
(select.POLLERR, 'closed'),
(select.POLLHUP, 'closed'),
(select.POLLNVAL, 'closed'),
)
class testobj:
def __init__(self):
self.read = False
self.write = False
self.closed = False
self.expt = False
self.error_handled = False
def handle_read_event(self):
self.read = True
def handle_write_event(self):
self.write = True
def handle_close(self):
self.closed = True
def handle_expt_event(self):
self.expt = True
def handle_error(self):
self.error_handled = True
for flag, expectedattr in expected:
tobj = testobj()
self.assertEqual(getattr(tobj, expectedattr), False)
asyncore.readwrite(tobj, flag)
# Only the attribute modified by the routine we expect to be
# called should be True.
for attr in attributes:
self.assertEqual(getattr(tobj, attr), attr==expectedattr)
# check that ExitNow exceptions in the object handler method
# bubbles all the way up through asyncore readwrite call
tr1 = exitingdummy()
self.assertRaises(asyncore.ExitNow, asyncore.readwrite, tr1, flag)
# check that an exception other than ExitNow in the object handler
# method causes the handle_error method to get called
tr2 = crashingdummy()
self.assertEqual(tr2.error_handled, False)
asyncore.readwrite(tr2, flag)
self.assertEqual(tr2.error_handled, True)
def test_closeall(self):
self.closeall_check(False)
def test_closeall_default(self):
self.closeall_check(True)
def closeall_check(self, usedefault):
# Check that close_all() closes everything in a given map
l = []
testmap = {}
for i in range(10):
c = dummychannel()
l.append(c)
self.assertEqual(c.socket.closed, False)
testmap[i] = c
if usedefault:
socketmap = asyncore.socket_map
try:
asyncore.socket_map = testmap
asyncore.close_all()
finally:
testmap, asyncore.socket_map = asyncore.socket_map, socketmap
else:
asyncore.close_all(testmap)
self.assertEqual(len(testmap), 0)
for c in l:
self.assertEqual(c.socket.closed, True)
def test_compact_traceback(self):
try:
raise Exception("I don't like spam!")
except:
real_t, real_v, real_tb = sys.exc_info()
r = asyncore.compact_traceback()
else:
self.fail("Expected exception")
(f, function, line), t, v, info = r
self.assertEqual(os.path.split(f)[-1], 'test_asyncore.py')
self.assertEqual(function, 'test_compact_traceback')
self.assertEqual(t, real_t)
self.assertEqual(v, real_v)
self.assertEqual(info, '[%s|%s|%s]' % (f, function, line))
class DispatcherTests(unittest.TestCase):
def setUp(self):
pass
def tearDown(self):
asyncore.close_all()
def test_basic(self):
d = asyncore.dispatcher()
self.assertEqual(d.readable(), True)
self.assertEqual(d.writable(), True)
def test_repr(self):
d = asyncore.dispatcher()
self.assertEqual(repr(d), '<asyncore.dispatcher at %#x>' % id(d))
def test_log(self):
d = asyncore.dispatcher()
# capture output of dispatcher.log() (to stderr)
l1 = "Lovely spam! Wonderful spam!"
l2 = "I don't like spam!"
with support.captured_stderr() as stderr:
d.log(l1)
d.log(l2)
lines = stderr.getvalue().splitlines()
self.assertEqual(lines, ['log: %s' % l1, 'log: %s' % l2])
def test_log_info(self):
d = asyncore.dispatcher()
# capture output of dispatcher.log_info() (to stdout via print)
l1 = "Have you got anything without spam?"
l2 = "Why can't she have egg bacon spam and sausage?"
l3 = "THAT'S got spam in it!"
with support.captured_stdout() as stdout:
d.log_info(l1, 'EGGS')
d.log_info(l2)
d.log_info(l3, 'SPAM')
lines = stdout.getvalue().splitlines()
expected = ['EGGS: %s' % l1, 'info: %s' % l2, 'SPAM: %s' % l3]
self.assertEqual(lines, expected)
def test_unhandled(self):
d = asyncore.dispatcher()
d.ignore_log_types = ()
# capture output of dispatcher.log_info() (to stdout via print)
with support.captured_stdout() as stdout:
d.handle_expt()
d.handle_read()
d.handle_write()
d.handle_connect()
lines = stdout.getvalue().splitlines()
expected = ['warning: unhandled incoming priority event',
'warning: unhandled read event',
'warning: unhandled write event',
'warning: unhandled connect event']
self.assertEqual(lines, expected)
def test_strerror(self):
# refers to bug #8573
err = asyncore._strerror(errno.EPERM)
if hasattr(os, 'strerror'):
self.assertEqual(err, os.strerror(errno.EPERM))
err = asyncore._strerror(-1)
self.assertTrue(err != "")
class dispatcherwithsend_noread(asyncore.dispatcher_with_send):
def readable(self):
return False
def handle_connect(self):
pass
class DispatcherWithSendTests(unittest.TestCase):
def setUp(self):
pass
def tearDown(self):
asyncore.close_all()
@threading_helper.reap_threads
def test_send(self):
evt = threading.Event()
sock = socket.socket()
sock.settimeout(3)
port = socket_helper.bind_port(sock)
cap = BytesIO()
args = (evt, cap, sock)
t = threading.Thread(target=capture_server, args=args)
t.start()
try:
# wait a little longer for the server to initialize (it sometimes
# refuses connections on slow machines without this wait)
time.sleep(0.2)
data = b"Suppose there isn't a 16-ton weight?"
d = dispatcherwithsend_noread()
d.create_socket()
d.connect((socket_helper.HOST, port))
# give time for socket to connect
time.sleep(0.1)
d.send(data)
d.send(data)
d.send(b'\n')
n = 1000
while d.out_buffer and n > 0:
asyncore.poll()
n -= 1
evt.wait()
self.assertEqual(cap.getvalue(), data*2)
finally:
threading_helper.join_thread(t)
@unittest.skipUnless(hasattr(asyncore, 'file_wrapper'),
'asyncore.file_wrapper required')
class FileWrapperTest(unittest.TestCase):
def setUp(self):
self.d = b"It's not dead, it's sleeping!"
with open(os_helper.TESTFN, 'wb') as file:
file.write(self.d)
def tearDown(self):
os_helper.unlink(os_helper.TESTFN)
def test_recv(self):
fd = os.open(os_helper.TESTFN, os.O_RDONLY)
w = asyncore.file_wrapper(fd)
os.close(fd)
self.assertNotEqual(w.fd, fd)
self.assertNotEqual(w.fileno(), fd)
self.assertEqual(w.recv(13), b"It's not dead")
self.assertEqual(w.read(6), b", it's")
w.close()
self.assertRaises(OSError, w.read, 1)
def test_send(self):
d1 = b"Come again?"
d2 = b"I want to buy some cheese."
fd = os.open(os_helper.TESTFN, os.O_WRONLY | os.O_APPEND)
w = asyncore.file_wrapper(fd)
os.close(fd)
w.write(d1)
w.send(d2)
w.close()
with open(os_helper.TESTFN, 'rb') as file:
self.assertEqual(file.read(), self.d + d1 + d2)
@unittest.skipUnless(hasattr(asyncore, 'file_dispatcher'),
'asyncore.file_dispatcher required')
def test_dispatcher(self):
fd = os.open(os_helper.TESTFN, os.O_RDONLY)
data = []
class FileDispatcher(asyncore.file_dispatcher):
def handle_read(self):
data.append(self.recv(29))
s = FileDispatcher(fd)
os.close(fd)
asyncore.loop(timeout=0.01, use_poll=True, count=2)
self.assertEqual(b"".join(data), self.d)
def test_resource_warning(self):
# Issue #11453
fd = os.open(os_helper.TESTFN, os.O_RDONLY)
f = asyncore.file_wrapper(fd)
os.close(fd)
with warnings_helper.check_warnings(('', ResourceWarning)):
f = None
support.gc_collect()
def test_close_twice(self):
fd = os.open(os_helper.TESTFN, os.O_RDONLY)
f = asyncore.file_wrapper(fd)
os.close(fd)
os.close(f.fd) # file_wrapper dupped fd
with self.assertRaises(OSError):
f.close()
self.assertEqual(f.fd, -1)
# calling close twice should not fail
f.close()
class BaseTestHandler(asyncore.dispatcher):
def __init__(self, sock=None):
asyncore.dispatcher.__init__(self, sock)
self.flag = False
def handle_accept(self):
raise Exception("handle_accept not supposed to be called")
def handle_accepted(self):
raise Exception("handle_accepted not supposed to be called")
def handle_connect(self):
raise Exception("handle_connect not supposed to be called")
def handle_expt(self):
raise Exception("handle_expt not supposed to be called")
def handle_close(self):
raise Exception("handle_close not supposed to be called")
def handle_error(self):
raise
class BaseServer(asyncore.dispatcher):
"""A server which listens on an address and dispatches the
connection to a handler.
"""
def __init__(self, family, addr, handler=BaseTestHandler):
asyncore.dispatcher.__init__(self)
self.create_socket(family)
self.set_reuse_addr()
bind_af_aware(self.socket, addr)
self.listen(5)
self.handler = handler
@property
def address(self):
return self.socket.getsockname()
def handle_accepted(self, sock, addr):
self.handler(sock)
def handle_error(self):
raise
class BaseClient(BaseTestHandler):
def __init__(self, family, address):
BaseTestHandler.__init__(self)
self.create_socket(family)
self.connect(address)
def handle_connect(self):
pass
class BaseTestAPI:
def tearDown(self):
asyncore.close_all(ignore_all=True)
def loop_waiting_for_flag(self, instance, timeout=5):
timeout = float(timeout) / 100
count = 100
while asyncore.socket_map and count > 0:
asyncore.loop(timeout=0.01, count=1, use_poll=self.use_poll)
if instance.flag:
return
count -= 1
time.sleep(timeout)
self.fail("flag not set")
def test_handle_connect(self):
# make sure handle_connect is called on connect()
class TestClient(BaseClient):
def handle_connect(self):
self.flag = True
server = BaseServer(self.family, self.addr)
client = TestClient(self.family, server.address)
self.loop_waiting_for_flag(client)
def test_handle_accept(self):
# make sure handle_accept() is called when a client connects
class TestListener(BaseTestHandler):
def __init__(self, family, addr):
BaseTestHandler.__init__(self)
self.create_socket(family)
bind_af_aware(self.socket, addr)
self.listen(5)
self.address = self.socket.getsockname()
def handle_accept(self):
self.flag = True
server = TestListener(self.family, self.addr)
client = BaseClient(self.family, server.address)
self.loop_waiting_for_flag(server)
def test_handle_accepted(self):
# make sure handle_accepted() is called when a client connects
class TestListener(BaseTestHandler):
def __init__(self, family, addr):
BaseTestHandler.__init__(self)
self.create_socket(family)
bind_af_aware(self.socket, addr)
self.listen(5)
self.address = self.socket.getsockname()
def handle_accept(self):
asyncore.dispatcher.handle_accept(self)
def handle_accepted(self, sock, addr):
sock.close()
self.flag = True
server = TestListener(self.family, self.addr)
client = BaseClient(self.family, server.address)
self.loop_waiting_for_flag(server)
def test_handle_read(self):
# make sure handle_read is called on data received
class TestClient(BaseClient):
def handle_read(self):
self.flag = True
class TestHandler(BaseTestHandler):
def __init__(self, conn):
BaseTestHandler.__init__(self, conn)
self.send(b'x' * 1024)
server = BaseServer(self.family, self.addr, TestHandler)
client = TestClient(self.family, server.address)
self.loop_waiting_for_flag(client)
def test_handle_write(self):
# make sure handle_write is called
class TestClient(BaseClient):
def handle_write(self):
self.flag = True
server = BaseServer(self.family, self.addr)
client = TestClient(self.family, server.address)
self.loop_waiting_for_flag(client)
def test_handle_close(self):
# make sure handle_close is called when the other end closes
# the connection
class TestClient(BaseClient):
def handle_read(self):
# in order to make handle_close be called we are supposed
# to make at least one recv() call
self.recv(1024)
def handle_close(self):
self.flag = True
self.close()
class TestHandler(BaseTestHandler):
def __init__(self, conn):
BaseTestHandler.__init__(self, conn)
self.close()
server = BaseServer(self.family, self.addr, TestHandler)
client = TestClient(self.family, server.address)
self.loop_waiting_for_flag(client)
def test_handle_close_after_conn_broken(self):
# Check that ECONNRESET/EPIPE is correctly handled (issues #5661 and
# #11265).
data = b'\0' * 128
class TestClient(BaseClient):
def handle_write(self):
self.send(data)
def handle_close(self):
self.flag = True
self.close()
def handle_expt(self):
self.flag = True
self.close()
class TestHandler(BaseTestHandler):
def handle_read(self):
self.recv(len(data))
self.close()
def writable(self):
return False
server = BaseServer(self.family, self.addr, TestHandler)
client = TestClient(self.family, server.address)
self.loop_waiting_for_flag(client)
@unittest.skipIf(sys.platform.startswith("sunos"),
"OOB support is broken on Solaris")
def test_handle_expt(self):
# Make sure handle_expt is called on OOB data received.
# Note: this might fail on some platforms as OOB data is
# tenuously supported and rarely used.
if HAS_UNIX_SOCKETS and self.family == socket.AF_UNIX:
self.skipTest("Not applicable to AF_UNIX sockets.")
if sys.platform == "darwin" and self.use_poll:
self.skipTest("poll may fail on macOS; see issue #28087")
class TestClient(BaseClient):
def handle_expt(self):
self.socket.recv(1024, socket.MSG_OOB)
self.flag = True
class TestHandler(BaseTestHandler):
def __init__(self, conn):
BaseTestHandler.__init__(self, conn)
self.socket.send(bytes(chr(244), 'latin-1'), socket.MSG_OOB)
server = BaseServer(self.family, self.addr, TestHandler)
client = TestClient(self.family, server.address)
self.loop_waiting_for_flag(client)
def test_handle_error(self):
class TestClient(BaseClient):
def handle_write(self):
1.0 / 0
def handle_error(self):
self.flag = True
try:
raise
except ZeroDivisionError:
pass
else:
raise Exception("exception not raised")
server = BaseServer(self.family, self.addr)
client = TestClient(self.family, server.address)
self.loop_waiting_for_flag(client)
def test_connection_attributes(self):
server = BaseServer(self.family, self.addr)
client = BaseClient(self.family, server.address)
# we start disconnected
self.assertFalse(server.connected)
self.assertTrue(server.accepting)
# this can't be taken for granted across all platforms
#self.assertFalse(client.connected)
self.assertFalse(client.accepting)
# execute some loops so that client connects to server
asyncore.loop(timeout=0.01, use_poll=self.use_poll, count=100)
self.assertFalse(server.connected)
self.assertTrue(server.accepting)
self.assertTrue(client.connected)
self.assertFalse(client.accepting)
# disconnect the client
client.close()
self.assertFalse(server.connected)
self.assertTrue(server.accepting)
self.assertFalse(client.connected)
self.assertFalse(client.accepting)
# stop serving
server.close()
self.assertFalse(server.connected)
self.assertFalse(server.accepting)
def test_create_socket(self):
s = asyncore.dispatcher()
s.create_socket(self.family)
self.assertEqual(s.socket.type, socket.SOCK_STREAM)
self.assertEqual(s.socket.family, self.family)
self.assertEqual(s.socket.gettimeout(), 0)
self.assertFalse(s.socket.get_inheritable())
def test_bind(self):
if HAS_UNIX_SOCKETS and self.family == socket.AF_UNIX:
self.skipTest("Not applicable to AF_UNIX sockets.")
s1 = asyncore.dispatcher()
s1.create_socket(self.family)
s1.bind(self.addr)
s1.listen(5)
port = s1.socket.getsockname()[1]
s2 = asyncore.dispatcher()
s2.create_socket(self.family)
# EADDRINUSE indicates the socket was correctly bound
self.assertRaises(OSError, s2.bind, (self.addr[0], port))
def test_set_reuse_addr(self):
if HAS_UNIX_SOCKETS and self.family == socket.AF_UNIX:
self.skipTest("Not applicable to AF_UNIX sockets.")
with socket.socket(self.family) as sock:
try:
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
except OSError:
unittest.skip("SO_REUSEADDR not supported on this platform")
else:
# if SO_REUSEADDR succeeded for sock we expect asyncore
# to do the same
s = asyncore.dispatcher(socket.socket(self.family))
self.assertFalse(s.socket.getsockopt(socket.SOL_SOCKET,
socket.SO_REUSEADDR))
s.socket.close()
s.create_socket(self.family)
s.set_reuse_addr()
self.assertTrue(s.socket.getsockopt(socket.SOL_SOCKET,
socket.SO_REUSEADDR))
@threading_helper.reap_threads
def test_quick_connect(self):
# see: http://bugs.python.org/issue10340
if self.family not in (socket.AF_INET, getattr(socket, "AF_INET6", object())):
self.skipTest("test specific to AF_INET and AF_INET6")
server = BaseServer(self.family, self.addr)
# run the thread 500 ms: the socket should be connected in 200 ms
t = threading.Thread(target=lambda: asyncore.loop(timeout=0.1,
count=5))
t.start()
try:
with socket.socket(self.family, socket.SOCK_STREAM) as s:
s.settimeout(.2)
s.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER,
struct.pack('ii', 1, 0))
try:
s.connect(server.address)
except OSError:
pass
finally:
threading_helper.join_thread(t)
class TestAPI_UseIPv4Sockets(BaseTestAPI):
family = socket.AF_INET
addr = (socket_helper.HOST, 0)
@unittest.skipUnless(socket_helper.IPV6_ENABLED, 'IPv6 support required')
class TestAPI_UseIPv6Sockets(BaseTestAPI):
family = socket.AF_INET6
addr = (socket_helper.HOSTv6, 0)
@unittest.skipUnless(HAS_UNIX_SOCKETS, 'Unix sockets required')
class TestAPI_UseUnixSockets(BaseTestAPI):
if HAS_UNIX_SOCKETS:
family = socket.AF_UNIX
addr = os_helper.TESTFN
def tearDown(self):
os_helper.unlink(self.addr)
BaseTestAPI.tearDown(self)
class TestAPI_UseIPv4Select(TestAPI_UseIPv4Sockets, unittest.TestCase):
use_poll = False
@unittest.skipUnless(hasattr(select, 'poll'), 'select.poll required')
class TestAPI_UseIPv4Poll(TestAPI_UseIPv4Sockets, unittest.TestCase):
use_poll = True
class TestAPI_UseIPv6Select(TestAPI_UseIPv6Sockets, unittest.TestCase):
use_poll = False
@unittest.skipUnless(hasattr(select, 'poll'), 'select.poll required')
class TestAPI_UseIPv6Poll(TestAPI_UseIPv6Sockets, unittest.TestCase):
use_poll = True
class TestAPI_UseUnixSocketsSelect(TestAPI_UseUnixSockets, unittest.TestCase):
use_poll = False
@unittest.skipUnless(hasattr(select, 'poll'), 'select.poll required')
class TestAPI_UseUnixSocketsPoll(TestAPI_UseUnixSockets, unittest.TestCase):
use_poll = True
if __name__ == "__main__":
unittest.main()

View File

@ -21,13 +21,11 @@ from test import support
from test.support import threading_helper
from test.support import socket_helper
from test.support import warnings_helper
from test.support import asynchat
from test.support import asyncore
from test.support.socket_helper import HOST, HOSTv6
asynchat = warnings_helper.import_deprecated('asynchat')
asyncore = warnings_helper.import_deprecated('asyncore')
support.requires_working_socket(module=True)
TIMEOUT = support.LOOPBACK_TIMEOUT

View File

@ -47,6 +47,7 @@ from test.support import os_helper
from test.support import socket_helper
from test.support import threading_helper
from test.support import warnings_helper
from test.support import asyncore
from test.support.logging_helper import TestHandler
import textwrap
import threading
@ -64,9 +65,6 @@ from socketserver import (ThreadingUDPServer, DatagramRequestHandler,
with warnings.catch_warnings():
from . import smtpd
asyncore = warnings_helper.import_deprecated('asyncore')
try:
import win32evtlog, win32evtlogutil, pywintypes
except ImportError:

View File

@ -15,11 +15,8 @@ from test import support as test_support
from test.support import hashlib_helper
from test.support import socket_helper
from test.support import threading_helper
from test.support import warnings_helper
asynchat = warnings_helper.import_deprecated('asynchat')
asyncore = warnings_helper.import_deprecated('asyncore')
from test.support import asynchat
from test.support import asyncore
test_support.requires_working_socket(module=True)

View File

@ -21,13 +21,11 @@ from test import support, mock_socket
from test.support import hashlib_helper
from test.support import socket_helper
from test.support import threading_helper
from test.support import warnings_helper
from test.support import asyncore
from unittest.mock import Mock
from . import smtpd
asyncore = warnings_helper.import_deprecated('asyncore')
support.requires_working_socket(module=True)

View File

@ -9,6 +9,7 @@ from test.support import os_helper
from test.support import socket_helper
from test.support import threading_helper
from test.support import warnings_helper
from test.support import asyncore
import socket
import select
import time
@ -30,9 +31,6 @@ except ImportError:
ctypes = None
asyncore = warnings_helper.import_deprecated('asyncore')
ssl = import_helper.import_module("ssl")
import _ssl

View File

@ -0,0 +1 @@
Remove modules :mod:`asyncore` and :mod:`asynchat`, which were deprecated by :pep:`594`.

View File

@ -96,9 +96,7 @@ static const char* _Py_stdlib_module_names[] = {
"argparse",
"array",
"ast",
"asynchat",
"asyncio",
"asyncore",
"atexit",
"audioop",
"base64",

View File

@ -43,8 +43,6 @@ OMIT_FILES = (
# build system
"lib2to3/",
# deprecated
"asyncore.py",
"asynchat.py",
"uu.py",
"xdrlib.py",
# other platforms