Skip to content

Commit cf7eaa4

Browse files
authored
Revert "bpo-28533: Remove asyncore, asynchat, smtpd modules (GH-29521)" (GH-29951)
This reverts commit 9bf2cbc.
1 parent 2bf5517 commit cf7eaa4

28 files changed

+3228
-34
lines changed

.github/CODEOWNERS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@ Lib/ast.py @isidentical
130130

131131
**/*typing* @gvanrossum @Fidget-Spinner
132132

133+
**/*asyncore @giampaolo
134+
**/*asynchat @giampaolo
133135
**/*ftplib @giampaolo
134136
**/*shutil @giampaolo
135137

Doc/library/asynchat.rst

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
:mod:`asynchat` --- Asynchronous socket command/response handler
2+
================================================================
3+
4+
.. module:: asynchat
5+
:synopsis: Support for asynchronous command/response protocols.
6+
7+
.. moduleauthor:: Sam Rushing <rushing@nightmare.com>
8+
.. sectionauthor:: Steve Holden <sholden@holdenweb.com>
9+
10+
**Source code:** :source:`Lib/asynchat.py`
11+
12+
.. deprecated:: 3.6
13+
Please use :mod:`asyncio` instead.
14+
15+
--------------
16+
17+
.. note::
18+
19+
This module exists for backwards compatibility only. For new code we
20+
recommend using :mod:`asyncio`.
21+
22+
This module builds on the :mod:`asyncore` infrastructure, simplifying
23+
asynchronous clients and servers and making it easier to handle protocols
24+
whose elements are terminated by arbitrary strings, or are of variable length.
25+
:mod:`asynchat` defines the abstract class :class:`async_chat` that you
26+
subclass, providing implementations of the :meth:`collect_incoming_data` and
27+
:meth:`found_terminator` methods. It uses the same asynchronous loop as
28+
:mod:`asyncore`, and the two types of channel, :class:`asyncore.dispatcher`
29+
and :class:`asynchat.async_chat`, can freely be mixed in the channel map.
30+
Typically an :class:`asyncore.dispatcher` server channel generates new
31+
:class:`asynchat.async_chat` channel objects as it receives incoming
32+
connection requests.
33+
34+
35+
.. class:: async_chat()
36+
37+
This class is an abstract subclass of :class:`asyncore.dispatcher`. To make
38+
practical use of the code you must subclass :class:`async_chat`, providing
39+
meaningful :meth:`collect_incoming_data` and :meth:`found_terminator`
40+
methods.
41+
The :class:`asyncore.dispatcher` methods can be used, although not all make
42+
sense in a message/response context.
43+
44+
Like :class:`asyncore.dispatcher`, :class:`async_chat` defines a set of
45+
events that are generated by an analysis of socket conditions after a
46+
:c:func:`select` call. Once the polling loop has been started the
47+
:class:`async_chat` object's methods are called by the event-processing
48+
framework with no action on the part of the programmer.
49+
50+
Two class attributes can be modified, to improve performance, or possibly
51+
even to conserve memory.
52+
53+
54+
.. data:: ac_in_buffer_size
55+
56+
The asynchronous input buffer size (default ``4096``).
57+
58+
59+
.. data:: ac_out_buffer_size
60+
61+
The asynchronous output buffer size (default ``4096``).
62+
63+
Unlike :class:`asyncore.dispatcher`, :class:`async_chat` allows you to
64+
define a :abbr:`FIFO (first-in, first-out)` queue of *producers*. A producer need
65+
have only one method, :meth:`more`, which should return data to be
66+
transmitted on the channel.
67+
The producer indicates exhaustion (*i.e.* that it contains no more data) by
68+
having its :meth:`more` method return the empty bytes object. At this point
69+
the :class:`async_chat` object removes the producer from the queue and starts
70+
using the next producer, if any. When the producer queue is empty the
71+
:meth:`handle_write` method does nothing. You use the channel object's
72+
:meth:`set_terminator` method to describe how to recognize the end of, or
73+
an important breakpoint in, an incoming transmission from the remote
74+
endpoint.
75+
76+
To build a functioning :class:`async_chat` subclass your input methods
77+
:meth:`collect_incoming_data` and :meth:`found_terminator` must handle the
78+
data that the channel receives asynchronously. The methods are described
79+
below.
80+
81+
82+
.. method:: async_chat.close_when_done()
83+
84+
Pushes a ``None`` on to the producer queue. When this producer is popped off
85+
the queue it causes the channel to be closed.
86+
87+
88+
.. method:: async_chat.collect_incoming_data(data)
89+
90+
Called with *data* holding an arbitrary amount of received data. The
91+
default method, which must be overridden, raises a
92+
:exc:`NotImplementedError` exception.
93+
94+
95+
.. method:: async_chat.discard_buffers()
96+
97+
In emergencies this method will discard any data held in the input and/or
98+
output buffers and the producer queue.
99+
100+
101+
.. method:: async_chat.found_terminator()
102+
103+
Called when the incoming data stream matches the termination condition set
104+
by :meth:`set_terminator`. The default method, which must be overridden,
105+
raises a :exc:`NotImplementedError` exception. The buffered input data
106+
should be available via an instance attribute.
107+
108+
109+
.. method:: async_chat.get_terminator()
110+
111+
Returns the current terminator for the channel.
112+
113+
114+
.. method:: async_chat.push(data)
115+
116+
Pushes data on to the channel's queue to ensure its transmission.
117+
This is all you need to do to have the channel write the data out to the
118+
network, although it is possible to use your own producers in more complex
119+
schemes to implement encryption and chunking, for example.
120+
121+
122+
.. method:: async_chat.push_with_producer(producer)
123+
124+
Takes a producer object and adds it to the producer queue associated with
125+
the channel. When all currently-pushed producers have been exhausted the
126+
channel will consume this producer's data by calling its :meth:`more`
127+
method and send the data to the remote endpoint.
128+
129+
130+
.. method:: async_chat.set_terminator(term)
131+
132+
Sets the terminating condition to be recognized on the channel. ``term``
133+
may be any of three types of value, corresponding to three different ways
134+
to handle incoming protocol data.
135+
136+
+-----------+---------------------------------------------+
137+
| term | Description |
138+
+===========+=============================================+
139+
| *string* | Will call :meth:`found_terminator` when the |
140+
| | string is found in the input stream |
141+
+-----------+---------------------------------------------+
142+
| *integer* | Will call :meth:`found_terminator` when the |
143+
| | indicated number of characters have been |
144+
| | received |
145+
+-----------+---------------------------------------------+
146+
| ``None`` | The channel continues to collect data |
147+
| | forever |
148+
+-----------+---------------------------------------------+
149+
150+
Note that any data following the terminator will be available for reading
151+
by the channel after :meth:`found_terminator` is called.
152+
153+
154+
.. _asynchat-example:
155+
156+
asynchat Example
157+
----------------
158+
159+
The following partial example shows how HTTP requests can be read with
160+
:class:`async_chat`. A web server might create an
161+
:class:`http_request_handler` object for each incoming client connection.
162+
Notice that initially the channel terminator is set to match the blank line at
163+
the end of the HTTP headers, and a flag indicates that the headers are being
164+
read.
165+
166+
Once the headers have been read, if the request is of type POST (indicating
167+
that further data are present in the input stream) then the
168+
``Content-Length:`` header is used to set a numeric terminator to read the
169+
right amount of data from the channel.
170+
171+
The :meth:`handle_request` method is called once all relevant input has been
172+
marshalled, after setting the channel terminator to ``None`` to ensure that
173+
any extraneous data sent by the web client are ignored. ::
174+
175+
176+
import asynchat
177+
178+
class http_request_handler(asynchat.async_chat):
179+
180+
def __init__(self, sock, addr, sessions, log):
181+
asynchat.async_chat.__init__(self, sock=sock)
182+
self.addr = addr
183+
self.sessions = sessions
184+
self.ibuffer = []
185+
self.obuffer = b""
186+
self.set_terminator(b"\r\n\r\n")
187+
self.reading_headers = True
188+
self.handling = False
189+
self.cgi_data = None
190+
self.log = log
191+
192+
def collect_incoming_data(self, data):
193+
"""Buffer the data"""
194+
self.ibuffer.append(data)
195+
196+
def found_terminator(self):
197+
if self.reading_headers:
198+
self.reading_headers = False
199+
self.parse_headers(b"".join(self.ibuffer))
200+
self.ibuffer = []
201+
if self.op.upper() == b"POST":
202+
clen = self.headers.getheader("content-length")
203+
self.set_terminator(int(clen))
204+
else:
205+
self.handling = True
206+
self.set_terminator(None)
207+
self.handle_request()
208+
elif not self.handling:
209+
self.set_terminator(None) # browsers sometimes over-send
210+
self.cgi_data = parse(self.headers, b"".join(self.ibuffer))
211+
self.handling = True
212+
self.ibuffer = []
213+
self.handle_request()

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy