# Subinterpreters
**/*interpreteridobject.* @ericsnowcurrently
**/*crossinterp* @ericsnowcurrently
-Lib/test/support/interpreters/ @ericsnowcurrently
Modules/_interp*module.c @ericsnowcurrently
+Lib/test/test__interp*.py @ericsnowcurrently
+Lib/concurrent/interpreters/ @ericsnowcurrently
+Lib/test/support/channels.py @ericsnowcurrently
+Doc/library/concurrent.interpreters.rst @ericsnowcurrently
Lib/test/test_interpreters/ @ericsnowcurrently
+Lib/concurrent/futures/interpreter.py @ericsnowcurrently
# Android
**/*Android* @mhsmith @freakboy3742
multiprocessing.shared_memory.rst
concurrent.rst
concurrent.futures.rst
+ concurrent.interpreters.rst
subprocess.rst
sched.rst
queue.rst
--- /dev/null
+:mod:`!concurrent.interpreters` --- Multiple interpreters in the same process
+=============================================================================
+
+.. module:: concurrent.interpreters
+ :synopsis: Multiple interpreters in the same process
+
+.. moduleauthor:: Eric Snow <ericsnowcurrently@gmail.com>
+.. sectionauthor:: Eric Snow <ericsnowcurrently@gmail.com>
+
+.. versionadded:: 3.14
+
+**Source code:** :source:`Lib/concurrent/interpreters.py`
+
+--------------
+
+
+Introduction
+------------
+
+The :mod:`!concurrent.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
+* 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 that
+different interpreters do not share any state. 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
+ :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::
+
+ from concurrent 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".
The :mod:`!concurrent` package
==============================
-Currently, there is only one module in this package:
+This package contains the following modules:
* :mod:`concurrent.futures` -- Launching parallel tasks
+* :mod:`concurrent.interpreters` -- Multiple interpreters in the same process
inspect.rst
annotationlib.rst
site.rst
+
+.. seealso::
+
+ * See the :mod:`concurrent.interpreters` module, which similarly
+ exposes core runtime functionality.
.. 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>`
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 had been available only through the C-API.
+
+That limitation is removed in the 3.14 release,
+with the new :mod:`concurrent.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
+(for example, :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
+
+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-threading <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>`.
+
+.. seealso::
+ :pep:`734`.
+
+
.. _whatsnew314-pep750:
PEP 750: Template strings
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
except _interpqueues.QueueError:
continue
except ModuleNotFoundError:
- # interpreters.queues doesn't exist, which means
+ # interpreters._queues doesn't exist, which means
# QueueEmpty doesn't. Act as though it does.
continue
else:
InterpreterError, InterpreterNotFoundError, NotShareableError,
is_shareable,
)
+from ._queues import (
+ create as create_queue,
+ Queue, QueueEmpty, QueueFull,
+)
__all__ = [
]
-_queuemod = None
-
-def __getattr__(name):
- if name in ('Queue', 'QueueEmpty', 'QueueFull', 'create_queue'):
- global create_queue, Queue, QueueEmpty, QueueFull
- ns = globals()
- from .queues import (
- create as create_queue,
- Queue, QueueEmpty, QueueFull,
- )
- return ns[name]
- else:
- raise AttributeError(name)
-
-
_EXEC_FAILURE_STR = """
{superstr}
def __repr__(self):
return f'{self._MODULE}.{self._NAME}'
-# return f'interpreters.queues.UNBOUND'
+# return f'interpreters._queues.UNBOUND'
UNBOUND = object.__new__(UnboundItem)
import time
import _interpchannels as _channels
-from . import _crossinterp
+from concurrent.interpreters import _crossinterp
# aliases:
from _interpchannels import (
ChannelError, ChannelNotFoundError, ChannelClosedError,
ChannelEmptyError, ChannelNotEmptyError,
)
-from ._crossinterp import (
+from concurrent.interpreters._crossinterp import (
UNBOUND_ERROR, UNBOUND_REMOVE,
)
from test.support import import_helper, skip_if_sanitizer
_channels = import_helper.import_module('_interpchannels')
-from test.support.interpreters import _crossinterp
+from concurrent.interpreters import _crossinterp
from test.test__interpreters import (
_interpreters,
_run_output,
from concurrent.futures.interpreter import (
ExecutionFailed, BrokenInterpreterPool,
)
+from concurrent.interpreters import _queues as 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
from test.support import import_helper
# Raise SkipTest if subinterpreters not supported.
_interpreters = import_helper.import_module('_interpreters')
+from concurrent import interpreters
from test.support import Py_GIL_DISABLED
-from test.support import interpreters
from test.support import force_not_colorized
import test._crossinterp_definitions as defs
-from test.support.interpreters import (
+from concurrent.interpreters import (
InterpreterError, InterpreterNotFoundError, ExecutionFailed,
)
from .utils import (
main, = interpreters.list_all()
interp = interpreters.create()
out = _run_output(interp, dedent("""
- from test.support import interpreters
+ from concurrent import interpreters
interp = interpreters.create()
print(interp.id)
"""))
main = interpreters.get_main()
interp = interpreters.create()
out = _run_output(interp, dedent("""
- from test.support import interpreters
+ from concurrent import interpreters
cur = interpreters.get_current()
print(cur.id)
"""))
with self.subTest('subinterpreter'):
interp = interpreters.create()
out = _run_output(interp, dedent("""
- from test.support import interpreters
+ from concurrent import interpreters
cur = interpreters.get_current()
print(id(cur))
cur = interpreters.get_current()
with self.subTest('per-interpreter'):
interp = interpreters.create()
out = _run_output(interp, dedent("""
- from test.support import interpreters
+ from concurrent import interpreters
cur = interpreters.get_current()
print(id(cur))
"""))
main, = interpreters.list_all()
interp = interpreters.create()
out = _run_output(interp, dedent(f"""
- from test.support import interpreters
+ from concurrent import interpreters
interp = interpreters.Interpreter({interp.id})
try:
interp.close()
self.assertEqual(set(interpreters.list_all()),
{main, interp1, interp2})
interp1.exec(dedent(f"""
- from test.support import interpreters
+ from concurrent import interpreters
interp2 = interpreters.Interpreter({interp2.id})
interp2.close()
interp3 = interpreters.create()
ham()
""")
scriptfile = self.make_script('script.py', tempdir, text="""
- from test.support import interpreters
+ from concurrent import interpreters
def script():
import spam
~~~~~~~~~~~^^^^^^^^
{interpmod_line.strip()}
raise ExecutionFailed(excinfo)
- test.support.interpreters.ExecutionFailed: RuntimeError: uh-oh!
+ concurrent.interpreters.ExecutionFailed: RuntimeError: uh-oh!
Uncaught in the interpreter:
# no module indirection
with self.subTest('no indirection'):
text = run(f"""
- from test.support import interpreters
+ from concurrent import interpreters
def spam():
# This a global var...
""")
with self.subTest('indirect as func, direct interp'):
text = run(f"""
- from test.support import interpreters
+ from concurrent import interpreters
import mymod
def spam():
# indirect as func, indirect interp
new_mod('mymod', f"""
- from test.support import interpreters
+ from concurrent import interpreters
def run(func):
interp = interpreters.create()
return interp.call(func)
from test.support import import_helper
# Raise SkipTest if subinterpreters not supported.
_channels = import_helper.import_module('_interpchannels')
-from test.support import interpreters
-from test.support.interpreters import channels
+from concurrent import interpreters
+from test.support import channels
from .utils import _run_output, TestBase
def test_send_recv_same_interpreter(self):
interp = interpreters.create()
interp.exec(dedent("""
- from test.support.interpreters import channels
+ from test.support import channels
r, s = channels.create()
orig = b'spam'
s.send_nowait(orig)
def test_send_recv_nowait_same_interpreter(self):
interp = interpreters.create()
interp.exec(dedent("""
- from test.support.interpreters import channels
+ from test.support import channels
r, s = channels.create()
orig = b'spam'
s.send_nowait(orig)
interp = interpreters.create()
_run_output(interp, dedent(f"""
- from test.support.interpreters import channels
+ from test.support import channels
sch = channels.SendChannel({sch.id})
obj1 = b'spam'
obj2 = b'eggs'
self.assertEqual(_channels.get_count(rch.id), 0)
_run_output(interp, dedent(f"""
- from test.support.interpreters import channels
+ from test.support import channels
sch = channels.SendChannel({sch.id})
sch.send_nowait(1, unbounditems=channels.UNBOUND)
sch.send_nowait(2, unbounditems=channels.UNBOUND_ERROR)
sch.send_nowait(1)
_run_output(interp1, dedent(f"""
- from test.support.interpreters import channels
+ from test.support import channels
rch = channels.RecvChannel({rch.id})
sch = channels.SendChannel({sch.id})
obj1 = rch.recv()
sch.send_nowait(obj1, unbounditems=channels.UNBOUND_REMOVE)
"""))
_run_output(interp2, dedent(f"""
- from test.support.interpreters import channels
+ from test.support import channels
rch = channels.RecvChannel({rch.id})
sch = channels.SendChannel({sch.id})
obj2 = rch.recv()
# The main interpreter's sys.path[0] should be used by subinterpreters.
script = '''
import sys
- from test.support import interpreters
+ from concurrent import interpreters
orig = sys.path[0]
# is reported, even when subinterpreters get cleaned up at the end.
import subprocess
argv = [sys.executable, '-c', '''if True:
- from test.support import interpreters
+ from concurrent import interpreters
interp = interpreters.create()
raise Exception
''']
from test.support import import_helper, Py_DEBUG
# Raise SkipTest if subinterpreters not supported.
_queues = import_helper.import_module('_interpqueues')
-from test.support import interpreters
-from test.support.interpreters import queues, _crossinterp
+from concurrent import interpreters
+from concurrent.interpreters import _queues as queues, _crossinterp
import test._crossinterp_definitions as defs
from .utils import _run_output, TestBase as _TestBase
interp = interpreters.create()
interp.exec(dedent(f"""
- from test.support.interpreters import queues
+ from concurrent.interpreters import _queues as queues
queue1 = queues.Queue({queue1.id})
"""));
def test_put_get_same_interpreter(self):
interp = interpreters.create()
interp.exec(dedent("""
- from test.support.interpreters import queues
+ from concurrent.interpreters import _queues as queues
queue = queues.create()
"""))
for methname in ('get', 'get_nowait'):
out = _run_output(
interp,
dedent(f"""
- from test.support.interpreters import queues
+ from concurrent.interpreters import _queues as queues
queue1 = queues.Queue({queue1.id})
queue2 = queues.Queue({queue2.id})
assert queue1.qsize() == 1, 'expected: queue1.qsize() == 1'
interp = interpreters.create()
_run_output(interp, dedent(f"""
- from test.support.interpreters import queues
+ from concurrent.interpreters import _queues as queues
queue = queues.Queue({queue.id})
obj1 = b'spam'
obj2 = b'eggs'
queue = queues.create()
interp = interpreters.create()
_run_output(interp, dedent(f"""
- from test.support.interpreters import queues
+ from concurrent.interpreters import _queues as queues
queue = queues.Queue({queue.id})
queue.put(1, unbounditems=queues.UNBOUND)
queue.put(2, unbounditems=queues.UNBOUND_ERROR)
queue.put(1)
_run_output(interp1, dedent(f"""
- from test.support.interpreters import queues
+ from concurrent.interpreters import _queues as queues
queue = queues.Queue({queue.id})
obj1 = queue.get()
queue.put(2, unbounditems=queues.UNBOUND)
queue.put(obj1, unbounditems=queues.UNBOUND_REMOVE)
"""))
_run_output(interp2, dedent(f"""
- from test.support.interpreters import queues
+ from concurrent.interpreters import _queues as queues
queue = queues.Queue({queue.id})
obj2 = queue.get()
obj1 = queue.get()
from test.support import threading_helper
# Raise SkipTest if subinterpreters not supported.
import_helper.import_module('_interpreters')
-from test.support import interpreters
+from concurrent import interpreters
from .utils import TestBase
import _interpreters
except ImportError as exc:
raise unittest.SkipTest(str(exc))
-from test.support import interpreters
+from concurrent import interpreters
try:
from test.support import force_not_colorized
from test.support import SHORT_TIMEOUT
try:
- from test.support import interpreters
+ from concurrent import interpreters
except ImportError:
interpreters = None
import textwrap
from test import support
try:
- from test.support import interpreters
+ from concurrent import interpreters
except ImportError:
interpreters = None
def setUpClass(cls):
global interpreters
try:
- from test.support import interpreters
+ from concurrent import interpreters
except ModuleNotFoundError:
raise unittest.SkipTest('subinterpreters required')
- import test.support.interpreters.channels
+ from test.support import channels # noqa: F401
+ cls.create_channel = staticmethod(channels.create)
@cpython_only
@no_rerun('channels (and queues) might have a refleak; see gh-122199')
def test_static_types_inherited_slots(self):
- rch, sch = interpreters.channels.create()
+ rch, sch = self.create_channel()
script = textwrap.dedent("""
import test.support
main_results = collate_results(raw)
interp = interpreters.create()
- interp.exec('from test.support import interpreters')
+ interp.exec('from concurrent import interpreters')
interp.prepare_main(sch=sch)
interp.exec(script)
raw = rch.recv_nowait()
LIBSUBDIRS= asyncio \
collections \
compression compression/_common compression/zstd \
- concurrent concurrent/futures \
+ concurrent concurrent/futures concurrent/interpreters \
csv \
ctypes ctypes/macholib \
curses \
test/subprocessdata \
test/support \
test/support/_hypothesis_stubs \
- test/support/interpreters \
test/test_asyncio \
test/test_capi \
test/test_cext \
--- /dev/null
+Add the :mod:`concurrent.interpreters` module. See :pep:`734`.
return 0;
}
+static int
+ensure_highlevel_module_loaded(void)
+{
+ PyObject *highlevel =
+ PyImport_ImportModule("concurrent.interpreters._channels");
+ if (highlevel == NULL) {
+ PyErr_Clear();
+ highlevel = PyImport_ImportModule("test.support.channels");
+ if (highlevel == NULL) {
+ return -1;
+ }
+ }
+ Py_DECREF(highlevel);
+ return 0;
+}
+
/* module state *************************************************************/
}
if (cls == NULL) {
// Force the module to be loaded, to register the type.
- PyObject *highlevel = PyImport_ImportModule("interpreters.channels");
- if (highlevel == NULL) {
- PyErr_Clear();
- highlevel = PyImport_ImportModule("test.support.interpreters.channels");
- if (highlevel == NULL) {
- return NULL;
- }
+ if (ensure_highlevel_module_loaded() < 0) {
+ return NULL;
}
- Py_DECREF(highlevel);
if (end == CHANNEL_SEND) {
cls = state->send_channel_type;
}
static int
ensure_highlevel_module_loaded(void)
{
- PyObject *highlevel = PyImport_ImportModule("interpreters.queues");
+ PyObject *highlevel =
+ PyImport_ImportModule("concurrent.interpreters._queues");
if (highlevel == NULL) {
- PyErr_Clear();
- highlevel = PyImport_ImportModule("test.support.interpreters.queues");
- if (highlevel == NULL) {
- return -1;
- }
+ return -1;
}
Py_DECREF(highlevel);
return 0;
{
module_state *state = get_module_state(mod);
-#define PREFIX "test.support.interpreters."
+#define PREFIX "concurrent.interpreters."
#define ADD_EXCTYPE(NAME, BASE, DOC) \
assert(state->NAME == NULL); \
if (add_exctype(mod, &state->NAME, PREFIX #NAME, DOC, BASE) < 0) { \
/* To run some code in a sub-interpreter.
-Generally you can use test.support.interpreters,
+Generally you can use the interpreters module,
but we keep this helper as a distinct implementation.
-That's especially important for testing test.support.interpreters.
+That's especially important for testing the interpreters module.
*/
static PyObject *
run_in_subinterp_with_config(PyObject *self, PyObject *args, PyObject *kwargs)
static PyTypeObject _PyExc_InterpreterError = {
PyVarObject_HEAD_INIT(NULL, 0)
- .tp_name = "interpreters.InterpreterError",
+ .tp_name = "concurrent.interpreters.InterpreterError",
.tp_doc = PyDoc_STR("A cross-interpreter operation failed"),
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
//.tp_traverse = ((PyTypeObject *)PyExc_Exception)->tp_traverse,
static PyTypeObject _PyExc_InterpreterNotFoundError = {
PyVarObject_HEAD_INIT(NULL, 0)
- .tp_name = "interpreters.InterpreterNotFoundError",
+ .tp_name = "concurrent.interpreters.InterpreterNotFoundError",
.tp_doc = PyDoc_STR("An interpreter was not found"),
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
//.tp_traverse = ((PyTypeObject *)PyExc_Exception)->tp_traverse,
static int
_init_notshareableerror(exceptions_t *state)
{
- const char *name = "interpreters.NotShareableError";
+ const char *name = "concurrent.interpreters.NotShareableError";
PyObject *base = PyExc_TypeError;
PyObject *ns = NULL;
PyObject *exctype = PyErr_NewException(name, base, ns);