mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 21:51:50 +00:00 
			
		
		
		
	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:
		
							parent
							
								
									c43714fbcd
								
							
						
					
					
						commit
						e56e33d271
					
				
					 21 changed files with 32 additions and 1762 deletions
				
			
		
							
								
								
									
										2
									
								
								.github/CODEOWNERS
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/CODEOWNERS
									
										
									
									
										vendored
									
									
								
							|  | @ -137,8 +137,6 @@ Lib/ast.py                    @isidentical | |||
| 
 | ||||
| **/*typing*                   @gvanrossum @Fidget-Spinner @JelleZijlstra @AlexWaygood | ||||
| 
 | ||||
| **/*asyncore                  @giampaolo | ||||
| **/*asynchat                  @giampaolo | ||||
| **/*ftplib                    @giampaolo | ||||
| **/*shutil                    @giampaolo | ||||
| 
 | ||||
|  |  | |||
|  | @ -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() | ||||
|  | @ -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() | ||||
|  | @ -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? | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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 | ||||
| 
 | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -161,11 +161,11 @@ def restore_warnings_filters(self, saved_filters): | |||
|         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) | ||||
|  |  | |||
|  | @ -77,7 +77,7 @@ | |||
| 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 @@ | |||
|     "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' | ||||
| 
 | ||||
|  |  | |||
|  | @ -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 @@ | |||
| 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): | ||||
|  | @ -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 @@ | |||
|      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}) | ||||
|  | @ -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() | ||||
|  | @ -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() | ||||
|  | @ -21,13 +21,11 @@ | |||
| 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 | ||||
|  |  | |||
|  | @ -47,6 +47,7 @@ | |||
| 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 @@ | |||
| with warnings.catch_warnings(): | ||||
|     from . import smtpd | ||||
| 
 | ||||
| asyncore = warnings_helper.import_deprecated('asyncore') | ||||
| 
 | ||||
| 
 | ||||
| try: | ||||
|     import win32evtlog, win32evtlogutil, pywintypes | ||||
| except ImportError: | ||||
|  |  | |||
|  | @ -15,11 +15,8 @@ | |||
| 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) | ||||
|  |  | |||
|  | @ -21,13 +21,11 @@ | |||
| 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) | ||||
| 
 | ||||
|  |  | |||
|  | @ -9,6 +9,7 @@ | |||
| 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 @@ | |||
|     ctypes = None | ||||
| 
 | ||||
| 
 | ||||
| asyncore = warnings_helper.import_deprecated('asyncore') | ||||
| 
 | ||||
| 
 | ||||
| ssl = import_helper.import_module("ssl") | ||||
| import _ssl | ||||
| 
 | ||||
|  |  | |||
|  | @ -0,0 +1 @@ | |||
| Remove modules :mod:`asyncore` and :mod:`asynchat`, which were deprecated by :pep:`594`. | ||||
							
								
								
									
										2
									
								
								Python/stdlib_module_names.h
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								Python/stdlib_module_names.h
									
										
									
										generated
									
									
									
								
							|  | @ -96,9 +96,7 @@ static const char* _Py_stdlib_module_names[] = { | |||
| "argparse", | ||||
| "array", | ||||
| "ast", | ||||
| "asynchat", | ||||
| "asyncio", | ||||
| "asyncore", | ||||
| "atexit", | ||||
| "audioop", | ||||
| "base64", | ||||
|  |  | |||
|  | @ -43,8 +43,6 @@ | |||
|     # build system | ||||
|     "lib2to3/", | ||||
|     # deprecated | ||||
|     "asyncore.py", | ||||
|     "asynchat.py", | ||||
|     "uu.py", | ||||
|     "xdrlib.py", | ||||
|     # other platforms | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Nikita Sobolev
						Nikita Sobolev