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: `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,125 @@ 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 has 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-senstive
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 `.
56172
57173
58174Reference
@@ -125,6 +241,8 @@ Interpreter objects
125241 Return the result of calling running the given function in the
126242 interpreter (in the current thread).
127243
244+ .. _interp-call-in-thread :
245+
128246 .. method :: call_in_thread(callable, /, *args, **kwargs)
129247
130248 Run the given function in the interpreter (in a new thread).
0 commit comments