python-peps/pep-0554/index.html

2084 lines
175 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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 554 Multiple Interpreters in the Stdlib | peps.python.org</title>
<link rel="shortcut icon" href="../_static/py.png">
<link rel="canonical" href="https://peps.python.org/pep-0554/">
<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 554 Multiple Interpreters in the Stdlib | peps.python.org'>
<meta property="og:description" content="CPython has supported multiple interpreters in the same process (AKA “subinterpreters”) since version 1.5 (1997). The feature has been available via the C-API. [c-api] Multiple interpreters operate in relative isolation from one another, which facilit...">
<meta property="og:type" content="website">
<meta property="og:url" content="https://peps.python.org/pep-0554/">
<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="CPython has supported multiple interpreters in the same process (AKA “subinterpreters”) since version 1.5 (1997). The feature has been available via the C-API. [c-api] Multiple interpreters operate in relative isolation from one another, which facilit...">
<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 554</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 554 Multiple Interpreters in the Stdlib</h1>
<dl class="rfc2822 field-list simple">
<dt class="field-odd">Author<span class="colon">:</span></dt>
<dd class="field-odd">Eric Snow &lt;ericsnowcurrently&#32;&#97;t&#32;gmail.com&gt;</dd>
<dt class="field-even">Discussions-To<span class="colon">:</span></dt>
<dd class="field-even"><a class="reference external" href="https://discuss.python.org/t/pep-554-multiple-interpreters-in-the-stdlib/24855">Discourse thread</a></dd>
<dt class="field-odd">Status<span class="colon">:</span></dt>
<dd class="field-odd"><abbr title="Replaced by another succeeding PEP">Superseded</abbr></dd>
<dt class="field-even">Type<span class="colon">:</span></dt>
<dd class="field-even"><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-odd">Created<span class="colon">:</span></dt>
<dd class="field-odd">05-Sep-2017</dd>
<dt class="field-even">Python-Version<span class="colon">:</span></dt>
<dd class="field-even">3.13</dd>
<dt class="field-odd">Post-History<span class="colon">:</span></dt>
<dd class="field-odd"><a class="reference external" href="https://mail.python.org/archives/list/python-ideas&#64;python.org/thread/HQQWEE527HG3ILJVKQTXVSJIQO6NUSIA/" title="Python-Ideas thread">07-Sep-2017</a>,
<a class="reference external" href="https://mail.python.org/archives/list/python-dev&#64;python.org/thread/NBWMA6LVD22XOUYC5ZMPBFWDQOECRP77/" title="Python-Dev thread">08-Sep-2017</a>,
<a class="reference external" href="https://mail.python.org/archives/list/python-dev&#64;python.org/thread/EG4FSFG5E3O22FTIUQOXMQ6X6B5X3DP7/" title="Python-Dev thread">13-Sep-2017</a>,
<a class="reference external" href="https://mail.python.org/archives/list/python-dev&#64;python.org/thread/BCSRGAMCYB3NGXNU42U66J56XNZVMQP2/" title="Python-Dev thread">05-Dec-2017</a>,
<a class="reference external" href="https://mail.python.org/archives/list/python-dev&#64;python.org/thread/X2KPCSRVBD2QD5GP5IMXXZTGZ46OXD3D/" title="Python-Dev thread">04-May-2020</a>,
<a class="reference external" href="https://discuss.python.org/t/pep-554-multiple-interpreters-in-the-stdlib/24855/2/" title="Discourse message">14-Mar-2023</a>,
<a class="reference external" href="https://discuss.python.org/t/pep-554-multiple-interpreters-in-the-stdlib/24855/26/" title="Discourse message">01-Nov-2023</a></dd>
<dt class="field-even">Superseded-By<span class="colon">:</span></dt>
<dd class="field-even"><a class="reference external" href="../pep-0734/">734</a></dd>
</dl>
<hr class="docutils" />
<section id="contents">
<details><summary>Table of Contents</summary><ul class="simple">
<li><a class="reference internal" href="#abstract">Abstract</a></li>
<li><a class="reference internal" href="#proposal">Proposal</a><ul>
<li><a class="reference internal" href="#the-interpreters-module">The “interpreters” Module</a></li>
<li><a class="reference internal" href="#api-summary-for-interpreters-module">API summary for interpreters module</a></li>
<li><a class="reference internal" href="#concurrent-futures-interpreterpoolexecutor">concurrent.futures.InterpreterPoolExecutor</a></li>
<li><a class="reference internal" href="#help-for-extension-module-maintainers">Help for Extension Module Maintainers</a></li>
</ul>
</li>
<li><a class="reference internal" href="#examples">Examples</a><ul>
<li><a class="reference internal" href="#run-isolated-code-in-current-os-thread">Run isolated code in current OS thread</a></li>
<li><a class="reference internal" href="#run-in-a-different-thread">Run in a different thread</a></li>
<li><a class="reference internal" href="#pre-populate-an-interpreter">Pre-populate an interpreter</a></li>
<li><a class="reference internal" href="#handling-an-exception">Handling an exception</a></li>
<li><a class="reference internal" href="#re-raising-an-exception">Re-raising an exception</a></li>
<li><a class="reference internal" href="#interact-with-the-main-namespace">Interact with the __main__ namespace</a></li>
<li><a class="reference internal" href="#synchronize-using-an-os-pipe">Synchronize using an OS pipe</a></li>
<li><a class="reference internal" href="#sharing-a-file-descriptor">Sharing a file descriptor</a></li>
<li><a class="reference internal" href="#passing-objects-via-pickle">Passing objects via pickle</a></li>
<li><a class="reference internal" href="#capturing-an-interpreter-s-stdout">Capturing an interpreters stdout</a></li>
<li><a class="reference internal" href="#running-a-module">Running a module</a></li>
<li><a class="reference internal" href="#running-as-script-including-zip-archives-directories">Running as script (including zip archives &amp; directories)</a></li>
<li><a class="reference internal" href="#using-a-channel-to-communicate">Using a channel to communicate</a></li>
<li><a class="reference internal" href="#sharing-a-memoryview-imagine-map-reduce">Sharing a memoryview (imagine map-reduce)</a></li>
</ul>
</li>
<li><a class="reference internal" href="#rationale">Rationale</a><ul>
<li><a class="reference internal" href="#concerns">Concerns</a></li>
</ul>
</li>
<li><a class="reference internal" href="#about-subinterpreters">About Subinterpreters</a><ul>
<li><a class="reference internal" href="#concurrency">Concurrency</a></li>
<li><a class="reference internal" href="#shared-data">Shared Data</a></li>
<li><a class="reference internal" href="#interpreter-isolation">Interpreter Isolation</a></li>
<li><a class="reference internal" href="#existing-usage">Existing Usage</a></li>
</ul>
</li>
<li><a class="reference internal" href="#alternate-python-implementations">Alternate Python Implementations</a></li>
<li><a class="reference internal" href="#interpreters-module-api">“interpreters” Module API</a><ul>
<li><a class="reference internal" href="#uncaught-exceptions">Uncaught Exceptions</a></li>
</ul>
</li>
<li><a class="reference internal" href="#interpreter-restrictions">Interpreter Restrictions</a></li>
<li><a class="reference internal" href="#api-for-communication">API For Communication</a><ul>
<li><a class="reference internal" href="#shareable-types">Shareable Types</a><ul>
<li><a class="reference internal" href="#communicating-through-os-pipes">Communicating Through OS Pipes</a></li>
</ul>
</li>
<li><a class="reference internal" href="#channels">Channels</a></li>
<li><a class="reference internal" href="#caveats-for-shared-objects">Caveats For Shared Objects</a></li>
</ul>
</li>
<li><a class="reference internal" href="#documentation">Documentation</a></li>
<li><a class="reference internal" href="#alternative-solutions">Alternative Solutions</a></li>
<li><a class="reference internal" href="#open-questions">Open Questions</a></li>
<li><a class="reference internal" href="#deferred-functionality">Deferred Functionality</a><ul>
<li><a class="reference internal" href="#add-convenience-api">Add convenience API</a></li>
<li><a class="reference internal" href="#avoid-possible-confusion-about-interpreters-running-in-the-current-thread">Avoid possible confusion about interpreters running in the current thread</a></li>
<li><a class="reference internal" href="#clarify-running-vs-has-threads">Clarify “running” vs. “has threads”</a></li>
<li><a class="reference internal" href="#a-dunder-method-for-sharing">A Dunder Method For Sharing</a></li>
<li><a class="reference internal" href="#interpreter-call">Interpreter.call()</a></li>
<li><a class="reference internal" href="#interpreter-run-in-thread">Interpreter.run_in_thread()</a></li>
<li><a class="reference internal" href="#synchronization-primitives">Synchronization Primitives</a></li>
<li><a class="reference internal" href="#csp-library">CSP Library</a></li>
<li><a class="reference internal" href="#syntactic-support">Syntactic Support</a></li>
<li><a class="reference internal" href="#multiprocessing">Multiprocessing</a></li>
<li><a class="reference internal" href="#c-extension-opt-in-opt-out">C-extension opt-in/opt-out</a></li>
<li><a class="reference internal" href="#poisoning-channels">Poisoning channels</a></li>
<li><a class="reference internal" href="#resetting-main">Resetting __main__</a></li>
<li><a class="reference internal" href="#resetting-an-interpreter-s-state">Resetting an interpreters state</a></li>
<li><a class="reference internal" href="#copy-an-existing-interpreter-s-state">Copy an existing interpreters state</a></li>
<li><a class="reference internal" href="#shareable-file-descriptors-and-sockets">Shareable file descriptors and sockets</a></li>
<li><a class="reference internal" href="#integration-with-async">Integration with async</a></li>
<li><a class="reference internal" href="#support-for-iteration">Support for iteration</a></li>
<li><a class="reference internal" href="#channel-context-managers">Channel context managers</a></li>
<li><a class="reference internal" href="#pipes-and-queues">Pipes and Queues</a></li>
<li><a class="reference internal" href="#return-a-lock-from-send">Return a lock from send()</a></li>
<li><a class="reference internal" href="#support-prioritization-in-channels">Support prioritization in channels</a></li>
<li><a class="reference internal" href="#support-inheriting-settings-and-more">Support inheriting settings (and more?)</a></li>
<li><a class="reference internal" href="#make-exceptions-shareable">Make exceptions shareable</a></li>
<li><a class="reference internal" href="#make-everything-shareable-through-serialization">Make everything shareable through serialization</a></li>
<li><a class="reference internal" href="#make-runfailederror-cause-lazy">Make RunFailedError.__cause__ lazy</a></li>
<li><a class="reference internal" href="#return-a-value-from-interp-exec">Return a value from <code class="docutils literal notranslate"><span class="pre">interp.exec()</span></code></a></li>
<li><a class="reference internal" href="#add-a-shareable-synchronization-primitive">Add a shareable synchronization primitive</a></li>
<li><a class="reference internal" href="#propagate-systemexit-and-keyboardinterrupt-differently">Propagate SystemExit and KeyboardInterrupt Differently</a></li>
<li><a class="reference internal" href="#add-an-explicit-release-and-close-to-channel-end-classes">Add an explicit release() and close() to channel end classes</a></li>
<li><a class="reference internal" href="#add-sendchannel-send-buffer">Add SendChannel.send_buffer()</a></li>
<li><a class="reference internal" href="#auto-run-in-a-thread">Auto-run in a thread</a></li>
</ul>
</li>
<li><a class="reference internal" href="#rejected-ideas">Rejected Ideas</a><ul>
<li><a class="reference internal" href="#explicit-channel-association">Explicit channel association</a></li>
<li><a class="reference internal" href="#add-an-api-based-on-pipes">Add an API based on pipes</a></li>
<li><a class="reference internal" href="#add-an-api-based-on-queues">Add an API based on queues</a></li>
<li><a class="reference internal" href="#enumerate">“enumerate”</a></li>
<li><a class="reference internal" href="#alternate-solutions-to-prevent-leaking-exceptions-across-interpreters">Alternate solutions to prevent leaking exceptions across interpreters</a></li>
<li><a class="reference internal" href="#always-associate-each-new-interpreter-with-its-own-thread">Always associate each new interpreter with its own thread</a></li>
<li><a class="reference internal" href="#only-associate-interpreters-upon-use">Only associate interpreters upon use</a></li>
<li><a class="reference internal" href="#allow-multiple-simultaneous-calls-to-interpreter-exec">Allow multiple simultaneous calls to Interpreter.exec()</a></li>
<li><a class="reference internal" href="#add-a-reraise-method-to-runfailederror">Add a “reraise” method to RunFailedError</a></li>
</ul>
</li>
<li><a class="reference internal" href="#implementation">Implementation</a></li>
<li><a class="reference internal" href="#references">References</a></li>
<li><a class="reference internal" href="#copyright">Copyright</a></li>
</ul>
</details></section>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>This PEP effectively continues in a cleaner form in <a class="pep reference internal" href="../pep-0734/" title="PEP 734 Multiple Interpreters in the Stdlib">PEP 734</a>.
This PEP is kept as-is for the sake of the various sections of
background information and deferred/rejected ideas that have
been stripped from <a class="pep reference internal" href="../pep-0734/" title="PEP 734 Multiple Interpreters in the Stdlib">PEP 734</a>.</p>
</div>
<section id="abstract">
<h2><a class="toc-backref" href="#abstract" role="doc-backlink">Abstract</a></h2>
<p>CPython has supported multiple interpreters in the same process (AKA
“subinterpreters”) since version 1.5 (1997). The feature has been
available via the C-API. <a class="reference internal" href="#c-api" id="id1"><span>[c-api]</span></a> Multiple interpreters operate in
<a class="reference internal" href="#interpreter-isolation">relative isolation from one another</a>, which
facilitates novel alternative approaches to
<a class="reference internal" href="#concurrency">concurrency</a>.</p>
<p>This proposal introduces the stdlib <code class="docutils literal notranslate"><span class="pre">interpreters</span></code> module. It exposes
the basic functionality of multiple interpreters already provided by the
C-API, along with basic support for communicating between interpreters.
This module is especially relevant since <a class="pep reference internal" href="../pep-0684/" title="PEP 684 A Per-Interpreter GIL">PEP 684</a> introduced a
per-interpreter GIL in Python 3.12.</p>
</section>
<section id="proposal">
<h2><a class="toc-backref" href="#proposal" role="doc-backlink">Proposal</a></h2>
<p>Summary:</p>
<ul class="simple">
<li>add a new stdlib module: “interpreters”</li>
<li>add concurrent.futures.InterpreterPoolExecutor</li>
<li>help for extension module maintainers</li>
</ul>
<section id="the-interpreters-module">
<h3><a class="toc-backref" href="#the-interpreters-module" role="doc-backlink">The “interpreters” Module</a></h3>
<p>The <code class="docutils literal notranslate"><span class="pre">interpreters</span></code> module will provide a high-level interface
to the multiple interpreter functionality, and wrap a new low-level
<code class="docutils literal notranslate"><span class="pre">_interpreters</span></code> (in the same way as the <code class="docutils literal notranslate"><span class="pre">threading</span></code> module).
See the <a class="reference internal" href="#examples">Examples</a> section for concrete usage and use cases.</p>
<p>Along with exposing the existing (in CPython) multiple interpreter
support, the module will also support a basic mechanism for
passing data between interpreters. That involves setting “shareable”
objects in the <code class="docutils literal notranslate"><span class="pre">__main__</span></code> module of a target subinterpreter. Some
such objects, like <code class="docutils literal notranslate"><span class="pre">os.pipe()</span></code>, may be used to communicate further.
The module will also provide a minimal implementation of “channels”
as a demonstration of cross-interpreter communication.</p>
<p>Note that <em>objects</em> are not shared between interpreters since they are
tied to the interpreter in which they were created. Instead, the
objects <em>data</em> is passed between interpreters. See the <a class="reference internal" href="#shared-data">Shared Data</a>
and <a class="reference internal" href="#api-for-communication">API For Communication</a> sections for more details about
sharing/communicating between interpreters.</p>
</section>
<section id="api-summary-for-interpreters-module">
<h3><a class="toc-backref" href="#api-summary-for-interpreters-module" role="doc-backlink">API summary for interpreters module</a></h3>
<p>Here is a summary of the API for the <code class="docutils literal notranslate"><span class="pre">interpreters</span></code> module. For a
more in-depth explanation of the proposed classes and functions, see
the <a class="reference internal" href="#interpreters-module-api">“interpreters” Module API</a> section below.</p>
<p>For creating and using interpreters:</p>
<table class="docutils align-default">
<thead>
<tr class="row-odd"><th class="head">signature</th>
<th class="head">description</th>
</tr>
</thead>
<tbody>
<tr class="row-even"><td><code class="docutils literal notranslate"><span class="pre">list_all()</span> <span class="pre">-&gt;</span> <span class="pre">[Interpreter]</span></code></td>
<td>Get all existing interpreters.</td>
</tr>
<tr class="row-odd"><td><code class="docutils literal notranslate"><span class="pre">get_current()</span> <span class="pre">-&gt;</span> <span class="pre">Interpreter</span></code></td>
<td>Get the currently running interpreter.</td>
</tr>
<tr class="row-even"><td><code class="docutils literal notranslate"><span class="pre">get_main()</span> <span class="pre">-&gt;</span> <span class="pre">Interpreter</span></code></td>
<td>Get the main interpreter.</td>
</tr>
<tr class="row-odd"><td><code class="docutils literal notranslate"><span class="pre">create()</span> <span class="pre">-&gt;</span> <span class="pre">Interpreter</span></code></td>
<td>Initialize a new (idle) Python interpreter.</td>
</tr>
</tbody>
</table>
<div class="line-block">
<div class="line"><br /></div>
</div>
<table class="docutils align-default">
<thead>
<tr class="row-odd"><th class="head">signature</th>
<th class="head">description</th>
</tr>
</thead>
<tbody>
<tr class="row-even"><td><code class="docutils literal notranslate"><span class="pre">class</span> <span class="pre">Interpreter</span></code></td>
<td>A single interpreter.</td>
</tr>
<tr class="row-odd"><td><code class="docutils literal notranslate"><span class="pre">.id</span></code></td>
<td>The interpreters ID (read-only).</td>
</tr>
<tr class="row-even"><td><code class="docutils literal notranslate"><span class="pre">.is_running()</span> <span class="pre">-&gt;</span> <span class="pre">bool</span></code></td>
<td>Is the interpreter currently executing code?</td>
</tr>
<tr class="row-odd"><td><code class="docutils literal notranslate"><span class="pre">.close()</span></code></td>
<td>Finalize and destroy the interpreter.</td>
</tr>
<tr class="row-even"><td><code class="docutils literal notranslate"><span class="pre">.set_main_attrs(**kwargs)</span></code></td>
<td>Bind “shareable” objects in <code class="docutils literal notranslate"><span class="pre">__main__</span></code>.</td>
</tr>
<tr class="row-odd"><td><code class="docutils literal notranslate"><span class="pre">.get_main_attr(name)</span></code></td>
<td>Get a “shareable” object from <code class="docutils literal notranslate"><span class="pre">__main__</span></code>.</td>
</tr>
<tr class="row-even"><td><code class="docutils literal notranslate"><span class="pre">.exec(src_str,</span> <span class="pre">/)</span></code></td>
<td><div class="line-block">
<div class="line">Run the given source code in the interpreter</div>
<div class="line">(in the current thread).</div>
</div>
</td>
</tr>
</tbody>
</table>
<p>For communicating between interpreters:</p>
<table class="docutils align-default">
<thead>
<tr class="row-odd"><th class="head">signature</th>
<th class="head">description</th>
</tr>
</thead>
<tbody>
<tr class="row-even"><td><code class="docutils literal notranslate"><span class="pre">is_shareable(obj)</span> <span class="pre">-&gt;</span> <span class="pre">Bool</span></code></td>
<td><div class="line-block">
<div class="line">Can the objects data be passed</div>
<div class="line">between interpreters?</div>
</div>
</td>
</tr>
<tr class="row-odd"><td><code class="docutils literal notranslate"><span class="pre">create_channel()</span> <span class="pre">-&gt;</span> <span class="pre">(RecvChannel,</span> <span class="pre">SendChannel)</span></code></td>
<td><div class="line-block">
<div class="line">Create a new channel for passing</div>
<div class="line">data between interpreters.</div>
</div>
</td>
</tr>
</tbody>
</table>
</section>
<section id="concurrent-futures-interpreterpoolexecutor">
<h3><a class="toc-backref" href="#concurrent-futures-interpreterpoolexecutor" role="doc-backlink">concurrent.futures.InterpreterPoolExecutor</a></h3>
<p>An executor will be added that extends <code class="docutils literal notranslate"><span class="pre">ThreadPoolExecutor</span></code> to run
per-thread tasks in subinterpreters. Initially, the only supported
tasks will be whatever <code class="docutils literal notranslate"><span class="pre">Interpreter.exec()</span></code> takes (e.g. a <code class="docutils literal notranslate"><span class="pre">str</span></code>
script). However, we may also support some functions, as well as
eventually a separate method for pickling the task and arguments,
to reduce friction (at the expense of performance
for short-running tasks).</p>
</section>
<section id="help-for-extension-module-maintainers">
<h3><a class="toc-backref" href="#help-for-extension-module-maintainers" role="doc-backlink">Help for Extension Module Maintainers</a></h3>
<p>In practice, an extension that implements multi-phase init (<a class="pep reference internal" href="../pep-0489/" title="PEP 489 Multi-phase extension module initialization">PEP 489</a>)
is considered isolated and thus compatible with multiple interpreters.
Otherwise it is “incompatible”.</p>
<p>Many extension modules are still incompatible. The maintainers and
users of such extension modules will both benefit when they are updated
to support multiple interpreters. In the meantime, users may become
confused by failures when using multiple interpreters, which could
negatively impact extension maintainers. See <a class="reference internal" href="#concerns">Concerns</a> below.</p>
<p>To mitigate that impact and accelerate compatibility, we will do the
following:</p>
<ul class="simple">
<li>be clear that extension modules are <em>not</em> required to support use in
multiple interpreters</li>
<li>raise <code class="docutils literal notranslate"><span class="pre">ImportError</span></code> when an incompatible module is imported
in a subinterpreter</li>
<li>provide resources (e.g. docs) to help maintainers reach compatibility</li>
<li>reach out to the maintainers of Cython and of the most used extension
modules (on PyPI) to get feedback and possibly provide assistance</li>
</ul>
</section>
</section>
<section id="examples">
<h2><a class="toc-backref" href="#examples" role="doc-backlink">Examples</a></h2>
<section id="run-isolated-code-in-current-os-thread">
<h3><a class="toc-backref" href="#run-isolated-code-in-current-os-thread" role="doc-backlink">Run isolated code in current OS thread</a></h3>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">interp</span> <span class="o">=</span> <span class="n">interpreters</span><span class="o">.</span><span class="n">create</span><span class="p">()</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">&#39;before&#39;</span><span class="p">)</span>
<span class="n">interp</span><span class="o">.</span><span class="n">exec</span><span class="p">(</span><span class="s1">&#39;print(&quot;during&quot;)&#39;</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">&#39;after&#39;</span><span class="p">)</span>
</pre></div>
</div>
</section>
<section id="run-in-a-different-thread">
<h3><a class="toc-backref" href="#run-in-a-different-thread" role="doc-backlink">Run in a different thread</a></h3>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">interp</span> <span class="o">=</span> <span class="n">interpreters</span><span class="o">.</span><span class="n">create</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">run</span><span class="p">():</span>
<span class="n">interp</span><span class="o">.</span><span class="n">exec</span><span class="p">(</span><span class="s1">&#39;print(&quot;during&quot;)&#39;</span><span class="p">)</span>
<span class="n">t</span> <span class="o">=</span> <span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">run</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">&#39;before&#39;</span><span class="p">)</span>
<span class="n">t</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
<span class="n">t</span><span class="o">.</span><span class="n">join</span><span class="p">()</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">&#39;after&#39;</span><span class="p">)</span>
</pre></div>
</div>
</section>
<section id="pre-populate-an-interpreter">
<h3><a class="toc-backref" href="#pre-populate-an-interpreter" role="doc-backlink">Pre-populate an interpreter</a></h3>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">interp</span> <span class="o">=</span> <span class="n">interpreters</span><span class="o">.</span><span class="n">create</span><span class="p">()</span>
<span class="n">interp</span><span class="o">.</span><span class="n">exec</span><span class="p">(</span><span class="n">tw</span><span class="o">.</span><span class="n">dedent</span><span class="p">(</span><span class="s2">&quot;&quot;&quot;</span>
<span class="s2"> import some_lib</span>
<span class="s2"> import an_expensive_module</span>
<span class="s2"> some_lib.set_up()</span>
<span class="s2"> &quot;&quot;&quot;</span><span class="p">))</span>
<span class="n">wait_for_request</span><span class="p">()</span>
<span class="n">interp</span><span class="o">.</span><span class="n">exec</span><span class="p">(</span><span class="n">tw</span><span class="o">.</span><span class="n">dedent</span><span class="p">(</span><span class="s2">&quot;&quot;&quot;</span>
<span class="s2"> some_lib.handle_request()</span>
<span class="s2"> &quot;&quot;&quot;</span><span class="p">))</span>
</pre></div>
</div>
</section>
<section id="handling-an-exception">
<h3><a class="toc-backref" href="#handling-an-exception" role="doc-backlink">Handling an exception</a></h3>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">interp</span> <span class="o">=</span> <span class="n">interpreters</span><span class="o">.</span><span class="n">create</span><span class="p">()</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">interp</span><span class="o">.</span><span class="n">exec</span><span class="p">(</span><span class="n">tw</span><span class="o">.</span><span class="n">dedent</span><span class="p">(</span><span class="s2">&quot;&quot;&quot;</span>
<span class="s2"> raise KeyError</span>
<span class="s2"> &quot;&quot;&quot;</span><span class="p">))</span>
<span class="k">except</span> <span class="n">interpreters</span><span class="o">.</span><span class="n">RunFailedError</span> <span class="k">as</span> <span class="n">exc</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;got the error from the subinterpreter: </span><span class="si">{</span><span class="n">exc</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
</pre></div>
</div>
</section>
<section id="re-raising-an-exception">
<h3><a class="toc-backref" href="#re-raising-an-exception" role="doc-backlink">Re-raising an exception</a></h3>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">interp</span> <span class="o">=</span> <span class="n">interpreters</span><span class="o">.</span><span class="n">create</span><span class="p">()</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">interp</span><span class="o">.</span><span class="n">exec</span><span class="p">(</span><span class="n">tw</span><span class="o">.</span><span class="n">dedent</span><span class="p">(</span><span class="s2">&quot;&quot;&quot;</span>
<span class="s2"> raise KeyError</span>
<span class="s2"> &quot;&quot;&quot;</span><span class="p">))</span>
<span class="k">except</span> <span class="n">interpreters</span><span class="o">.</span><span class="n">RunFailedError</span> <span class="k">as</span> <span class="n">exc</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">exc</span><span class="o">.</span><span class="n">__cause__</span>
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;got a KeyError from the subinterpreter&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p>Note that this pattern is a candidate for later improvement.</p>
</section>
<section id="interact-with-the-main-namespace">
<h3><a class="toc-backref" href="#interact-with-the-main-namespace" role="doc-backlink">Interact with the __main__ namespace</a></h3>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">interp</span> <span class="o">=</span> <span class="n">interpreters</span><span class="o">.</span><span class="n">create</span><span class="p">()</span>
<span class="n">interp</span><span class="o">.</span><span class="n">set_main_attrs</span><span class="p">(</span><span class="n">a</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">b</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>
<span class="n">interp</span><span class="o">.</span><span class="n">exec</span><span class="p">(</span><span class="n">tw</span><span class="o">.</span><span class="n">dedent</span><span class="p">(</span><span class="s2">&quot;&quot;&quot;</span>
<span class="s2"> res = do_something(a, b)</span>
<span class="s2"> &quot;&quot;&quot;</span><span class="p">))</span>
<span class="n">res</span> <span class="o">=</span> <span class="n">interp</span><span class="o">.</span><span class="n">get_main_attr</span><span class="p">(</span><span class="s1">&#39;res&#39;</span><span class="p">)</span>
</pre></div>
</div>
</section>
<section id="synchronize-using-an-os-pipe">
<h3><a class="toc-backref" href="#synchronize-using-an-os-pipe" role="doc-backlink">Synchronize using an OS pipe</a></h3>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">interp</span> <span class="o">=</span> <span class="n">interpreters</span><span class="o">.</span><span class="n">create</span><span class="p">()</span>
<span class="n">r1</span><span class="p">,</span> <span class="n">s1</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">pipe</span><span class="p">()</span>
<span class="n">r2</span><span class="p">,</span> <span class="n">s2</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">pipe</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">task</span><span class="p">():</span>
<span class="n">interp</span><span class="o">.</span><span class="n">exec</span><span class="p">(</span><span class="n">tw</span><span class="o">.</span><span class="n">dedent</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;&quot;&quot;</span>
<span class="s2"> import os</span>
<span class="s2"> os.read(</span><span class="si">{</span><span class="n">r1</span><span class="si">}</span><span class="s2">, 1)</span>
<span class="s2"> print(&#39;during B&#39;)</span>
<span class="s2"> os.write(</span><span class="si">{</span><span class="n">s2</span><span class="si">}</span><span class="s2">, &#39;&#39;)</span>
<span class="s2"> &quot;&quot;&quot;</span><span class="p">))</span>
<span class="n">t</span> <span class="o">=</span> <span class="n">threading</span><span class="o">.</span><span class="n">thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">task</span><span class="p">)</span>
<span class="n">t</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">&#39;before&#39;</span><span class="p">)</span>
<span class="n">os</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">s1</span><span class="p">,</span> <span class="s1">&#39;&#39;</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">&#39;during A&#39;</span><span class="p">)</span>
<span class="n">os</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="n">r2</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">&#39;after&#39;</span><span class="p">)</span>
<span class="n">t</span><span class="o">.</span><span class="n">join</span><span class="p">()</span>
</pre></div>
</div>
</section>
<section id="sharing-a-file-descriptor">
<h3><a class="toc-backref" href="#sharing-a-file-descriptor" role="doc-backlink">Sharing a file descriptor</a></h3>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">interp</span> <span class="o">=</span> <span class="n">interpreters</span><span class="o">.</span><span class="n">create</span><span class="p">()</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s1">&#39;spamspamspam&#39;</span><span class="p">)</span> <span class="k">as</span> <span class="n">infile</span><span class="p">:</span>
<span class="n">interp</span><span class="o">.</span><span class="n">set_main_attrs</span><span class="p">(</span><span class="n">fd</span><span class="o">=</span><span class="n">infile</span><span class="o">.</span><span class="n">fileno</span><span class="p">())</span>
<span class="n">interp</span><span class="o">.</span><span class="n">exec</span><span class="p">(</span><span class="n">tw</span><span class="o">.</span><span class="n">dedent</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;&quot;&quot;</span>
<span class="s2"> import os</span>
<span class="s2"> for line in os.fdopen(fd):</span>
<span class="s2"> print(line)</span>
<span class="s2"> &quot;&quot;&quot;</span><span class="p">))</span>
</pre></div>
</div>
</section>
<section id="passing-objects-via-pickle">
<h3><a class="toc-backref" href="#passing-objects-via-pickle" role="doc-backlink">Passing objects via pickle</a></h3>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">interp</span> <span class="o">=</span> <span class="n">interpreters</span><span class="o">.</span><span class="n">create</span><span class="p">()</span>
<span class="n">r</span><span class="p">,</span> <span class="n">s</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">pipe</span><span class="p">()</span>
<span class="n">interp</span><span class="o">.</span><span class="n">exec</span><span class="p">(</span><span class="n">tw</span><span class="o">.</span><span class="n">dedent</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;&quot;&quot;</span>
<span class="s2"> import os</span>
<span class="s2"> import pickle</span>
<span class="s2"> reader = </span><span class="si">{</span><span class="n">r</span><span class="si">}</span>
<span class="s2"> &quot;&quot;&quot;</span><span class="p">))</span>
<span class="n">interp</span><span class="o">.</span><span class="n">exec</span><span class="p">(</span><span class="n">tw</span><span class="o">.</span><span class="n">dedent</span><span class="p">(</span><span class="s2">&quot;&quot;&quot;</span>
<span class="s2"> data = b&#39;&#39;</span>
<span class="s2"> c = os.read(reader, 1)</span>
<span class="s2"> while c != b&#39;</span><span class="se">\x00</span><span class="s2">&#39;:</span>
<span class="s2"> while c != b&#39;</span><span class="se">\x00</span><span class="s2">&#39;:</span>
<span class="s2"> data += c</span>
<span class="s2"> c = os.read(reader, 1)</span>
<span class="s2"> obj = pickle.loads(data)</span>
<span class="s2"> do_something(obj)</span>
<span class="s2"> c = os.read(reader, 1)</span>
<span class="s2"> &quot;&quot;&quot;</span><span class="p">))</span>
<span class="k">for</span> <span class="n">obj</span> <span class="ow">in</span> <span class="nb">input</span><span class="p">:</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">pickle</span><span class="o">.</span><span class="n">dumps</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span>
<span class="n">os</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">data</span><span class="p">)</span>
<span class="n">os</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="sa">b</span><span class="s1">&#39;</span><span class="se">\x00</span><span class="s1">&#39;</span><span class="p">)</span>
<span class="n">os</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="sa">b</span><span class="s1">&#39;</span><span class="se">\x00</span><span class="s1">&#39;</span><span class="p">)</span>
</pre></div>
</div>
</section>
<section id="capturing-an-interpreter-s-stdout">
<h3><a class="toc-backref" href="#capturing-an-interpreter-s-stdout" role="doc-backlink">Capturing an interpreters stdout</a></h3>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">interp</span> <span class="o">=</span> <span class="n">interpreters</span><span class="o">.</span><span class="n">create</span><span class="p">()</span>
<span class="n">stdout</span> <span class="o">=</span> <span class="n">io</span><span class="o">.</span><span class="n">StringIO</span><span class="p">()</span>
<span class="k">with</span> <span class="n">contextlib</span><span class="o">.</span><span class="n">redirect_stdout</span><span class="p">(</span><span class="n">stdout</span><span class="p">):</span>
<span class="n">interp</span><span class="o">.</span><span class="n">exec</span><span class="p">(</span><span class="n">tw</span><span class="o">.</span><span class="n">dedent</span><span class="p">(</span><span class="s2">&quot;&quot;&quot;</span>
<span class="s2"> print(&#39;spam!&#39;)</span>
<span class="s2"> &quot;&quot;&quot;</span><span class="p">))</span>
<span class="k">assert</span><span class="p">(</span><span class="n">stdout</span><span class="o">.</span><span class="n">getvalue</span><span class="p">()</span> <span class="o">==</span> <span class="s1">&#39;spam!&#39;</span><span class="p">)</span>
<span class="c1"># alternately:</span>
<span class="n">interp</span><span class="o">.</span><span class="n">exec</span><span class="p">(</span><span class="n">tw</span><span class="o">.</span><span class="n">dedent</span><span class="p">(</span><span class="s2">&quot;&quot;&quot;</span>
<span class="s2"> import contextlib, io</span>
<span class="s2"> stdout = io.StringIO()</span>
<span class="s2"> with contextlib.redirect_stdout(stdout):</span>
<span class="s2"> print(&#39;spam!&#39;)</span>
<span class="s2"> captured = stdout.getvalue()</span>
<span class="s2"> &quot;&quot;&quot;</span><span class="p">))</span>
<span class="n">captured</span> <span class="o">=</span> <span class="n">interp</span><span class="o">.</span><span class="n">get_main_attr</span><span class="p">(</span><span class="s1">&#39;captured&#39;</span><span class="p">)</span>
<span class="k">assert</span><span class="p">(</span><span class="n">captured</span> <span class="o">==</span> <span class="s1">&#39;spam!&#39;</span><span class="p">)</span>
</pre></div>
</div>
<p>A pipe (<code class="docutils literal notranslate"><span class="pre">os.pipe()</span></code>) could be used similarly.</p>
</section>
<section id="running-a-module">
<h3><a class="toc-backref" href="#running-a-module" role="doc-backlink">Running a module</a></h3>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">interp</span> <span class="o">=</span> <span class="n">interpreters</span><span class="o">.</span><span class="n">create</span><span class="p">()</span>
<span class="n">main_module</span> <span class="o">=</span> <span class="n">mod_name</span>
<span class="n">interp</span><span class="o">.</span><span class="n">exec</span><span class="p">(</span><span class="sa">f</span><span class="s1">&#39;import runpy; runpy.run_module(</span><span class="si">{</span><span class="n">main_module</span><span class="si">!r}</span><span class="s1">)&#39;</span><span class="p">)</span>
</pre></div>
</div>
</section>
<section id="running-as-script-including-zip-archives-directories">
<h3><a class="toc-backref" href="#running-as-script-including-zip-archives-directories" role="doc-backlink">Running as script (including zip archives &amp; directories)</a></h3>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">interp</span> <span class="o">=</span> <span class="n">interpreters</span><span class="o">.</span><span class="n">create</span><span class="p">()</span>
<span class="n">main_script</span> <span class="o">=</span> <span class="n">path_name</span>
<span class="n">interp</span><span class="o">.</span><span class="n">exec</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;import runpy; runpy.run_path(</span><span class="si">{</span><span class="n">main_script</span><span class="si">!r}</span><span class="s2">)&quot;</span><span class="p">)</span>
</pre></div>
</div>
</section>
<section id="using-a-channel-to-communicate">
<h3><a class="toc-backref" href="#using-a-channel-to-communicate" role="doc-backlink">Using a channel to communicate</a></h3>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">tasks_recv</span><span class="p">,</span> <span class="n">tasks</span> <span class="o">=</span> <span class="n">interpreters</span><span class="o">.</span><span class="n">create_channel</span><span class="p">()</span>
<span class="n">results</span><span class="p">,</span> <span class="n">results_send</span> <span class="o">=</span> <span class="n">interpreters</span><span class="o">.</span><span class="n">create_channel</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">worker</span><span class="p">():</span>
<span class="n">interp</span> <span class="o">=</span> <span class="n">interpreters</span><span class="o">.</span><span class="n">create</span><span class="p">()</span>
<span class="n">interp</span><span class="o">.</span><span class="n">set_main_attrs</span><span class="p">(</span><span class="n">tasks</span><span class="o">=</span><span class="n">tasks_recv</span><span class="p">,</span> <span class="n">results</span><span class="o">=</span><span class="n">results_send</span><span class="p">)</span>
<span class="n">interp</span><span class="o">.</span><span class="n">exec</span><span class="p">(</span><span class="n">tw</span><span class="o">.</span><span class="n">dedent</span><span class="p">(</span><span class="s2">&quot;&quot;&quot;</span>
<span class="s2"> def handle_request(req):</span>
<span class="s2"> ...</span>
<span class="s2"> def capture_exception(exc):</span>
<span class="s2"> ...</span>
<span class="s2"> while True:</span>
<span class="s2"> try:</span>
<span class="s2"> req = tasks.recv()</span>
<span class="s2"> except Exception:</span>
<span class="s2"> # channel closed</span>
<span class="s2"> break</span>
<span class="s2"> try:</span>
<span class="s2"> res = handle_request(req)</span>
<span class="s2"> except Exception as exc:</span>
<span class="s2"> res = capture_exception(exc)</span>
<span class="s2"> results.send_nowait(res)</span>
<span class="s2"> &quot;&quot;&quot;</span><span class="p">))</span>
<span class="n">threads</span> <span class="o">=</span> <span class="p">[</span><span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">worker</span><span class="p">)</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">20</span><span class="p">)]</span>
<span class="k">for</span> <span class="n">t</span> <span class="ow">in</span> <span class="n">threads</span><span class="p">:</span>
<span class="n">t</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
<span class="n">requests</span> <span class="o">=</span> <span class="o">...</span>
<span class="k">for</span> <span class="n">req</span> <span class="ow">in</span> <span class="n">requests</span><span class="p">:</span>
<span class="n">tasks</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="n">req</span><span class="p">)</span>
<span class="n">tasks</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
<span class="k">for</span> <span class="n">t</span> <span class="ow">in</span> <span class="n">threads</span><span class="p">:</span>
<span class="n">t</span><span class="o">.</span><span class="n">join</span><span class="p">()</span>
</pre></div>
</div>
</section>
<section id="sharing-a-memoryview-imagine-map-reduce">
<h3><a class="toc-backref" href="#sharing-a-memoryview-imagine-map-reduce" role="doc-backlink">Sharing a memoryview (imagine map-reduce)</a></h3>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">data</span><span class="p">,</span> <span class="n">chunksize</span> <span class="o">=</span> <span class="n">read_large_data_set</span><span class="p">()</span>
<span class="n">buf</span> <span class="o">=</span> <span class="nb">memoryview</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
<span class="n">numchunks</span> <span class="o">=</span> <span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">buf</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">/</span> <span class="n">chunksize</span>
<span class="n">results</span> <span class="o">=</span> <span class="nb">memoryview</span><span class="p">(</span><span class="sa">b</span><span class="s1">&#39;</span><span class="se">\0</span><span class="s1">&#39;</span> <span class="o">*</span> <span class="n">numchunks</span><span class="p">)</span>
<span class="n">tasks_recv</span><span class="p">,</span> <span class="n">tasks</span> <span class="o">=</span> <span class="n">interpreters</span><span class="o">.</span><span class="n">create_channel</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">worker</span><span class="p">():</span>
<span class="n">interp</span> <span class="o">=</span> <span class="n">interpreters</span><span class="o">.</span><span class="n">create</span><span class="p">()</span>
<span class="n">interp</span><span class="o">.</span><span class="n">set_main_attrs</span><span class="p">(</span><span class="n">data</span><span class="o">=</span><span class="n">buf</span><span class="p">,</span> <span class="n">results</span><span class="o">=</span><span class="n">results</span><span class="p">,</span> <span class="n">tasks</span><span class="o">=</span><span class="n">tasks_recv</span><span class="p">)</span>
<span class="n">interp</span><span class="o">.</span><span class="n">exec</span><span class="p">(</span><span class="n">tw</span><span class="o">.</span><span class="n">dedent</span><span class="p">(</span><span class="s2">&quot;&quot;&quot;</span>
<span class="s2"> while True:</span>
<span class="s2"> try:</span>
<span class="s2"> req = tasks.recv()</span>
<span class="s2"> except Exception:</span>
<span class="s2"> # channel closed</span>
<span class="s2"> break</span>
<span class="s2"> resindex, start, end = req</span>
<span class="s2"> chunk = data[start: end]</span>
<span class="s2"> res = reduce_chunk(chunk)</span>
<span class="s2"> results[resindex] = res</span>
<span class="s2"> &quot;&quot;&quot;</span><span class="p">))</span>
<span class="n">t</span> <span class="o">=</span> <span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">worker</span><span class="p">)</span>
<span class="n">t</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">numchunks</span><span class="p">):</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">workers_running</span><span class="p">():</span>
<span class="k">raise</span> <span class="o">...</span>
<span class="n">start</span> <span class="o">=</span> <span class="n">i</span> <span class="o">*</span> <span class="n">chunksize</span>
<span class="n">end</span> <span class="o">=</span> <span class="n">start</span> <span class="o">+</span> <span class="n">chunksize</span>
<span class="k">if</span> <span class="n">end</span> <span class="o">&gt;</span> <span class="nb">len</span><span class="p">(</span><span class="n">buf</span><span class="p">):</span>
<span class="n">end</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">buf</span><span class="p">)</span>
<span class="n">tasks</span><span class="o">.</span><span class="n">send</span><span class="p">((</span><span class="n">start</span><span class="p">,</span> <span class="n">end</span><span class="p">,</span> <span class="n">i</span><span class="p">))</span>
<span class="n">tasks</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
<span class="n">t</span><span class="o">.</span><span class="n">join</span><span class="p">()</span>
<span class="n">use_results</span><span class="p">(</span><span class="n">results</span><span class="p">)</span>
</pre></div>
</div>
</section>
</section>
<section id="rationale">
<h2><a class="toc-backref" href="#rationale" role="doc-backlink">Rationale</a></h2>
<p>Running code in multiple interpreters provides a useful level of
isolation within the same process. This can be leveraged in a number
of ways. Furthermore, subinterpreters provide a well-defined framework
in which such isolation may extended. (See <a class="pep reference internal" href="../pep-0684/" title="PEP 684 A Per-Interpreter GIL">PEP 684</a>.)</p>
<p>Alyssa (Nick) Coghlan explained some of the benefits through a comparison with
multi-processing <a class="reference internal" href="#benefits" id="id2"><span>[benefits]</span></a>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">[</span><span class="n">I</span><span class="p">]</span> <span class="n">expect</span> <span class="n">that</span> <span class="n">communicating</span> <span class="n">between</span> <span class="n">subinterpreters</span> <span class="ow">is</span> <span class="n">going</span>
<span class="n">to</span> <span class="n">end</span> <span class="n">up</span> <span class="n">looking</span> <span class="n">an</span> <span class="n">awful</span> <span class="n">lot</span> <span class="n">like</span> <span class="n">communicating</span> <span class="n">between</span>
<span class="n">subprocesses</span> <span class="n">via</span> <span class="n">shared</span> <span class="n">memory</span><span class="o">.</span>
<span class="n">The</span> <span class="n">trade</span><span class="o">-</span><span class="n">off</span> <span class="n">between</span> <span class="n">the</span> <span class="n">two</span> <span class="n">models</span> <span class="n">will</span> <span class="n">then</span> <span class="n">be</span> <span class="n">that</span> <span class="n">one</span> <span class="n">still</span>
<span class="n">just</span> <span class="n">looks</span> <span class="n">like</span> <span class="n">a</span> <span class="n">single</span> <span class="n">process</span> <span class="kn">from</span> <span class="nn">the</span> <span class="n">point</span> <span class="n">of</span> <span class="n">view</span> <span class="n">of</span> <span class="n">the</span>
<span class="n">outside</span> <span class="n">world</span><span class="p">,</span> <span class="ow">and</span> <span class="n">hence</span> <span class="n">doesn</span><span class="s1">&#39;t place any extra demands on the</span>
<span class="n">underlying</span> <span class="n">OS</span> <span class="n">beyond</span> <span class="n">those</span> <span class="n">required</span> <span class="n">to</span> <span class="n">run</span> <span class="n">CPython</span> <span class="k">with</span> <span class="n">a</span> <span class="n">single</span>
<span class="n">interpreter</span><span class="p">,</span> <span class="k">while</span> <span class="n">the</span> <span class="n">other</span> <span class="n">gives</span> <span class="n">much</span> <span class="n">stricter</span> <span class="n">isolation</span>
<span class="p">(</span><span class="n">including</span> <span class="n">isolating</span> <span class="n">C</span> <span class="nb">globals</span> <span class="ow">in</span> <span class="n">extension</span> <span class="n">modules</span><span class="p">),</span> <span class="n">but</span> <span class="n">also</span>
<span class="n">demands</span> <span class="n">much</span> <span class="n">more</span> <span class="kn">from</span> <span class="nn">the</span> <span class="n">OS</span> <span class="n">when</span> <span class="n">it</span> <span class="n">comes</span> <span class="n">to</span> <span class="n">its</span> <span class="n">IPC</span>
<span class="n">capabilities</span><span class="o">.</span>
<span class="n">The</span> <span class="n">security</span> <span class="n">risk</span> <span class="n">profiles</span> <span class="n">of</span> <span class="n">the</span> <span class="n">two</span> <span class="n">approaches</span> <span class="n">will</span> <span class="n">also</span> <span class="n">be</span> <span class="n">quite</span>
<span class="n">different</span><span class="p">,</span> <span class="n">since</span> <span class="n">using</span> <span class="n">subinterpreters</span> <span class="n">won</span><span class="s1">&#39;t require deliberately</span>
<span class="n">poking</span> <span class="n">holes</span> <span class="ow">in</span> <span class="n">the</span> <span class="n">process</span> <span class="n">isolation</span> <span class="n">that</span> <span class="n">operating</span> <span class="n">systems</span> <span class="n">give</span>
<span class="n">you</span> <span class="n">by</span> <span class="n">default</span><span class="o">.</span>
</pre></div>
</div>
<p>CPython has supported multiple interpreters, with increasing levels
of support, since version 1.5. While the feature has the potential
to be a powerful tool, it has suffered from neglect
because the multiple interpreter capabilities are not readily available
directly from Python. Exposing the existing functionality
in the stdlib will help reverse the situation.</p>
<p>This proposal is focused on enabling the fundamental capability of
multiple interpreters, isolated from each other,
in the same Python process. This is a
new area for Python so there is relative uncertainly about the best
tools to provide as companions to interpreters. Thus we minimize
the functionality we add in the proposal as much as possible.</p>
<section id="concerns">
<h3><a class="toc-backref" href="#concerns" role="doc-backlink">Concerns</a></h3>
<ul class="simple">
<li>“subinterpreters are not worth the trouble”</li>
</ul>
<p>Some have argued that subinterpreters do not add sufficient benefit
to justify making them an official part of Python. Adding features
to the language (or stdlib) has a cost in increasing the size of
the language. So an addition must pay for itself.</p>
<p>In this case, multiple interpreter support provide a novel concurrency
model focused on isolated threads of execution. Furthermore, they
provide an opportunity for changes in CPython that will allow
simultaneous use of multiple CPU cores (currently prevented
by the GILsee <a class="pep reference internal" href="../pep-0684/" title="PEP 684 A Per-Interpreter GIL">PEP 684</a>).</p>
<p>Alternatives to subinterpreters include threading, async, and
multiprocessing. Threading is limited by the GIL and async isnt
the right solution for every problem (nor for every person).
Multiprocessing is likewise valuable in some but not all situations.
Direct IPC (rather than via the multiprocessing module) provides
similar benefits but with the same caveat.</p>
<p>Notably, subinterpreters are not intended as a replacement for any of
the above. Certainly they overlap in some areas, but the benefits of
subinterpreters include isolation and (potentially) performance. In
particular, subinterpreters provide a direct route to an alternate
concurrency model (e.g. CSP) which has found success elsewhere and
will appeal to some Python users. That is the core value that the
<code class="docutils literal notranslate"><span class="pre">interpreters</span></code> module will provide.</p>
<ul class="simple">
<li>“stdlib support for multiple interpreters adds extra burden
on C extension authors”</li>
</ul>
<p>In the <a class="reference internal" href="#interpreter-isolation">Interpreter Isolation</a> section below we identify ways in
which isolation in CPythons subinterpreters is incomplete. Most
notable is extension modules that use C globals to store internal
state. (<a class="pep reference internal" href="../pep-3121/" title="PEP 3121 Extension Module Initialization and Finalization">PEP 3121</a> and <a class="pep reference internal" href="../pep-0489/" title="PEP 489 Multi-phase extension module initialization">PEP 489</a> provide a solution to that problem,
followed by some extra APIs that improve efficiency, e.g. <a class="pep reference internal" href="../pep-0573/" title="PEP 573 Module State Access from C Extension Methods">PEP 573</a>).</p>
<p>Consequently, projects that publish extension modules may face an
increased maintenance burden as their users start using subinterpreters,
where their modules may break. This situation is limited to modules
that use C globals (or use libraries that use C globals) to store
internal state. For numpy, the reported-bug rate is one every 6
months. <a class="reference internal" href="#bug-rate" id="id3"><span>[bug-rate]</span></a></p>
<p>Ultimately this comes down to a question of how often it will be a
problem in practice: how many projects would be affected, how often
their users will be affected, what the additional maintenance burden
will be for projects, and what the overall benefit of subinterpreters
is to offset those costs. The position of this PEP is that the actual
extra maintenance burden will be small and well below the threshold at
which subinterpreters are worth it.</p>
<ul class="simple">
<li>“creating a new concurrency API deserves much more thought and
experimentation, so the new module shouldnt go into the stdlib
right away, if ever”</li>
</ul>
<p>Introducing an API for a new concurrency model, like happened with
asyncio, is an extremely large project that requires a lot of careful
consideration. It is not something that can be done as simply as this
PEP proposes and likely deserves significant time on PyPI to mature.
(See <a class="reference external" href="https://mail.python.org/archives/list/python-dev&#64;python.org/message/TUEAZNZHVJGGLL4OFD32OW6JJDKM6FAS/">Nathaniels post</a> on python-dev.)</p>
<p>However, this PEP does not propose any new concurrency API.
At most it exposes minimal tools (e.g. subinterpreters, channels)
which may be used to write code that follows patterns associated with
(relatively) new-to-Python <a class="reference internal" href="#concurrency">concurrency models</a>.
Those tools could also be used as the basis for APIs for such
concurrency models. Again, this PEP does not propose any such API.</p>
<ul class="simple">
<li>“there is no point to exposing subinterpreters if they still share
the GIL”</li>
<li>“the effort to make the GIL per-interpreter is disruptive and risky”</li>
</ul>
<p>A common misconception is that this PEP also includes a promise that
interpreters will no longer share the GIL. When that is clarified,
the next question is “what is the point?”. This is already answered
at length in this PEP. Just to be clear, the value lies in:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="o">*</span> <span class="n">increase</span> <span class="n">exposure</span> <span class="n">of</span> <span class="n">the</span> <span class="n">existing</span> <span class="n">feature</span><span class="p">,</span> <span class="n">which</span> <span class="n">helps</span> <span class="n">improve</span>
<span class="n">the</span> <span class="n">code</span> <span class="n">health</span> <span class="n">of</span> <span class="n">the</span> <span class="n">entire</span> <span class="n">CPython</span> <span class="n">runtime</span>
<span class="o">*</span> <span class="n">expose</span> <span class="n">the</span> <span class="p">(</span><span class="n">mostly</span><span class="p">)</span> <span class="n">isolated</span> <span class="n">execution</span> <span class="n">of</span> <span class="n">interpreters</span>
<span class="o">*</span> <span class="n">preparation</span> <span class="k">for</span> <span class="n">per</span><span class="o">-</span><span class="n">interpreter</span> <span class="n">GIL</span>
<span class="o">*</span> <span class="n">encourage</span> <span class="n">experimentation</span>
</pre></div>
</div>
<ul class="simple">
<li>“data sharing can have a negative impact on cache performance
in multi-core scenarios”</li>
</ul>
<p>(See <a class="reference internal" href="#cache-line-ping-pong" id="id4"><span>[cache-line-ping-pong]</span></a>.)</p>
<p>This shouldnt be a problem for now as we have no immediate plans
to actually share data between interpreters, instead focusing
on copying.</p>
</section>
</section>
<section id="about-subinterpreters">
<h2><a class="toc-backref" href="#about-subinterpreters" role="doc-backlink">About Subinterpreters</a></h2>
<section id="concurrency">
<h3><a class="toc-backref" href="#concurrency" role="doc-backlink">Concurrency</a></h3>
<p>Concurrency is a challenging area of software development. Decades of
research and practice have led to a wide variety of concurrency models,
each with different goals. Most center on correctness and usability.</p>
<p>One class of concurrency models focuses on isolated threads of
execution that interoperate through some message passing scheme. A
notable example is Communicating Sequential Processes <a class="reference internal" href="#csp" id="id5"><span>[CSP]</span></a> (upon
which Gos concurrency is roughly based). The intended isolation
inherent to CPythons interpreters makes them well-suited
to this approach.</p>
</section>
<section id="shared-data">
<h3><a class="toc-backref" href="#shared-data" role="doc-backlink">Shared Data</a></h3>
<p>CPythons interpreters are inherently isolated (with caveats
explained below), in contrast to threads. So the same
communicate-via-shared-memory approach doesnt work. Without an
alternative, effective use of concurrency via multiple interpreters
is significantly limited.</p>
<p>The key challenge here is that sharing objects between interpreters
faces complexity due to various constraints on object ownership,
visibility, and mutability. At a conceptual level its easier to
reason about concurrency when objects only exist in one interpreter
at a time. At a technical level, CPythons current memory model
limits how Python <em>objects</em> may be shared safely between interpreters;
effectively, objects are bound to the interpreter in which they were
created. Furthermore, the complexity of <em>object</em> sharing increases as
interpreters become more isolated, e.g. after GIL removal (though this
is mitigated somewhat for some “immortal” objects (see <a class="pep reference internal" href="../pep-0683/" title="PEP 683 Immortal Objects, Using a Fixed Refcount">PEP 683</a>).</p>
<p>Consequently, the mechanism for sharing needs to be carefully considered.
There are a number of valid solutions, several of which may be
appropriate to support in Pythons stdlib and C-API. Any such solution
is likely to share many characteristics with the others.</p>
<p>In the meantime, we propose here a minimal solution
(<code class="docutils literal notranslate"><span class="pre">Interpreter.set_main_attrs()</span></code>), which sets some precedent for how
objects are shared. More importantly, it facilitates the introduction
of more advanced approaches later and allows them to coexist and cooperate.
In part to demonstrate that, we will provide a basic implementation of
“channels”, as a somewhat more advanced sharing solution.</p>
<p>Separate proposals may cover:</p>
<ul class="simple">
<li>the addition of a public C-API based on the implementation
<code class="docutils literal notranslate"><span class="pre">Interpreter.set_main_attrs()</span></code></li>
<li>the addition of other sharing approaches to the “interpreters” module</li>
</ul>
<p>The fundamental enabling feature for communication is that most objects
can be converted to some encoding of underlying raw data, which is safe
to be passed between interpreters. For example, an <code class="docutils literal notranslate"><span class="pre">int</span></code> object can
be turned into a C <code class="docutils literal notranslate"><span class="pre">long</span></code> value, sent to another interpreter, and
turned back into an <code class="docutils literal notranslate"><span class="pre">int</span></code> object there. As another example,
<code class="docutils literal notranslate"><span class="pre">None</span></code> may be passed as-is.</p>
<p>Regardless, the effort to determine the best way forward here is mostly
outside the scope of this PEP. In the meantime, this proposal describes
a basic interim solution using pipes (<code class="docutils literal notranslate"><span class="pre">os.pipe()</span></code>), as well as
providing a dedicated capability (“channels”).
See <a class="reference internal" href="#api-for-communication">API For Communication</a> below.</p>
</section>
<section id="interpreter-isolation">
<h3><a class="toc-backref" href="#interpreter-isolation" role="doc-backlink">Interpreter Isolation</a></h3>
<p>CPythons interpreters are intended to be strictly isolated from each
other. Each interpreter has its own copy of all modules, classes,
functions, and variables. The same applies to state in C, including in
extension modules. The CPython C-API docs explain more. <a class="reference internal" href="#caveats" id="id6"><span>[caveats]</span></a></p>
<p>However, there are ways in which interpreters do share some state.
First of all, some process-global state remains shared:</p>
<ul class="simple">
<li>file descriptors</li>
<li>low-level env vars</li>
<li>process memory (though allocators <em>are</em> isolated)</li>
<li>builtin types (e.g. dict, bytes)</li>
<li>singletons (e.g. None)</li>
<li>underlying static module data (e.g. functions) for
builtin/extension/frozen modules</li>
</ul>
<p>There are no plans to change this.</p>
<p>Second, some isolation is faulty due to bugs or implementations that did
not take subinterpreters into account. This includes things like
extension modules that rely on C globals. <a class="reference internal" href="#cryptography" id="id7"><span>[cryptography]</span></a> In these
cases bugs should be opened (some are already):</p>
<ul class="simple">
<li>readline module hook functions (<a class="reference external" href="http://bugs.python.org/issue4202">http://bugs.python.org/issue4202</a>)</li>
<li>memory leaks on re-init (<a class="reference external" href="http://bugs.python.org/issue21387">http://bugs.python.org/issue21387</a>)</li>
</ul>
<p>Finally, some potential isolation is missing due to the current design
of CPython. Improvements are currently going on to address gaps in this
area:</p>
<ul class="simple">
<li>extensions using the <code class="docutils literal notranslate"><span class="pre">PyGILState_*</span></code> API are somewhat incompatible <a class="reference internal" href="#gilstate" id="id8"><span>[gilstate]</span></a></li>
</ul>
</section>
<section id="existing-usage">
<h3><a class="toc-backref" href="#existing-usage" role="doc-backlink">Existing Usage</a></h3>
<p>Multiple interpreter support has not been a widely used feature.
In fact, there have been only a handful of documented cases of
widespread usage, including
<a class="reference external" href="https://github.com/GrahamDumpleton/mod_wsgi">mod_wsgi</a>,
<a class="reference external" href="https://github.com/ceph/ceph/pull/14971">OpenStack Ceph</a>, and
<a class="reference external" href="https://github.com/ninia/jep">JEP</a>. On the one hand, these cases
provide confidence that existing multiple interpreter support is
relatively stable. On the other hand, there isnt much of a sample
size from which to judge the utility of the feature.</p>
</section>
</section>
<section id="alternate-python-implementations">
<h2><a class="toc-backref" href="#alternate-python-implementations" role="doc-backlink">Alternate Python Implementations</a></h2>
<p>Ive solicited feedback from various Python implementors about support
for subinterpreters. Each has indicated that they would be able to
support multiple interpreters in the same process (if they choose to)
without a lot of trouble. Here are the projects I contacted:</p>
<ul class="simple">
<li>jython (<a class="reference internal" href="#jython" id="id9"><span>[jython]</span></a>)</li>
<li>ironpython (personal correspondence)</li>
<li>pypy (personal correspondence)</li>
<li>micropython (personal correspondence)</li>
</ul>
</section>
<section id="interpreters-module-api">
<span id="interpreters-is-shareable"></span><span id="interpreters-interpreter"></span><span id="interpreters-create"></span><span id="interpreters-get-current"></span><span id="interpreters-list-all"></span><h2><a class="toc-backref" href="#interpreters-module-api" role="doc-backlink">“interpreters” Module API</a></h2>
<p>The module provides the following functions:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">list_all</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="n">Interpreter</span><span class="p">]</span>
<span class="n">Return</span> <span class="n">a</span> <span class="nb">list</span> <span class="n">of</span> <span class="nb">all</span> <span class="n">existing</span> <span class="n">interpreters</span><span class="o">.</span>
<span class="n">get_current</span><span class="p">()</span> <span class="o">=&gt;</span> <span class="n">Interpreter</span>
<span class="n">Return</span> <span class="n">the</span> <span class="n">currently</span> <span class="n">running</span> <span class="n">interpreter</span><span class="o">.</span>
<span class="n">get_main</span><span class="p">()</span> <span class="o">=&gt;</span> <span class="n">Interpreter</span>
<span class="n">Return</span> <span class="n">the</span> <span class="n">main</span> <span class="n">interpreter</span><span class="o">.</span> <span class="n">If</span> <span class="n">the</span> <span class="n">Python</span> <span class="n">implementation</span>
<span class="n">has</span> <span class="n">no</span> <span class="n">concept</span> <span class="n">of</span> <span class="n">a</span> <span class="n">main</span> <span class="n">interpreter</span> <span class="n">then</span> <span class="k">return</span> <span class="kc">None</span><span class="o">.</span>
<span class="n">create</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="n">Interpreter</span>
<span class="n">Initialize</span> <span class="n">a</span> <span class="n">new</span> <span class="n">Python</span> <span class="n">interpreter</span> <span class="ow">and</span> <span class="k">return</span> <span class="n">it</span><span class="o">.</span>
<span class="n">It</span> <span class="n">will</span> <span class="n">remain</span> <span class="n">idle</span> <span class="n">until</span> <span class="n">something</span> <span class="ow">is</span> <span class="n">run</span> <span class="ow">in</span> <span class="n">it</span> <span class="ow">and</span> <span class="n">always</span>
<span class="n">run</span> <span class="ow">in</span> <span class="n">its</span> <span class="n">own</span> <span class="n">thread</span><span class="o">.</span>
<span class="n">is_shareable</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">bool</span><span class="p">:</span>
<span class="n">Return</span> <span class="kc">True</span> <span class="k">if</span> <span class="n">the</span> <span class="nb">object</span> <span class="n">may</span> <span class="n">be</span> <span class="s2">&quot;shared&quot;</span> <span class="n">between</span> <span class="n">interpreters</span><span class="o">.</span>
<span class="n">This</span> <span class="n">does</span> <span class="ow">not</span> <span class="n">necessarily</span> <span class="n">mean</span> <span class="n">that</span> <span class="n">the</span> <span class="n">actual</span> <span class="n">objects</span> <span class="n">will</span> <span class="n">be</span>
<span class="n">shared</span><span class="o">.</span> <span class="n">Instead</span><span class="p">,</span> <span class="n">it</span> <span class="n">means</span> <span class="n">that</span> <span class="n">the</span> <span class="n">objects</span><span class="s1">&#39; underlying data will</span>
<span class="n">be</span> <span class="n">shared</span> <span class="ow">in</span> <span class="n">a</span> <span class="n">cross</span><span class="o">-</span><span class="n">interpreter</span> <span class="n">way</span><span class="p">,</span> <span class="n">whether</span> <span class="n">via</span> <span class="n">a</span> <span class="n">proxy</span><span class="p">,</span> <span class="n">a</span>
<span class="n">copy</span><span class="p">,</span> <span class="ow">or</span> <span class="n">some</span> <span class="n">other</span> <span class="n">means</span><span class="o">.</span>
</pre></div>
</div>
<p>The module also provides the following class:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>class Interpreter(id):
id -&gt; int:
The interpreter&#39;s ID. (read-only)
is_running() -&gt; bool:
Return whether or not the interpreter&#39;s &quot;exec()&quot; is currently
executing code. Code running in subthreads is ignored.
Calling this on the current interpreter will always return True.
close():
Finalize and destroy the interpreter.
This may not be called on an already running interpreter.
Doing so results in a RuntimeError.
set_main_attrs(iterable_or_mapping, /):
set_main_attrs(**kwargs):
Set attributes in the interpreter&#39;s __main__ module
corresponding to the given name-value pairs. Each value
must be a &quot;shareable&quot; object and will be converted to a new
object (e.g. copy, proxy) in whatever way that object&#39;s type
defines. If an attribute with the same name is already set,
it will be overwritten.
This method is helpful for setting up an interpreter before
calling exec().
get_main_attr(name, default=None, /):
Return the value of the corresponding attribute of the
interpreter&#39;s __main__ module. If the attribute isn&#39;t set
then the default is returned. If it is set, but the value
isn&#39;t &quot;shareable&quot; then a ValueError is raised.
This may be used to introspect the __main__ module, as well
as a very basic mechanism for &quot;returning&quot; one or more results
from Interpreter.exec().
exec(source_str, /):
Run the provided Python source code in the interpreter,
in its __main__ module.
This may not be called on an already running interpreter.
Doing so results in a RuntimeError.
An &quot;interp.exec()&quot; call is similar to a builtin exec() call
(or to calling a function that returns None). Once
&quot;interp.exec()&quot; completes, the code that called &quot;exec()&quot;
continues executing (in the original interpreter). Likewise,
if there is any uncaught exception then it effectively
(see below) propagates into the code where ``interp.exec()``
was called. Like exec() (and threads), but unlike function
calls, there is no return value. If any &quot;return&quot; value from
the code is needed, send the data out via a pipe (os.pipe())
or channel or other cross-interpreter communication mechanism.
The big difference from exec() or functions is that
&quot;interp.exec()&quot; executes the code in an entirely different
interpreter, with entirely separate state. The interpreters
are completely isolated from each other, so the state of the
original interpreter (including the code it was executing in
the current OS thread) does not affect the state of the target
interpreter (the one that will execute the code). Likewise,
the target does not affect the original, nor any of its other
threads.
Instead, the state of the original interpreter (for this thread)
is frozen, and the code it&#39;s executing code completely blocks.
At that point, the target interpreter is given control of the
OS thread. Then, when it finishes executing, the original
interpreter gets control back and continues executing.
So calling &quot;interp.exec()&quot; will effectively cause the current
Python thread to completely pause. Sometimes you won&#39;t want
that pause, in which case you should make the &quot;exec()&quot; call in
another thread. To do so, add a function that calls
&quot;interp.exec()&quot; and then run that function in a normal
&quot;threading.Thread&quot;.
Note that the interpreter&#39;s state is never reset, neither
before &quot;interp.exec()&quot; executes the code nor after. Thus the
interpreter state is preserved between calls to
&quot;interp.exec()&quot;. This includes &quot;sys.modules&quot;, the &quot;builtins&quot;
module, and the internal state of C extension modules.
Also note that &quot;interp.exec()&quot; executes in the namespace of the
&quot;__main__&quot; module, just like scripts, the REPL, &quot;-m&quot;, and
&quot;-c&quot;. Just as the interpreter&#39;s state is not ever reset, the
&quot;__main__&quot; module is never reset. You can imagine
concatenating the code from each &quot;interp.exec()&quot; call into one
long script. This is the same as how the REPL operates.
Supported code: source text.
</pre></div>
</div>
<p>In addition to the functionality of <code class="docutils literal notranslate"><span class="pre">Interpreter.set_main_attrs()</span></code>,
the module provides a related way to pass data between interpreters:
channels. See <a class="reference internal" href="#channels">Channels</a> below.</p>
<section id="uncaught-exceptions">
<h3><a class="toc-backref" href="#uncaught-exceptions" role="doc-backlink">Uncaught Exceptions</a></h3>
<p>Regarding uncaught exceptions in <code class="docutils literal notranslate"><span class="pre">Interpreter.exec()</span></code>, we noted that
they are “effectively” propagated into the code where <code class="docutils literal notranslate"><span class="pre">interp.exec()</span></code>
was called. To prevent leaking exceptions (and tracebacks) between
interpreters, we create a surrogate of the exception and its traceback
(see <a class="reference external" href="https://docs.python.org/3/library/traceback.html#traceback.TracebackException" title="(in Python v3.12)"><code class="xref py py-class docutils literal notranslate"><span class="pre">traceback.TracebackException</span></code></a>), set it to <code class="docutils literal notranslate"><span class="pre">__cause__</span></code>
on a new <code class="docutils literal notranslate"><span class="pre">interpreters.RunFailedError</span></code>, and raise that.</p>
<p>Directly raising (a proxy of) the exception is problematic since its
harder to distinguish between an error in the <code class="docutils literal notranslate"><span class="pre">interp.exec()</span></code> call
and an uncaught exception from the subinterpreter.</p>
</section>
</section>
<section id="interpreter-restrictions">
<h2><a class="toc-backref" href="#interpreter-restrictions" role="doc-backlink">Interpreter Restrictions</a></h2>
<p>Every new interpreter created by <code class="docutils literal notranslate"><span class="pre">interpreters.create()</span></code>
now has specific restrictions on any code it runs. This includes the
following:</p>
<ul class="simple">
<li>importing an extension module fails if it does not implement
multi-phase init</li>
<li>daemon threads may not be created</li>
<li><code class="docutils literal notranslate"><span class="pre">os.fork()</span></code> is not allowed (so no <code class="docutils literal notranslate"><span class="pre">multiprocessing</span></code>)</li>
<li><code class="docutils literal notranslate"><span class="pre">os.exec*()</span></code> is not allowed
(but “fork+exec”, a la <code class="docutils literal notranslate"><span class="pre">subprocess</span></code> is okay)</li>
</ul>
<p>Note that interpreters created with the existing C-API do not have these
restrictions. The same is true for the “main” interpreter, so
existing use of Python will not change.</p>
<p>We may choose to later loosen some of the above restrictions or provide
a way to enable/disable granular restrictions individually. Regardless,
requiring multi-phase init from extension modules will always be a
default restriction.</p>
</section>
<section id="api-for-communication">
<h2><a class="toc-backref" href="#api-for-communication" role="doc-backlink">API For Communication</a></h2>
<p>As discussed in <a class="reference internal" href="#shared-data">Shared Data</a> above, multiple interpreter support
is less useful without a mechanism for sharing data (communicating)
between them. Sharing actual Python objects between interpreters,
however, has enough potential problems that we are avoiding support
for that in this proposal. Nor, as mentioned earlier, are we adding
anything more than a basic mechanism for communication.</p>
<p>That mechanism is the <code class="docutils literal notranslate"><span class="pre">Interpreter.set_main_attrs()</span></code> method.
It may be used to set up global variables before <code class="docutils literal notranslate"><span class="pre">Interpreter.exec()</span></code>
is called. The name-value pairs passed to <code class="docutils literal notranslate"><span class="pre">set_main_attrs()</span></code> are
bound as attributes of the interpreters <code class="docutils literal notranslate"><span class="pre">__main__</span></code> module.
The values must be “shareable”. See <a class="reference internal" href="#shareable-types">Shareable Types</a> below.</p>
<p>Additional approaches to communicating and sharing objects are enabled
through <code class="docutils literal notranslate"><span class="pre">Interpreter.set_main_attrs()</span></code>. A shareable object could be
implemented which works like a queue, but with cross-interpreter safety.
In fact, this PEP does include an example of such an approach: channels.</p>
<section id="shareable-types">
<h3><a class="toc-backref" href="#shareable-types" role="doc-backlink">Shareable Types</a></h3>
<p>An object is “shareable” if its type supports shareable instances.
The type must implement a new internal protocol, which is used to
convert an object to interpreter-independent data and then converted
back to an object on the other side. Also see
<a class="reference internal" href="#interpreters-is-shareable">is_shareable()</a> above.</p>
<p>A minimal set of simple, immutable builtin types will be supported
initially, including:</p>
<ul class="simple">
<li><code class="docutils literal notranslate"><span class="pre">None</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">bool</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">bytes</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">str</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">int</span></code></li>
<li><code class="docutils literal notranslate"><span class="pre">float</span></code></li>
</ul>
<p>We will also support a small number of complex types initially:</p>
<ul class="simple">
<li><code class="docutils literal notranslate"><span class="pre">memoryview</span></code>, to allow sharing <a class="pep reference internal" href="../pep-3118/" title="PEP 3118 Revising the buffer protocol">PEP 3118</a> buffers</li>
<li><a class="reference internal" href="#channels">channels</a></li>
</ul>
<p>Further builtin types may be supported later, complex or not.
Limiting the initial shareable types is a practical matter, reducing
the potential complexity of the initial implementation. There are a
number of strategies we may pursue in the future to expand supported
objects, once we have more experience with interpreter isolation.</p>
<p>In the meantime, a separate proposal will discuss making the internal
protocol (and C-API) used by <code class="docutils literal notranslate"><span class="pre">Interpreter.set_main_attrs()</span></code> public.
With that protocol, support for other types could be added
by extension modules.</p>
<section id="communicating-through-os-pipes">
<h4><a class="toc-backref" href="#communicating-through-os-pipes" role="doc-backlink">Communicating Through OS Pipes</a></h4>
<p>Even without a dedicated object for communication, users may already
use existing tools. For example, one basic approach for sending data
between interpreters is to use a pipe (see <code class="docutils literal notranslate"><span class="pre">os.pipe()</span></code>):</p>
<ol class="arabic simple">
<li>interpreter A calls <code class="docutils literal notranslate"><span class="pre">os.pipe()</span></code> to get a read/write pair
of file descriptors (both <code class="docutils literal notranslate"><span class="pre">int</span></code> objects)</li>
<li>interpreter A calls <code class="docutils literal notranslate"><span class="pre">interp.set_main_attrs()</span></code>, binding the read FD
(or embeds it using string formatting)</li>
<li>interpreter A calls <code class="docutils literal notranslate"><span class="pre">interp.exec()</span></code> on interpreter B</li>
<li>interpreter A writes some bytes to the write FD</li>
<li>interpreter B reads those bytes</li>
</ol>
<p>Several of the earlier examples demonstrate this, such as
<a class="reference internal" href="#synchronize-using-an-os-pipe">Synchronize using an OS pipe</a>.</p>
</section>
</section>
<section id="channels">
<span id="interpreters-sendchannel"></span><span id="interpreters-recvchannel"></span><span id="interpreters-create-channel"></span><h3><a class="toc-backref" href="#channels" role="doc-backlink">Channels</a></h3>
<p>The <code class="docutils literal notranslate"><span class="pre">interpreters</span></code> module will include a dedicated solution for
passing object data between interpreters: channels. They are included
in the module in part to provide an easier mechanism than using
<code class="docutils literal notranslate"><span class="pre">os.pipe()</span></code> and in part to demonstrate how libraries may take
advantage of <code class="docutils literal notranslate"><span class="pre">Interpreter.set_main_attrs()</span></code>
and the protocol it uses.</p>
<p>A channel is a simplex FIFO. It is a basic, opt-in data sharing
mechanism that draws inspiration from pipes, queues, and CSPs
channels. <a class="reference internal" href="#fifo" id="id10"><span>[fifo]</span></a> The main difference from pipes is that channels can
be associated with zero or more interpreters on either end. Like
queues, which are also many-to-many, channels are buffered (though
they also offer methods with unbuffered semantics).</p>
<p>Channels have two operations: send and receive. A key characteristic
of those operations is that channels transmit data derived from Python
objects rather than the objects themselves. When objects are sent,
their data is extracted. When the “object” is received in the other
interpreter, the data is converted back into an object owned by that
interpreter.</p>
<p>To make this work, the mutable shared state will be managed by the
Python runtime, not by any of the interpreters. Initially we will
support only one type of objects for shared state: the channels provided
by <code class="docutils literal notranslate"><span class="pre">interpreters.create_channel()</span></code>. Channels, in turn, will carefully
manage passing objects between interpreters.</p>
<p>This approach, including keeping the API minimal, helps us avoid further
exposing any underlying complexity to Python users.</p>
<p>The <code class="docutils literal notranslate"><span class="pre">interpreters</span></code> module provides the following function related
to channels:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">create_channel</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="p">(</span><span class="n">RecvChannel</span><span class="p">,</span> <span class="n">SendChannel</span><span class="p">):</span>
<span class="n">Create</span> <span class="n">a</span> <span class="n">new</span> <span class="n">channel</span> <span class="ow">and</span> <span class="k">return</span> <span class="p">(</span><span class="n">recv</span><span class="p">,</span> <span class="n">send</span><span class="p">),</span> <span class="n">the</span> <span class="n">RecvChannel</span>
<span class="ow">and</span> <span class="n">SendChannel</span> <span class="n">corresponding</span> <span class="n">to</span> <span class="n">the</span> <span class="n">ends</span> <span class="n">of</span> <span class="n">the</span> <span class="n">channel</span><span class="o">.</span>
<span class="n">Both</span> <span class="n">ends</span> <span class="n">of</span> <span class="n">the</span> <span class="n">channel</span> <span class="n">are</span> <span class="n">supported</span> <span class="s2">&quot;shared&quot;</span> <span class="n">objects</span> <span class="p">(</span><span class="n">i</span><span class="o">.</span><span class="n">e</span><span class="o">.</span>
<span class="n">may</span> <span class="n">be</span> <span class="n">safely</span> <span class="n">shared</span> <span class="n">by</span> <span class="n">different</span> <span class="n">interpreters</span><span class="o">.</span> <span class="n">Thus</span> <span class="n">they</span>
<span class="n">may</span> <span class="n">be</span> <span class="nb">set</span> <span class="n">using</span> <span class="s2">&quot;Interpreter.set_main_attrs()&quot;</span><span class="o">.</span>
</pre></div>
</div>
<p>The module also provides the following channel-related classes:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">RecvChannel</span><span class="p">(</span><span class="nb">id</span><span class="p">):</span>
<span class="n">The</span> <span class="n">receiving</span> <span class="n">end</span> <span class="n">of</span> <span class="n">a</span> <span class="n">channel</span><span class="o">.</span> <span class="n">An</span> <span class="n">interpreter</span> <span class="n">may</span> <span class="n">use</span> <span class="n">this</span> <span class="n">to</span>
<span class="n">receive</span> <span class="n">objects</span> <span class="kn">from</span> <span class="nn">another</span> <span class="n">interpreter</span><span class="o">.</span> <span class="n">Any</span> <span class="nb">type</span> <span class="n">supported</span> <span class="n">by</span>
<span class="n">Interpreter</span><span class="o">.</span><span class="n">set_main_attrs</span><span class="p">()</span> <span class="n">will</span> <span class="n">be</span> <span class="n">supported</span> <span class="n">here</span><span class="p">,</span> <span class="n">though</span> <span class="n">at</span>
<span class="n">first</span> <span class="n">only</span> <span class="n">a</span> <span class="n">few</span> <span class="n">of</span> <span class="n">the</span> <span class="n">simple</span><span class="p">,</span> <span class="n">immutable</span> <span class="n">builtin</span> <span class="n">types</span>
<span class="n">will</span> <span class="n">be</span> <span class="n">supported</span><span class="o">.</span>
<span class="nb">id</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span>
<span class="n">The</span> <span class="n">channel</span><span class="s1">&#39;s unique ID. The &quot;send&quot; end has the same one.</span>
<span class="n">recv</span><span class="p">(</span><span class="o">*</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="n">Return</span> <span class="n">the</span> <span class="nb">next</span> <span class="nb">object</span> <span class="kn">from</span> <span class="nn">the</span> <span class="n">channel</span><span class="o">.</span> <span class="n">If</span> <span class="n">none</span> <span class="n">have</span> <span class="n">been</span>
<span class="n">sent</span> <span class="n">then</span> <span class="n">wait</span> <span class="n">until</span> <span class="n">the</span> <span class="nb">next</span> <span class="n">send</span> <span class="p">(</span><span class="ow">or</span> <span class="n">until</span> <span class="n">the</span> <span class="n">timeout</span> <span class="ow">is</span> <span class="n">hit</span><span class="p">)</span><span class="o">.</span>
<span class="n">At</span> <span class="n">the</span> <span class="n">least</span><span class="p">,</span> <span class="n">the</span> <span class="nb">object</span> <span class="n">will</span> <span class="n">be</span> <span class="n">equivalent</span> <span class="n">to</span> <span class="n">the</span> <span class="n">sent</span> <span class="nb">object</span><span class="o">.</span>
<span class="n">That</span> <span class="n">will</span> <span class="n">almost</span> <span class="n">always</span> <span class="n">mean</span> <span class="n">the</span> <span class="n">same</span> <span class="nb">type</span> <span class="k">with</span> <span class="n">the</span> <span class="n">same</span> <span class="n">data</span><span class="p">,</span>
<span class="n">though</span> <span class="n">it</span> <span class="n">could</span> <span class="n">also</span> <span class="n">be</span> <span class="n">a</span> <span class="n">compatible</span> <span class="n">proxy</span><span class="o">.</span> <span class="n">Regardless</span><span class="p">,</span> <span class="n">it</span> <span class="n">may</span>
<span class="n">use</span> <span class="n">a</span> <span class="n">copy</span> <span class="n">of</span> <span class="n">that</span> <span class="n">data</span> <span class="ow">or</span> <span class="n">actually</span> <span class="n">share</span> <span class="n">the</span> <span class="n">data</span><span class="o">.</span> <span class="n">That</span><span class="s1">&#39;s up</span>
<span class="n">to</span> <span class="n">the</span> <span class="nb">object</span><span class="s1">&#39;s type.</span>
<span class="n">recv_nowait</span><span class="p">(</span><span class="n">default</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="n">Return</span> <span class="n">the</span> <span class="nb">next</span> <span class="nb">object</span> <span class="kn">from</span> <span class="nn">the</span> <span class="n">channel</span><span class="o">.</span> <span class="n">If</span> <span class="n">none</span> <span class="n">have</span> <span class="n">been</span>
<span class="n">sent</span> <span class="n">then</span> <span class="k">return</span> <span class="n">the</span> <span class="n">default</span><span class="o">.</span> <span class="n">Otherwise</span><span class="p">,</span> <span class="n">this</span> <span class="ow">is</span> <span class="n">the</span> <span class="n">same</span>
<span class="k">as</span> <span class="n">the</span> <span class="s2">&quot;recv()&quot;</span> <span class="n">method</span><span class="o">.</span>
<span class="k">class</span> <span class="nc">SendChannel</span><span class="p">(</span><span class="nb">id</span><span class="p">):</span>
<span class="n">The</span> <span class="n">sending</span> <span class="n">end</span> <span class="n">of</span> <span class="n">a</span> <span class="n">channel</span><span class="o">.</span> <span class="n">An</span> <span class="n">interpreter</span> <span class="n">may</span> <span class="n">use</span> <span class="n">this</span> <span class="n">to</span>
<span class="n">send</span> <span class="n">objects</span> <span class="n">to</span> <span class="n">another</span> <span class="n">interpreter</span><span class="o">.</span> <span class="n">Any</span> <span class="nb">type</span> <span class="n">supported</span> <span class="n">by</span>
<span class="n">Interpreter</span><span class="o">.</span><span class="n">set_main_attrs</span><span class="p">()</span> <span class="n">will</span> <span class="n">be</span> <span class="n">supported</span> <span class="n">here</span><span class="p">,</span> <span class="n">though</span>
<span class="n">at</span> <span class="n">first</span> <span class="n">only</span> <span class="n">a</span> <span class="n">few</span> <span class="n">of</span> <span class="n">the</span> <span class="n">simple</span><span class="p">,</span> <span class="n">immutable</span> <span class="n">builtin</span> <span class="n">types</span>
<span class="n">will</span> <span class="n">be</span> <span class="n">supported</span><span class="o">.</span>
<span class="nb">id</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span>
<span class="n">The</span> <span class="n">channel</span><span class="s1">&#39;s unique ID. The &quot;recv&quot; end has the same one.</span>
<span class="n">send</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="n">Send</span> <span class="n">the</span> <span class="nb">object</span> <span class="p">(</span><span class="n">i</span><span class="o">.</span><span class="n">e</span><span class="o">.</span> <span class="n">its</span> <span class="n">data</span><span class="p">)</span> <span class="n">to</span> <span class="n">the</span> <span class="s2">&quot;recv&quot;</span> <span class="n">end</span> <span class="n">of</span> <span class="n">the</span>
<span class="n">channel</span><span class="o">.</span> <span class="n">Wait</span> <span class="n">until</span> <span class="n">the</span> <span class="nb">object</span> <span class="ow">is</span> <span class="n">received</span><span class="o">.</span> <span class="n">If</span> <span class="n">the</span> <span class="nb">object</span>
<span class="ow">is</span> <span class="ow">not</span> <span class="n">shareable</span> <span class="n">then</span> <span class="ne">ValueError</span> <span class="ow">is</span> <span class="n">raised</span><span class="o">.</span>
<span class="n">The</span> <span class="n">builtin</span> <span class="nb">memoryview</span> <span class="ow">is</span> <span class="n">supported</span><span class="p">,</span> <span class="n">so</span> <span class="n">sending</span> <span class="n">a</span> <span class="n">buffer</span>
<span class="n">across</span> <span class="n">involves</span> <span class="n">first</span> <span class="n">wrapping</span> <span class="n">the</span> <span class="nb">object</span> <span class="ow">in</span> <span class="n">a</span> <span class="nb">memoryview</span>
<span class="ow">and</span> <span class="n">then</span> <span class="n">sending</span> <span class="n">that</span><span class="o">.</span>
<span class="n">send_nowait</span><span class="p">(</span><span class="n">obj</span><span class="p">):</span>
<span class="n">Send</span> <span class="n">the</span> <span class="nb">object</span> <span class="n">to</span> <span class="n">the</span> <span class="s2">&quot;recv&quot;</span> <span class="n">end</span> <span class="n">of</span> <span class="n">the</span> <span class="n">channel</span><span class="o">.</span> <span class="n">This</span>
<span class="n">behaves</span> <span class="n">the</span> <span class="n">same</span> <span class="k">as</span> <span class="s2">&quot;send()&quot;</span><span class="p">,</span> <span class="k">except</span> <span class="k">for</span> <span class="n">the</span> <span class="n">waiting</span> <span class="n">part</span><span class="o">.</span>
<span class="n">If</span> <span class="n">no</span> <span class="n">interpreter</span> <span class="ow">is</span> <span class="n">currently</span> <span class="n">receiving</span> <span class="p">(</span><span class="n">waiting</span> <span class="n">on</span> <span class="n">the</span>
<span class="n">other</span> <span class="n">end</span><span class="p">)</span> <span class="n">then</span> <span class="n">queue</span> <span class="n">the</span> <span class="nb">object</span> <span class="ow">and</span> <span class="k">return</span> <span class="kc">False</span><span class="o">.</span> <span class="n">Otherwise</span>
<span class="k">return</span> <span class="kc">True</span><span class="o">.</span>
</pre></div>
</div>
</section>
<section id="caveats-for-shared-objects">
<h3><a class="toc-backref" href="#caveats-for-shared-objects" role="doc-backlink">Caveats For Shared Objects</a></h3>
<p>Again, Python objects are not shared between interpreters.
However, in some cases data those objects wrap is actually shared
and not just copied. One example might be <a class="pep reference internal" href="../pep-3118/" title="PEP 3118 Revising the buffer protocol">PEP 3118</a> buffers.</p>
<p>In those cases the object in the original interpreter is kept alive
until the shared data in the other interpreter is no longer used.
Then object destruction can happen like normal in the original
interpreter, along with the previously shared data.</p>
</section>
</section>
<section id="documentation">
<h2><a class="toc-backref" href="#documentation" role="doc-backlink">Documentation</a></h2>
<p>The new stdlib docs page for the <code class="docutils literal notranslate"><span class="pre">interpreters</span></code> module will include
the following:</p>
<ul class="simple">
<li>(at the top) a clear note that support for multiple interpreters
is not required from extension modules</li>
<li>some explanation about what subinterpreters are</li>
<li>brief examples of how to use multiple interpreters
(and communicating between them)</li>
<li>a summary of the limitations of using multiple interpreters</li>
<li>(for extension maintainers) a link to the resources for ensuring
multiple interpreters compatibility</li>
<li>much of the API information in this PEP</li>
</ul>
<p>Docs about resources for extension maintainers already exist on the
<a class="reference external" href="https://docs.python.org/3/howto/isolating-extensions.html">Isolating Extension Modules</a> howto page. Any
extra help will be added there. For example, it may prove helpful
to discuss strategies for dealing with linked libraries that keep
their own subinterpreter-incompatible global state.</p>
<p>Note that the documentation will play a large part in mitigating any
negative impact that the new <code class="docutils literal notranslate"><span class="pre">interpreters</span></code> module might have on
extension module maintainers.</p>
<p>Also, the <code class="docutils literal notranslate"><span class="pre">ImportError</span></code> for incompatible extension modules will be
updated to clearly say it is due to missing multiple interpreters
compatibility and that extensions are not required to provide it. This
will help set user expectations properly.</p>
</section>
<section id="alternative-solutions">
<h2><a class="toc-backref" href="#alternative-solutions" role="doc-backlink">Alternative Solutions</a></h2>
<p>One possible alternative to a new module is to add support for interpreters
to <code class="docutils literal notranslate"><span class="pre">concurrent.futures</span></code>. There are several reasons why that wouldnt work:</p>
<ul class="simple">
<li>the obvious place to look for multiple interpreters support
is an “interpreters” module, much as with “threading”, etc.</li>
<li><code class="docutils literal notranslate"><span class="pre">concurrent.futures</span></code> is all about executing functions
but currently we dont have a good way to run a function
from one interpreter in another</li>
</ul>
<p>Similar reasoning applies for support in the <code class="docutils literal notranslate"><span class="pre">multiprocessing</span></code> module.</p>
</section>
<section id="open-questions">
<h2><a class="toc-backref" href="#open-questions" role="doc-backlink">Open Questions</a></h2>
<ul class="simple">
<li>will is be too confusing that <code class="docutils literal notranslate"><span class="pre">interp.exec()</span></code> runs in the current thread?</li>
<li>should we add pickling fallbacks right now for <code class="docutils literal notranslate"><span class="pre">interp.exec()</span></code>, and/or
<code class="docutils literal notranslate"><span class="pre">Interpreter.set_main_attrs()</span></code> and <code class="docutils literal notranslate"><span class="pre">Interpreter.get_main_attr()</span></code>?</li>
<li>should we support (limited) functions in <code class="docutils literal notranslate"><span class="pre">interp.exec()</span></code> right now?</li>
<li>rename <code class="docutils literal notranslate"><span class="pre">Interpreter.close()</span></code> to <code class="docutils literal notranslate"><span class="pre">Interpreter.destroy()</span></code>?</li>
<li>drop <code class="docutils literal notranslate"><span class="pre">Interpreter.get_main_attr()</span></code>, since we have channels?</li>
<li>should channels be its own PEP?</li>
</ul>
</section>
<section id="deferred-functionality">
<h2><a class="toc-backref" href="#deferred-functionality" role="doc-backlink">Deferred Functionality</a></h2>
<p>In the interest of keeping this proposal minimal, the following
functionality has been left out for future consideration. Note that
this is not a judgement against any of said capability, but rather a
deferment. That said, each is arguably valid.</p>
<section id="add-convenience-api">
<h3><a class="toc-backref" href="#add-convenience-api" role="doc-backlink">Add convenience API</a></h3>
<p>There are a number of things I can imagine would smooth out
<em>hypothetical</em> rough edges with the new module:</p>
<ul class="simple">
<li>add something like <code class="docutils literal notranslate"><span class="pre">Interpreter.run()</span></code> or <code class="docutils literal notranslate"><span class="pre">Interpreter.call()</span></code>
that calls <code class="docutils literal notranslate"><span class="pre">interp.exec()</span></code> and falls back to pickle</li>
<li>fall back to pickle in <code class="docutils literal notranslate"><span class="pre">Interpreter.set_main_attrs()</span></code>
and <code class="docutils literal notranslate"><span class="pre">Interpreter.get_main_attr()</span></code></li>
</ul>
<p>These would be easy to do if this proves to be a pain point.</p>
</section>
<section id="avoid-possible-confusion-about-interpreters-running-in-the-current-thread">
<h3><a class="toc-backref" href="#avoid-possible-confusion-about-interpreters-running-in-the-current-thread" role="doc-backlink">Avoid possible confusion about interpreters running in the current thread</a></h3>
<p>One regular point of confusion has been that <code class="docutils literal notranslate"><span class="pre">Interpreter.exec()</span></code>
executes in the current OS thread, temporarily blocking the current
Python thread. It may be worth doing something to avoid that confusion.</p>
<p>Some possible solutions for this hypothetical problem:</p>
<ul class="simple">
<li>by default, run in a new thread?</li>
<li>add <code class="docutils literal notranslate"><span class="pre">Interpreter.exec_in_thread()</span></code>?</li>
<li>add <code class="docutils literal notranslate"><span class="pre">Interpreter.exec_in_current_thread()</span></code>?</li>
</ul>
<p>In earlier versions of this PEP the method was <code class="docutils literal notranslate"><span class="pre">interp.run()</span></code>.
The simple change to <code class="docutils literal notranslate"><span class="pre">interp.exec()</span></code> alone will probably reduce
confusion sufficiently, when coupled with educating users via
the docs. It it turns out to be a real problem, we can pursue
one of the alternatives at that point.</p>
</section>
<section id="clarify-running-vs-has-threads">
<h3><a class="toc-backref" href="#clarify-running-vs-has-threads" role="doc-backlink">Clarify “running” vs. “has threads”</a></h3>
<p><code class="docutils literal notranslate"><span class="pre">Interpreter.is_running()</span></code> refers specifically to whether or not
<code class="docutils literal notranslate"><span class="pre">Interpreter.exec()</span></code> (or similar) is running somewhere. It does not
say anything about if the interpreter has any subthreads running. That
information might be helpful.</p>
<p>Some things we could do:</p>
<ul class="simple">
<li>rename <code class="docutils literal notranslate"><span class="pre">Interpreter.is_running()</span></code> to <code class="docutils literal notranslate"><span class="pre">Interpreter.is_running_main()</span></code></li>
<li>add <code class="docutils literal notranslate"><span class="pre">Interpreter.has_threads()</span></code>, to complement <code class="docutils literal notranslate"><span class="pre">Interpreter.is_running()</span></code></li>
<li>expand to <code class="docutils literal notranslate"><span class="pre">Interpreter.is_running(main=True,</span> <span class="pre">threads=False)</span></code></li>
</ul>
<p>None of these are urgent and any could be done later, if desired.</p>
</section>
<section id="a-dunder-method-for-sharing">
<h3><a class="toc-backref" href="#a-dunder-method-for-sharing" role="doc-backlink">A Dunder Method For Sharing</a></h3>
<p>We could add a special method, like <code class="docutils literal notranslate"><span class="pre">__xid__</span></code> to correspond to <code class="docutils literal notranslate"><span class="pre">tp_xid</span></code>.
At the very least, it would allow Python types to convert their instances
to some other type that implements <code class="docutils literal notranslate"><span class="pre">tp_xid</span></code>.</p>
<p>The problem is that exposing this capability to Python code presents
a degree of complixity that hasnt been explored yet, nor is there
a compelling case to investigate that complexity.</p>
</section>
<section id="interpreter-call">
<h3><a class="toc-backref" href="#interpreter-call" role="doc-backlink">Interpreter.call()</a></h3>
<p>It would be convenient to run existing functions in subinterpreters
directly. <code class="docutils literal notranslate"><span class="pre">Interpreter.exec()</span></code> could be adjusted to support this or
a <code class="docutils literal notranslate"><span class="pre">call()</span></code> method could be added:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">Interpreter</span><span class="o">.</span><span class="n">call</span><span class="p">(</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">kwargs</span><span class="p">)</span>
</pre></div>
</div>
<p>This suffers from the same problem as sharing objects between
interpreters via queues. The minimal solution (running a source string)
is sufficient for us to get the feature out where it can be explored.</p>
</section>
<section id="interpreter-run-in-thread">
<h3><a class="toc-backref" href="#interpreter-run-in-thread" role="doc-backlink">Interpreter.run_in_thread()</a></h3>
<p>This method would make a <code class="docutils literal notranslate"><span class="pre">interp.exec()</span></code> call for you in a thread.
Doing this using only <code class="docutils literal notranslate"><span class="pre">threading.Thread</span></code> and <code class="docutils literal notranslate"><span class="pre">interp.exec()</span></code> is
relatively trivial so weve left it out.</p>
</section>
<section id="synchronization-primitives">
<h3><a class="toc-backref" href="#synchronization-primitives" role="doc-backlink">Synchronization Primitives</a></h3>
<p>The <code class="docutils literal notranslate"><span class="pre">threading</span></code> module provides a number of synchronization primitives
for coordinating concurrent operations. This is especially necessary
due to the shared-state nature of threading. In contrast,
interpreters do not share state. Data sharing is restricted to the
runtimes shareable objects capability, which does away with the need
for explicit synchronization. If any sort of opt-in shared state
support is added to CPythons interpreters in the future, that same
effort can introduce synchronization primitives to meet that need.</p>
</section>
<section id="csp-library">
<h3><a class="toc-backref" href="#csp-library" role="doc-backlink">CSP Library</a></h3>
<p>A <code class="docutils literal notranslate"><span class="pre">csp</span></code> module would not be a large step away from the functionality
provided by this PEP. However, adding such a module is outside the
minimalist goals of this proposal.</p>
</section>
<section id="syntactic-support">
<h3><a class="toc-backref" href="#syntactic-support" role="doc-backlink">Syntactic Support</a></h3>
<p>The <code class="docutils literal notranslate"><span class="pre">Go</span></code> language provides a concurrency model based on CSP,
so its similar to the concurrency model that multiple interpreters
support. However, <code class="docutils literal notranslate"><span class="pre">Go</span></code> also provides syntactic support, as well as
several builtin concurrency primitives, to make concurrency a
first-class feature. Conceivably, similar syntactic (and builtin)
support could be added to Python using interpreters. However,
that is <em>way</em> outside the scope of this PEP!</p>
</section>
<section id="multiprocessing">
<h3><a class="toc-backref" href="#multiprocessing" role="doc-backlink">Multiprocessing</a></h3>
<p>The <code class="docutils literal notranslate"><span class="pre">multiprocessing</span></code> module could support interpreters in the same
way it supports threads and processes. In fact, the modules
maintainer, Davin Potts, has indicated this is a reasonable feature
request. However, it is outside the narrow scope of this PEP.</p>
</section>
<section id="c-extension-opt-in-opt-out">
<h3><a class="toc-backref" href="#c-extension-opt-in-opt-out" role="doc-backlink">C-extension opt-in/opt-out</a></h3>
<p>By using the <code class="docutils literal notranslate"><span class="pre">PyModuleDef_Slot</span></code> introduced by <a class="pep reference internal" href="../pep-0489/" title="PEP 489 Multi-phase extension module initialization">PEP 489</a>, we could
easily add a mechanism by which C-extension modules could opt out of
multiple interpreter support. Then the import machinery, when operating
in a subinterpreter, would need to check the module for support.
It would raise an ImportError if unsupported.</p>
<p>Alternately we could support opting in to multiple interpreters support.
However, that would probably exclude many more modules (unnecessarily)
than the opt-out approach. Also, note that <a class="pep reference internal" href="../pep-0489/" title="PEP 489 Multi-phase extension module initialization">PEP 489</a> defined that an
extensions use of the PEPs machinery implies multiple interpreters
support.</p>
<p>The scope of adding the ModuleDef slot and fixing up the import
machinery is non-trivial, but could be worth it. It all depends on
how many extension modules break under subinterpreters. Given that
there are relatively few cases we know of through mod_wsgi, we can
leave this for later.</p>
</section>
<section id="poisoning-channels">
<h3><a class="toc-backref" href="#poisoning-channels" role="doc-backlink">Poisoning channels</a></h3>
<p>CSP has the concept of poisoning a channel. Once a channel has been
poisoned, any <code class="docutils literal notranslate"><span class="pre">send()</span></code> or <code class="docutils literal notranslate"><span class="pre">recv()</span></code> call on it would raise a special
exception, effectively ending execution in the interpreter that tried
to use the poisoned channel.</p>
<p>This could be accomplished by adding a <code class="docutils literal notranslate"><span class="pre">poison()</span></code> method to both ends
of the channel. The <code class="docutils literal notranslate"><span class="pre">close()</span></code> method can be used in this way
(mostly), but these semantics are relatively specialized and can wait.</p>
</section>
<section id="resetting-main">
<h3><a class="toc-backref" href="#resetting-main" role="doc-backlink">Resetting __main__</a></h3>
<p>As proposed, every call to <code class="docutils literal notranslate"><span class="pre">Interpreter.exec()</span></code> will execute in the
namespace of the interpreters existing <code class="docutils literal notranslate"><span class="pre">__main__</span></code> module. This means
that data persists there between <code class="docutils literal notranslate"><span class="pre">interp.exec()</span></code> calls. Sometimes
this isnt desirable and you want to execute in a fresh <code class="docutils literal notranslate"><span class="pre">__main__</span></code>.
Also, you dont necessarily want to leak objects there that you arent
using any more.</p>
<p>Note that the following wont work right because it will clear too much
(e.g. <code class="docutils literal notranslate"><span class="pre">__name__</span></code> and the other “__dunder__” attributes:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">interp</span><span class="o">.</span><span class="n">exec</span><span class="p">(</span><span class="s1">&#39;globals().clear()&#39;</span><span class="p">)</span>
</pre></div>
</div>
<p>Possible solutions include:</p>
<ul class="simple">
<li>a <code class="docutils literal notranslate"><span class="pre">create()</span></code> arg to indicate resetting <code class="docutils literal notranslate"><span class="pre">__main__</span></code> after each
<code class="docutils literal notranslate"><span class="pre">interp.exec()</span></code> call</li>
<li>an <code class="docutils literal notranslate"><span class="pre">Interpreter.reset_main</span></code> flag to support opting in or out
after the fact</li>
<li>an <code class="docutils literal notranslate"><span class="pre">Interpreter.reset_main()</span></code> method to opt in when desired</li>
<li><code class="docutils literal notranslate"><span class="pre">importlib.util.reset_globals()</span></code> <a class="reference internal" href="#reset-globals" id="id11"><span>[reset_globals]</span></a></li>
</ul>
<p>Also note that resetting <code class="docutils literal notranslate"><span class="pre">__main__</span></code> does nothing about state stored
in other modules. So any solution would have to be clear about the
scope of what is being reset. Conceivably we could invent a mechanism
by which any (or every) module could be reset, unlike <code class="docutils literal notranslate"><span class="pre">reload()</span></code>
which does not clear the module before loading into it.</p>
<p>Regardless, since <code class="docutils literal notranslate"><span class="pre">__main__</span></code> is the execution namespace of the
interpreter, resetting it has a much more direct correlation to
interpreters and their dynamic state than does resetting other modules.
So a more generic module reset mechanism may prove unnecessary.</p>
<p>This isnt a critical feature initially. It can wait until later
if desirable.</p>
</section>
<section id="resetting-an-interpreter-s-state">
<h3><a class="toc-backref" href="#resetting-an-interpreter-s-state" role="doc-backlink">Resetting an interpreters state</a></h3>
<p>It may be nice to re-use an existing subinterpreter instead of
spinning up a new one. Since an interpreter has substantially more
state than just the <code class="docutils literal notranslate"><span class="pre">__main__</span></code> module, it isnt so easy to put an
interpreter back into a pristine/fresh state. In fact, there <em>may</em>
be parts of the state that cannot be reset from Python code.</p>
<p>A possible solution is to add an <code class="docutils literal notranslate"><span class="pre">Interpreter.reset()</span></code> method. This
would put the interpreter back into the state it was in when newly
created. If called on a running interpreter it would fail (hence the
main interpreter could never be reset). This would likely be more
efficient than creating a new interpreter, though that depends on
what optimizations will be made later to interpreter creation.</p>
<p>While this would potentially provide functionality that is not
otherwise available from Python code, it isnt a fundamental
functionality. So in the spirit of minimalism here, this can wait.
Regardless, I doubt it would be controversial to add it post-PEP.</p>
</section>
<section id="copy-an-existing-interpreter-s-state">
<h3><a class="toc-backref" href="#copy-an-existing-interpreter-s-state" role="doc-backlink">Copy an existing interpreters state</a></h3>
<p>Relatedly, it may be useful to support creating a new interpreter
based on an existing one, e.g. <code class="docutils literal notranslate"><span class="pre">Interpreter.copy()</span></code>. This ties
into the idea that a snapshot could be made of an interpreters memory,
which would make starting up CPython, or creating new interpreters,
faster in general. The same mechanism could be used for a
hypothetical <code class="docutils literal notranslate"><span class="pre">Interpreter.reset()</span></code>, as described previously.</p>
</section>
<section id="shareable-file-descriptors-and-sockets">
<h3><a class="toc-backref" href="#shareable-file-descriptors-and-sockets" role="doc-backlink">Shareable file descriptors and sockets</a></h3>
<p>Given that file descriptors and sockets are process-global resources,
making them shareable is a reasonable idea. They would be a good
candidate for the first effort at expanding the supported shareable
types. They arent strictly necessary for the initial API.</p>
</section>
<section id="integration-with-async">
<h3><a class="toc-backref" href="#integration-with-async" role="doc-backlink">Integration with async</a></h3>
<p>Per Antoine Pitrou <a class="reference internal" href="#async" id="id12"><span>[async]</span></a>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>Has any thought been given to how FIFOs could integrate with async
code driven by an event loop (e.g. asyncio)? I think the model of
executing several asyncio (or Tornado) applications each in their
own subinterpreter may prove quite interesting to reconcile multi-
core concurrency with ease of programming. That would require the
FIFOs to be able to synchronize on something an event loop can wait
on (probably a file descriptor?).
</pre></div>
</div>
<p>The basic functionality of multiple interpreters support does not depend
on async and can be added later.</p>
<p>A possible solution is to provide async implementations of the blocking
channel methods (<code class="docutils literal notranslate"><span class="pre">recv()</span></code>, and <code class="docutils literal notranslate"><span class="pre">send()</span></code>).</p>
<p>Alternately, “readiness callbacks” could be used to simplify use in
async scenarios. This would mean adding an optional <code class="docutils literal notranslate"><span class="pre">callback</span></code>
(kw-only) parameter to the <code class="docutils literal notranslate"><span class="pre">recv_nowait()</span></code> and <code class="docutils literal notranslate"><span class="pre">send_nowait()</span></code>
channel methods. The callback would be called once the object was sent
or received (respectively).</p>
<p>(Note that making channels buffered makes readiness callbacks less
important.)</p>
</section>
<section id="support-for-iteration">
<h3><a class="toc-backref" href="#support-for-iteration" role="doc-backlink">Support for iteration</a></h3>
<p>Supporting iteration on <code class="docutils literal notranslate"><span class="pre">RecvChannel</span></code> (via <code class="docutils literal notranslate"><span class="pre">__iter__()</span></code> or
<code class="docutils literal notranslate"><span class="pre">_next__()</span></code>) may be useful. A trivial implementation would use the
<code class="docutils literal notranslate"><span class="pre">recv()</span></code> method, similar to how files do iteration. Since this isnt
a fundamental capability and has a simple analog, adding iteration
support can wait until later.</p>
</section>
<section id="channel-context-managers">
<h3><a class="toc-backref" href="#channel-context-managers" role="doc-backlink">Channel context managers</a></h3>
<p>Context manager support on <code class="docutils literal notranslate"><span class="pre">RecvChannel</span></code> and <code class="docutils literal notranslate"><span class="pre">SendChannel</span></code> may be
helpful. The implementation would be simple, wrapping a call to
<code class="docutils literal notranslate"><span class="pre">close()</span></code> (or maybe <code class="docutils literal notranslate"><span class="pre">release()</span></code>) like files do. As with iteration,
this can wait.</p>
</section>
<section id="pipes-and-queues">
<h3><a class="toc-backref" href="#pipes-and-queues" role="doc-backlink">Pipes and Queues</a></h3>
<p>With the proposed object passing mechanism of “os.pipe()”, other similar
basic types arent strictly required to achieve the minimal useful
functionality of multiple interpreters. Such types include pipes
(like unbuffered channels, but one-to-one) and queues (like channels,
but more generic). See below in <a class="reference internal" href="#rejected-ideas">Rejected Ideas</a> for more information.</p>
<p>Even though these types arent part of this proposal, they may still
be useful in the context of concurrency. Adding them later is entirely
reasonable. The could be trivially implemented as wrappers around
channels. Alternatively they could be implemented for efficiency at the
same low level as channels.</p>
</section>
<section id="return-a-lock-from-send">
<h3><a class="toc-backref" href="#return-a-lock-from-send" role="doc-backlink">Return a lock from send()</a></h3>
<p>When sending an object through a channel, you dont have a way of knowing
when the object gets received on the other end. One way to work around
this is to return a locked <code class="docutils literal notranslate"><span class="pre">threading.Lock</span></code> from <code class="docutils literal notranslate"><span class="pre">SendChannel.send()</span></code>
that unlocks once the object is received.</p>
<p>Alternately, the proposed <code class="docutils literal notranslate"><span class="pre">SendChannel.send()</span></code> (blocking) and
<code class="docutils literal notranslate"><span class="pre">SendChannel.send_nowait()</span></code> provide an explicit distinction that is
less likely to confuse users.</p>
<p>Note that returning a lock would matter for buffered channels
(i.e. queues). For unbuffered channels it is a non-issue.</p>
</section>
<section id="support-prioritization-in-channels">
<h3><a class="toc-backref" href="#support-prioritization-in-channels" role="doc-backlink">Support prioritization in channels</a></h3>
<p>A simple example is <code class="docutils literal notranslate"><span class="pre">queue.PriorityQueue</span></code> in the stdlib.</p>
</section>
<section id="support-inheriting-settings-and-more">
<h3><a class="toc-backref" href="#support-inheriting-settings-and-more" role="doc-backlink">Support inheriting settings (and more?)</a></h3>
<p>Folks might find it useful, when creating a new interpreter, to be
able to indicate that they would like some things “inherited” by the
new interpreter. The mechanism could be a strict copy or it could be
copy-on-write. The motivating example is with the warnings module
(e.g. copy the filters).</p>
<p>The feature isnt critical, nor would it be widely useful, so it
can wait until theres interest. Notably, both suggested solutions
will require significant work, especially when it comes to complex
objects and most especially for mutable containers of mutable
complex objects.</p>
</section>
<section id="make-exceptions-shareable">
<h3><a class="toc-backref" href="#make-exceptions-shareable" role="doc-backlink">Make exceptions shareable</a></h3>
<p>Exceptions are propagated out of <code class="docutils literal notranslate"><span class="pre">run()</span></code> calls, so it isnt a big
leap to make them shareable. However, as noted elsewhere,
it isnt essential or (particularly common) so we can wait on doing
that.</p>
</section>
<section id="make-everything-shareable-through-serialization">
<h3><a class="toc-backref" href="#make-everything-shareable-through-serialization" role="doc-backlink">Make everything shareable through serialization</a></h3>
<p>We could use pickle (or marshal) to serialize everything and thus
make them shareable. Doing this is potentially inefficient,
but it may be a matter of convenience in the end.
We can add it later, but trying to remove it later
would be significantly more painful.</p>
</section>
<section id="make-runfailederror-cause-lazy">
<h3><a class="toc-backref" href="#make-runfailederror-cause-lazy" role="doc-backlink">Make RunFailedError.__cause__ lazy</a></h3>
<p>An uncaught exception in a subinterpreter (from <code class="docutils literal notranslate"><span class="pre">interp.exec()</span></code>) is
copied to the calling interpreter and set as <code class="docutils literal notranslate"><span class="pre">__cause__</span></code> on a
<code class="docutils literal notranslate"><span class="pre">RunFailedError</span></code> which is then raised. That copying part involves
some sort of deserialization in the calling interpreter, which can be
expensive (e.g. due to imports) yet is not always necessary.</p>
<p>So it may be useful to use an <code class="docutils literal notranslate"><span class="pre">ExceptionProxy</span></code> type to wrap the
serialized exception and only deserialize it when needed. That could
be via <code class="docutils literal notranslate"><span class="pre">ExceptionProxy__getattribute__()</span></code> or perhaps through
<code class="docutils literal notranslate"><span class="pre">RunFailedError.resolve()</span></code> (which would raise the deserialized
exception and set <code class="docutils literal notranslate"><span class="pre">RunFailedError.__cause__</span></code> to the exception.</p>
<p>It may also make sense to have <code class="docutils literal notranslate"><span class="pre">RunFailedError.__cause__</span></code> be a
descriptor that does the lazy deserialization (and set <code class="docutils literal notranslate"><span class="pre">__cause__</span></code>)
on the <code class="docutils literal notranslate"><span class="pre">RunFailedError</span></code> instance.</p>
</section>
<section id="return-a-value-from-interp-exec">
<h3><a class="toc-backref" href="#return-a-value-from-interp-exec" role="doc-backlink">Return a value from <code class="docutils literal notranslate"><span class="pre">interp.exec()</span></code></a></h3>
<p>Currently <code class="docutils literal notranslate"><span class="pre">interp.exec()</span></code> always returns None. One idea is to return
the return value from whatever the subinterpreter ran. However, for now
it doesnt make sense. The only thing folks can run is a string of
code (i.e. a script). This is equivalent to <code class="docutils literal notranslate"><span class="pre">PyRun_StringFlags()</span></code>,
<code class="docutils literal notranslate"><span class="pre">exec()</span></code>, or a module body. None of those “return” anything. We can
revisit this once <code class="docutils literal notranslate"><span class="pre">interp.exec()</span></code> supports functions, etc.</p>
</section>
<section id="add-a-shareable-synchronization-primitive">
<h3><a class="toc-backref" href="#add-a-shareable-synchronization-primitive" role="doc-backlink">Add a shareable synchronization primitive</a></h3>
<p>This would be <code class="docutils literal notranslate"><span class="pre">_threading.Lock</span></code> (or something like it) where
interpreters would actually share the underlying mutex. The main
concern is that locks and isolated interpreters may not mix well
(as learned in Go).</p>
<p>We can add this later if it proves desirable without much trouble.</p>
</section>
<section id="propagate-systemexit-and-keyboardinterrupt-differently">
<h3><a class="toc-backref" href="#propagate-systemexit-and-keyboardinterrupt-differently" role="doc-backlink">Propagate SystemExit and KeyboardInterrupt Differently</a></h3>
<p>The exception types that inherit from <code class="docutils literal notranslate"><span class="pre">BaseException</span></code> (aside from
<code class="docutils literal notranslate"><span class="pre">Exception</span></code>) are usually treated specially. These types are:
<code class="docutils literal notranslate"><span class="pre">KeyboardInterrupt</span></code>, <code class="docutils literal notranslate"><span class="pre">SystemExit</span></code>, and <code class="docutils literal notranslate"><span class="pre">GeneratorExit</span></code>. It may
make sense to treat them specially when it comes to propagation from
<code class="docutils literal notranslate"><span class="pre">interp.exec()</span></code>. Here are some options:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="o">*</span> <span class="n">propagate</span> <span class="n">like</span> <span class="n">normal</span> <span class="n">via</span> <span class="n">RunFailedError</span>
<span class="o">*</span> <span class="n">do</span> <span class="ow">not</span> <span class="n">propagate</span> <span class="p">(</span><span class="n">handle</span> <span class="n">them</span> <span class="n">somehow</span> <span class="ow">in</span> <span class="n">the</span> <span class="n">subinterpreter</span><span class="p">)</span>
<span class="o">*</span> <span class="n">propagate</span> <span class="n">them</span> <span class="n">directly</span> <span class="p">(</span><span class="n">avoid</span> <span class="n">RunFailedError</span><span class="p">)</span>
<span class="o">*</span> <span class="n">propagate</span> <span class="n">them</span> <span class="n">directly</span> <span class="p">(</span><span class="nb">set</span> <span class="n">RunFailedError</span> <span class="k">as</span> <span class="n">__cause__</span><span class="p">)</span>
</pre></div>
</div>
<p>We arent going to worry about handling them differently. Threads
already ignore <code class="docutils literal notranslate"><span class="pre">SystemExit</span></code>, so for now we will follow that pattern.</p>
</section>
<section id="add-an-explicit-release-and-close-to-channel-end-classes">
<h3><a class="toc-backref" href="#add-an-explicit-release-and-close-to-channel-end-classes" role="doc-backlink">Add an explicit release() and close() to channel end classes</a></h3>
<p>It can be convenient to have an explicit way to close a channel against
further global use. Likewise it could be useful to have an explicit
way to release one of the channel ends relative to the current
interpreter. Among other reasons, such a mechanism is useful for
communicating overall state between interpreters without the extra
boilerplate that passing objects through a channel directly would
require.</p>
<p>The challenge is getting automatic release/close right without making
it hard to understand. This is especially true when dealing with a
non-empty channel. We should be able to get by without release/close
for now.</p>
</section>
<section id="add-sendchannel-send-buffer">
<h3><a class="toc-backref" href="#add-sendchannel-send-buffer" role="doc-backlink">Add SendChannel.send_buffer()</a></h3>
<p>This method would allow no-copy sending of an object through a channel
if it supports the <a class="pep reference internal" href="../pep-3118/" title="PEP 3118 Revising the buffer protocol">PEP 3118</a> buffer protocol (e.g. memoryview).</p>
<p>Support for this is not fundamental to channels and can be added on
later without much disruption.</p>
</section>
<section id="auto-run-in-a-thread">
<h3><a class="toc-backref" href="#auto-run-in-a-thread" role="doc-backlink">Auto-run in a thread</a></h3>
<p>The PEP proposes a hard separation between subinterpreters and threads:
if you want to run in a thread you must create the thread yourself and
call <code class="docutils literal notranslate"><span class="pre">interp.exec()</span></code> in it. However, it might be convenient if
<code class="docutils literal notranslate"><span class="pre">interp.exec()</span></code> could do that for you, meaning there would be less
boilerplate.</p>
<p>Furthermore, we anticipate that users will want to run in a thread much
more often than not. So it would make sense to make this the default
behavior. We would add a kw-only param “threaded” (default <code class="docutils literal notranslate"><span class="pre">True</span></code>)
to <code class="docutils literal notranslate"><span class="pre">interp.exec()</span></code> to allow the run-in-the-current-thread operation.</p>
</section>
</section>
<section id="rejected-ideas">
<h2><a class="toc-backref" href="#rejected-ideas" role="doc-backlink">Rejected Ideas</a></h2>
<section id="explicit-channel-association">
<h3><a class="toc-backref" href="#explicit-channel-association" role="doc-backlink">Explicit channel association</a></h3>
<p>Interpreters are implicitly associated with channels upon <code class="docutils literal notranslate"><span class="pre">recv()</span></code> and
<code class="docutils literal notranslate"><span class="pre">send()</span></code> calls. They are de-associated with <code class="docutils literal notranslate"><span class="pre">release()</span></code> calls. The
alternative would be explicit methods. It would be either
<code class="docutils literal notranslate"><span class="pre">add_channel()</span></code> and <code class="docutils literal notranslate"><span class="pre">remove_channel()</span></code> methods on <code class="docutils literal notranslate"><span class="pre">Interpreter</span></code>
objects or something similar on channel objects.</p>
<p>In practice, this level of management shouldnt be necessary for users.
So adding more explicit support would only add clutter to the API.</p>
</section>
<section id="add-an-api-based-on-pipes">
<h3><a class="toc-backref" href="#add-an-api-based-on-pipes" role="doc-backlink">Add an API based on pipes</a></h3>
<p>A pipe would be a simplex FIFO between exactly two interpreters. For
most use cases this would be sufficient. It could potentially simplify
the implementation as well. However, it isnt a big step to supporting
a many-to-many simplex FIFO via channels. Also, with pipes the API
ends up being slightly more complicated, requiring naming the pipes.</p>
</section>
<section id="add-an-api-based-on-queues">
<h3><a class="toc-backref" href="#add-an-api-based-on-queues" role="doc-backlink">Add an API based on queues</a></h3>
<p>Queues and buffered channels are almost the same thing. The main
difference is that channels have a stronger relationship with context
(i.e. the associated interpreter).</p>
<p>The name “Channel” was used instead of “Queue” to avoid confusion with
the stdlib <code class="docutils literal notranslate"><span class="pre">queue.Queue</span></code>.</p>
</section>
<section id="enumerate">
<h3><a class="toc-backref" href="#enumerate" role="doc-backlink">“enumerate”</a></h3>
<p>The <code class="docutils literal notranslate"><span class="pre">list_all()</span></code> function provides the list of all interpreters.
In the threading module, which partly inspired the proposed API, the
function is called <code class="docutils literal notranslate"><span class="pre">enumerate()</span></code>. The name is different here to
avoid confusing Python users that are not already familiar with the
threading API. For them “enumerate” is rather unclear, whereas
“list_all” is clear.</p>
</section>
<section id="alternate-solutions-to-prevent-leaking-exceptions-across-interpreters">
<h3><a class="toc-backref" href="#alternate-solutions-to-prevent-leaking-exceptions-across-interpreters" role="doc-backlink">Alternate solutions to prevent leaking exceptions across interpreters</a></h3>
<p>In function calls, uncaught exceptions propagate to the calling frame.
The same approach could be taken with <code class="docutils literal notranslate"><span class="pre">interp.exec()</span></code>. However, this
would mean that exception objects would leak across the inter-interpreter
boundary. Likewise, the frames in the traceback would potentially leak.</p>
<p>While that might not be a problem currently, it would be a problem once
interpreters get better isolation relative to memory management (which
is necessary to stop sharing the GIL between interpreters). Weve
resolved the semantics of how the exceptions propagate by raising a
<code class="docutils literal notranslate"><span class="pre">RunFailedError</span></code> instead, for which <code class="docutils literal notranslate"><span class="pre">__cause__</span></code> wraps a safe proxy
for the original exception and traceback.</p>
<p>Rejected possible solutions:</p>
<ul class="simple">
<li>reproduce the exception and traceback in the original interpreter
and raise that.</li>
<li>raise a subclass of RunFailedError that proxies the original
exception and traceback.</li>
<li>raise RuntimeError instead of RunFailedError</li>
<li>convert at the boundary (a la <code class="docutils literal notranslate"><span class="pre">subprocess.CalledProcessError</span></code>)
(requires a cross-interpreter representation)</li>
<li>support customization via <code class="docutils literal notranslate"><span class="pre">Interpreter.excepthook</span></code>
(requires a cross-interpreter representation)</li>
<li>wrap in a proxy at the boundary (including with support for
something like <code class="docutils literal notranslate"><span class="pre">err.raise()</span></code> to propagate the traceback).</li>
<li>return the exception (or its proxy) from <code class="docutils literal notranslate"><span class="pre">interp.exec()</span></code> instead of
raising it</li>
<li>return a result object (like <code class="docutils literal notranslate"><span class="pre">subprocess</span></code> does) <a class="reference internal" href="#result-object" id="id13"><span>[result-object]</span></a>
(unnecessary complexity?)</li>
<li>throw the exception away and expect users to deal with unhandled
exceptions explicitly in the script they pass to <code class="docutils literal notranslate"><span class="pre">interp.exec()</span></code>
(they can pass error info out via channels);
with threads you have to do something similar</li>
</ul>
</section>
<section id="always-associate-each-new-interpreter-with-its-own-thread">
<h3><a class="toc-backref" href="#always-associate-each-new-interpreter-with-its-own-thread" role="doc-backlink">Always associate each new interpreter with its own thread</a></h3>
<p>As implemented in the C-API, an interpreter is not inherently tied to
any thread. Furthermore, it will run in any existing thread, whether
created by Python or not. You only have to activate one of its thread
states (<code class="docutils literal notranslate"><span class="pre">PyThreadState</span></code>) in the thread first. This means that the
same thread may run more than one interpreter (though obviously
not at the same time).</p>
<p>The proposed module maintains this behavior. Interpreters are not
tied to threads. Only calls to <code class="docutils literal notranslate"><span class="pre">Interpreter.exec()</span></code> are. However,
one of the key objectives of this PEP is to provide a more
human-centric concurrency model. With that in mind, from a conceptual
standpoint the module <em>might</em> be easier to understand if each
interpreter were associated with its own thread.</p>
<p>That would mean <code class="docutils literal notranslate"><span class="pre">interpreters.create()</span></code> would create a new thread
and <code class="docutils literal notranslate"><span class="pre">Interpreter.exec()</span></code> would only execute in that thread (and
nothing else would). The benefit is that users would not have to
wrap <code class="docutils literal notranslate"><span class="pre">Interpreter.exec()</span></code> calls in a new <code class="docutils literal notranslate"><span class="pre">threading.Thread</span></code>. Nor
would they be in a position to accidentally pause the current
interpreter (in the current thread) while their interpreter
executes.</p>
<p>The idea is rejected because the benefit is small and the cost is high.
The difference from the capability in the C-API would be potentially
confusing. The implicit creation of threads is magical. The early
creation of threads is potentially wasteful. The inability to run
arbitrary interpreters in an existing thread would prevent some valid
use cases, frustrating users. Tying interpreters to threads would
require extra runtime modifications. It would also make the modules
implementation overly complicated. Finally, it might not even make
the module easier to understand.</p>
</section>
<section id="only-associate-interpreters-upon-use">
<h3><a class="toc-backref" href="#only-associate-interpreters-upon-use" role="doc-backlink">Only associate interpreters upon use</a></h3>
<p>Associate interpreters with channel ends only once <code class="docutils literal notranslate"><span class="pre">recv()</span></code>,
<code class="docutils literal notranslate"><span class="pre">send()</span></code>, etc. are called.</p>
<p>Doing this is potentially confusing and also can lead to unexpected
races where a channel is auto-closed before it can be used in the
original (creating) interpreter.</p>
</section>
<section id="allow-multiple-simultaneous-calls-to-interpreter-exec">
<h3><a class="toc-backref" href="#allow-multiple-simultaneous-calls-to-interpreter-exec" role="doc-backlink">Allow multiple simultaneous calls to Interpreter.exec()</a></h3>
<p>This would make sense especially if <code class="docutils literal notranslate"><span class="pre">Interpreter.exec()</span></code> were to
manage new threads for you (which weve rejected). Essentially,
each call would run independently, which would be mostly fine
from a narrow technical standpoint, since each interpreter
can have multiple threads.</p>
<p>The problem is that the interpreter has only one <code class="docutils literal notranslate"><span class="pre">__main__</span></code> module
and simultaneous <code class="docutils literal notranslate"><span class="pre">Interpreter.exec()</span></code> calls would have to sort out
sharing <code class="docutils literal notranslate"><span class="pre">__main__</span></code> or wed have to invent a new mechanism. Neither
would be simple enough to be worth doing.</p>
</section>
<section id="add-a-reraise-method-to-runfailederror">
<h3><a class="toc-backref" href="#add-a-reraise-method-to-runfailederror" role="doc-backlink">Add a “reraise” method to RunFailedError</a></h3>
<p>While having <code class="docutils literal notranslate"><span class="pre">__cause__</span></code> set on <code class="docutils literal notranslate"><span class="pre">RunFailedError</span></code> helps produce a
more useful traceback, its less helpful when handling the original
error. To help facilitate this, we could add
<code class="docutils literal notranslate"><span class="pre">RunFailedError.reraise()</span></code>. This method would enable the following
pattern:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">try</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">interp</span><span class="o">.</span><span class="n">exec</span><span class="p">(</span><span class="n">script</span><span class="p">)</span>
<span class="k">except</span> <span class="n">RunFailedError</span> <span class="k">as</span> <span class="n">exc</span><span class="p">:</span>
<span class="n">exc</span><span class="o">.</span><span class="n">reraise</span><span class="p">()</span>
<span class="k">except</span> <span class="n">MyException</span><span class="p">:</span>
<span class="o">...</span>
</pre></div>
</div>
<p>This would be made even simpler if there existed a <code class="docutils literal notranslate"><span class="pre">__reraise__</span></code>
protocol.</p>
<p>All that said, this is completely unnecessary. Using <code class="docutils literal notranslate"><span class="pre">__cause__</span></code>
is good enough:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">try</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">interp</span><span class="o">.</span><span class="n">exec</span><span class="p">(</span><span class="n">script</span><span class="p">)</span>
<span class="k">except</span> <span class="n">RunFailedError</span> <span class="k">as</span> <span class="n">exc</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">exc</span><span class="o">.</span><span class="n">__cause__</span>
<span class="k">except</span> <span class="n">MyException</span><span class="p">:</span>
<span class="o">...</span>
</pre></div>
</div>
<p>Note that in extreme cases it may require a little extra boilerplate:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">try</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">interp</span><span class="o">.</span><span class="n">exec</span><span class="p">(</span><span class="n">script</span><span class="p">)</span>
<span class="k">except</span> <span class="n">RunFailedError</span> <span class="k">as</span> <span class="n">exc</span><span class="p">:</span>
<span class="k">if</span> <span class="n">exc</span><span class="o">.</span><span class="n">__cause__</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">exc</span><span class="o">.</span><span class="n">__cause__</span>
<span class="k">raise</span> <span class="c1"># re-raise</span>
<span class="k">except</span> <span class="n">MyException</span><span class="p">:</span>
<span class="o">...</span>
</pre></div>
</div>
</section>
</section>
<section id="implementation">
<h2><a class="toc-backref" href="#implementation" role="doc-backlink">Implementation</a></h2>
<p>The implementation of the PEP has 4 parts:</p>
<ul class="simple">
<li>the high-level module described in this PEP (mostly a light wrapper
around a low-level C extension</li>
<li>the low-level C extension module</li>
<li>additions to the internal C-API needed by the low-level module</li>
<li>secondary fixes/changes in the CPython runtime that facilitate
the low-level module (among other benefits)</li>
</ul>
<p>These are at various levels of completion, with more done the lower
you go:</p>
<ul class="simple">
<li>the high-level module has been, at best, roughly implemented.
However, fully implementing it will be almost trivial.</li>
<li>the low-level module is mostly complete. The bulk of the
implementation was merged into master in December 2018 as the
“_xxsubinterpreters” module (for the sake of testing multiple
interpreters functionality). Only the exception propagation
implementation remains to be finished, which will not require
extensive work.</li>
<li>all necessary C-API work has been finished</li>
<li>all anticipated work in the runtime has been finished</li>
</ul>
<p>The implementation effort for <a class="pep reference internal" href="../pep-0554/" title="PEP 554 Multiple Interpreters in the Stdlib">PEP 554</a> is being tracked as part of
a larger project aimed at improving multi-core support in CPython.
<a class="reference internal" href="#multi-core-project" id="id14"><span>[multi-core-project]</span></a></p>
</section>
<section id="references">
<h2><a class="toc-backref" href="#references" role="doc-backlink">References</a></h2>
<div role="list" class="citation-list">
<div class="citation" id="c-api" role="doc-biblioentry">
<dt class="label" id="c-api">[<a href="#id1">c-api</a>]</dt>
<dd><a class="reference external" href="https://docs.python.org/3/c-api/init.html#sub-interpreter-support">https://docs.python.org/3/c-api/init.html#sub-interpreter-support</a></div>
<div class="citation" id="csp" role="doc-biblioentry">
<dt class="label" id="csp">[<a href="#id5">CSP</a>]</dt>
<dd><a class="reference external" href="https://en.wikipedia.org/wiki/Communicating_sequential_processes">https://en.wikipedia.org/wiki/Communicating_sequential_processes</a>
<a class="reference external" href="https://github.com/futurecore/python-csp">https://github.com/futurecore/python-csp</a></div>
<div class="citation" id="fifo" role="doc-biblioentry">
<dt class="label" id="fifo">[<a href="#id10">fifo</a>]</dt>
<dd><a class="reference external" href="https://docs.python.org/3/library/multiprocessing.html#multiprocessing.Pipe">https://docs.python.org/3/library/multiprocessing.html#multiprocessing.Pipe</a>
<a class="reference external" href="https://docs.python.org/3/library/multiprocessing.html#multiprocessing.Queue">https://docs.python.org/3/library/multiprocessing.html#multiprocessing.Queue</a>
<a class="reference external" href="https://docs.python.org/3/library/queue.html#module-queue">https://docs.python.org/3/library/queue.html#module-queue</a>
<a class="reference external" href="http://stackless.readthedocs.io/en/2.7-slp/library/stackless/channels.html">http://stackless.readthedocs.io/en/2.7-slp/library/stackless/channels.html</a>
<a class="reference external" href="https://golang.org/doc/effective_go.html#sharing">https://golang.org/doc/effective_go.html#sharing</a>
<a class="reference external" href="http://www.jtolds.com/writing/2016/03/go-channels-are-bad-and-you-should-feel-bad/">http://www.jtolds.com/writing/2016/03/go-channels-are-bad-and-you-should-feel-bad/</a></div>
<div class="citation" id="caveats" role="doc-biblioentry">
<dt class="label" id="caveats">[<a href="#id6">caveats</a>]</dt>
<dd><a class="reference external" href="https://docs.python.org/3/c-api/init.html#bugs-and-caveats">https://docs.python.org/3/c-api/init.html#bugs-and-caveats</a></div>
<div class="citation" id="cryptography" role="doc-biblioentry">
<dt class="label" id="cryptography">[<a href="#id7">cryptography</a>]</dt>
<dd><a class="reference external" href="https://github.com/pyca/cryptography/issues/2299">https://github.com/pyca/cryptography/issues/2299</a></div>
<div class="citation" id="gilstate" role="doc-biblioentry">
<dt class="label" id="gilstate">[<a href="#id8">gilstate</a>]</dt>
<dd><a class="reference external" href="https://bugs.python.org/issue10915">https://bugs.python.org/issue10915</a>
<a class="reference external" href="http://bugs.python.org/issue15751">http://bugs.python.org/issue15751</a></div>
<div class="citation" id="bug-rate" role="doc-biblioentry">
<dt class="label" id="bug-rate">[<a href="#id3">bug-rate</a>]</dt>
<dd><a class="reference external" href="https://mail.python.org/pipermail/python-ideas/2017-September/047094.html">https://mail.python.org/pipermail/python-ideas/2017-September/047094.html</a></div>
<div class="citation" id="benefits" role="doc-biblioentry">
<dt class="label" id="benefits">[<a href="#id2">benefits</a>]</dt>
<dd><a class="reference external" href="https://mail.python.org/pipermail/python-ideas/2017-September/047122.html">https://mail.python.org/pipermail/python-ideas/2017-September/047122.html</a></div>
<div class="citation" id="reset-globals" role="doc-biblioentry">
<dt class="label" id="reset-globals">[<a href="#id11">reset_globals</a>]</dt>
<dd><a class="reference external" href="https://mail.python.org/pipermail/python-dev/2017-September/149545.html">https://mail.python.org/pipermail/python-dev/2017-September/149545.html</a></div>
<div class="citation" id="async" role="doc-biblioentry">
<dt class="label" id="async">[<a href="#id12">async</a>]</dt>
<dd><a class="reference external" href="https://mail.python.org/pipermail/python-dev/2017-September/149420.html">https://mail.python.org/pipermail/python-dev/2017-September/149420.html</a>
<a class="reference external" href="https://mail.python.org/pipermail/python-dev/2017-September/149585.html">https://mail.python.org/pipermail/python-dev/2017-September/149585.html</a></div>
<div class="citation" id="result-object" role="doc-biblioentry">
<dt class="label" id="result-object">[<a href="#id13">result-object</a>]</dt>
<dd><a class="reference external" href="https://mail.python.org/pipermail/python-dev/2017-September/149562.html">https://mail.python.org/pipermail/python-dev/2017-September/149562.html</a></div>
<div class="citation" id="jython" role="doc-biblioentry">
<dt class="label" id="jython">[<a href="#id9">jython</a>]</dt>
<dd><a class="reference external" href="https://mail.python.org/pipermail/python-ideas/2017-May/045771.html">https://mail.python.org/pipermail/python-ideas/2017-May/045771.html</a></div>
<div class="citation" id="multi-core-project" role="doc-biblioentry">
<dt class="label" id="multi-core-project">[<a href="#id14">multi-core-project</a>]</dt>
<dd><a class="reference external" href="https://github.com/ericsnowcurrently/multi-core-python">https://github.com/ericsnowcurrently/multi-core-python</a></div>
<div class="citation" id="cache-line-ping-pong" role="doc-biblioentry">
<dt class="label" id="cache-line-ping-pong">[<a href="#id4">cache-line-ping-pong</a>]</dt>
<dd><a class="reference external" href="https://mail.python.org/archives/list/python-dev&#64;python.org/message/3HVRFWHDMWPNR367GXBILZ4JJAUQ2STZ/">https://mail.python.org/archives/list/python-dev&#64;python.org/message/3HVRFWHDMWPNR367GXBILZ4JJAUQ2STZ/</a></div>
</div>
<ul class="simple">
<li><dl class="simple">
<dt>mp-conn</dt><dd><a class="reference external" href="https://docs.python.org/3/library/multiprocessing.html#connection-objects">https://docs.python.org/3/library/multiprocessing.html#connection-objects</a></dd>
</dl>
</li>
<li><dl class="simple">
<dt>main-thread</dt><dd><a class="reference external" href="https://mail.python.org/pipermail/python-ideas/2017-September/047144.html">https://mail.python.org/pipermail/python-ideas/2017-September/047144.html</a>
<a class="reference external" href="https://mail.python.org/pipermail/python-dev/2017-September/149566.html">https://mail.python.org/pipermail/python-dev/2017-September/149566.html</a></dd>
</dl>
</li>
<li><dl class="simple">
<dt>petr-c-ext</dt><dd><a class="reference external" href="https://mail.python.org/pipermail/import-sig/2016-June/001062.html">https://mail.python.org/pipermail/import-sig/2016-June/001062.html</a>
<a class="reference external" href="https://mail.python.org/pipermail/python-ideas/2016-April/039748.html">https://mail.python.org/pipermail/python-ideas/2016-April/039748.html</a></dd>
</dl>
</li>
</ul>
</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-0554.rst">https://github.com/python/peps/blob/main/peps/pep-0554.rst</a></p>
<p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0554.rst">2023-11-28 02:32:35 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="#proposal">Proposal</a><ul>
<li><a class="reference internal" href="#the-interpreters-module">The “interpreters” Module</a></li>
<li><a class="reference internal" href="#api-summary-for-interpreters-module">API summary for interpreters module</a></li>
<li><a class="reference internal" href="#concurrent-futures-interpreterpoolexecutor">concurrent.futures.InterpreterPoolExecutor</a></li>
<li><a class="reference internal" href="#help-for-extension-module-maintainers">Help for Extension Module Maintainers</a></li>
</ul>
</li>
<li><a class="reference internal" href="#examples">Examples</a><ul>
<li><a class="reference internal" href="#run-isolated-code-in-current-os-thread">Run isolated code in current OS thread</a></li>
<li><a class="reference internal" href="#run-in-a-different-thread">Run in a different thread</a></li>
<li><a class="reference internal" href="#pre-populate-an-interpreter">Pre-populate an interpreter</a></li>
<li><a class="reference internal" href="#handling-an-exception">Handling an exception</a></li>
<li><a class="reference internal" href="#re-raising-an-exception">Re-raising an exception</a></li>
<li><a class="reference internal" href="#interact-with-the-main-namespace">Interact with the __main__ namespace</a></li>
<li><a class="reference internal" href="#synchronize-using-an-os-pipe">Synchronize using an OS pipe</a></li>
<li><a class="reference internal" href="#sharing-a-file-descriptor">Sharing a file descriptor</a></li>
<li><a class="reference internal" href="#passing-objects-via-pickle">Passing objects via pickle</a></li>
<li><a class="reference internal" href="#capturing-an-interpreter-s-stdout">Capturing an interpreters stdout</a></li>
<li><a class="reference internal" href="#running-a-module">Running a module</a></li>
<li><a class="reference internal" href="#running-as-script-including-zip-archives-directories">Running as script (including zip archives &amp; directories)</a></li>
<li><a class="reference internal" href="#using-a-channel-to-communicate">Using a channel to communicate</a></li>
<li><a class="reference internal" href="#sharing-a-memoryview-imagine-map-reduce">Sharing a memoryview (imagine map-reduce)</a></li>
</ul>
</li>
<li><a class="reference internal" href="#rationale">Rationale</a><ul>
<li><a class="reference internal" href="#concerns">Concerns</a></li>
</ul>
</li>
<li><a class="reference internal" href="#about-subinterpreters">About Subinterpreters</a><ul>
<li><a class="reference internal" href="#concurrency">Concurrency</a></li>
<li><a class="reference internal" href="#shared-data">Shared Data</a></li>
<li><a class="reference internal" href="#interpreter-isolation">Interpreter Isolation</a></li>
<li><a class="reference internal" href="#existing-usage">Existing Usage</a></li>
</ul>
</li>
<li><a class="reference internal" href="#alternate-python-implementations">Alternate Python Implementations</a></li>
<li><a class="reference internal" href="#interpreters-module-api">“interpreters” Module API</a><ul>
<li><a class="reference internal" href="#uncaught-exceptions">Uncaught Exceptions</a></li>
</ul>
</li>
<li><a class="reference internal" href="#interpreter-restrictions">Interpreter Restrictions</a></li>
<li><a class="reference internal" href="#api-for-communication">API For Communication</a><ul>
<li><a class="reference internal" href="#shareable-types">Shareable Types</a><ul>
<li><a class="reference internal" href="#communicating-through-os-pipes">Communicating Through OS Pipes</a></li>
</ul>
</li>
<li><a class="reference internal" href="#channels">Channels</a></li>
<li><a class="reference internal" href="#caveats-for-shared-objects">Caveats For Shared Objects</a></li>
</ul>
</li>
<li><a class="reference internal" href="#documentation">Documentation</a></li>
<li><a class="reference internal" href="#alternative-solutions">Alternative Solutions</a></li>
<li><a class="reference internal" href="#open-questions">Open Questions</a></li>
<li><a class="reference internal" href="#deferred-functionality">Deferred Functionality</a><ul>
<li><a class="reference internal" href="#add-convenience-api">Add convenience API</a></li>
<li><a class="reference internal" href="#avoid-possible-confusion-about-interpreters-running-in-the-current-thread">Avoid possible confusion about interpreters running in the current thread</a></li>
<li><a class="reference internal" href="#clarify-running-vs-has-threads">Clarify “running” vs. “has threads”</a></li>
<li><a class="reference internal" href="#a-dunder-method-for-sharing">A Dunder Method For Sharing</a></li>
<li><a class="reference internal" href="#interpreter-call">Interpreter.call()</a></li>
<li><a class="reference internal" href="#interpreter-run-in-thread">Interpreter.run_in_thread()</a></li>
<li><a class="reference internal" href="#synchronization-primitives">Synchronization Primitives</a></li>
<li><a class="reference internal" href="#csp-library">CSP Library</a></li>
<li><a class="reference internal" href="#syntactic-support">Syntactic Support</a></li>
<li><a class="reference internal" href="#multiprocessing">Multiprocessing</a></li>
<li><a class="reference internal" href="#c-extension-opt-in-opt-out">C-extension opt-in/opt-out</a></li>
<li><a class="reference internal" href="#poisoning-channels">Poisoning channels</a></li>
<li><a class="reference internal" href="#resetting-main">Resetting __main__</a></li>
<li><a class="reference internal" href="#resetting-an-interpreter-s-state">Resetting an interpreters state</a></li>
<li><a class="reference internal" href="#copy-an-existing-interpreter-s-state">Copy an existing interpreters state</a></li>
<li><a class="reference internal" href="#shareable-file-descriptors-and-sockets">Shareable file descriptors and sockets</a></li>
<li><a class="reference internal" href="#integration-with-async">Integration with async</a></li>
<li><a class="reference internal" href="#support-for-iteration">Support for iteration</a></li>
<li><a class="reference internal" href="#channel-context-managers">Channel context managers</a></li>
<li><a class="reference internal" href="#pipes-and-queues">Pipes and Queues</a></li>
<li><a class="reference internal" href="#return-a-lock-from-send">Return a lock from send()</a></li>
<li><a class="reference internal" href="#support-prioritization-in-channels">Support prioritization in channels</a></li>
<li><a class="reference internal" href="#support-inheriting-settings-and-more">Support inheriting settings (and more?)</a></li>
<li><a class="reference internal" href="#make-exceptions-shareable">Make exceptions shareable</a></li>
<li><a class="reference internal" href="#make-everything-shareable-through-serialization">Make everything shareable through serialization</a></li>
<li><a class="reference internal" href="#make-runfailederror-cause-lazy">Make RunFailedError.__cause__ lazy</a></li>
<li><a class="reference internal" href="#return-a-value-from-interp-exec">Return a value from <code class="docutils literal notranslate"><span class="pre">interp.exec()</span></code></a></li>
<li><a class="reference internal" href="#add-a-shareable-synchronization-primitive">Add a shareable synchronization primitive</a></li>
<li><a class="reference internal" href="#propagate-systemexit-and-keyboardinterrupt-differently">Propagate SystemExit and KeyboardInterrupt Differently</a></li>
<li><a class="reference internal" href="#add-an-explicit-release-and-close-to-channel-end-classes">Add an explicit release() and close() to channel end classes</a></li>
<li><a class="reference internal" href="#add-sendchannel-send-buffer">Add SendChannel.send_buffer()</a></li>
<li><a class="reference internal" href="#auto-run-in-a-thread">Auto-run in a thread</a></li>
</ul>
</li>
<li><a class="reference internal" href="#rejected-ideas">Rejected Ideas</a><ul>
<li><a class="reference internal" href="#explicit-channel-association">Explicit channel association</a></li>
<li><a class="reference internal" href="#add-an-api-based-on-pipes">Add an API based on pipes</a></li>
<li><a class="reference internal" href="#add-an-api-based-on-queues">Add an API based on queues</a></li>
<li><a class="reference internal" href="#enumerate">“enumerate”</a></li>
<li><a class="reference internal" href="#alternate-solutions-to-prevent-leaking-exceptions-across-interpreters">Alternate solutions to prevent leaking exceptions across interpreters</a></li>
<li><a class="reference internal" href="#always-associate-each-new-interpreter-with-its-own-thread">Always associate each new interpreter with its own thread</a></li>
<li><a class="reference internal" href="#only-associate-interpreters-upon-use">Only associate interpreters upon use</a></li>
<li><a class="reference internal" href="#allow-multiple-simultaneous-calls-to-interpreter-exec">Allow multiple simultaneous calls to Interpreter.exec()</a></li>
<li><a class="reference internal" href="#add-a-reraise-method-to-runfailederror">Add a “reraise” method to RunFailedError</a></li>
</ul>
</li>
<li><a class="reference internal" href="#implementation">Implementation</a></li>
<li><a class="reference internal" href="#references">References</a></li>
<li><a class="reference internal" href="#copyright">Copyright</a></li>
</ul>
<br>
<a id="source" href="https://github.com/python/peps/blob/main/peps/pep-0554.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>