CINXE.COM

PEP 713 – Callable Modules | 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 713 – Callable Modules | peps.python.org</title> <link rel="shortcut icon" href="../_static/py.png"> <link rel="canonical" href="https://peps.python.org/pep-0713/"> <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 713 – Callable Modules | peps.python.org'> <meta property="og:description" content="Modules are currently not directly callable. Classes can define a __call__ method that makes instance objects callable, but defining a similarly named function in the global module scope has no effect, and that function can only be called by importing o..."> <meta property="og:type" content="website"> <meta property="og:url" content="https://peps.python.org/pep-0713/"> <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="Modules are currently not directly callable. Classes can define a __call__ method that makes instance objects callable, but defining a similarly named function in the global module scope has no effect, and that function can only be called by importing o..."> <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 713</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 713 – Callable Modules</h1> <dl class="rfc2822 field-list simple"> <dt class="field-odd">Author<span class="colon">:</span></dt> <dd class="field-odd">Amethyst Reese &lt;amethyst at n7.gg&gt;</dd> <dt class="field-even">Sponsor<span class="colon">:</span></dt> <dd class="field-even">Łukasz Langa &lt;lukasz at python.org&gt;</dd> <dt class="field-odd">Discussions-To<span class="colon">:</span></dt> <dd class="field-odd"><a class="reference external" href="https://discuss.python.org/t/pep-713-callable-modules/26127">Discourse thread</a></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">20-Apr-2023</dd> <dt class="field-odd">Python-Version<span class="colon">:</span></dt> <dd class="field-odd">3.12</dd> <dt class="field-even">Post-History<span class="colon">:</span></dt> <dd class="field-even"><a class="reference external" href="https://discuss.python.org/t/pep-713-callable-modules/26127" title="Discourse thread">23-Apr-2023</a></dd> <dt class="field-odd">Resolution<span class="colon">:</span></dt> <dd class="field-odd"><a class="reference external" href="https://discuss.python.org/t/26127/86">Discourse message</a></dd> </dl> <hr class="docutils" /> <section id="contents"> <details><summary>Table of Contents</summary><ul class="simple"> <li><a class="reference internal" href="#rejection-notice">Rejection Notice</a></li> <li><a class="reference internal" href="#abstract">Abstract</a></li> <li><a class="reference internal" href="#motivation">Motivation</a></li> <li><a class="reference internal" href="#specification">Specification</a></li> <li><a class="reference internal" href="#backwards-compatibility-and-impact-on-performance">Backwards Compatibility and Impact on Performance</a></li> <li><a class="reference internal" href="#how-to-teach-this">How to Teach This</a></li> <li><a class="reference internal" href="#reference-implementation">Reference Implementation</a></li> <li><a class="reference internal" href="#rejected-ideas">Rejected Ideas</a></li> <li><a class="reference internal" href="#copyright">Copyright</a></li> </ul> </details></section> <section id="rejection-notice"> <h2><a class="toc-backref" href="#rejection-notice" role="doc-backlink">Rejection Notice</a></h2> <p>The Steering Council didn’t feel that there was a compelling reason to have this PEP, even though it clearly could be done from a consistency point of view. If this idea comes up again in the future, this is a useful prior discussion to refer to.</p> </section> <section id="abstract"> <h2><a class="toc-backref" href="#abstract" role="doc-backlink">Abstract</a></h2> <p>Modules are currently not directly callable. Classes can define a <code class="docutils literal notranslate"><span class="pre">__call__</span></code> method that makes instance objects callable, but defining a similarly named function in the global module scope has no effect, and that function can only be called by importing or referencing it directly as <code class="docutils literal notranslate"><span class="pre">module.__call__</span></code>. <a class="pep reference internal" href="../pep-0562/" title="PEP 562 – Module __getattr__ and __dir__">PEP 562</a> added support for <a class="reference external" href="https://docs.python.org/3/reference/datamodel.html#object.__getattr__" title="(in Python v3.13)"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__getattr__()</span></code></a> and <a class="reference external" href="https://docs.python.org/3/reference/datamodel.html#object.__dir__" title="(in Python v3.13)"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__dir__()</span></code></a> for modules, but defining <code class="docutils literal notranslate"><span class="pre">__getattr__</span></code> to return a value for <code class="docutils literal notranslate"><span class="pre">__call__</span></code> still does not make a module callable.</p> <p>This PEP proposes support for making modules directly callable by defining a <code class="docutils literal notranslate"><span class="pre">__call__</span></code> object in the module’s global namespace, either as a standard function, or an arbitrary callable object.</p> </section> <section id="motivation"> <h2><a class="toc-backref" href="#motivation" role="doc-backlink">Motivation</a></h2> <p>Many modules have only a single primary interface to their functionality. In many cases, that interface is a single callable object, where being able to import and use the module directly as a callable provides a more “Pythonic” interface for users:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># user.py</span> <span class="kn">import</span> <span class="nn">fancy</span> <span class="nd">@fancy</span> <span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="o">...</span><span class="p">):</span> <span class="o">...</span> </pre></div> </div> <p>Currently, providing this style of interface requires modifying the module object at runtime to make it callable.</p> <p>This is commonly done by replacing the module object in <code class="docutils literal notranslate"><span class="pre">sys.modules</span></code> with a callable alternative (such as a function or class instance):</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># fancy.py</span> <span class="k">def</span> <span class="nf">fancy</span><span class="p">(</span><span class="o">...</span><span class="p">):</span> <span class="o">...</span> <span class="n">sys</span><span class="o">.</span><span class="n">modules</span><span class="p">[</span><span class="vm">__name__</span><span class="p">]</span> <span class="o">=</span> <span class="n">fancy</span> </pre></div> </div> <p>This has the effect of making the original module effectively unreachable without further hooks from the author, even with <code class="docutils literal notranslate"><span class="pre">from</span> <span class="pre">module</span> <span class="pre">import</span> <span class="pre">member</span></code>. It also results in a “module” object that is missing all of the special module attributes, including <code class="docutils literal notranslate"><span class="pre">__doc__</span></code>, <code class="docutils literal notranslate"><span class="pre">__package__</span></code>, <code class="docutils literal notranslate"><span class="pre">__path__</span></code>, etc.</p> <p>Alternatively, a module author can choose to override the module’s <code class="docutils literal notranslate"><span class="pre">__class__</span></code> property with a custom type that provides a callable interface:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># fancy.py</span> <span class="k">def</span> <span class="nf">fancy</span><span class="p">(</span><span class="o">...</span><span class="p">):</span> <span class="o">...</span> <span class="k">class</span> <span class="nc">FancyModule</span><span class="p">(</span><span class="n">types</span><span class="o">.</span><span class="n">ModuleType</span><span class="p">):</span> <span class="k">def</span> <span class="fm">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">...</span><span class="p">):</span> <span class="k">return</span> <span class="n">fancy</span><span class="p">(</span><span class="o">...</span><span class="p">)</span> <span class="n">sys</span><span class="o">.</span><span class="n">modules</span><span class="p">[</span><span class="vm">__name__</span><span class="p">]</span><span class="o">.</span><span class="vm">__class__</span> <span class="o">=</span> <span class="n">FancyModule</span> </pre></div> </div> <p>The downside of either approach is that it not only results in extra boilerplate, but also results in type checker failures because they don’t recognize that the module is callable at runtime:</p> <div class="highlight-console notranslate"><div class="highlight"><pre><span></span><span class="gp">$ </span>mypy<span class="w"> </span>user.py <span class="go">user.py:3: error: Module not callable [operator]</span> <span class="go">Found 1 error in 1 file (checked 1 source file)</span> </pre></div> </div> </section> <section id="specification"> <h2><a class="toc-backref" href="#specification" role="doc-backlink">Specification</a></h2> <p>When a module object is called, and a <code class="docutils literal notranslate"><span class="pre">__call__</span></code> object is found (either as the result of a <code class="docutils literal notranslate"><span class="pre">__getattr__</span></code> or <code class="docutils literal notranslate"><span class="pre">__dict__</span></code> lookup), then that object will be called with the given arguments.</p> <p>If a <code class="docutils literal notranslate"><span class="pre">__call__</span></code> object is not found, then a <code class="docutils literal notranslate"><span class="pre">TypeError</span></code> will be raised, matching the existing behavior.</p> <p>All of these examples would be considered valid, callable modules:</p> <div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># hello.py</span> <span class="k">def</span> <span class="fm">__call__</span><span class="p">(</span><span class="o">...</span><span class="p">):</span> <span class="k">pass</span> </pre></div> </div> <div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># hello.py</span> <span class="k">class</span> <span class="nc">Hello</span><span class="p">:</span> <span class="k">pass</span> <span class="fm">__call__</span> <span class="o">=</span> <span class="n">Hello</span> </pre></div> </div> <div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># hello.py</span> <span class="k">def</span> <span class="nf">hello</span><span class="p">():</span> <span class="k">pass</span> <span class="k">def</span> <span class="fm">__getattr__</span><span class="p">(</span><span class="n">name</span><span class="p">):</span> <span class="k">if</span> <span class="n">name</span> <span class="o">==</span> <span class="s2">&quot;__call__&quot;</span><span class="p">:</span> <span class="k">return</span> <span class="n">hello</span> </pre></div> </div> <p>The first two styles should generally be preferred, as it allows for easier static analysis from tools like type checkers, though the third form would be allowed in order to make the implementation more consistent.</p> <p>The intent is to allow arbitrary callable object to be assigned to the module’s <code class="docutils literal notranslate"><span class="pre">__call__</span></code> property or returned by the module’s <code class="docutils literal notranslate"><span class="pre">__getattr__</span></code> method, enabling module authors to pick the most suitable mechanism for making their module callable by users.</p> </section> <section id="backwards-compatibility-and-impact-on-performance"> <h2><a class="toc-backref" href="#backwards-compatibility-and-impact-on-performance" role="doc-backlink">Backwards Compatibility and Impact on Performance</a></h2> <p>This PEP is not expected to cause any backwards incompatibility. Any modules that already contain a <code class="docutils literal notranslate"><span class="pre">__call__</span></code> object will continue to function the same as before, though with the additional ability to be called directly. It is considered unlikely that modules with an existing <code class="docutils literal notranslate"><span class="pre">__call__</span></code> object would depend on the existing behavior of raising <code class="docutils literal notranslate"><span class="pre">TypeError</span></code> when called.</p> <p>Performance implications of this PEP are minimal, as it defines a new interface. Calling a module would trigger a lookup for the name <code class="docutils literal notranslate"><span class="pre">__call__</span></code> on a module object. Existing workarounds for creating callable modules already depend on this behavior for generic objects, resulting in similar performance for these callable modules.</p> <p>Type checkers will likely need to be updated accordingly to treat modules with a <code class="docutils literal notranslate"><span class="pre">__call__</span></code> object as callable. This should be possible to support in type checkers when checking code targeted at older Python versions that do not support callable modules, with the expectation that these modules would also include one of the workarounds mentioned earlier to make the module callable.</p> </section> <section id="how-to-teach-this"> <h2><a class="toc-backref" href="#how-to-teach-this" role="doc-backlink">How to Teach This</a></h2> <p>The documentation for <a class="reference external" href="https://docs.python.org/3/reference/datamodel.html#types" title="(in Python v3.13)"><span class="xref std std-ref">callable types</span></a> will include modules in the list, with a link to <a class="reference external" href="https://docs.python.org/3/reference/datamodel.html#object.__call__" title="(in Python v3.13)"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__call__()</span></code></a>. The <a class="reference external" href="https://docs.python.org/3/reference/datamodel.html#id15" title="(in Python v3.13)"><span>Emulating callable objects</span></a> documentation will include a section covering callable modules, with example code, similar to the section for <a class="reference external" href="https://docs.python.org/3/reference/datamodel.html#customizing-module-attribute-access">customizing module attribute access</a>.</p> </section> <section id="reference-implementation"> <h2><a class="toc-backref" href="#reference-implementation" role="doc-backlink">Reference Implementation</a></h2> <p>The proposed implementation of callable modules is available in <a class="reference external" href="https://github.com/python/cpython/pull/103742">CPython PR #103742</a>.</p> </section> <section id="rejected-ideas"> <h2><a class="toc-backref" href="#rejected-ideas" role="doc-backlink">Rejected Ideas</a></h2> <p>Given the introduction of <code class="docutils literal notranslate"><span class="pre">__getattr__</span></code> and <code class="docutils literal notranslate"><span class="pre">__dir__</span></code>, and the proposal to enable use of <code class="docutils literal notranslate"><span class="pre">__call__</span></code>, it was considered if it was worth allowing use of <em>all</em> <a class="reference external" href="https://docs.python.org/3/reference/datamodel.html#specialnames" title="(in Python v3.13)"><span>Special method names</span></a> for modules, such as <code class="docutils literal notranslate"><span class="pre">__or__</span></code> and <code class="docutils literal notranslate"><span class="pre">__iter__</span></code>. While this would not be completely undesired, it increases the potential for backward compatibility concerns, and these other special methods are likely to provide less utility to library authors in comparison to <code class="docutils literal notranslate"><span class="pre">__call__</span></code>.</p> </section> <section id="copyright"> <h2><a class="toc-backref" href="#copyright" role="doc-backlink">Copyright</a></h2> <p>This document is placed in the public domain or under the CC0-1.0-Universal license, whichever is more permissive.</p> </section> </section> <hr class="docutils" /> <p>Source: <a class="reference external" href="https://github.com/python/peps/blob/main/peps/pep-0713.rst">https://github.com/python/peps/blob/main/peps/pep-0713.rst</a></p> <p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0713.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="#rejection-notice">Rejection Notice</a></li> <li><a class="reference internal" href="#abstract">Abstract</a></li> <li><a class="reference internal" href="#motivation">Motivation</a></li> <li><a class="reference internal" href="#specification">Specification</a></li> <li><a class="reference internal" href="#backwards-compatibility-and-impact-on-performance">Backwards Compatibility and Impact on Performance</a></li> <li><a class="reference internal" href="#how-to-teach-this">How to Teach This</a></li> <li><a class="reference internal" href="#reference-implementation">Reference Implementation</a></li> <li><a class="reference internal" href="#rejected-ideas">Rejected Ideas</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-0713.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