Skip to content

Commit fc82cb9

Browse files
gh-134939: Fill Out the concurrent.interpreters Docs (gh-135902)
1 parent 486587d commit fc82cb9

File tree

1 file changed

+206
-17
lines changed

1 file changed

+206
-17
lines changed

Doc/library/concurrent.interpreters.rst

Lines changed: 206 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,26 @@
1313

1414
--------------
1515

16-
17-
Introduction
18-
------------
19-
2016
The :mod:`!concurrent.interpreters` module constructs higher-level
2117
interfaces on top of the lower level :mod:`!_interpreters` module.
2218

23-
.. XXX Add references to the upcoming HOWTO docs in the seealso block.
19+
The module is primarily meant to provide a basic API for managing
20+
interpreters (AKA "subinterpreters") and running things in them.
21+
Running mostly involves switching to an interpreter (in the current
22+
thread) and calling a function in that execution context.
23+
24+
For concurrency, interpreters themselves (and this module) don't
25+
provide much more than isolation, which on its own isn't useful.
26+
Actual concurrency is available separately through
27+
:mod:`threads <threading>` See `below <interp-concurrency_>`_
2428

2529
.. seealso::
2630

31+
:class:`~concurrent.futures.InterpreterPoolExecutor`
32+
combines threads with interpreters in a familiar interface.
33+
34+
.. XXX Add references to the upcoming HOWTO docs in the seealso block.
35+
2736
:ref:`isolating-extensions-howto`
2837
how to update an extension module to support multiple interpreters
2938

@@ -41,18 +50,155 @@ interfaces on top of the lower level :mod:`!_interpreters` module.
4150
Key details
4251
-----------
4352

44-
Before we dive into examples, there are a small number of details
53+
Before we dive in further, there are a small number of details
4554
to keep in mind about using multiple interpreters:
4655

47-
* isolated, by default
56+
* `isolated <interp-isolation_>`_, by default
4857
* no implicit threads
4958
* not all PyPI packages support use in multiple interpreters yet
5059

5160
.. XXX Are there other relevant details to list?
5261
53-
In the context of multiple interpreters, "isolated" means that
54-
different interpreters do not share any state. In practice, there is some
55-
process-global data they all share, but that is managed by the runtime.
62+
63+
.. _interpreters-intro:
64+
65+
Introduction
66+
------------
67+
68+
An "interpreter" is effectively the execution context of the Python
69+
runtime. It contains all of the state the runtime needs to execute
70+
a program. This includes things like the import state and builtins.
71+
(Each thread, even if there's only the main thread, has some extra
72+
runtime state, in addition to the current interpreter, related to
73+
the current exception and the bytecode eval loop.)
74+
75+
The concept and functionality of the interpreter have been a part of
76+
Python since version 2.2, but the feature was only available through
77+
the C-API and not well known, and the `isolation <interp-isolation_>`_
78+
was relatively incomplete until version 3.12.
79+
80+
.. _interp-isolation:
81+
82+
Multiple Interpreters and Isolation
83+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
84+
85+
A Python implementation may support using multiple interpreters in the
86+
same process. CPython has this support. Each interpreter is
87+
effectively isolated from the others (with a limited number of
88+
carefully managed process-global exceptions to the rule).
89+
90+
That isolation is primarily useful as a strong separation between
91+
distinct logical components of a program, where you want to have
92+
careful control of how those components interact.
93+
94+
.. note::
95+
96+
Interpreters in the same process can technically never be strictly
97+
isolated from one another since there are few restrictions on memory
98+
access within the same process. The Python runtime makes a best
99+
effort at isolation but extension modules may easily violate that.
100+
Therefore, do not use multiple interpreters in security-sensitive
101+
situations, where they shouldn't have access to each other's data.
102+
103+
Running in an Interpreter
104+
^^^^^^^^^^^^^^^^^^^^^^^^^
105+
106+
Running in a different interpreter involves switching to it in the
107+
current thread and then calling some function. The runtime will
108+
execute the function using the current interpreter's state. The
109+
:mod:`!concurrent.interpreters` module provides a basic API for
110+
creating and managing interpreters, as well as the switch-and-call
111+
operation.
112+
113+
No other threads are automatically started for the operation.
114+
There is `a helper <interp-call-in-thread_>`_ for that though.
115+
There is another dedicated helper for calling the builtin
116+
:func:`exec` in an interpreter.
117+
118+
When :func:`exec` (or :func:`eval`) are called in an interpreter,
119+
they run using the interpreter's :mod:`!__main__` module as the
120+
"globals" namespace. The same is true for functions that aren't
121+
associated with any module. This is the same as how scripts invoked
122+
from the command-line run in the :mod:`!__main__` module.
123+
124+
125+
.. _interp-concurrency:
126+
127+
Concurrency and Parallelism
128+
^^^^^^^^^^^^^^^^^^^^^^^^^^^
129+
130+
As noted earlier, interpreters do not provide any concurrency
131+
on their own. They strictly represent the isolated execution
132+
context the runtime will use *in the current thread*. That isolation
133+
makes them similar to processes, but they still enjoy in-process
134+
efficiency, like threads.
135+
136+
All that said, interpreters do naturally support certain flavors of
137+
concurrency, as a powerful side effect of that isolation.
138+
There's a powerful side effect of that isolation. It enables a
139+
different approach to concurrency than you can take with async or
140+
threads. It's a similar concurrency model to CSP or the actor model,
141+
a model which is relatively easy to reason about.
142+
143+
You can take advantage of that concurrency model in a single thread,
144+
switching back and forth between interpreters, Stackless-style.
145+
However, this model is more useful when you combine interpreters
146+
with multiple threads. This mostly involves starting a new thread,
147+
where you switch to another interpreter and run what you want there.
148+
149+
Each actual thread in Python, even if you're only running in the main
150+
thread, has its own *current* execution context. Multiple threads can
151+
use the same interpreter or different ones.
152+
153+
At a high level, you can think of the combination of threads and
154+
interpreters as threads with opt-in sharing.
155+
156+
As a significant bonus, interpreters are sufficiently isolated that
157+
they do not share the :term:`GIL`, which means combining threads with
158+
multiple interpreters enables full multi-core parallelism.
159+
(This has been the case since Python 3.12.)
160+
161+
Communication Between Interpreters
162+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
163+
164+
In practice, multiple interpreters are useful only if we have a way
165+
to communicate between them. This usually involves some form of
166+
message passing, but can even mean sharing data in some carefully
167+
managed way.
168+
169+
With this in mind, the :mod:`!concurrent.interpreters` module provides
170+
a :class:`queue.Queue` implementation, available through
171+
:func:`create_queue`.
172+
173+
.. _interp-object-sharing:
174+
175+
"Sharing" Objects
176+
^^^^^^^^^^^^^^^^^
177+
178+
Any data actually shared between interpreters loses the thread-safety
179+
provided by the :term:`GIL`. There are various options for dealing with
180+
this in extension modules. However, from Python code the lack of
181+
thread-safety means objects can't actually be shared, with a few
182+
exceptions. Instead, a copy must be created, which means mutable
183+
objects won't stay in sync.
184+
185+
By default, most objects are copied with :mod:`pickle` when they are
186+
passed to another interpreter. Nearly all of the immutable builtin
187+
objects are either directly shared or copied efficiently. For example:
188+
189+
* :const:`None`
190+
* :class:`bool` (:const:`True` and :const:`False`)
191+
* :class:`bytes`
192+
* :class:`str`
193+
* :class:`int`
194+
* :class:`float`
195+
* :class:`tuple` (of similarly supported objects)
196+
197+
There is a small number of Python types that actually share mutable
198+
data between interpreters:
199+
200+
* :class:`memoryview`
201+
* :class:`Queue`
56202

57203

58204
Reference
@@ -73,12 +219,19 @@ This module defines the following functions:
73219
.. function:: get_main()
74220

75221
Return an :class:`Interpreter` object for the main interpreter.
222+
This is the interpreter the runtime created to run the :term:`REPL`
223+
or the script given at the command-line. It is usually the only one.
76224

77225
.. function:: create()
78226

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

230+
.. function:: create_queue()
231+
232+
Initialize a new cross-interpreter queue and return a :class:`Queue`
233+
object for it.
234+
82235

83236
Interpreter objects
84237
^^^^^^^^^^^^^^^^^^^
@@ -94,7 +247,7 @@ Interpreter objects
94247

95248
(read-only)
96249

97-
The interpreter's ID.
250+
The underlying interpreter's ID.
98251

99252
.. attribute:: whence
100253

@@ -113,8 +266,10 @@ Interpreter objects
113266

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

116-
Bind "shareable" objects in the interpreter's
117-
:mod:`!__main__` module.
269+
Bind objects in the interpreter's :mod:`!__main__` module.
270+
271+
Some objects are actually shared and some are copied efficiently,
272+
but most are copied via :mod:`pickle`. See :ref:`interp-object-sharing`.
118273

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

@@ -125,6 +280,8 @@ Interpreter objects
125280
Return the result of calling running the given function in the
126281
interpreter (in the current thread).
127282

283+
.. _interp-call-in-thread:
284+
128285
.. method:: call_in_thread(callable, /, *args, **kwargs)
129286

130287
Run the given function in the interpreter (in a new thread).
@@ -159,7 +316,36 @@ Exceptions
159316
an object cannot be sent to another interpreter.
160317

161318

162-
.. XXX Add functions for communicating between interpreters.
319+
Communicating Between Interpreters
320+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
321+
322+
.. class:: Queue(id)
323+
324+
A wrapper around a low-level, cross-interpreter queue, which
325+
implements the :class:`queue.Queue` interface. The underlying queue
326+
can only be created through :func:`create_queue`.
327+
328+
Some objects are actually shared and some are copied efficiently,
329+
but most are copied via :mod:`pickle`. See :ref:`interp-object-sharing`.
330+
331+
.. attribute:: id
332+
333+
(read-only)
334+
335+
The queue's ID.
336+
337+
338+
.. exception:: QueueEmptyError
339+
340+
This exception, a subclass of :exc:`queue.Empty`, is raised from
341+
:meth:`!Queue.get` and :meth:`!Queue.get_nowait` when the queue
342+
is empty.
343+
344+
.. exception:: QueueFullError
345+
346+
This exception, a subclass of :exc:`queue.Full`, is raised from
347+
:meth:`!Queue.put` and :meth:`!Queue.put_nowait` when the queue
348+
is full.
163349

164350

165351
Basic usage
@@ -184,6 +370,12 @@ Creating an interpreter and running code in it::
184370
print('spam!')
185371
"""))
186372

373+
def run(arg):
374+
return arg
375+
376+
res = interp.call(run, 'spam!')
377+
print(res)
378+
187379
def run():
188380
print('spam!')
189381

@@ -193,6 +385,3 @@ Creating an interpreter and running code in it::
193385

194386
t = interp.call_in_thread(run)
195387
t.join()
196-
197-
198-
.. XXX Explain about object "sharing".

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy