CINXE.COM
PEP 630 – Isolating Extension 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 630 – Isolating Extension Modules | peps.python.org</title> <link rel="shortcut icon" href="../_static/py.png"> <link rel="canonical" href="https://peps.python.org/pep-0630/"> <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 630 – Isolating Extension Modules | peps.python.org'> <meta property="og:description" content="Traditionally, state belonging to Python extension modules was kept in C static variables, which have process-wide scope. This document describes problems of such per-process state and efforts to make per-module state—a better default—possible and easy ..."> <meta property="og:type" content="website"> <meta property="og:url" content="https://peps.python.org/pep-0630/"> <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="Traditionally, state belonging to Python extension modules was kept in C static variables, which have process-wide scope. This document describes problems of such per-process state and efforts to make per-module state—a better default—possible and easy ..."> <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 630</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 630 – Isolating Extension Modules</h1> <dl class="rfc2822 field-list simple"> <dt class="field-odd">Author<span class="colon">:</span></dt> <dd class="field-odd">Petr Viktorin <encukou at gmail.com></dd> <dt class="field-even">Discussions-To<span class="colon">:</span></dt> <dd class="field-even"><a class="reference external" href="https://mail.python.org/archives/list/capi-sig@python.org/">Capi-SIG list</a></dd> <dt class="field-odd">Status<span class="colon">:</span></dt> <dd class="field-odd"><abbr title="Accepted and implementation complete, or no longer active">Final</abbr></dd> <dt class="field-even">Type<span class="colon">:</span></dt> <dd class="field-even"><abbr title="Non-normative PEP containing background, guidelines or other information relevant to the Python ecosystem">Informational</abbr></dd> <dt class="field-odd">Created<span class="colon">:</span></dt> <dd class="field-odd">25-Aug-2020</dd> <dt class="field-even">Post-History<span class="colon">:</span></dt> <dd class="field-even">16-Jul-2020</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="#about-this-document">About This Document</a></li> <li><a class="reference internal" href="#motivation">Motivation</a></li> <li><a class="reference internal" href="#rationale-for-per-module-state">Rationale for Per-module State</a><ul> <li><a class="reference internal" href="#goal-easy-to-use-module-state">Goal: Easy-to-Use Module State</a></li> <li><a class="reference internal" href="#non-goals-speedups-and-the-gil">Non-goals: Speedups and the GIL</a></li> </ul> </li> <li><a class="reference internal" href="#making-modules-safe-with-multiple-interpreters">Making Modules Safe with Multiple Interpreters</a><ul> <li><a class="reference internal" href="#isolated-module-objects">Isolated Module Objects</a></li> <li><a class="reference internal" href="#surprising-edge-cases">Surprising Edge Cases</a></li> <li><a class="reference internal" href="#managing-global-state">Managing Global State</a></li> <li><a class="reference internal" href="#managing-per-module-state">Managing Per-Module State</a></li> <li><a class="reference internal" href="#opt-out-limiting-to-one-module-object-per-process">Opt-Out: Limiting to One Module Object per Process</a></li> <li><a class="reference internal" href="#module-state-access-from-functions">Module State Access from Functions</a></li> </ul> </li> <li><a class="reference internal" href="#heap-types">Heap Types</a><ul> <li><a class="reference internal" href="#defining-heap-types">Defining Heap Types</a></li> <li><a class="reference internal" href="#garbage-collection-protocol">Garbage Collection Protocol</a></li> <li><a class="reference internal" href="#module-state-access-from-classes">Module State Access from Classes</a></li> <li><a class="reference internal" href="#module-state-access-from-regular-methods">Module State Access from Regular Methods</a></li> <li><a class="reference internal" href="#module-state-access-from-slot-methods-getters-and-setters">Module State Access from Slot Methods, Getters and Setters</a></li> <li><a class="reference internal" href="#lifetime-of-the-module-state">Lifetime of the Module State</a></li> </ul> </li> <li><a class="reference internal" href="#open-issues">Open Issues</a><ul> <li><a class="reference internal" href="#type-checking">Type Checking</a></li> <li><a class="reference internal" href="#metaclasses">Metaclasses</a></li> <li><a class="reference internal" href="#per-class-scope">Per-Class Scope</a></li> <li><a class="reference internal" href="#lossless-conversion-to-heap-types">Lossless Conversion to Heap Types</a></li> </ul> </li> <li><a class="reference internal" href="#copyright">Copyright</a></li> </ul> </details></section> <div class="pep-banner canonical-doc sticky-banner admonition important"> <p class="admonition-title">Important</p> <p>This PEP is a historical document. The up-to-date, canonical documentation can now be found at <a class="reference external" href="https://docs.python.org/3.11/howto/isolating-extensions.html">Isolating Extension Modules HOWTO</a>.</p> <p class="close-button">×</p> <p>See <a class="pep reference internal" href="../pep-0001/" title="PEP 1 – PEP Purpose and Guidelines">PEP 1</a> for how to propose changes.</p> </div> <section id="abstract"> <h2><a class="toc-backref" href="#abstract" role="doc-backlink">Abstract</a></h2> <p>Traditionally, state belonging to Python extension modules was kept in C <code class="docutils literal notranslate"><span class="pre">static</span></code> variables, which have process-wide scope. This document describes problems of such per-process state and efforts to make per-module state—a better default—possible and easy to use.</p> <p>The document also describes how to switch to per-module state where possible. This transition involves allocating space for that state, potentially switching from static types to heap types, and—perhaps most importantly—accessing per-module state from code.</p> </section> <section id="about-this-document"> <h2><a class="toc-backref" href="#about-this-document" role="doc-backlink">About This Document</a></h2> <p>As an <a class="pep reference internal" href="../pep-0001/#pep-types" title="PEP 1 – PEP Purpose and Guidelines § PEP Types">informational PEP</a>, this document does not introduce any changes; those should be done in their own PEPs (or issues, if small enough). Rather, it covers the motivation behind an effort that spans multiple releases, and instructs early adopters on how to use the finished features.</p> <p>Once support is reasonably complete, this content can be moved to Python’s documentation as a <a class="reference external" href="https://docs.python.org/3/howto/index.html">HOWTO</a>. Meanwhile, in the spirit of documentation-driven development, gaps identified in this PEP can show where to focus the effort, and it can be updated as new features are implemented.</p> <p>Whenever this PEP mentions <em>extension modules</em>, the advice also applies to <em>built-in</em> modules.</p> <div class="admonition note"> <p class="admonition-title">Note</p> <p>This PEP contains generic advice. When following it, always take into account the specifics of your project.</p> <p>For example, while much of this advice applies to the C parts of Python’s standard library, the PEP does not factor in stdlib specifics (unusual backward compatibility issues, access to private API, etc.).</p> </div> <p>PEPs related to this effort are:</p> <ul class="simple"> <li><a class="pep reference internal" href="../pep-0384/" title="PEP 384 – Defining a Stable ABI">PEP 384</a> – <em>Defining a Stable ABI</em>, which added a C API for creating heap types</li> <li><a class="pep reference internal" href="../pep-0489/" title="PEP 489 – Multi-phase extension module initialization">PEP 489</a> – <em>Multi-phase extension module initialization</em></li> <li><a class="pep reference internal" href="../pep-0573/" title="PEP 573 – Module State Access from C Extension Methods">PEP 573</a> – <em>Module State Access from C Extension Methods</em></li> </ul> <p>This document is concerned with Python’s public C API, which is not offered by all implementations of Python. However, nothing in this PEP is specific to CPython.</p> <p>As with any Informational PEP, this text does not necessarily represent a Python community consensus or recommendation.</p> </section> <section id="motivation"> <h2><a class="toc-backref" href="#motivation" role="doc-backlink">Motivation</a></h2> <p>An <em>interpreter</em> is the context in which Python code runs. It contains configuration (e.g. the import path) and runtime state (e.g. the set of imported modules).</p> <p>Python supports running multiple interpreters in one process. There are two cases to think about—users may run interpreters:</p> <ul class="simple"> <li>in sequence, with several <code class="docutils literal notranslate"><span class="pre">Py_InitializeEx</span></code>/<code class="docutils literal notranslate"><span class="pre">Py_FinalizeEx</span></code> cycles, and</li> <li>in parallel, managing “sub-interpreters” using <code class="docutils literal notranslate"><span class="pre">Py_NewInterpreter</span></code>/<code class="docutils literal notranslate"><span class="pre">Py_EndInterpreter</span></code>.</li> </ul> <p>Both cases (and combinations of them) would be most useful when embedding Python within a library. Libraries generally shouldn’t make assumptions about the application that uses them, which includes assuming a process-wide “main Python interpreter”.</p> <p>Currently, CPython doesn’t handle this use case well. Many extension modules (and even some stdlib modules) use <em>per-process</em> global state, because C <code class="docutils literal notranslate"><span class="pre">static</span></code> variables are extremely easy to use. Thus, data that should be specific to an interpreter ends up being shared between interpreters. Unless the extension developer is careful, it is very easy to introduce edge cases that lead to crashes when a module is loaded in more than one interpreter in the same process.</p> <p>Unfortunately, <em>per-interpreter</em> state is not easy to achieve—extension authors tend to not keep multiple interpreters in mind when developing, and it is currently cumbersome to test the behavior.</p> </section> <section id="rationale-for-per-module-state"> <h2><a class="toc-backref" href="#rationale-for-per-module-state" role="doc-backlink">Rationale for Per-module State</a></h2> <p>Instead of focusing on per-interpreter state, Python’s C API is evolving to better support the more granular <em>per-module</em> state. By default, C-level data will be attached to a <em>module object</em>. Each interpreter will then create its own module object, keeping the data separate. For testing the isolation, multiple module objects corresponding to a single extension can even be loaded in a single interpreter.</p> <p>Per-module state provides an easy way to think about lifetime and resource ownership: the extension module will initialize when a module object is created, and clean up when it’s freed. In this regard, a module is just like any other <code class="docutils literal notranslate"><span class="pre">PyObject</span> <span class="pre">*</span></code>; there are no “on interpreter shutdown” hooks to think—or forget—about.</p> <section id="goal-easy-to-use-module-state"> <h3><a class="toc-backref" href="#goal-easy-to-use-module-state" role="doc-backlink">Goal: Easy-to-Use Module State</a></h3> <p>It is currently cumbersome or impossible to do everything the C API offers while keeping modules isolated. Enabled by <a class="pep reference internal" href="../pep-0384/" title="PEP 384 – Defining a Stable ABI">PEP 384</a>, changes in <a class="pep reference internal" href="../pep-0489/" title="PEP 489 – Multi-phase extension module initialization">PEP 489</a> and <a class="pep reference internal" href="../pep-0573/" title="PEP 573 – Module State Access from C Extension Methods">PEP 573</a> (and future planned ones) aim to first make it <em>possible</em> to build modules this way, and then to make it <em>easy</em> to write new modules this way and to convert old ones, so that it can become a natural default.</p> <p>Even if per-module state becomes the default, there will be use cases for different levels of encapsulation: per-process, per-interpreter, per-thread or per-task state. The goal is to treat these as exceptional cases: they should be possible, but extension authors will need to think more carefully about them.</p> </section> <section id="non-goals-speedups-and-the-gil"> <h3><a class="toc-backref" href="#non-goals-speedups-and-the-gil" role="doc-backlink">Non-goals: Speedups and the GIL</a></h3> <p>There is some effort to speed up CPython on multi-core CPUs by making the GIL per-interpreter. While isolating interpreters helps that effort, defaulting to per-module state will be beneficial even if no speedup is achieved, as it makes supporting multiple interpreters safer by default.</p> </section> </section> <section id="making-modules-safe-with-multiple-interpreters"> <h2><a class="toc-backref" href="#making-modules-safe-with-multiple-interpreters" role="doc-backlink">Making Modules Safe with Multiple Interpreters</a></h2> <p>There are many ways to correctly support multiple interpreters in extension modules. The rest of this text describes the preferred way to write such a module, or to convert an existing one.</p> <p>Note that support is a work in progress; the API for some features your module needs might not yet be ready.</p> <p>A full example module is available as <a class="reference external" href="https://github.com/python/cpython/blob/master/Modules/xxlimited.c">xxlimited</a>.</p> <p>This section assumes that “<em>you</em>” are an extension module author.</p> <section id="isolated-module-objects"> <h3><a class="toc-backref" href="#isolated-module-objects" role="doc-backlink">Isolated Module Objects</a></h3> <p>The key point to keep in mind when developing an extension module is that several module objects can be created from a single shared library. For example:</p> <div class="highlight-pycon notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="kn">import</span><span class="w"> </span><span class="nn">sys</span> <span class="gp">>>> </span><span class="kn">import</span><span class="w"> </span><span class="nn">binascii</span> <span class="gp">>>> </span><span class="n">old_binascii</span> <span class="o">=</span> <span class="n">binascii</span> <span class="gp">>>> </span><span class="k">del</span> <span class="n">sys</span><span class="o">.</span><span class="n">modules</span><span class="p">[</span><span class="s1">'binascii'</span><span class="p">]</span> <span class="gp">>>> </span><span class="kn">import</span><span class="w"> </span><span class="nn">binascii</span> <span class="c1"># create a new module object</span> <span class="gp">>>> </span><span class="n">old_binascii</span> <span class="o">==</span> <span class="n">binascii</span> <span class="go">False</span> </pre></div> </div> <p>As a rule of thumb, the two modules should be completely independent. All objects and state specific to the module should be encapsulated within the module object, not shared with other module objects, and cleaned up when the module object is deallocated. Exceptions are possible (see <a class="reference internal" href="#managing-global-state">Managing Global State</a>), but they will need more thought and attention to edge cases than code that follows this rule of thumb.</p> <p>While some modules could do with less stringent restrictions, isolated modules make it easier to set clear expectations (and guidelines) that work across a variety of use cases.</p> </section> <section id="surprising-edge-cases"> <h3><a class="toc-backref" href="#surprising-edge-cases" role="doc-backlink">Surprising Edge Cases</a></h3> <p>Note that isolated modules do create some surprising edge cases. Most notably, each module object will typically not share its classes and exceptions with other similar modules. Continuing from the <a class="reference internal" href="#isolated-module-objects">example above</a>, note that <code class="docutils literal notranslate"><span class="pre">old_binascii.Error</span></code> and <code class="docutils literal notranslate"><span class="pre">binascii.Error</span></code> are separate objects. In the following code, the exception is <em>not</em> caught:</p> <div class="highlight-pycon notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">old_binascii</span><span class="o">.</span><span class="n">Error</span> <span class="o">==</span> <span class="n">binascii</span><span class="o">.</span><span class="n">Error</span> <span class="go">False</span> <span class="gp">>>> </span><span class="k">try</span><span class="p">:</span> <span class="gp">... </span> <span class="n">old_binascii</span><span class="o">.</span><span class="n">unhexlify</span><span class="p">(</span><span class="sa">b</span><span class="s1">'qwertyuiop'</span><span class="p">)</span> <span class="gp">... </span><span class="k">except</span> <span class="n">binascii</span><span class="o">.</span><span class="n">Error</span><span class="p">:</span> <span class="gp">... </span> <span class="nb">print</span><span class="p">(</span><span class="s1">'boo'</span><span class="p">)</span> <span class="gp">...</span> <span class="gt">Traceback (most recent call last):</span> File <span class="nb">"<stdin>"</span>, line <span class="m">2</span>, in <span class="n"><module></span> <span class="gr">binascii.Error</span>: <span class="n">Non-hexadecimal digit found</span> </pre></div> </div> <p>This is expected. Notice that pure-Python modules behave the same way: it is a part of how Python works.</p> <p>The goal is to make extension modules safe at the C level, not to make hacks behave intuitively. Mutating <code class="docutils literal notranslate"><span class="pre">sys.modules</span></code> “manually” counts as a hack.</p> </section> <section id="managing-global-state"> <h3><a class="toc-backref" href="#managing-global-state" role="doc-backlink">Managing Global State</a></h3> <p>Sometimes, state of a Python module is not specific to that module, but to the entire process (or something else “more global” than a module). For example:</p> <ul class="simple"> <li>The <code class="docutils literal notranslate"><span class="pre">readline</span></code> module manages <em>the</em> terminal.</li> <li>A module running on a circuit board wants to control <em>the</em> on-board LED.</li> </ul> <p>In these cases, the Python module should provide <em>access</em> to the global state, rather than <em>own</em> it. If possible, write the module so that multiple copies of it can access the state independently (along with other libraries, whether for Python or other languages).</p> <p>If that is not possible, consider explicit locking.</p> <p>If it is necessary to use process-global state, the simplest way to avoid issues with multiple interpreters is to explicitly prevent a module from being loaded more than once per process—see <a class="reference internal" href="#opt-out-limiting-to-one-module-object-per-process">Opt-Out: Limiting to One Module Object per Process</a>.</p> </section> <section id="managing-per-module-state"> <h3><a class="toc-backref" href="#managing-per-module-state" role="doc-backlink">Managing Per-Module State</a></h3> <p>To use per-module state, use <a class="reference external" href="https://docs.python.org/3/c-api/module.html#multi-phase-initialization">multi-phase extension module initialization</a> introduced in <a class="pep reference internal" href="../pep-0489/" title="PEP 489 – Multi-phase extension module initialization">PEP 489</a>. This signals that your module supports multiple interpreters correctly.</p> <p>Set <code class="docutils literal notranslate"><span class="pre">PyModuleDef.m_size</span></code> to a positive number to request that many bytes of storage local to the module. Usually, this will be set to the size of some module-specific <code class="docutils literal notranslate"><span class="pre">struct</span></code>, which can store all of the module’s C-level state. In particular, it is where you should put pointers to classes (including exceptions, but excluding static types) and settings (e.g. <code class="docutils literal notranslate"><span class="pre">csv</span></code>’s <a class="reference external" href="https://docs.python.org/3/library/csv.html#csv.field_size_limit">field_size_limit</a>) which the C code needs to function.</p> <div class="admonition note"> <p class="admonition-title">Note</p> <p>Another option is to store state in the module’s <code class="docutils literal notranslate"><span class="pre">__dict__</span></code>, but you must avoid crashing when users modify <code class="docutils literal notranslate"><span class="pre">__dict__</span></code> from Python code. This means error- and type-checking at the C level, which is easy to get wrong and hard to test sufficiently.</p> </div> <p>If the module state includes <code class="docutils literal notranslate"><span class="pre">PyObject</span></code> pointers, the module object must hold references to those objects and implement the module-level hooks <code class="docutils literal notranslate"><span class="pre">m_traverse</span></code>, <code class="docutils literal notranslate"><span class="pre">m_clear</span></code> and <code class="docutils literal notranslate"><span class="pre">m_free</span></code>. These work like <code class="docutils literal notranslate"><span class="pre">tp_traverse</span></code>, <code class="docutils literal notranslate"><span class="pre">tp_clear</span></code> and <code class="docutils literal notranslate"><span class="pre">tp_free</span></code> of a class. Adding them will require some work and make the code longer; this is the price for modules which can be unloaded cleanly.</p> <p>An example of a module with per-module state is currently available as <a class="reference external" href="https://github.com/python/cpython/blob/master/Modules/xxlimited.c">xxlimited</a>; example module initialization shown at the bottom of the file.</p> </section> <section id="opt-out-limiting-to-one-module-object-per-process"> <h3><a class="toc-backref" href="#opt-out-limiting-to-one-module-object-per-process" role="doc-backlink">Opt-Out: Limiting to One Module Object per Process</a></h3> <p>A non-negative <code class="docutils literal notranslate"><span class="pre">PyModuleDef.m_size</span></code> signals that a module supports multiple interpreters correctly. If this is not yet the case for your module, you can explicitly make your module loadable only once per process. For example:</p> <div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="k">static</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">loaded</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span> <span class="k">static</span><span class="w"> </span><span class="kt">int</span> <span class="nf">exec_module</span><span class="p">(</span><span class="n">PyObject</span><span class="o">*</span><span class="w"> </span><span class="n">module</span><span class="p">)</span> <span class="p">{</span> <span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">loaded</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="n">PyErr_SetString</span><span class="p">(</span><span class="n">PyExc_ImportError</span><span class="p">,</span> <span class="w"> </span><span class="s">"cannot load module more than once per process"</span><span class="p">);</span> <span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="mi">-1</span><span class="p">;</span> <span class="w"> </span><span class="p">}</span> <span class="w"> </span><span class="n">loaded</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span> <span class="w"> </span><span class="c1">// ... rest of initialization</span> <span class="p">}</span> </pre></div> </div> </section> <section id="module-state-access-from-functions"> <h3><a class="toc-backref" href="#module-state-access-from-functions" role="doc-backlink">Module State Access from Functions</a></h3> <p>Accessing the state from module-level functions is straightforward. Functions get the module object as their first argument; for extracting the state, you can use <code class="docutils literal notranslate"><span class="pre">PyModule_GetState</span></code>:</p> <div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="k">static</span><span class="w"> </span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span> <span class="nf">func</span><span class="p">(</span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="n">module</span><span class="p">,</span><span class="w"> </span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="n">args</span><span class="p">)</span> <span class="p">{</span> <span class="w"> </span><span class="n">my_struct</span><span class="w"> </span><span class="o">*</span><span class="n">state</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="n">my_struct</span><span class="o">*</span><span class="p">)</span><span class="n">PyModule_GetState</span><span class="p">(</span><span class="n">module</span><span class="p">);</span> <span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">state</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="nb">NULL</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">NULL</span><span class="p">;</span> <span class="w"> </span><span class="p">}</span> <span class="w"> </span><span class="c1">// ... rest of logic</span> <span class="p">}</span> </pre></div> </div> <div class="admonition note"> <p class="admonition-title">Note</p> <p><code class="docutils literal notranslate"><span class="pre">PyModule_GetState</span></code> may return NULL without setting an exception if there is no module state, i.e. <code class="docutils literal notranslate"><span class="pre">PyModuleDef.m_size</span></code> was zero. In your own module, you’re in control of <code class="docutils literal notranslate"><span class="pre">m_size</span></code>, so this is easy to prevent.</p> </div> </section> </section> <section id="heap-types"> <h2><a class="toc-backref" href="#heap-types" role="doc-backlink">Heap Types</a></h2> <p>Traditionally, types defined in C code are <em>static</em>; that is, <code class="docutils literal notranslate"><span class="pre">static</span> <span class="pre">PyTypeObject</span></code> structures defined directly in code and initialized using <code class="docutils literal notranslate"><span class="pre">PyType_Ready()</span></code>.</p> <p>Such types are necessarily shared across the process. Sharing them between module objects requires paying attention to any state they own or access. To limit the possible issues, static types are immutable at the Python level: for example, you can’t set <code class="docutils literal notranslate"><span class="pre">str.myattribute</span> <span class="pre">=</span> <span class="pre">123</span></code>.</p> <div class="admonition note"> <p class="admonition-title">Note</p> <p>Sharing truly immutable objects between interpreters is fine, as long as they don’t provide access to mutable objects. However, in CPython, every Python object has a mutable implementation detail: the reference count. Changes to the refcount are guarded by the GIL. Thus, code that shares any Python objects across interpreters implicitly depends on CPython’s current, process-wide GIL.</p> </div> <p>Because they are immutable and process-global, static types cannot access “their” module state. If any method of such a type requires access to module state, the type must be converted to a <em>heap-allocated type</em>, or <em>heap type</em> for short. These correspond more closely to classes created by Python’s <code class="docutils literal notranslate"><span class="pre">class</span></code> statement.</p> <p>For new modules, using heap types by default is a good rule of thumb.</p> <p>Static types can be converted to heap types, but note that the heap type API was not designed for “lossless” conversion from static types – that is, creating a type that works exactly like a given static type. Unlike static types, heap type objects are mutable by default. Also, when rewriting the class definition in a new API, you are likely to unintentionally change a few details (e.g. pickle-ability or inherited slots). Always test the details that are important to you.</p> <section id="defining-heap-types"> <h3><a class="toc-backref" href="#defining-heap-types" role="doc-backlink">Defining Heap Types</a></h3> <p>Heap types can be created by filling a <code class="docutils literal notranslate"><span class="pre">PyType_Spec</span></code> structure, a description or “blueprint” of a class, and calling <code class="docutils literal notranslate"><span class="pre">PyType_FromModuleAndSpec()</span></code> to construct a new class object.</p> <div class="admonition note"> <p class="admonition-title">Note</p> <p>Other functions, like <code class="docutils literal notranslate"><span class="pre">PyType_FromSpec()</span></code>, can also create heap types, but <code class="docutils literal notranslate"><span class="pre">PyType_FromModuleAndSpec()</span></code> associates the module with the class, allowing access to the module state from methods.</p> </div> <p>The class should generally be stored in <em>both</em> the module state (for safe access from C) and the module’s <code class="docutils literal notranslate"><span class="pre">__dict__</span></code> (for access from Python code).</p> </section> <section id="garbage-collection-protocol"> <h3><a class="toc-backref" href="#garbage-collection-protocol" role="doc-backlink">Garbage Collection Protocol</a></h3> <p>Instances of heap types hold a reference to their type. This ensures that the type isn’t destroyed before all its instances are, but may result in reference cycles that need to be broken by the garbage collector.</p> <p>To avoid memory leaks, instances of heap types must implement the garbage collection protocol. That is, heap types should:</p> <ul class="simple"> <li>Have the <code class="docutils literal notranslate"><span class="pre">Py_TPFLAGS_HAVE_GC</span></code> flag.</li> <li>Define a traverse function using <code class="docutils literal notranslate"><span class="pre">Py_tp_traverse</span></code>, which visits the type (e.g. using <code class="docutils literal notranslate"><span class="pre">Py_VISIT(Py_TYPE(self));</span></code>).</li> </ul> <p>Please refer to the <a class="reference external" href="https://docs.python.org/3/c-api/typeobj.html">documentation</a> of <a class="reference external" href="https://docs.python.org/3/c-api/typeobj.html#Py_TPFLAGS_HAVE_GC">Py_TPFLAGS_HAVE_GC</a> and <cite>tp_traverse <https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_traverse></cite> for additional considerations.</p> <p>If your traverse function delegates to the <code class="docutils literal notranslate"><span class="pre">tp_traverse</span></code> of its base class (or another type), ensure that <code class="docutils literal notranslate"><span class="pre">Py_TYPE(self)</span></code> is visited only once. Note that only heap type are expected to visit the type in <code class="docutils literal notranslate"><span class="pre">tp_traverse</span></code>.</p> <p>For example, if your traverse function includes:</p> <div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="n">base</span><span class="o">-></span><span class="n">tp_traverse</span><span class="p">(</span><span class="n">self</span><span class="p">,</span><span class="w"> </span><span class="n">visit</span><span class="p">,</span><span class="w"> </span><span class="n">arg</span><span class="p">)</span> </pre></div> </div> <p>…and <code class="docutils literal notranslate"><span class="pre">base</span></code> may be a static type, then it should also include:</p> <div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">base</span><span class="o">-></span><span class="n">tp_flags</span><span class="w"> </span><span class="o">&</span><span class="w"> </span><span class="n">Py_TPFLAGS_HEAPTYPE</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="c1">// a heap type's tp_traverse already visited Py_TYPE(self)</span> <span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="n">Py_VISIT</span><span class="p">(</span><span class="n">Py_TYPE</span><span class="p">(</span><span class="n">self</span><span class="p">));</span> <span class="p">}</span> </pre></div> </div> <p>It is not necessary to handle the type’s reference count in <code class="docutils literal notranslate"><span class="pre">tp_new</span></code> and <code class="docutils literal notranslate"><span class="pre">tp_clear</span></code>.</p> </section> <section id="module-state-access-from-classes"> <h3><a class="toc-backref" href="#module-state-access-from-classes" role="doc-backlink">Module State Access from Classes</a></h3> <p>If you have a type object defined with <code class="docutils literal notranslate"><span class="pre">PyType_FromModuleAndSpec()</span></code>, you can call <code class="docutils literal notranslate"><span class="pre">PyType_GetModule</span></code> to get the associated module, and then <code class="docutils literal notranslate"><span class="pre">PyModule_GetState</span></code> to get the module’s state.</p> <p>To save a some tedious error-handling boilerplate code, you can combine these two steps with <code class="docutils literal notranslate"><span class="pre">PyType_GetModuleState</span></code>, resulting in:</p> <div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="n">my_struct</span><span class="w"> </span><span class="o">*</span><span class="n">state</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="n">my_struct</span><span class="o">*</span><span class="p">)</span><span class="n">PyType_GetModuleState</span><span class="p">(</span><span class="n">type</span><span class="p">);</span> <span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">state</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="nb">NULL</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">NULL</span><span class="p">;</span> <span class="p">}</span> </pre></div> </div> </section> <section id="module-state-access-from-regular-methods"> <h3><a class="toc-backref" href="#module-state-access-from-regular-methods" role="doc-backlink">Module State Access from Regular Methods</a></h3> <p>Accessing the module-level state from methods of a class is somewhat more complicated, but is possible thanks to the changes introduced in <a class="pep reference internal" href="../pep-0573/" title="PEP 573 – Module State Access from C Extension Methods">PEP 573</a>. To get the state, you need to first get the <em>defining class</em>, and then get the module state from it.</p> <p>The largest roadblock is getting <em>the class a method was defined in</em>, or that method’s “defining class” for short. The defining class can have a reference to the module it is part of.</p> <p>Do not confuse the defining class with <code class="docutils literal notranslate"><span class="pre">Py_TYPE(self)</span></code>. If the method is called on a <em>subclass</em> of your type, <code class="docutils literal notranslate"><span class="pre">Py_TYPE(self)</span></code> will refer to that subclass, which may be defined in different module than yours.</p> <div class="admonition note"> <p class="admonition-title">Note</p> <p>The following Python code can illustrate the concept. <code class="docutils literal notranslate"><span class="pre">Base.get_defining_class</span></code> returns <code class="docutils literal notranslate"><span class="pre">Base</span></code> even if <code class="docutils literal notranslate"><span class="pre">type(self)</span> <span class="pre">==</span> <span class="pre">Sub</span></code>:</p> <div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">class</span><span class="w"> </span><span class="nc">Base</span><span class="p">:</span> <span class="k">def</span><span class="w"> </span><span class="nf">get_defining_class</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="vm">__class__</span> <span class="k">class</span><span class="w"> </span><span class="nc">Sub</span><span class="p">(</span><span class="n">Base</span><span class="p">):</span> <span class="k">pass</span> </pre></div> </div> </div> <p>For a method to get its “defining class”, it must use the <code class="docutils literal notranslate"><span class="pre">METH_METHOD</span> <span class="pre">|</span> <span class="pre">METH_FASTCALL</span> <span class="pre">|</span> <span class="pre">METH_KEYWORDS</span></code> <a class="reference external" href="https://docs.python.org/3/c-api/structures.html#c.PyMethodDef">calling convention</a> and the corresponding <a class="reference external" href="https://docs.python.org/3/c-api/structures.html#c.PyCMethod">PyCMethod signature</a>:</p> <div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="n">PyCMethod</span><span class="p">(</span> <span class="w"> </span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="p">,</span><span class="w"> </span><span class="c1">// object the method was called on</span> <span class="w"> </span><span class="n">PyTypeObject</span><span class="w"> </span><span class="o">*</span><span class="n">defining_class</span><span class="p">,</span><span class="w"> </span><span class="c1">// defining class</span> <span class="w"> </span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="k">const</span><span class="w"> </span><span class="o">*</span><span class="n">args</span><span class="p">,</span><span class="w"> </span><span class="c1">// C array of arguments</span> <span class="w"> </span><span class="n">Py_ssize_t</span><span class="w"> </span><span class="n">nargs</span><span class="p">,</span><span class="w"> </span><span class="c1">// length of "args"</span> <span class="w"> </span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="n">kwnames</span><span class="p">)</span><span class="w"> </span><span class="c1">// NULL, or dict of keyword arguments</span> </pre></div> </div> <p>Once you have the defining class, call <code class="docutils literal notranslate"><span class="pre">PyType_GetModuleState</span></code> to get the state of its associated module.</p> <p>For example:</p> <div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="k">static</span><span class="w"> </span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span> <span class="nf">example_method</span><span class="p">(</span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="p">,</span> <span class="w"> </span><span class="n">PyTypeObject</span><span class="w"> </span><span class="o">*</span><span class="n">defining_class</span><span class="p">,</span> <span class="w"> </span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="k">const</span><span class="w"> </span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="w"> </span><span class="n">Py_ssize_t</span><span class="w"> </span><span class="n">nargs</span><span class="p">,</span> <span class="w"> </span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="n">kwnames</span><span class="p">)</span> <span class="p">{</span> <span class="w"> </span><span class="n">my_struct</span><span class="w"> </span><span class="o">*</span><span class="n">state</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="n">my_struct</span><span class="o">*</span><span class="p">)</span><span class="n">PyType_GetModuleState</span><span class="p">(</span><span class="n">defining_class</span><span class="p">);</span> <span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">state</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="nb">NULL</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">NULL</span><span class="p">;</span> <span class="w"> </span><span class="p">}</span> <span class="w"> </span><span class="p">...</span><span class="w"> </span><span class="c1">// rest of logic</span> <span class="p">}</span> <span class="n">PyDoc_STRVAR</span><span class="p">(</span><span class="n">example_method_doc</span><span class="p">,</span><span class="w"> </span><span class="s">"..."</span><span class="p">);</span> <span class="k">static</span><span class="w"> </span><span class="n">PyMethodDef</span><span class="w"> </span><span class="n">my_methods</span><span class="p">[]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="p">{</span><span class="s">"example_method"</span><span class="p">,</span> <span class="w"> </span><span class="p">(</span><span class="n">PyCFunction</span><span class="p">)(</span><span class="kt">void</span><span class="p">(</span><span class="o">*</span><span class="p">)(</span><span class="kt">void</span><span class="p">))</span><span class="n">example_method</span><span class="p">,</span> <span class="w"> </span><span class="n">METH_METHOD</span><span class="o">|</span><span class="n">METH_FASTCALL</span><span class="o">|</span><span class="n">METH_KEYWORDS</span><span class="p">,</span> <span class="w"> </span><span class="n">example_method_doc</span><span class="p">}</span> <span class="w"> </span><span class="p">{</span><span class="nb">NULL</span><span class="p">},</span> <span class="p">}</span> </pre></div> </div> </section> <section id="module-state-access-from-slot-methods-getters-and-setters"> <h3><a class="toc-backref" href="#module-state-access-from-slot-methods-getters-and-setters" role="doc-backlink">Module State Access from Slot Methods, Getters and Setters</a></h3> <div class="admonition note"> <p class="admonition-title">Note</p> <p>This is new in Python 3.11.</p> </div> <p>Slot methods – the fast C equivalents for special methods, such as <a class="reference external" href="https://docs.python.org/3/c-api/typeobj.html#c.PyNumberMethods.nb_add">nb_add</a> for <code class="docutils literal notranslate"><span class="pre">__add__</span></code> or <a class="reference external" href="https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_new">tp_new</a> for initialization – have a very simple API that doesn’t allow passing in the defining class, unlike with <code class="docutils literal notranslate"><span class="pre">PyCMethod</span></code>. The same goes for getters and setters defined with <a class="reference external" href="https://docs.python.org/3/c-api/structures.html#c.PyGetSetDef">PyGetSetDef</a>.</p> <p>To access the module state in these cases, use the <a class="reference external" href="https://docs.python.org/3/c-api/typeobj.html#c.PyType_GetModuleByDef">PyType_GetModuleByDef</a> function, and pass in the module definition. Once you have the module, call <a class="reference external" href="https://docs.python.org/3/c-api/module.html#c.PyModule_GetState">PyModule_GetState</a> to get the state:</p> <div class="highlight-c notranslate"><div class="highlight"><pre><span></span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="n">module</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">PyType_GetModuleByDef</span><span class="p">(</span><span class="n">Py_TYPE</span><span class="p">(</span><span class="n">self</span><span class="p">),</span><span class="w"> </span><span class="o">&</span><span class="n">module_def</span><span class="p">);</span> <span class="n">my_struct</span><span class="w"> </span><span class="o">*</span><span class="n">state</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="n">my_struct</span><span class="o">*</span><span class="p">)</span><span class="n">PyModule_GetState</span><span class="p">(</span><span class="n">module</span><span class="p">);</span> <span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">state</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="nb">NULL</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> <span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">NULL</span><span class="p">;</span> <span class="p">}</span> </pre></div> </div> <p><code class="docutils literal notranslate"><span class="pre">PyType_GetModuleByDef</span></code> works by searching the <a class="reference external" href="https://docs.python.org/3/glossary.html#term-method-resolution-order">MRO</a> (i.e. all superclasses) for the first superclass that has a corresponding module.</p> <div class="admonition note"> <p class="admonition-title">Note</p> <p>In very exotic cases (inheritance chains spanning multiple modules created from the same definition), <code class="docutils literal notranslate"><span class="pre">PyType_GetModuleByDef</span></code> might not return the module of the true defining class. However, it will always return a module with the same definition, ensuring a compatible C memory layout.</p> </div> </section> <section id="lifetime-of-the-module-state"> <h3><a class="toc-backref" href="#lifetime-of-the-module-state" role="doc-backlink">Lifetime of the Module State</a></h3> <p>When a module object is garbage-collected, its module state is freed. For each pointer to (a part of) the module state, you must hold a reference to the module object.</p> <p>Usually this is not an issue, because types created with <code class="docutils literal notranslate"><span class="pre">PyType_FromModuleAndSpec</span></code>, and their instances, hold a reference to the module. However, you must be careful in reference counting when you reference module state from other places, such as callbacks for external libraries.</p> </section> </section> <section id="open-issues"> <h2><a class="toc-backref" href="#open-issues" role="doc-backlink">Open Issues</a></h2> <p>Several issues around per-module state and heap types are still open.</p> <p>Discussions about improving the situation are best held on the <a class="reference external" href="https://mail.python.org/mailman3/lists/capi-sig.python.org/">capi-sig mailing list</a>.</p> <section id="type-checking"> <h3><a class="toc-backref" href="#type-checking" role="doc-backlink">Type Checking</a></h3> <p>Currently (as of Python 3.10), heap types have no good API to write <code class="docutils literal notranslate"><span class="pre">Py*_Check</span></code> functions (like <code class="docutils literal notranslate"><span class="pre">PyUnicode_Check</span></code> exists for <code class="docutils literal notranslate"><span class="pre">str</span></code>, a static type), and so it is not easy to ensure that instances have a particular C layout.</p> </section> <section id="metaclasses"> <h3><a class="toc-backref" href="#metaclasses" role="doc-backlink">Metaclasses</a></h3> <p>Currently (as of Python 3.10), there is no good API to specify the <em>metaclass</em> of a heap type; that is, the <code class="docutils literal notranslate"><span class="pre">ob_type</span></code> field of the type object.</p> </section> <section id="per-class-scope"> <h3><a class="toc-backref" href="#per-class-scope" role="doc-backlink">Per-Class Scope</a></h3> <p>It is also not possible to attach state to <em>types</em>. While <code class="docutils literal notranslate"><span class="pre">PyHeapTypeObject</span></code> is a variable-size object (<code class="docutils literal notranslate"><span class="pre">PyVarObject</span></code>), its variable-size storage is currently consumed by slots. Fixing this is complicated by the fact that several classes in an inheritance hierarchy may need to reserve some state.</p> </section> <section id="lossless-conversion-to-heap-types"> <h3><a class="toc-backref" href="#lossless-conversion-to-heap-types" role="doc-backlink">Lossless Conversion to Heap Types</a></h3> <p>The heap type API was not designed for “lossless” conversion from static types; that is, creating a type that works exactly like a given static type. The best way to address it would probably be to write a guide that covers known “gotchas”.</p> </section> </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-0630.rst">https://github.com/python/peps/blob/main/peps/pep-0630.rst</a></p> <p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0630.rst">2025-02-01 08:55:40 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="#about-this-document">About This Document</a></li> <li><a class="reference internal" href="#motivation">Motivation</a></li> <li><a class="reference internal" href="#rationale-for-per-module-state">Rationale for Per-module State</a><ul> <li><a class="reference internal" href="#goal-easy-to-use-module-state">Goal: Easy-to-Use Module State</a></li> <li><a class="reference internal" href="#non-goals-speedups-and-the-gil">Non-goals: Speedups and the GIL</a></li> </ul> </li> <li><a class="reference internal" href="#making-modules-safe-with-multiple-interpreters">Making Modules Safe with Multiple Interpreters</a><ul> <li><a class="reference internal" href="#isolated-module-objects">Isolated Module Objects</a></li> <li><a class="reference internal" href="#surprising-edge-cases">Surprising Edge Cases</a></li> <li><a class="reference internal" href="#managing-global-state">Managing Global State</a></li> <li><a class="reference internal" href="#managing-per-module-state">Managing Per-Module State</a></li> <li><a class="reference internal" href="#opt-out-limiting-to-one-module-object-per-process">Opt-Out: Limiting to One Module Object per Process</a></li> <li><a class="reference internal" href="#module-state-access-from-functions">Module State Access from Functions</a></li> </ul> </li> <li><a class="reference internal" href="#heap-types">Heap Types</a><ul> <li><a class="reference internal" href="#defining-heap-types">Defining Heap Types</a></li> <li><a class="reference internal" href="#garbage-collection-protocol">Garbage Collection Protocol</a></li> <li><a class="reference internal" href="#module-state-access-from-classes">Module State Access from Classes</a></li> <li><a class="reference internal" href="#module-state-access-from-regular-methods">Module State Access from Regular Methods</a></li> <li><a class="reference internal" href="#module-state-access-from-slot-methods-getters-and-setters">Module State Access from Slot Methods, Getters and Setters</a></li> <li><a class="reference internal" href="#lifetime-of-the-module-state">Lifetime of the Module State</a></li> </ul> </li> <li><a class="reference internal" href="#open-issues">Open Issues</a><ul> <li><a class="reference internal" href="#type-checking">Type Checking</a></li> <li><a class="reference internal" href="#metaclasses">Metaclasses</a></li> <li><a class="reference internal" href="#per-class-scope">Per-Class Scope</a></li> <li><a class="reference internal" href="#lossless-conversion-to-heap-types">Lossless Conversion to Heap Types</a></li> </ul> </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-0630.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>