1313
1414--------------
1515
16-
17- Introduction
18- ------------
19-
2016The :mod: `!concurrent.interpreters ` module constructs higher-level
2117interfaces 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.
4150Key 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
4554to 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
58204Reference
@@ -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
83236Interpreter 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
165351Basic 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