CINXE.COM

PEP 334 – Simple Coroutines via SuspendIteration | 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 334 – Simple Coroutines via SuspendIteration | peps.python.org</title> <link rel="shortcut icon" href="../_static/py.png"> <link rel="canonical" href="https://peps.python.org/pep-0334/"> <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 334 – Simple Coroutines via SuspendIteration | peps.python.org'> <meta property="og:description" content="Asynchronous application frameworks such as Twisted 1 and Peak 2, are based on a cooperative multitasking via event queues or deferred execution. While this approach to application development does not involve threads and thus avoids a whole class of p..."> <meta property="og:type" content="website"> <meta property="og:url" content="https://peps.python.org/pep-0334/"> <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="Asynchronous application frameworks such as Twisted 1 and Peak 2, are based on a cooperative multitasking via event queues or deferred execution. While this approach to application development does not involve threads and thus avoids a whole class of p..."> <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 334</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 334 – Simple Coroutines via SuspendIteration</h1> <dl class="rfc2822 field-list simple"> <dt class="field-odd">Author<span class="colon">:</span></dt> <dd class="field-odd">Clark C. Evans &lt;cce&#32;&#97;t&#32;clarkevans.com&gt;</dd> <dt class="field-even">Status<span class="colon">:</span></dt> <dd class="field-even"><abbr title="Removed from consideration by sponsor or authors">Withdrawn</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">26-Aug-2004</dd> <dt class="field-odd">Python-Version<span class="colon">:</span></dt> <dd class="field-odd">3.0</dd> <dt class="field-even">Post-History<span class="colon">:</span></dt> <dd class="field-even"><p></p></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="#semantics">Semantics</a><ul> <li><a class="reference internal" href="#simple-iterators">Simple Iterators</a></li> <li><a class="reference internal" href="#introducing-suspenditeration">Introducing SuspendIteration</a></li> <li><a class="reference internal" href="#application-iterators">Application Iterators</a></li> <li><a class="reference internal" href="#complicating-factors">Complicating Factors</a></li> <li><a class="reference internal" href="#resource-cleanup">Resource Cleanup</a></li> <li><a class="reference internal" href="#api-and-limitations">API and Limitations</a></li> </ul> </li> <li><a class="reference internal" href="#low-level-implementation">Low-Level Implementation</a></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>Asynchronous application frameworks such as Twisted <a class="footnote-reference brackets" href="#id9" id="id1">[1]</a> and Peak <a class="footnote-reference brackets" href="#id10" id="id2">[2]</a>, are based on a cooperative multitasking via event queues or deferred execution. While this approach to application development does not involve threads and thus avoids a whole class of problems <a class="footnote-reference brackets" href="#id11" id="id3">[3]</a>, it creates a different sort of programming challenge. When an I/O operation would block, a user request must suspend so that other requests can proceed. The concept of a coroutine <a class="footnote-reference brackets" href="#id12" id="id4">[4]</a> promises to help the application developer grapple with this state management difficulty.</p> <p>This PEP proposes a limited approach to coroutines based on an extension to the <a class="pep reference internal" href="../pep-0234/" title="PEP 234 – Iterators">iterator protocol</a>. Currently, an iterator may raise a StopIteration exception to indicate that it is done producing values. This proposal adds another exception to this protocol, SuspendIteration, which indicates that the given iterator may have more values to produce, but is unable to do so at this time.</p> </section> <section id="rationale"> <h2><a class="toc-backref" href="#rationale" role="doc-backlink">Rationale</a></h2> <p>There are two current approaches to bringing co-routines to Python. Christian Tismer’s Stackless <a class="footnote-reference brackets" href="#id13" id="id5">[6]</a> involves a ground-up restructuring of Python’s execution model by hacking the ‘C’ stack. While this approach works, its operation is hard to describe and keep portable. A related approach is to compile Python code to Parrot <a class="footnote-reference brackets" href="#id14" id="id6">[7]</a>, a register-based virtual machine, which has coroutines. Unfortunately, neither of these solutions is portable with IronPython (CLR) or Jython (JavaVM).</p> <p>It is thought that a more limited approach, based on iterators, could provide a coroutine facility to application programmers and still be portable across runtimes.</p> <ul class="simple"> <li>Iterators keep their state in local variables that are not on the “C” stack. Iterators can be viewed as classes, with state stored in member variables that are persistent across calls to its next() method.</li> <li>While an uncaught exception may terminate a function’s execution, an uncaught exception need not invalidate an iterator. The proposed exception, SuspendIteration, uses this feature. In other words, just because one call to next() results in an exception does not necessarily need to imply that the iterator itself is no longer capable of producing values.</li> </ul> <p>There are four places where this new exception impacts:</p> <ul class="simple"> <li>The <a class="pep reference internal" href="../pep-0255/" title="PEP 255 – Simple Generators">PEP 255</a> simple generator mechanism could be extended to safely ‘catch’ this SuspendIteration exception, stuff away its current state, and pass the exception on to the caller.</li> <li>Various iterator filters <a class="footnote-reference brackets" href="#id15" id="id7">[9]</a> in the standard library, such as itertools.izip should be made aware of this exception so that it can transparently propagate SuspendIteration.</li> <li>Iterators generated from I/O operations, such as a file or socket reader, could be modified to have a non-blocking variety. This option would raise a subclass of SuspendIteration if the requested operation would block.</li> <li>The asyncore library could be updated to provide a basic ‘runner’ that pulls from an iterator; if the SuspendIteration exception is caught, then it moves on to the next iterator in its runlist <a class="footnote-reference brackets" href="#id16" id="id8">[10]</a>. External frameworks like Twisted would provide alternative implementations, perhaps based on FreeBSD’s kqueue or Linux’s epoll.</li> </ul> <p>While these may seem dramatic changes, it is a very small amount of work compared with the utility provided by continuations.</p> </section> <section id="semantics"> <h2><a class="toc-backref" href="#semantics" role="doc-backlink">Semantics</a></h2> <p>This section will explain, at a high level, how the introduction of this new SuspendIteration exception would behave.</p> <section id="simple-iterators"> <h3><a class="toc-backref" href="#simple-iterators" role="doc-backlink">Simple Iterators</a></h3> <p>The current functionality of iterators is best seen with a simple example which produces two values ‘one’ and ‘two’.</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">States</span><span class="p">:</span> <span class="k">def</span> <span class="fm">__iter__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">_next</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">state_one</span> <span class="k">return</span> <span class="bp">self</span> <span class="k">def</span> <span class="nf">next</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_next</span><span class="p">()</span> <span class="k">def</span> <span class="nf">state_one</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">_next</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">state_two</span> <span class="k">return</span> <span class="s2">&quot;one&quot;</span> <span class="k">def</span> <span class="nf">state_two</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">_next</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">state_stop</span> <span class="k">return</span> <span class="s2">&quot;two&quot;</span> <span class="k">def</span> <span class="nf">state_stop</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">raise</span> <span class="ne">StopIteration</span> <span class="nb">print</span> <span class="nb">list</span><span class="p">(</span><span class="n">States</span><span class="p">())</span> </pre></div> </div> <p>An equivalent iteration could, of course, be created by the following generator:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">States</span><span class="p">():</span> <span class="k">yield</span> <span class="s1">&#39;one&#39;</span> <span class="k">yield</span> <span class="s1">&#39;two&#39;</span> <span class="nb">print</span> <span class="nb">list</span><span class="p">(</span><span class="n">States</span><span class="p">())</span> </pre></div> </div> </section> <section id="introducing-suspenditeration"> <h3><a class="toc-backref" href="#introducing-suspenditeration" role="doc-backlink">Introducing SuspendIteration</a></h3> <p>Suppose that between producing ‘one’ and ‘two’, the generator above could block on a socket read. In this case, we would want to raise SuspendIteration to signal that the iterator is not done producing, but is unable to provide a value at the current moment.</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">random</span> <span class="kn">import</span> <span class="n">randint</span> <span class="kn">from</span> <span class="nn">time</span> <span class="kn">import</span> <span class="n">sleep</span> <span class="k">class</span> <span class="nc">SuspendIteration</span><span class="p">(</span><span class="ne">Exception</span><span class="p">):</span> <span class="k">pass</span> <span class="k">class</span> <span class="nc">NonBlockingResource</span><span class="p">:</span> <span class="w"> </span><span class="sd">&quot;&quot;&quot;Randomly unable to produce the second value&quot;&quot;&quot;</span> <span class="k">def</span> <span class="fm">__iter__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">_next</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">state_one</span> <span class="k">return</span> <span class="bp">self</span> <span class="k">def</span> <span class="nf">next</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_next</span><span class="p">()</span> <span class="k">def</span> <span class="nf">state_one</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">_next</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">state_suspend</span> <span class="k">return</span> <span class="s2">&quot;one&quot;</span> <span class="k">def</span> <span class="nf">state_suspend</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="n">rand</span> <span class="o">=</span> <span class="n">randint</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">10</span><span class="p">)</span> <span class="k">if</span> <span class="mi">2</span> <span class="o">==</span> <span class="n">rand</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">_next</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">state_two</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">state_two</span><span class="p">()</span> <span class="k">raise</span> <span class="n">SuspendIteration</span><span class="p">()</span> <span class="k">def</span> <span class="nf">state_two</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">_next</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">state_stop</span> <span class="k">return</span> <span class="s2">&quot;two&quot;</span> <span class="k">def</span> <span class="nf">state_stop</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">raise</span> <span class="ne">StopIteration</span> <span class="k">def</span> <span class="nf">sleeplist</span><span class="p">(</span><span class="n">iterator</span><span class="p">,</span> <span class="n">timeout</span> <span class="o">=</span> <span class="mf">.1</span><span class="p">):</span> <span class="w"> </span><span class="sd">&quot;&quot;&quot;</span> <span class="sd"> Do other things (e.g. sleep) while resource is</span> <span class="sd"> unable to provide the next value</span> <span class="sd"> &quot;&quot;&quot;</span> <span class="n">it</span> <span class="o">=</span> <span class="nb">iter</span><span class="p">(</span><span class="n">iterator</span><span class="p">)</span> <span class="n">retval</span> <span class="o">=</span> <span class="p">[]</span> <span class="k">while</span> <span class="kc">True</span><span class="p">:</span> <span class="k">try</span><span class="p">:</span> <span class="n">retval</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">it</span><span class="o">.</span><span class="n">next</span><span class="p">())</span> <span class="k">except</span> <span class="n">SuspendIteration</span><span class="p">:</span> <span class="n">sleep</span><span class="p">(</span><span class="n">timeout</span><span class="p">)</span> <span class="k">continue</span> <span class="k">except</span> <span class="ne">StopIteration</span><span class="p">:</span> <span class="k">break</span> <span class="k">return</span> <span class="n">retval</span> <span class="nb">print</span> <span class="n">sleeplist</span><span class="p">(</span><span class="n">NonBlockingResource</span><span class="p">())</span> </pre></div> </div> <p>In a real-world situation, the NonBlockingResource would be a file iterator, socket handle, or other I/O based producer. The sleeplist would instead be an async reactor, such as those found in asyncore or Twisted. The non-blocking resource could, of course, be written as a generator:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">NonBlockingResource</span><span class="p">():</span> <span class="k">yield</span> <span class="s2">&quot;one&quot;</span> <span class="k">while</span> <span class="kc">True</span><span class="p">:</span> <span class="n">rand</span> <span class="o">=</span> <span class="n">randint</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">10</span><span class="p">)</span> <span class="k">if</span> <span class="mi">2</span> <span class="o">==</span> <span class="n">rand</span><span class="p">:</span> <span class="k">break</span> <span class="k">raise</span> <span class="n">SuspendIteration</span><span class="p">()</span> <span class="k">yield</span> <span class="s2">&quot;two&quot;</span> </pre></div> </div> <p>It is not necessary to add a keyword, ‘suspend’, since most real content generators will not be in application code, they will be in low-level I/O based operations. Since most programmers need not be exposed to the SuspendIteration() mechanism, a keyword is not needed.</p> </section> <section id="application-iterators"> <h3><a class="toc-backref" href="#application-iterators" role="doc-backlink">Application Iterators</a></h3> <p>The previous example is rather contrived, a more ‘real-world’ example would be a web page generator which yields HTML content, and pulls from a database. Note that this is an example of neither the ‘producer’ nor the ‘consumer’, but rather of a filter.</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">ListAlbums</span><span class="p">(</span><span class="n">cursor</span><span class="p">):</span> <span class="n">cursor</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="s2">&quot;SELECT title, artist FROM album&quot;</span><span class="p">)</span> <span class="k">yield</span> <span class="s1">&#39;&lt;html&gt;&lt;body&gt;&lt;table&gt;&lt;tr&gt;&lt;td&gt;Title&lt;/td&gt;&lt;td&gt;Artist&lt;/td&gt;&lt;/tr&gt;&#39;</span> <span class="k">for</span> <span class="p">(</span><span class="n">title</span><span class="p">,</span> <span class="n">artist</span><span class="p">)</span> <span class="ow">in</span> <span class="n">cursor</span><span class="p">:</span> <span class="k">yield</span> <span class="s1">&#39;&lt;tr&gt;&lt;td&gt;</span><span class="si">%s</span><span class="s1">&lt;/td&gt;&lt;td&gt;</span><span class="si">%s</span><span class="s1">&lt;/td&gt;&lt;/tr&gt;&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="n">title</span><span class="p">,</span> <span class="n">artist</span><span class="p">)</span> <span class="k">yield</span> <span class="s1">&#39;&lt;/table&gt;&lt;/body&gt;&lt;/html&gt;&#39;</span> </pre></div> </div> <p>The problem, of course, is that the database may block for some time before any rows are returned, and that during execution, rows may be returned in blocks of 10 or 100 at a time. Ideally, if the database blocks for the next set of rows, another user connection could be serviced. Note the complete absence of SuspendIterator in the above code. If done correctly, application developers would be able to focus on functionality rather than concurrency issues.</p> <p>The iterator created by the above generator should do the magic necessary to maintain state, yet pass the exception through to a lower-level async framework. Here is an example of what the corresponding iterator would look like if coded up as a class:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">ListAlbums</span><span class="p">:</span> <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">cursor</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">cursor</span> <span class="o">=</span> <span class="n">cursor</span> <span class="k">def</span> <span class="fm">__iter__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">cursor</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="s2">&quot;SELECT title, artist FROM album&quot;</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">_iter</span> <span class="o">=</span> <span class="nb">iter</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_cursor</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">_next</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">state_head</span> <span class="k">return</span> <span class="bp">self</span> <span class="k">def</span> <span class="nf">next</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_next</span><span class="p">()</span> <span class="k">def</span> <span class="nf">state_head</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">_next</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">state_cursor</span> <span class="k">return</span> <span class="s2">&quot;&lt;html&gt;&lt;body&gt;&lt;table&gt;&lt;tr&gt;&lt;td&gt;</span><span class="se">\</span> <span class="s2"> Title&lt;/td&gt;&lt;td&gt;Artist&lt;/td&gt;&lt;/tr&gt;&quot;</span> <span class="k">def</span> <span class="nf">state_tail</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">_next</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">state_stop</span> <span class="k">return</span> <span class="s2">&quot;&lt;/table&gt;&lt;/body&gt;&lt;/html&gt;&quot;</span> <span class="k">def</span> <span class="nf">state_cursor</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">try</span><span class="p">:</span> <span class="p">(</span><span class="n">title</span><span class="p">,</span><span class="n">artist</span><span class="p">)</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_iter</span><span class="o">.</span><span class="n">next</span><span class="p">()</span> <span class="k">return</span> <span class="s1">&#39;&lt;tr&gt;&lt;td&gt;</span><span class="si">%s</span><span class="s1">&lt;/td&gt;&lt;td&gt;</span><span class="si">%s</span><span class="s1">&lt;/td&gt;&lt;/tr&gt;&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="n">title</span><span class="p">,</span> <span class="n">artist</span><span class="p">)</span> <span class="k">except</span> <span class="ne">StopIteration</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">_next</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">state_tail</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">next</span><span class="p">()</span> <span class="k">except</span> <span class="n">SuspendIteration</span><span class="p">:</span> <span class="c1"># just pass-through</span> <span class="k">raise</span> <span class="k">def</span> <span class="nf">state_stop</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">raise</span> <span class="ne">StopIteration</span> </pre></div> </div> </section> <section id="complicating-factors"> <h3><a class="toc-backref" href="#complicating-factors" role="doc-backlink">Complicating Factors</a></h3> <p>While the above example is straightforward, things are a bit more complicated if the intermediate generator ‘condenses’ values, that is, it pulls in two or more values for each value it produces. For example,</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">pair</span><span class="p">(</span><span class="n">iterLeft</span><span class="p">,</span><span class="n">iterRight</span><span class="p">):</span> <span class="n">rhs</span> <span class="o">=</span> <span class="nb">iter</span><span class="p">(</span><span class="n">iterRight</span><span class="p">)</span> <span class="n">lhs</span> <span class="o">=</span> <span class="nb">iter</span><span class="p">(</span><span class="n">iterLeft</span><span class="p">)</span> <span class="k">while</span> <span class="kc">True</span><span class="p">:</span> <span class="k">yield</span> <span class="p">(</span><span class="n">rhs</span><span class="o">.</span><span class="n">next</span><span class="p">(),</span> <span class="n">lhs</span><span class="o">.</span><span class="n">next</span><span class="p">())</span> </pre></div> </div> <p>In this case, the corresponding iterator behavior has to be a bit more subtle to handle the case of either the right or left iterator raising SuspendIteration. It seems to be a matter of decomposing the generator to recognize intermediate states where a SuspendIterator exception from the producing context could happen.</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">pair</span><span class="p">:</span> <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">iterLeft</span><span class="p">,</span> <span class="n">iterRight</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">iterLeft</span> <span class="o">=</span> <span class="n">iterLeft</span> <span class="bp">self</span><span class="o">.</span><span class="n">iterRight</span> <span class="o">=</span> <span class="n">iterRight</span> <span class="k">def</span> <span class="fm">__iter__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">rhs</span> <span class="o">=</span> <span class="nb">iter</span><span class="p">(</span><span class="n">iterRight</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">lhs</span> <span class="o">=</span> <span class="nb">iter</span><span class="p">(</span><span class="n">iterLeft</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">_temp_rhs</span> <span class="o">=</span> <span class="kc">None</span> <span class="bp">self</span><span class="o">.</span><span class="n">_temp_lhs</span> <span class="o">=</span> <span class="kc">None</span> <span class="bp">self</span><span class="o">.</span><span class="n">_next</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">state_rhs</span> <span class="k">return</span> <span class="bp">self</span> <span class="k">def</span> <span class="nf">next</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_next</span><span class="p">()</span> <span class="k">def</span> <span class="nf">state_rhs</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">_temp_rhs</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">rhs</span><span class="o">.</span><span class="n">next</span><span class="p">()</span> <span class="bp">self</span><span class="o">.</span><span class="n">_next</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">state_lhs</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">next</span><span class="p">()</span> <span class="k">def</span> <span class="nf">state_lhs</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">_temp_lhs</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">lhs</span><span class="o">.</span><span class="n">next</span><span class="p">()</span> <span class="bp">self</span><span class="o">.</span><span class="n">_next</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">state_pair</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">next</span><span class="p">()</span> <span class="k">def</span> <span class="nf">state_pair</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">_next</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">state_rhs</span> <span class="k">return</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_temp_rhs</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">_temp_lhs</span><span class="p">)</span> </pre></div> </div> <p>This proposal assumes that a corresponding iterator written using this class-based method is possible for existing generators. The challenge seems to be the identification of distinct states within the generator where suspension could occur.</p> </section> <section id="resource-cleanup"> <h3><a class="toc-backref" href="#resource-cleanup" role="doc-backlink">Resource Cleanup</a></h3> <p>The current generator mechanism has a strange interaction with exceptions where a ‘yield’ statement is not allowed within a try/finally block. The SuspendIterator exception provides another similar issue. The impacts of this issue are not clear. However it may be that re-writing the generator into a state machine, as the previous section did, could resolve this issue allowing for the situation to be no-worse than, and perhaps even removing the yield/finally situation. More investigation is needed in this area.</p> </section> <section id="api-and-limitations"> <h3><a class="toc-backref" href="#api-and-limitations" role="doc-backlink">API and Limitations</a></h3> <p>This proposal only covers ‘suspending’ a chain of iterators, and does not cover (of course) suspending general functions, methods, or “C” extension function. While there could be no direct support for creating generators in “C” code, native “C” iterators which comply with the SuspendIterator semantics are certainly possible.</p> </section> </section> <section id="low-level-implementation"> <h2><a class="toc-backref" href="#low-level-implementation" role="doc-backlink">Low-Level Implementation</a></h2> <p>The author of the PEP is not yet familiar with the Python execution model to comment in this area.</p> </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="id9" role="doc-footnote"> <dt class="label" id="id9">[<a href="#id1">1</a>]</dt> <dd>Twisted (<a class="reference external" href="http://twistedmatrix.com">http://twistedmatrix.com</a>)</aside> <aside class="footnote brackets" id="id10" role="doc-footnote"> <dt class="label" id="id10">[<a href="#id2">2</a>]</dt> <dd>Peak (<a class="reference external" href="http://peak.telecommunity.com">http://peak.telecommunity.com</a>)</aside> <aside class="footnote brackets" id="id11" role="doc-footnote"> <dt class="label" id="id11">[<a href="#id3">3</a>]</dt> <dd>C10K (<a class="reference external" href="http://www.kegel.com/c10k.html">http://www.kegel.com/c10k.html</a>)</aside> <aside class="footnote brackets" id="id12" role="doc-footnote"> <dt class="label" id="id12">[<a href="#id4">4</a>]</dt> <dd>Coroutines (<a class="reference external" href="http://c2.com/cgi/wiki?CallWithCurrentContinuation">http://c2.com/cgi/wiki?CallWithCurrentContinuation</a>)</aside> <aside class="footnote brackets" id="id13" role="doc-footnote"> <dt class="label" id="id13">[<a href="#id5">6</a>]</dt> <dd>Stackless Python (<a class="reference external" href="http://stackless.com">http://stackless.com</a>)</aside> <aside class="footnote brackets" id="id14" role="doc-footnote"> <dt class="label" id="id14">[<a href="#id6">7</a>]</dt> <dd>Parrot /w coroutines (<a class="reference external" href="http://www.sidhe.org/~dan/blog/archives/000178.html">http://www.sidhe.org/~dan/blog/archives/000178.html</a>)</aside> <aside class="footnote brackets" id="id15" role="doc-footnote"> <dt class="label" id="id15">[<a href="#id7">9</a>]</dt> <dd>itertools - Functions creating iterators (<a class="reference external" href="http://docs.python.org/library/itertools.html">http://docs.python.org/library/itertools.html</a>)</aside> <aside class="footnote brackets" id="id16" role="doc-footnote"> <dt class="label" id="id16">[<a href="#id8">10</a>]</dt> <dd>Microthreads in Python, David Mertz (<a class="reference external" href="http://www-106.ibm.com/developerworks/linux/library/l-pythrd.html">http://www-106.ibm.com/developerworks/linux/library/l-pythrd.html</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-0334.rst">https://github.com/python/peps/blob/main/peps/pep-0334.rst</a></p> <p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0334.rst">2023-09-09 17:39:29 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="#semantics">Semantics</a><ul> <li><a class="reference internal" href="#simple-iterators">Simple Iterators</a></li> <li><a class="reference internal" href="#introducing-suspenditeration">Introducing SuspendIteration</a></li> <li><a class="reference internal" href="#application-iterators">Application Iterators</a></li> <li><a class="reference internal" href="#complicating-factors">Complicating Factors</a></li> <li><a class="reference internal" href="#resource-cleanup">Resource Cleanup</a></li> <li><a class="reference internal" href="#api-and-limitations">API and Limitations</a></li> </ul> </li> <li><a class="reference internal" href="#low-level-implementation">Low-Level Implementation</a></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-0334.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