Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

gh-134939: Add the interpreters Module #133958

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: main
Choose a base branch
Loading
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion 6 .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -281,9 +281,13 @@ Doc/howto/clinic.rst @erlend-aasland
# Subinterpreters
**/*interpreteridobject.* @ericsnowcurrently
**/*crossinterp* @ericsnowcurrently
Lib/test/support/interpreters/ @ericsnowcurrently
Lib/interpreters/ @ericsnowcurrently
Modules/_interp*module.c @ericsnowcurrently
Lib/test/test_interpreters/ @ericsnowcurrently
Lib/test/test__interp*.py @ericsnowcurrently
Lib/test/support/channels.py @ericsnowcurrently
Lib/concurrent/futures/interpreter.py @ericsnowcurrently
Doc/library/interpreters.rst @ericsnowcurrently

# Android
**/*Android* @mhsmith @freakboy3742
Expand Down
2 changes: 2 additions & 0 deletions 2 Doc/library/concurrency.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ multitasking). Here's an overview:
queue.rst
contextvars.rst

Also see the :mod:`interpreters` module.


The following are support modules for some of the above services:

Expand Down
198 changes: 198 additions & 0 deletions 198 Doc/library/interpreters.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
:mod:`!interpreters` --- Multiple Interpreters in the Same Process
==================================================================

.. module:: interpreters
:synopsis: Multiple Interpreters in the Same Process

.. moduleauthor:: Eric Snow <ericsnowcurrently@gmail.com>
.. sectionauthor:: Eric Snow <ericsnowcurrently@gmail.com>

.. versionadded:: 3.14
AA-Turner marked this conversation as resolved.
Show resolved Hide resolved

**Source code:** :source:`Lib/interpreters/__init__.py`

--------------


Introduction
------------

The :mod:`!interpreters` module constructs higher-level interfaces
on top of the lower level :mod:`!_interpreters` module.

.. XXX Add references to the upcoming HOWTO docs in the seealso block.

.. seealso::

:ref:`isolating-extensions-howto`
how to update an extension module to support multiple interpreters

:pep:`554`

:pep:`734`

:pep:`684`

.. XXX Why do we disallow multiple interpreters on WASM?

.. include:: ../includes/wasm-notavail.rst


Key Details
-----------

Before we dive into examples, there are a small number of details
to keep in mind about using multiple interpreters:

* isolated, by default
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This deserves more elaboration, or a link, given it is the first mention of isolation & in the 'key details' block.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a little explanation. We can elaborate in a follow-up PR if needed.

* no implicit threads
* not all PyPI packages support use in multiple interpreters yet

.. XXX Are there other relevant details to list?

In the context of multiple interpreters, "isolated" means each
interpreter shares no state between them. In practice, there is some
process-global data they all share, but that is managed by the runtime.


Reference
---------

This module defines the following functions:

.. function:: list_all()

Return a :class:`list` of :class:`Interpreter` objects,
one for each existing interpreter.

.. function:: get_current()

Return an :class:`Interpreter` object for the currently running
interpreter.

.. function:: get_main()

Return an :class:`Interpreter` object for the main interpreter.

.. function:: create()

Initialize a new (idle) Python interpreter
and return a :class:`Interpreter` object for it.


Interpreter objects
^^^^^^^^^^^^^^^^^^^

.. class:: Interpreter(id)

A single interpreter in the current process.

Generally, :class:`Interpreter` shouldn't be called directly.
Instead, use :func:`create` or one of the other module functions.

.. attribute:: id

(read-only)

The interpreter's ID.

.. attribute:: whence

(read-only)

A string describing where the interpreter came from.

.. method:: is_running()

Return ``True`` if the interpreter is currently executing code
in its :mod:`!__main__` module and ``False`` otherwise.

.. method:: close()

Finalize and destroy the interpreter.

.. method:: prepare_main(ns=None, **kwargs)

Bind "shareable" objects in the interpreter's
ZeroIntensity marked this conversation as resolved.
Show resolved Hide resolved
:mod:`!__main__` module.

.. method:: exec(code, /, dedent=True)

Run the given source code in the interpreter (in the current thread).

.. method:: call(callable, /, *args, **kwargs)

Return the result of calling running the given function in the
interpreter (in the current thread).

.. method:: call_in_thread(callable, /, *args, **kwargs)

Run the given function in the interpreter (in a new thread).

Exceptions
^^^^^^^^^^

.. exception:: InterpreterError

This exception, a subclass of :exc:`Exception`, is raised when
an interpreter-related error happens.

.. exception:: InterpreterNotFoundError

This exception, a subclass of :exc:`InterpreterError`, is raised when
the targeted interpreter no longer exists.

.. exception:: ExecutionFailed

This exception, a subclass of :exc:`InterpreterError`, is raised when
the running code raised an uncaught exception.

.. attribute:: excinfo

A basic snapshot of the exception raised in the other interpreter.

.. XXX Document the excinfoattrs?

.. exception:: NotShareableError

This exception, a subclass of :exc:`TypeError`, is raised when
an object cannot be sent to another interpreter.


.. XXX Add functions for communicating between interpreters.


Basic Usage
-----------

Creating an interpreter and running code in it::

import interpreters

interp = interpreters.create()

# Run in the current OS thread.

interp.exec('print("spam!")')

interp.exec("""if True:
print('spam!')
""")

from textwrap import dedent
interp.exec(dedent("""
print('spam!')
"""))

def run():
print('spam!')

interp.call(run)

# Run in new OS thread.

t = interp.call_in_thread(run)
t.join()


.. XXX Explain about object "sharing".
1 change: 1 addition & 0 deletions 1 Doc/library/python.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ overview:
builtins.rst
__main__.rst
warnings.rst
interpreters.rst
dataclasses.rst
contextlib.rst
abc.rst
Expand Down
98 changes: 98 additions & 0 deletions 98 Doc/whatsnew/3.14.rst
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ and improvements in user-friendliness and correctness.
.. PEP-sized items next.

* :ref:`PEP 649 and 749: deferred evaluation of annotations <whatsnew314-pep649>`
* :ref:`PEP 734: Multiple Interpreters in the Stdlib <whatsnew314-pep734>`
* :ref:`PEP 741: Python Configuration C API <whatsnew314-pep741>`
* :ref:`PEP 750: Template strings <whatsnew314-pep750>`
* :ref:`PEP 758: Allow except and except* expressions without parentheses <whatsnew314-pep758>`
Expand Down Expand Up @@ -123,6 +124,101 @@ of Python. See :ref:`below <whatsnew314-refcount>` for details.
New features
============

.. _whatsnew314-pep734:

PEP 734: Multiple Interpreters in the Stdlib
--------------------------------------------

The CPython runtime supports running multiple copies of Python in the
same process simultaneously and has done so for over 20 years.
Each of these separate copies is called an "interpreter".
However, the feature has been available only through the C-API.

That limitation is removed in the 3.14 release,
with the new :mod:`interpreters` module!

There are at least two notable reasons why using multiple interpreters
is worth considering:

* they support a new (to Python), human-friendly concurrency model
* true multi-core parallelism

For some use cases, concurrency in software enables efficiency and
can simplify software, at a high level. At the same time, implementing
and maintaining all but the simplest concurrency is often a struggle
for the human brain. That especially applies to plain threads
(e.g. :mod:`threading`), where all memory is shared between all threads.

With multiple isolated interpreters, you can take advantage of a class
of concurrency models, like CSP or the actor model, that have found
success in other programming languages, like Smalltalk, Erlang,
Haskell, and Go. Think of multiple interpreters like threads
but with opt-in sharing.

Regarding multi-core parallelism: as of the 3.12 release, interpreters
are now sufficiently isolated from one another to be used in parallel.
(See :pep:`684`.) This unlocks a variety of CPU-intensive use cases
for Python that were limited by the :term:`GIL`.

Using multiple interpreters is similar in many ways to
:mod:`multiprocessing`, in that they both provide isolated logical
"processes" that can run in parallel, with no sharing by default.
However, when using multiple interpreters, an application will use
fewer system resources and will operate more efficiently (since it
stays within the same process). Think of multiple interpreters as
having the isolation of processes with the efficiency of threads.

.. XXX Add an example or two.
.. XXX Link to the not-yet-added HOWTO doc.

While the feature has been around for decades, multiple interpreters
have not been used widely, due to low awareness and the lack of a stdlib
module. Consequently, they currently have several notable limitations,
which will improve significantly now that the feature is finally
going mainstream.

Current limitations:

* starting each interpreter has not been optimized yet
* each interpreter uses more memory than necessary
(we will be working next on extensive internal sharing between
interpreters)
* there aren't many options *yet* for truly sharing objects or other
data between interpreters (other than :type:`memoryview`)
* many extension modules on PyPI are not compatible with multiple
interpreters yet (stdlib extension modules *are* compatible)
* the approach to writing applications that use multiple isolated
interpreters is mostly unfamiliar to Python users, for now
AA-Turner marked this conversation as resolved.
Show resolved Hide resolved

The impact of these limitations will depend on future CPython
improvements, how interpreters are used, and what the community solves
through PyPI packages. Depending on the use case, the limitations may
not have much impact, so try it out!

Furthermore, future CPython releases will reduce or eliminate overhead
and provide utilities that are less appropriate on PyPI. In the
meantime, most of the limitations can also be addressed through
extension modules, meaning PyPI packages can fill any gap for 3.14, and
even back to 3.12 where interpreters were finally properly isolated and
stopped sharing the :term:`GIL`. Likewise, we expect to slowly see
libraries on PyPI for high-level abstractions on top of interpreters.

Regarding extension modules, work is in progress to update some PyPI
projects, as well as tools like Cython, PyBind11, Nanobind, and PyO3.
The steps for isolating an extension module are found at
:ref:`isolating-extensions-howto`. Isolating a module has a lot of
overlap with what is required to support
:ref:`free-threadeding <whatsnew314-free-threaded-cpython>`,
so the ongoing work in the community in that area will help accelerate
support for multiple interpreters.

Also added in 3.14: :ref:`concurrent.futures.InterpreterPoolExecutor
<whatsnew314-concurrent-futures-interp-pool>`.
AA-Turner marked this conversation as resolved.
Show resolved Hide resolved

.. seealso::
:pep:`734`.


.. _whatsnew314-pep750:

PEP 750: Template strings
Expand Down Expand Up @@ -1108,6 +1204,8 @@ calendar
concurrent.futures
------------------

.. _whatsnew314-concurrent-futures-interp-pool:

* Add :class:`~concurrent.futures.InterpreterPoolExecutor`,
which exposes "subinterpreters" (multiple Python interpreters in the
same process) to Python code. This is separate from the proposed API
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@

import time
import _interpchannels as _channels
from . import _crossinterp
from interpreters import _crossinterp

# aliases:
from _interpchannels import (
ChannelError, ChannelNotFoundError, ChannelClosedError, # noqa: F401
ChannelEmptyError, ChannelNotEmptyError, # noqa: F401
)
from ._crossinterp import (
from interpreters._crossinterp import (
UNBOUND_ERROR, UNBOUND_REMOVE,
)

Expand Down
2 changes: 1 addition & 1 deletion 2 Lib/test/test__interpchannels.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from test.support import import_helper, skip_if_sanitizer

_channels = import_helper.import_module('_interpchannels')
from test.support.interpreters import _crossinterp
from interpreters import _crossinterp
from test.test__interpreters import (
_interpreters,
_run_output,
Expand Down
2 changes: 1 addition & 1 deletion 2 Lib/test/test_concurrent_futures/test_interpreter_pool.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
from concurrent.futures.interpreter import (
ExecutionFailed, BrokenInterpreterPool,
)
from interpreters import queues
import _interpreters
from test import support
import test.test_asyncio.utils as testasyncio_utils
from test.support.interpreters import queues

from .executor import ExecutorTest, mul
from .util import BaseTestCase, InterpreterPoolMixin, setup_module
Expand Down
Loading
Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.