CINXE.COM
PEP 3152 – Cofunctions | 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 3152 – Cofunctions | peps.python.org</title> <link rel="shortcut icon" href="../_static/py.png"> <link rel="canonical" href="https://peps.python.org/pep-3152/"> <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 3152 – Cofunctions | peps.python.org'> <meta property="og:description" content="A syntax is proposed for defining and calling a special type of generator called a ‘cofunction’. It is designed to provide a streamlined way of writing generator-based coroutines, and allow the early detection of certain kinds of error that are easily ..."> <meta property="og:type" content="website"> <meta property="og:url" content="https://peps.python.org/pep-3152/"> <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="A syntax is proposed for defining and calling a special type of generator called a ‘cofunction’. It is designed to provide a streamlined way of writing generator-based coroutines, and allow the early detection of certain kinds of error that are easily ..."> <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> » </li> <li><a href="../pep-0000/">PEP Index</a> » </li> <li>PEP 3152</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 3152 – Cofunctions</h1> <dl class="rfc2822 field-list simple"> <dt class="field-odd">Author<span class="colon">:</span></dt> <dd class="field-odd">Gregory Ewing <greg.ewing at canterbury.ac.nz></dd> <dt class="field-even">Status<span class="colon">:</span></dt> <dd class="field-even"><abbr title="Formally declined and will not be accepted">Rejected</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">13-Feb-2009</dd> <dt class="field-odd">Python-Version<span class="colon">:</span></dt> <dd class="field-odd">3.3</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><ul> <li><a class="reference internal" href="#rejection">Rejection</a></li> </ul> </li> <li><a class="reference internal" href="#specification">Specification</a><ul> <li><a class="reference internal" href="#cofunction-definitions">Cofunction definitions</a></li> <li><a class="reference internal" href="#cocalls">Cocalls</a></li> <li><a class="reference internal" href="#new-builtins-attributes-and-c-api-functions">New builtins, attributes and C API functions</a></li> </ul> </li> <li><a class="reference internal" href="#motivation-and-rationale">Motivation and Rationale</a></li> <li><a class="reference internal" href="#prototype-implementation">Prototype Implementation</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>A syntax is proposed for defining and calling a special type of generator called a ‘cofunction’. It is designed to provide a streamlined way of writing generator-based coroutines, and allow the early detection of certain kinds of error that are easily made when writing such code, which otherwise tend to cause hard-to-diagnose symptoms.</p> <p>This proposal builds on the ‘yield from’ mechanism described in PEP 380, and describes some of the semantics of cofunctions in terms of it. However, it would be possible to define and implement cofunctions independently of <a class="pep reference internal" href="../pep-0380/" title="PEP 380 – Syntax for Delegating to a Subgenerator">PEP 380</a> if so desired.</p> <section id="rejection"> <h3><a class="toc-backref" href="#rejection" role="doc-backlink">Rejection</a></h3> <p>See <a class="reference external" href="https://mail.python.org/pipermail/python-dev/2015-April/139503.html">https://mail.python.org/pipermail/python-dev/2015-April/139503.html</a></p> </section> </section> <section id="specification"> <h2><a class="toc-backref" href="#specification" role="doc-backlink">Specification</a></h2> <section id="cofunction-definitions"> <h3><a class="toc-backref" href="#cofunction-definitions" role="doc-backlink">Cofunction definitions</a></h3> <p>A new keyword <code class="docutils literal notranslate"><span class="pre">codef</span></code> is introduced which is used in place of <code class="docutils literal notranslate"><span class="pre">def</span></code> to define a cofunction. A cofunction is a special kind of generator having the following characteristics:</p> <ol class="arabic simple"> <li>A cofunction is always a generator, even if it does not contain any <code class="docutils literal notranslate"><span class="pre">yield</span></code> or <code class="docutils literal notranslate"><span class="pre">yield</span> <span class="pre">from</span></code> expressions.</li> <li>A cofunction cannot be called the same way as an ordinary function. An exception is raised if an ordinary call to a cofunction is attempted.</li> </ol> </section> <section id="cocalls"> <h3><a class="toc-backref" href="#cocalls" role="doc-backlink">Cocalls</a></h3> <p>Calls from one cofunction to another are made by marking the call with a new keyword <code class="docutils literal notranslate"><span class="pre">cocall</span></code>. The expression</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">cocall</span> <span class="n">f</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwds</span><span class="p">)</span> </pre></div> </div> <p>is semantically equivalent to</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">yield from</span> <span class="n">f</span><span class="o">.</span><span class="n">__cocall__</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwds</span><span class="p">)</span> </pre></div> </div> <p>except that the object returned by __cocall__ is expected to be an iterator, so the step of calling iter() on it is skipped.</p> <p>The full syntax of a cocall expression is described by the following grammar lines:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">atom</span><span class="p">:</span> <span class="n">cocall</span> <span class="o">|</span> <span class="o"><</span><span class="n">existing</span> <span class="n">alternatives</span> <span class="k">for</span> <span class="n">atom</span><span class="o">></span> <span class="n">cocall</span><span class="p">:</span> <span class="s1">'cocall'</span> <span class="n">atom</span> <span class="n">cotrailer</span><span class="o">*</span> <span class="s1">'('</span> <span class="p">[</span><span class="n">arglist</span><span class="p">]</span> <span class="s1">')'</span> <span class="n">cotrailer</span><span class="p">:</span> <span class="s1">'['</span> <span class="n">subscriptlist</span> <span class="s1">']'</span> <span class="o">|</span> <span class="s1">'.'</span> <span class="n">NAME</span> </pre></div> </div> <p>The <code class="docutils literal notranslate"><span class="pre">cocall</span></code> keyword is syntactically valid only inside a cofunction. A SyntaxError will result if it is used in any other context.</p> <p>Objects which implement __cocall__ are expected to return an object obeying the iterator protocol. Cofunctions respond to __cocall__ the same way as ordinary generator functions respond to __call__, i.e. by returning a generator-iterator.</p> <p>Certain objects that wrap other callable objects, notably bound methods, will be given __cocall__ implementations that delegate to the underlying object.</p> </section> <section id="new-builtins-attributes-and-c-api-functions"> <h3><a class="toc-backref" href="#new-builtins-attributes-and-c-api-functions" role="doc-backlink">New builtins, attributes and C API functions</a></h3> <p>To facilitate interfacing cofunctions with non-coroutine code, there will be a built-in function <code class="docutils literal notranslate"><span class="pre">costart</span></code> whose definition is equivalent to</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">costart</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwds</span><span class="p">):</span> <span class="k">return</span> <span class="n">obj</span><span class="o">.</span><span class="n">__cocall__</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwds</span><span class="p">)</span> </pre></div> </div> <p>There will also be a corresponding C API function</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">PyObject</span> <span class="o">*</span><span class="n">PyObject_CoCall</span><span class="p">(</span><span class="n">PyObject</span> <span class="o">*</span><span class="n">obj</span><span class="p">,</span> <span class="n">PyObject</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="n">PyObject</span> <span class="o">*</span><span class="n">kwds</span><span class="p">)</span> </pre></div> </div> <p>It is left unspecified for now whether a cofunction is a distinct type of object or, like a generator function, is simply a specially-marked function instance. If the latter, a read-only boolean attribute <code class="docutils literal notranslate"><span class="pre">__iscofunction__</span></code> should be provided to allow testing whether a given function object is a cofunction.</p> </section> </section> <section id="motivation-and-rationale"> <h2><a class="toc-backref" href="#motivation-and-rationale" role="doc-backlink">Motivation and Rationale</a></h2> <p>The <code class="docutils literal notranslate"><span class="pre">yield</span> <span class="pre">from</span></code> syntax is reasonably self-explanatory when used for the purpose of delegating part of the work of a generator to another function. It can also be used to good effect in the implementation of generator-based coroutines, but it reads somewhat awkwardly when used for that purpose, and tends to obscure the true intent of the code.</p> <p>Furthermore, using generators as coroutines is somewhat error-prone. If one forgets to use <code class="docutils literal notranslate"><span class="pre">yield</span> <span class="pre">from</span></code> when it should have been used, or uses it when it shouldn’t have, the symptoms that result can be obscure and confusing.</p> <p>Finally, sometimes there is a need for a function to be a coroutine even though it does not yield anything, and in these cases it is necessary to resort to kludges such as <code class="docutils literal notranslate"><span class="pre">if</span> <span class="pre">0:</span> <span class="pre">yield</span></code> to force it to be a generator.</p> <p>The <code class="docutils literal notranslate"><span class="pre">codef</span></code> and <code class="docutils literal notranslate"><span class="pre">cocall</span></code> constructs address the first issue by making the syntax directly reflect the intent, that is, that the function forms part of a coroutine.</p> <p>The second issue is addressed by making it impossible to mix coroutine and non-coroutine code in ways that don’t make sense. If the rules are violated, an exception is raised that points out exactly what and where the problem is.</p> <p>Lastly, the need for dummy yields is eliminated by making the form of definition determine whether the function is a coroutine, rather than what it contains.</p> </section> <section id="prototype-implementation"> <h2><a class="toc-backref" href="#prototype-implementation" role="doc-backlink">Prototype Implementation</a></h2> <p>An implementation in the form of patches to Python 3.1.2 can be found here:</p> <p><a class="reference external" href="http://www.cosc.canterbury.ac.nz/greg.ewing/python/generators/cofunctions.html">http://www.cosc.canterbury.ac.nz/greg.ewing/python/generators/cofunctions.html</a></p> </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-3152.rst">https://github.com/python/peps/blob/main/peps/pep-3152.rst</a></p> <p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-3152.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><ul> <li><a class="reference internal" href="#rejection">Rejection</a></li> </ul> </li> <li><a class="reference internal" href="#specification">Specification</a><ul> <li><a class="reference internal" href="#cofunction-definitions">Cofunction definitions</a></li> <li><a class="reference internal" href="#cocalls">Cocalls</a></li> <li><a class="reference internal" href="#new-builtins-attributes-and-c-api-functions">New builtins, attributes and C API functions</a></li> </ul> </li> <li><a class="reference internal" href="#motivation-and-rationale">Motivation and Rationale</a></li> <li><a class="reference internal" href="#prototype-implementation">Prototype Implementation</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-3152.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>