CINXE.COM

PEP 3153 – Asynchronous IO support | peps.python.org

<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="color-scheme" content="light dark"> <title>PEP 3153 – Asynchronous IO support | peps.python.org</title> <link rel="shortcut icon" href="../_static/py.png"> <link rel="canonical" href="https://peps.python.org/pep-3153/"> <link rel="stylesheet" href="../_static/style.css" type="text/css"> <link rel="stylesheet" href="../_static/mq.css" type="text/css"> <link rel="stylesheet" href="../_static/pygments.css" type="text/css" media="(prefers-color-scheme: light)" id="pyg-light"> <link rel="stylesheet" href="../_static/pygments_dark.css" type="text/css" media="(prefers-color-scheme: dark)" id="pyg-dark"> <link rel="alternate" type="application/rss+xml" title="Latest PEPs" href="https://peps.python.org/peps.rss"> <meta property="og:title" content='PEP 3153 – Asynchronous IO support | peps.python.org'> <meta property="og:description" content="This PEP describes an abstraction of asynchronous IO for the Python standard library."> <meta property="og:type" content="website"> <meta property="og:url" content="https://peps.python.org/pep-3153/"> <meta property="og:site_name" content="Python Enhancement Proposals (PEPs)"> <meta property="og:image" content="https://peps.python.org/_static/og-image.png"> <meta property="og:image:alt" content="Python PEPs"> <meta property="og:image:width" content="200"> <meta property="og:image:height" content="200"> <meta name="description" content="This PEP describes an abstraction of asynchronous IO for the Python standard library."> <meta name="theme-color" content="#3776ab"> </head> <body> <svg xmlns="http://www.w3.org/2000/svg" style="display: none;"> <symbol id="svg-sun-half" viewBox="0 0 24 24" pointer-events="all"> <title>Following system colour scheme</title> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <circle cx="12" cy="12" r="9"></circle> <path d="M12 3v18m0-12l4.65-4.65M12 14.3l7.37-7.37M12 19.6l8.85-8.85"></path> </svg> </symbol> <symbol id="svg-moon" viewBox="0 0 24 24" pointer-events="all"> <title>Selected dark colour scheme</title> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z"></path> </svg> </symbol> <symbol id="svg-sun" viewBox="0 0 24 24" pointer-events="all"> <title>Selected light colour scheme</title> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <circle cx="12" cy="12" r="5"></circle> <line x1="12" y1="1" x2="12" y2="3"></line> <line x1="12" y1="21" x2="12" y2="23"></line> <line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line> <line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line> <line x1="1" y1="12" x2="3" y2="12"></line> <line x1="21" y1="12" x2="23" y2="12"></line> <line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line> <line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line> </svg> </symbol> </svg> <script> document.documentElement.dataset.colour_scheme = localStorage.getItem("colour_scheme") || "auto" </script> <section id="pep-page-section"> <header> <h1>Python Enhancement Proposals</h1> <ul class="breadcrumbs"> <li><a href="https://www.python.org/" title="The Python Programming Language">Python</a> &raquo; </li> <li><a href="../pep-0000/">PEP Index</a> &raquo; </li> <li>PEP 3153</li> </ul> <button id="colour-scheme-cycler" onClick="setColourScheme(nextColourScheme())"> <svg aria-hidden="true" class="colour-scheme-icon-when-auto"><use href="#svg-sun-half"></use></svg> <svg aria-hidden="true" class="colour-scheme-icon-when-dark"><use href="#svg-moon"></use></svg> <svg aria-hidden="true" class="colour-scheme-icon-when-light"><use href="#svg-sun"></use></svg> <span class="visually-hidden">Toggle light / dark / auto colour theme</span> </button> </header> <article> <section id="pep-content"> <h1 class="page-title">PEP 3153 – Asynchronous IO support</h1> <dl class="rfc2822 field-list simple"> <dt class="field-odd">Author<span class="colon">:</span></dt> <dd class="field-odd">Laurens Van Houtven &lt;_&#32;&#97;t&#32;lvh.cc&gt;</dd> <dt class="field-even">Status<span class="colon">:</span></dt> <dd class="field-even"><abbr title="Replaced by another succeeding PEP">Superseded</abbr></dd> <dt class="field-odd">Type<span class="colon">:</span></dt> <dd class="field-odd"><abbr title="Normative PEP with a new feature for Python, implementation change for CPython or interoperability standard for the ecosystem">Standards Track</abbr></dd> <dt class="field-even">Created<span class="colon">:</span></dt> <dd class="field-even">29-May-2011</dd> <dt class="field-odd">Post-History<span class="colon">:</span></dt> <dd class="field-odd"><p></p></dd> <dt class="field-even">Superseded-By<span class="colon">:</span></dt> <dd class="field-even"><a class="reference external" href="../pep-3156/">3156</a></dd> </dl> <hr class="docutils" /> <section id="contents"> <details><summary>Table of Contents</summary><ul class="simple"> <li><a class="reference internal" href="#abstract">Abstract</a></li> <li><a class="reference internal" href="#rationale">Rationale</a></li> <li><a class="reference internal" href="#communication-abstractions">Communication abstractions</a><ul> <li><a class="reference internal" href="#transports">Transports</a></li> <li><a class="reference internal" href="#protocols">Protocols</a></li> <li><a class="reference internal" href="#why-separate-protocols-and-transports">Why separate protocols and transports?</a></li> </ul> </li> <li><a class="reference internal" href="#flow-control">Flow control</a><ul> <li><a class="reference internal" href="#consumers">Consumers</a></li> <li><a class="reference internal" href="#producers">Producers</a><ul> <li><a class="reference internal" href="#considered-api-alternatives">Considered API alternatives</a><ul> <li><a class="reference internal" href="#generators-as-producers">Generators as producers</a></li> </ul> </li> </ul> </li> </ul> </li> <li><a class="reference internal" href="#references">References</a></li> <li><a class="reference internal" href="#copyright">Copyright</a></li> </ul> </details></section> <section id="abstract"> <h2><a class="toc-backref" href="#abstract" role="doc-backlink">Abstract</a></h2> <p>This PEP describes an abstraction of asynchronous IO for the Python standard library.</p> <p>The goal is to reach an abstraction that can be implemented by many different asynchronous IO backends and provides a target for library developers to write code portable between those different backends.</p> </section> <section id="rationale"> <h2><a class="toc-backref" href="#rationale" role="doc-backlink">Rationale</a></h2> <p>People who want to write asynchronous code in Python right now have a few options:</p> <ul class="simple"> <li><code class="docutils literal notranslate"><span class="pre">asyncore</span></code> and <code class="docutils literal notranslate"><span class="pre">asynchat</span></code></li> <li>something bespoke, most likely based on the <code class="docutils literal notranslate"><span class="pre">select</span></code> module</li> <li>using a third party library, such as <a class="reference external" href="http://www.twistedmatrix.com/">Twisted</a> or <a class="reference external" href="http://www.gevent.org/">gevent</a></li> </ul> <p>Unfortunately, each of these options has its downsides, which this PEP tries to address.</p> <p>Despite having been part of the Python standard library for a long time, the asyncore module suffers from fundamental flaws following from an inflexible API that does not stand up to the expectations of a modern asynchronous networking module.</p> <p>Moreover, its approach is too simplistic to provide developers with all the tools they need in order to fully exploit the potential of asynchronous networking.</p> <p>The most popular solution right now used in production involves the use of third party libraries. These often provide satisfactory solutions, but there is a lack of compatibility between these libraries, which tends to make codebases very tightly coupled to the library they use.</p> <p>This current lack of portability between different asynchronous IO libraries causes a lot of duplicated effort for third party library developers. A sufficiently powerful abstraction could mean that asynchronous code gets written once, but used everywhere.</p> <p>An eventual added goal would be for standard library implementations of wire and network protocols to evolve towards being real protocol implementations, as opposed to standalone libraries that do everything including calling <code class="docutils literal notranslate"><span class="pre">recv()</span></code> blockingly. This means they could be easily reused for both synchronous and asynchronous code.</p> </section> <section id="communication-abstractions"> <h2><a class="toc-backref" href="#communication-abstractions" role="doc-backlink">Communication abstractions</a></h2> <section id="transports"> <h3><a class="toc-backref" href="#transports" role="doc-backlink">Transports</a></h3> <p>Transports provide a uniform API for reading bytes from and writing bytes to different kinds of connections. Transports in this PEP are always ordered, reliable, bidirectional, stream-oriented two-endpoint connections. This might be a TCP socket, an SSL connection, a pipe (named or otherwise), a serial port… It may abstract a file descriptor on POSIX platforms or a Handle on Windows or some other data structure appropriate to a particular platform. It encapsulates all of the particular implementation details of using that platform data structure and presents a uniform interface for application developers.</p> <p>Transports talk to two things: the other side of the connection on one hand, and a protocol on the other. It’s a bridge between the specific underlying transfer mechanism and the protocol. Its job can be described as allowing the protocol to just send and receive bytes, taking care of all of the magic that needs to happen to those bytes to be eventually sent across the wire.</p> <p>The primary feature of a transport is sending bytes to a protocol and receiving bytes from the underlying protocol. Writing to the transport is done using the <code class="docutils literal notranslate"><span class="pre">write</span></code> and <code class="docutils literal notranslate"><span class="pre">write_sequence</span></code> methods. The latter method is a performance optimization, to allow software to take advantage of specific capabilities in some transport mechanisms. Specifically, this allows transports to use <a class="reference external" href="http://pubs.opengroup.org/onlinepubs/009695399/functions/writev.html">writev</a> instead of <a class="reference external" href="http://pubs.opengroup.org/onlinepubs/009695399/functions/write.html">write</a> or <a class="reference external" href="http://pubs.opengroup.org/onlinepubs/009695399/functions/send.html">send</a>, also known as scatter/gather IO.</p> <p>A transport can be paused and resumed. This will cause it to buffer data coming from protocols and stop sending received data to the protocol.</p> <p>A transport can also be closed, half-closed and aborted. A closed transport will finish writing all of the data queued in it to the underlying mechanism, and will then stop reading or writing data. Aborting a transport stops it, closing the connection without sending any data that is still queued.</p> <p>Further writes will result in exceptions being thrown. A half-closed transport may not be written to anymore, but will still accept incoming data.</p> </section> <section id="protocols"> <h3><a class="toc-backref" href="#protocols" role="doc-backlink">Protocols</a></h3> <p>Protocols are probably more familiar to new users. The terminology is consistent with what you would expect from something called a protocol: the protocols most people think of first, like HTTP, IRC, SMTP… are all examples of something that would be implemented in a protocol.</p> <p>The shortest useful definition of a protocol is a (usually two-way) bridge between the transport and the rest of the application logic. A protocol will receive bytes from a transport and translates that information into some behavior, typically resulting in some method calls on an object. Similarly, application logic calls some methods on the protocol, which the protocol translates into bytes and communicates to the transport.</p> <p>One of the simplest protocols is a line-based protocol, where data is delimited by <code class="docutils literal notranslate"><span class="pre">\r\n</span></code>. The protocol will receive bytes from the transport and buffer them until there is at least one complete line. Once that’s done, it will pass this line along to some object. Ideally that would be accomplished using a callable or even a completely separate object composed by the protocol, but it could also be implemented by subclassing (as is the case with Twisted’s <code class="docutils literal notranslate"><span class="pre">LineReceiver</span></code>). For the other direction, the protocol could have a <code class="docutils literal notranslate"><span class="pre">write_line</span></code> method, which adds the required <code class="docutils literal notranslate"><span class="pre">\r\n</span></code> and passes the new bytes buffer on to the transport.</p> <p>This PEP suggests a generalized <code class="docutils literal notranslate"><span class="pre">LineReceiver</span></code> called <code class="docutils literal notranslate"><span class="pre">ChunkProtocol</span></code>, where a “chunk” is a message in a stream, delimited by the specified delimiter. Instances take a delimiter and a callable that will be called with a chunk of data once it’s received (as opposed to Twisted’s subclassing behavior). <code class="docutils literal notranslate"><span class="pre">ChunkProtocol</span></code> also has a <code class="docutils literal notranslate"><span class="pre">write_chunk</span></code> method analogous to the <code class="docutils literal notranslate"><span class="pre">write_line</span></code> method described above.</p> </section> <section id="why-separate-protocols-and-transports"> <h3><a class="toc-backref" href="#why-separate-protocols-and-transports" role="doc-backlink">Why separate protocols and transports?</a></h3> <p>This separation between protocol and transport often confuses people who first come across it. In fact, the standard library itself does not make this distinction in many cases, particularly not in the API it provides to users.</p> <p>It is nonetheless a very useful distinction. In the worst case, it simplifies the implementation by clear separation of concerns. However, it often serves the far more useful purpose of being able to reuse protocols across different transports.</p> <p>Consider a simple RPC protocol. The same bytes may be transferred across many different transports, for example pipes or sockets. To help with this, we separate the protocol out from the transport. The protocol just reads and writes bytes, and doesn’t really care what mechanism is used to eventually transfer those bytes.</p> <p>This also allows for protocols to be stacked or nested easily, allowing for even more code reuse. A common example of this is JSON-RPC: according to the specification, it can be used across both sockets and HTTP <a class="footnote-reference brackets" href="#jsonrpc" id="id1">[1]</a>. In practice, it tends to be primarily encapsulated in HTTP. The protocol-transport abstraction allows us to build a stack of protocols and transports that allow you to use HTTP as if it were a transport. For JSON-RPC, that might get you a stack somewhat like this:</p> <ol class="arabic simple"> <li>TCP socket transport</li> <li>HTTP protocol</li> <li>HTTP-based transport</li> <li>JSON-RPC protocol</li> <li>Application code</li> </ol> </section> </section> <section id="flow-control"> <h2><a class="toc-backref" href="#flow-control" role="doc-backlink">Flow control</a></h2> <section id="consumers"> <h3><a class="toc-backref" href="#consumers" role="doc-backlink">Consumers</a></h3> <p>Consumers consume bytes produced by producers. Together with producers, they make flow control possible.</p> <p>Consumers primarily play a passive role in flow control. They get called whenever a producer has some data available. They then process that data, and typically yield control back to the producer.</p> <p>Consumers typically implement buffers of some sort. They make flow control possible by telling their producer about the current status of those buffers. A consumer can instruct a producer to stop producing entirely, stop producing temporarily, or resume producing if it has been told to pause previously.</p> <p>Producers are registered to the consumer using the <code class="docutils literal notranslate"><span class="pre">register</span></code> method.</p> </section> <section id="producers"> <h3><a class="toc-backref" href="#producers" role="doc-backlink">Producers</a></h3> <p>Where consumers consume bytes, producers produce them.</p> <p>Producers are modeled after the <a class="reference external" href="http://twistedmatrix.com/documents/current/api/twisted.internet.interfaces.IPushProducer.html">IPushProducer</a> interface found in Twisted. Although there is an <a class="reference external" href="http://twistedmatrix.com/documents/current/api/twisted.internet.interfaces.IPullProducer.html">IPullProducer</a> as well, it is on the whole far less interesting and therefore probably out of the scope of this PEP.</p> <p>Although producers can be told to stop producing entirely, the two most interesting methods they have are <code class="docutils literal notranslate"><span class="pre">pause</span></code> and <code class="docutils literal notranslate"><span class="pre">resume</span></code>. These are usually called by the consumer, to signify whether it is ready to process (“consume”) more data or not. Consumers and producers cooperate to make flow control possible.</p> <p>In addition to the Twisted <a class="reference external" href="http://twistedmatrix.com/documents/current/api/twisted.internet.interfaces.IPushProducer.html">IPushProducer</a> interface, producers have a <code class="docutils literal notranslate"><span class="pre">half_register</span></code> method which is called with the consumer when the consumer tries to register that producer. In most cases, this will just be a case of setting <code class="docutils literal notranslate"><span class="pre">self.consumer</span> <span class="pre">=</span> <span class="pre">consumer</span></code>, but some producers may require more complex preconditions or behavior when a consumer is registered. End-users are not supposed to call this method directly.</p> <section id="considered-api-alternatives"> <h4><a class="toc-backref" href="#considered-api-alternatives" role="doc-backlink">Considered API alternatives</a></h4> <section id="generators-as-producers"> <h5><a class="toc-backref" href="#generators-as-producers" role="doc-backlink">Generators as producers</a></h5> <p>Generators have been suggested as way to implement producers. However, there appear to be a few problems with this.</p> <p>First of all, there is a conceptual problem. A generator, in a sense, is “passive”. It needs to be told, through a method call, to take action. A producer is “active”: it initiates those method calls. A real producer has a symmetric relationship with its consumer. In the case of a generator-turned-producer, only the consumer would have a reference, and the producer is blissfully unaware of the consumer’s existence.</p> <p>This conceptual problem translates into a few technical issues as well. After a successful <code class="docutils literal notranslate"><span class="pre">write</span></code> method call on its consumer, a (push) producer is free to take action once more. In the case of a generator, it would need to be told, either by asking for the next object through the iteration protocol (a process which could block indefinitely), or perhaps by throwing some kind of signal exception into it.</p> <p>This signaling setup may provide a technically feasible solution, but it is still unsatisfactory. For one, this introduces unwarranted complexity in the consumer, which now not only needs to understand how to receive and process data, but also how to ask for new data and deal with the case of no new data being available.</p> <p>This latter edge case is particularly problematic. It needs to be taken care of, since the entire operation is not allowed to block. However, generators can not raise an exception on iteration without terminating, thereby losing the state of the generator. As a result, signaling a lack of available data would have to be done using a sentinel value, instead of being done using th exception mechanism.</p> <p>Last but not least, nobody produced actually working code demonstrating how they could be used.</p> </section> </section> </section> </section> <section id="references"> <h2><a class="toc-backref" href="#references" role="doc-backlink">References</a></h2> <aside class="footnote-list brackets"> <aside class="footnote brackets" id="jsonrpc" role="doc-footnote"> <dt class="label" id="jsonrpc">[<a href="#id1">1</a>]</dt> <dd>Sections <a class="reference external" href="http://json-rpc.org/wiki/specification#a2.1JSON-RPCoverstreamconnections">2.1</a> and <a class="reference external" href="http://json-rpc.org/wiki/specification#a2.2JSON-RPCoverHTTP">2.2</a> .</aside> </aside> </section> <section id="copyright"> <h2><a class="toc-backref" href="#copyright" role="doc-backlink">Copyright</a></h2> <p>This document has been placed in the public domain.</p> </section> </section> <hr class="docutils" /> <p>Source: <a class="reference external" href="https://github.com/python/peps/blob/main/peps/pep-3153.rst">https://github.com/python/peps/blob/main/peps/pep-3153.rst</a></p> <p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-3153.rst">2025-02-01 08:59:27 GMT</a></p> </article> <nav id="pep-sidebar"> <h2>Contents</h2> <ul> <li><a class="reference internal" href="#abstract">Abstract</a></li> <li><a class="reference internal" href="#rationale">Rationale</a></li> <li><a class="reference internal" href="#communication-abstractions">Communication abstractions</a><ul> <li><a class="reference internal" href="#transports">Transports</a></li> <li><a class="reference internal" href="#protocols">Protocols</a></li> <li><a class="reference internal" href="#why-separate-protocols-and-transports">Why separate protocols and transports?</a></li> </ul> </li> <li><a class="reference internal" href="#flow-control">Flow control</a><ul> <li><a class="reference internal" href="#consumers">Consumers</a></li> <li><a class="reference internal" href="#producers">Producers</a><ul> <li><a class="reference internal" href="#considered-api-alternatives">Considered API alternatives</a><ul> <li><a class="reference internal" href="#generators-as-producers">Generators as producers</a></li> </ul> </li> </ul> </li> </ul> </li> <li><a class="reference internal" href="#references">References</a></li> <li><a class="reference internal" href="#copyright">Copyright</a></li> </ul> <br> <a id="source" href="https://github.com/python/peps/blob/main/peps/pep-3153.rst">Page Source (GitHub)</a> </nav> </section> <script src="../_static/colour_scheme.js"></script> <script src="../_static/wrap_tables.js"></script> <script src="../_static/sticky_banner.js"></script> </body> </html>

Pages: 1 2 3 4 5 6 7 8 9 10