[3.14] gh-134939: Add the concurrent.interpreters Module (gh-135414)
PEP-734 has been accepted (for 3.14). (FTR, I'm opposed to putting this under the concurrent package, but doing so is the SC condition under which the module can land in 3.14.) (cherry picked from commit 62143736b, AKA gh-133958)
This commit is contained in:
parent
8cb7d9a810
commit
04273adae0
6
.github/CODEOWNERS
vendored
6
.github/CODEOWNERS
vendored
@ -281,9 +281,13 @@ Doc/howto/clinic.rst @erlend-aasland
|
|||||||
# Subinterpreters
|
# Subinterpreters
|
||||||
**/*interpreteridobject.* @ericsnowcurrently
|
**/*interpreteridobject.* @ericsnowcurrently
|
||||||
**/*crossinterp* @ericsnowcurrently
|
**/*crossinterp* @ericsnowcurrently
|
||||||
Lib/test/support/interpreters/ @ericsnowcurrently
|
|
||||||
Modules/_interp*module.c @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/test/test_interpreters/ @ericsnowcurrently
|
||||||
|
Lib/concurrent/futures/interpreter.py @ericsnowcurrently
|
||||||
|
|
||||||
# Android
|
# Android
|
||||||
**/*Android* @mhsmith @freakboy3742
|
**/*Android* @mhsmith @freakboy3742
|
||||||
|
@ -18,6 +18,7 @@ multitasking). Here's an overview:
|
|||||||
multiprocessing.shared_memory.rst
|
multiprocessing.shared_memory.rst
|
||||||
concurrent.rst
|
concurrent.rst
|
||||||
concurrent.futures.rst
|
concurrent.futures.rst
|
||||||
|
concurrent.interpreters.rst
|
||||||
subprocess.rst
|
subprocess.rst
|
||||||
sched.rst
|
sched.rst
|
||||||
queue.rst
|
queue.rst
|
||||||
|
198
Doc/library/concurrent.interpreters.rst
Normal file
198
Doc/library/concurrent.interpreters.rst
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
: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".
|
@ -1,6 +1,7 @@
|
|||||||
The :mod:`!concurrent` package
|
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.futures` -- Launching parallel tasks
|
||||||
|
* :mod:`concurrent.interpreters` -- Multiple interpreters in the same process
|
||||||
|
@ -27,3 +27,8 @@ overview:
|
|||||||
inspect.rst
|
inspect.rst
|
||||||
annotationlib.rst
|
annotationlib.rst
|
||||||
site.rst
|
site.rst
|
||||||
|
|
||||||
|
.. seealso::
|
||||||
|
|
||||||
|
* See the :mod:`concurrent.interpreters` module, which similarly
|
||||||
|
exposes core runtime functionality.
|
||||||
|
@ -83,6 +83,7 @@ and improvements in user-friendliness and correctness.
|
|||||||
.. PEP-sized items next.
|
.. PEP-sized items next.
|
||||||
|
|
||||||
* :ref:`PEP 649 and 749: deferred evaluation of annotations <whatsnew314-pep649>`
|
* :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 741: Python Configuration C API <whatsnew314-pep741>`
|
||||||
* :ref:`PEP 750: Template strings <whatsnew314-pep750>`
|
* :ref:`PEP 750: Template strings <whatsnew314-pep750>`
|
||||||
* :ref:`PEP 758: Allow except and except* expressions without parentheses <whatsnew314-pep758>`
|
* :ref:`PEP 758: Allow except and except* expressions without parentheses <whatsnew314-pep758>`
|
||||||
@ -123,6 +124,101 @@ of Python. See :ref:`below <whatsnew314-refcount>` for details.
|
|||||||
New features
|
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:
|
.. _whatsnew314-pep750:
|
||||||
|
|
||||||
PEP 750: Template strings
|
PEP 750: Template strings
|
||||||
@ -1109,6 +1205,8 @@ calendar
|
|||||||
concurrent.futures
|
concurrent.futures
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
.. _whatsnew314-concurrent-futures-interp-pool:
|
||||||
|
|
||||||
* Add :class:`~concurrent.futures.InterpreterPoolExecutor`,
|
* Add :class:`~concurrent.futures.InterpreterPoolExecutor`,
|
||||||
which exposes "subinterpreters" (multiple Python interpreters in the
|
which exposes "subinterpreters" (multiple Python interpreters in the
|
||||||
same process) to Python code. This is separate from the proposed API
|
same process) to Python code. This is separate from the proposed API
|
||||||
|
@ -167,7 +167,7 @@ class WorkerContext(_thread.WorkerContext):
|
|||||||
except _interpqueues.QueueError:
|
except _interpqueues.QueueError:
|
||||||
continue
|
continue
|
||||||
except ModuleNotFoundError:
|
except ModuleNotFoundError:
|
||||||
# interpreters.queues doesn't exist, which means
|
# interpreters._queues doesn't exist, which means
|
||||||
# QueueEmpty doesn't. Act as though it does.
|
# QueueEmpty doesn't. Act as though it does.
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
|
@ -9,6 +9,10 @@ from _interpreters import (
|
|||||||
InterpreterError, InterpreterNotFoundError, NotShareableError,
|
InterpreterError, InterpreterNotFoundError, NotShareableError,
|
||||||
is_shareable,
|
is_shareable,
|
||||||
)
|
)
|
||||||
|
from ._queues import (
|
||||||
|
create as create_queue,
|
||||||
|
Queue, QueueEmpty, QueueFull,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
@ -20,21 +24,6 @@ __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 = """
|
_EXEC_FAILURE_STR = """
|
||||||
{superstr}
|
{superstr}
|
||||||
|
|
@ -61,7 +61,7 @@ class UnboundItem:
|
|||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f'{self._MODULE}.{self._NAME}'
|
return f'{self._MODULE}.{self._NAME}'
|
||||||
# return f'interpreters.queues.UNBOUND'
|
# return f'interpreters._queues.UNBOUND'
|
||||||
|
|
||||||
|
|
||||||
UNBOUND = object.__new__(UnboundItem)
|
UNBOUND = object.__new__(UnboundItem)
|
@ -2,14 +2,14 @@
|
|||||||
|
|
||||||
import time
|
import time
|
||||||
import _interpchannels as _channels
|
import _interpchannels as _channels
|
||||||
from . import _crossinterp
|
from concurrent.interpreters import _crossinterp
|
||||||
|
|
||||||
# aliases:
|
# aliases:
|
||||||
from _interpchannels import (
|
from _interpchannels import (
|
||||||
ChannelError, ChannelNotFoundError, ChannelClosedError,
|
ChannelError, ChannelNotFoundError, ChannelClosedError,
|
||||||
ChannelEmptyError, ChannelNotEmptyError,
|
ChannelEmptyError, ChannelNotEmptyError,
|
||||||
)
|
)
|
||||||
from ._crossinterp import (
|
from concurrent.interpreters._crossinterp import (
|
||||||
UNBOUND_ERROR, UNBOUND_REMOVE,
|
UNBOUND_ERROR, UNBOUND_REMOVE,
|
||||||
)
|
)
|
||||||
|
|
@ -9,7 +9,7 @@ import unittest
|
|||||||
from test.support import import_helper, skip_if_sanitizer
|
from test.support import import_helper, skip_if_sanitizer
|
||||||
|
|
||||||
_channels = import_helper.import_module('_interpchannels')
|
_channels = import_helper.import_module('_interpchannels')
|
||||||
from test.support.interpreters import _crossinterp
|
from concurrent.interpreters import _crossinterp
|
||||||
from test.test__interpreters import (
|
from test.test__interpreters import (
|
||||||
_interpreters,
|
_interpreters,
|
||||||
_run_output,
|
_run_output,
|
||||||
|
@ -8,10 +8,10 @@ import unittest
|
|||||||
from concurrent.futures.interpreter import (
|
from concurrent.futures.interpreter import (
|
||||||
ExecutionFailed, BrokenInterpreterPool,
|
ExecutionFailed, BrokenInterpreterPool,
|
||||||
)
|
)
|
||||||
|
from concurrent.interpreters import _queues as queues
|
||||||
import _interpreters
|
import _interpreters
|
||||||
from test import support
|
from test import support
|
||||||
import test.test_asyncio.utils as testasyncio_utils
|
import test.test_asyncio.utils as testasyncio_utils
|
||||||
from test.support.interpreters import queues
|
|
||||||
|
|
||||||
from .executor import ExecutorTest, mul
|
from .executor import ExecutorTest, mul
|
||||||
from .util import BaseTestCase, InterpreterPoolMixin, setup_module
|
from .util import BaseTestCase, InterpreterPoolMixin, setup_module
|
||||||
|
@ -13,11 +13,11 @@ from test.support import script_helper
|
|||||||
from test.support import import_helper
|
from test.support import import_helper
|
||||||
# Raise SkipTest if subinterpreters not supported.
|
# Raise SkipTest if subinterpreters not supported.
|
||||||
_interpreters = import_helper.import_module('_interpreters')
|
_interpreters = import_helper.import_module('_interpreters')
|
||||||
|
from concurrent import interpreters
|
||||||
from test.support import Py_GIL_DISABLED
|
from test.support import Py_GIL_DISABLED
|
||||||
from test.support import interpreters
|
|
||||||
from test.support import force_not_colorized
|
from test.support import force_not_colorized
|
||||||
import test._crossinterp_definitions as defs
|
import test._crossinterp_definitions as defs
|
||||||
from test.support.interpreters import (
|
from concurrent.interpreters import (
|
||||||
InterpreterError, InterpreterNotFoundError, ExecutionFailed,
|
InterpreterError, InterpreterNotFoundError, ExecutionFailed,
|
||||||
)
|
)
|
||||||
from .utils import (
|
from .utils import (
|
||||||
@ -133,7 +133,7 @@ class CreateTests(TestBase):
|
|||||||
main, = interpreters.list_all()
|
main, = interpreters.list_all()
|
||||||
interp = interpreters.create()
|
interp = interpreters.create()
|
||||||
out = _run_output(interp, dedent("""
|
out = _run_output(interp, dedent("""
|
||||||
from test.support import interpreters
|
from concurrent import interpreters
|
||||||
interp = interpreters.create()
|
interp = interpreters.create()
|
||||||
print(interp.id)
|
print(interp.id)
|
||||||
"""))
|
"""))
|
||||||
@ -196,7 +196,7 @@ class GetCurrentTests(TestBase):
|
|||||||
main = interpreters.get_main()
|
main = interpreters.get_main()
|
||||||
interp = interpreters.create()
|
interp = interpreters.create()
|
||||||
out = _run_output(interp, dedent("""
|
out = _run_output(interp, dedent("""
|
||||||
from test.support import interpreters
|
from concurrent import interpreters
|
||||||
cur = interpreters.get_current()
|
cur = interpreters.get_current()
|
||||||
print(cur.id)
|
print(cur.id)
|
||||||
"""))
|
"""))
|
||||||
@ -213,7 +213,7 @@ class GetCurrentTests(TestBase):
|
|||||||
with self.subTest('subinterpreter'):
|
with self.subTest('subinterpreter'):
|
||||||
interp = interpreters.create()
|
interp = interpreters.create()
|
||||||
out = _run_output(interp, dedent("""
|
out = _run_output(interp, dedent("""
|
||||||
from test.support import interpreters
|
from concurrent import interpreters
|
||||||
cur = interpreters.get_current()
|
cur = interpreters.get_current()
|
||||||
print(id(cur))
|
print(id(cur))
|
||||||
cur = interpreters.get_current()
|
cur = interpreters.get_current()
|
||||||
@ -225,7 +225,7 @@ class GetCurrentTests(TestBase):
|
|||||||
with self.subTest('per-interpreter'):
|
with self.subTest('per-interpreter'):
|
||||||
interp = interpreters.create()
|
interp = interpreters.create()
|
||||||
out = _run_output(interp, dedent("""
|
out = _run_output(interp, dedent("""
|
||||||
from test.support import interpreters
|
from concurrent import interpreters
|
||||||
cur = interpreters.get_current()
|
cur = interpreters.get_current()
|
||||||
print(id(cur))
|
print(id(cur))
|
||||||
"""))
|
"""))
|
||||||
@ -582,7 +582,7 @@ class TestInterpreterClose(TestBase):
|
|||||||
main, = interpreters.list_all()
|
main, = interpreters.list_all()
|
||||||
interp = interpreters.create()
|
interp = interpreters.create()
|
||||||
out = _run_output(interp, dedent(f"""
|
out = _run_output(interp, dedent(f"""
|
||||||
from test.support import interpreters
|
from concurrent import interpreters
|
||||||
interp = interpreters.Interpreter({interp.id})
|
interp = interpreters.Interpreter({interp.id})
|
||||||
try:
|
try:
|
||||||
interp.close()
|
interp.close()
|
||||||
@ -599,7 +599,7 @@ class TestInterpreterClose(TestBase):
|
|||||||
self.assertEqual(set(interpreters.list_all()),
|
self.assertEqual(set(interpreters.list_all()),
|
||||||
{main, interp1, interp2})
|
{main, interp1, interp2})
|
||||||
interp1.exec(dedent(f"""
|
interp1.exec(dedent(f"""
|
||||||
from test.support import interpreters
|
from concurrent import interpreters
|
||||||
interp2 = interpreters.Interpreter({interp2.id})
|
interp2 = interpreters.Interpreter({interp2.id})
|
||||||
interp2.close()
|
interp2.close()
|
||||||
interp3 = interpreters.create()
|
interp3 = interpreters.create()
|
||||||
@ -806,7 +806,7 @@ class TestInterpreterExec(TestBase):
|
|||||||
ham()
|
ham()
|
||||||
""")
|
""")
|
||||||
scriptfile = self.make_script('script.py', tempdir, text="""
|
scriptfile = self.make_script('script.py', tempdir, text="""
|
||||||
from test.support import interpreters
|
from concurrent import interpreters
|
||||||
|
|
||||||
def script():
|
def script():
|
||||||
import spam
|
import spam
|
||||||
@ -827,7 +827,7 @@ class TestInterpreterExec(TestBase):
|
|||||||
~~~~~~~~~~~^^^^^^^^
|
~~~~~~~~~~~^^^^^^^^
|
||||||
{interpmod_line.strip()}
|
{interpmod_line.strip()}
|
||||||
raise ExecutionFailed(excinfo)
|
raise ExecutionFailed(excinfo)
|
||||||
test.support.interpreters.ExecutionFailed: RuntimeError: uh-oh!
|
concurrent.interpreters.ExecutionFailed: RuntimeError: uh-oh!
|
||||||
|
|
||||||
Uncaught in the interpreter:
|
Uncaught in the interpreter:
|
||||||
|
|
||||||
@ -1281,7 +1281,7 @@ class TestInterpreterCall(TestBase):
|
|||||||
# no module indirection
|
# no module indirection
|
||||||
with self.subTest('no indirection'):
|
with self.subTest('no indirection'):
|
||||||
text = run(f"""
|
text = run(f"""
|
||||||
from test.support import interpreters
|
from concurrent import interpreters
|
||||||
|
|
||||||
def spam():
|
def spam():
|
||||||
# This a global var...
|
# This a global var...
|
||||||
@ -1301,7 +1301,7 @@ class TestInterpreterCall(TestBase):
|
|||||||
""")
|
""")
|
||||||
with self.subTest('indirect as func, direct interp'):
|
with self.subTest('indirect as func, direct interp'):
|
||||||
text = run(f"""
|
text = run(f"""
|
||||||
from test.support import interpreters
|
from concurrent import interpreters
|
||||||
import mymod
|
import mymod
|
||||||
|
|
||||||
def spam():
|
def spam():
|
||||||
@ -1317,7 +1317,7 @@ class TestInterpreterCall(TestBase):
|
|||||||
|
|
||||||
# indirect as func, indirect interp
|
# indirect as func, indirect interp
|
||||||
new_mod('mymod', f"""
|
new_mod('mymod', f"""
|
||||||
from test.support import interpreters
|
from concurrent import interpreters
|
||||||
def run(func):
|
def run(func):
|
||||||
interp = interpreters.create()
|
interp = interpreters.create()
|
||||||
return interp.call(func)
|
return interp.call(func)
|
||||||
|
@ -8,8 +8,8 @@ import time
|
|||||||
from test.support import import_helper
|
from test.support import import_helper
|
||||||
# Raise SkipTest if subinterpreters not supported.
|
# Raise SkipTest if subinterpreters not supported.
|
||||||
_channels = import_helper.import_module('_interpchannels')
|
_channels = import_helper.import_module('_interpchannels')
|
||||||
from test.support import interpreters
|
from concurrent import interpreters
|
||||||
from test.support.interpreters import channels
|
from test.support import channels
|
||||||
from .utils import _run_output, TestBase
|
from .utils import _run_output, TestBase
|
||||||
|
|
||||||
|
|
||||||
@ -171,7 +171,7 @@ class TestSendRecv(TestBase):
|
|||||||
def test_send_recv_same_interpreter(self):
|
def test_send_recv_same_interpreter(self):
|
||||||
interp = interpreters.create()
|
interp = interpreters.create()
|
||||||
interp.exec(dedent("""
|
interp.exec(dedent("""
|
||||||
from test.support.interpreters import channels
|
from test.support import channels
|
||||||
r, s = channels.create()
|
r, s = channels.create()
|
||||||
orig = b'spam'
|
orig = b'spam'
|
||||||
s.send_nowait(orig)
|
s.send_nowait(orig)
|
||||||
@ -244,7 +244,7 @@ class TestSendRecv(TestBase):
|
|||||||
def test_send_recv_nowait_same_interpreter(self):
|
def test_send_recv_nowait_same_interpreter(self):
|
||||||
interp = interpreters.create()
|
interp = interpreters.create()
|
||||||
interp.exec(dedent("""
|
interp.exec(dedent("""
|
||||||
from test.support.interpreters import channels
|
from test.support import channels
|
||||||
r, s = channels.create()
|
r, s = channels.create()
|
||||||
orig = b'spam'
|
orig = b'spam'
|
||||||
s.send_nowait(orig)
|
s.send_nowait(orig)
|
||||||
@ -387,7 +387,7 @@ class TestSendRecv(TestBase):
|
|||||||
interp = interpreters.create()
|
interp = interpreters.create()
|
||||||
|
|
||||||
_run_output(interp, dedent(f"""
|
_run_output(interp, dedent(f"""
|
||||||
from test.support.interpreters import channels
|
from test.support import channels
|
||||||
sch = channels.SendChannel({sch.id})
|
sch = channels.SendChannel({sch.id})
|
||||||
obj1 = b'spam'
|
obj1 = b'spam'
|
||||||
obj2 = b'eggs'
|
obj2 = b'eggs'
|
||||||
@ -482,7 +482,7 @@ class TestSendRecv(TestBase):
|
|||||||
self.assertEqual(_channels.get_count(rch.id), 0)
|
self.assertEqual(_channels.get_count(rch.id), 0)
|
||||||
|
|
||||||
_run_output(interp, dedent(f"""
|
_run_output(interp, dedent(f"""
|
||||||
from test.support.interpreters import channels
|
from test.support import channels
|
||||||
sch = channels.SendChannel({sch.id})
|
sch = channels.SendChannel({sch.id})
|
||||||
sch.send_nowait(1, unbounditems=channels.UNBOUND)
|
sch.send_nowait(1, unbounditems=channels.UNBOUND)
|
||||||
sch.send_nowait(2, unbounditems=channels.UNBOUND_ERROR)
|
sch.send_nowait(2, unbounditems=channels.UNBOUND_ERROR)
|
||||||
@ -518,7 +518,7 @@ class TestSendRecv(TestBase):
|
|||||||
|
|
||||||
sch.send_nowait(1)
|
sch.send_nowait(1)
|
||||||
_run_output(interp1, dedent(f"""
|
_run_output(interp1, dedent(f"""
|
||||||
from test.support.interpreters import channels
|
from test.support import channels
|
||||||
rch = channels.RecvChannel({rch.id})
|
rch = channels.RecvChannel({rch.id})
|
||||||
sch = channels.SendChannel({sch.id})
|
sch = channels.SendChannel({sch.id})
|
||||||
obj1 = rch.recv()
|
obj1 = rch.recv()
|
||||||
@ -526,7 +526,7 @@ class TestSendRecv(TestBase):
|
|||||||
sch.send_nowait(obj1, unbounditems=channels.UNBOUND_REMOVE)
|
sch.send_nowait(obj1, unbounditems=channels.UNBOUND_REMOVE)
|
||||||
"""))
|
"""))
|
||||||
_run_output(interp2, dedent(f"""
|
_run_output(interp2, dedent(f"""
|
||||||
from test.support.interpreters import channels
|
from test.support import channels
|
||||||
rch = channels.RecvChannel({rch.id})
|
rch = channels.RecvChannel({rch.id})
|
||||||
sch = channels.SendChannel({sch.id})
|
sch = channels.SendChannel({sch.id})
|
||||||
obj2 = rch.recv()
|
obj2 = rch.recv()
|
||||||
|
@ -119,7 +119,7 @@ class StartupTests(TestBase):
|
|||||||
# The main interpreter's sys.path[0] should be used by subinterpreters.
|
# The main interpreter's sys.path[0] should be used by subinterpreters.
|
||||||
script = '''
|
script = '''
|
||||||
import sys
|
import sys
|
||||||
from test.support import interpreters
|
from concurrent import interpreters
|
||||||
|
|
||||||
orig = sys.path[0]
|
orig = sys.path[0]
|
||||||
|
|
||||||
@ -170,7 +170,7 @@ class FinalizationTests(TestBase):
|
|||||||
# is reported, even when subinterpreters get cleaned up at the end.
|
# is reported, even when subinterpreters get cleaned up at the end.
|
||||||
import subprocess
|
import subprocess
|
||||||
argv = [sys.executable, '-c', '''if True:
|
argv = [sys.executable, '-c', '''if True:
|
||||||
from test.support import interpreters
|
from concurrent import interpreters
|
||||||
interp = interpreters.create()
|
interp = interpreters.create()
|
||||||
raise Exception
|
raise Exception
|
||||||
''']
|
''']
|
||||||
|
@ -7,8 +7,8 @@ import unittest
|
|||||||
from test.support import import_helper, Py_DEBUG
|
from test.support import import_helper, Py_DEBUG
|
||||||
# Raise SkipTest if subinterpreters not supported.
|
# Raise SkipTest if subinterpreters not supported.
|
||||||
_queues = import_helper.import_module('_interpqueues')
|
_queues = import_helper.import_module('_interpqueues')
|
||||||
from test.support import interpreters
|
from concurrent import interpreters
|
||||||
from test.support.interpreters import queues, _crossinterp
|
from concurrent.interpreters import _queues as queues, _crossinterp
|
||||||
import test._crossinterp_definitions as defs
|
import test._crossinterp_definitions as defs
|
||||||
from .utils import _run_output, TestBase as _TestBase
|
from .utils import _run_output, TestBase as _TestBase
|
||||||
|
|
||||||
@ -127,7 +127,7 @@ class QueueTests(TestBase):
|
|||||||
|
|
||||||
interp = interpreters.create()
|
interp = interpreters.create()
|
||||||
interp.exec(dedent(f"""
|
interp.exec(dedent(f"""
|
||||||
from test.support.interpreters import queues
|
from concurrent.interpreters import _queues as queues
|
||||||
queue1 = queues.Queue({queue1.id})
|
queue1 = queues.Queue({queue1.id})
|
||||||
"""));
|
"""));
|
||||||
|
|
||||||
@ -325,7 +325,7 @@ class TestQueueOps(TestBase):
|
|||||||
def test_put_get_same_interpreter(self):
|
def test_put_get_same_interpreter(self):
|
||||||
interp = interpreters.create()
|
interp = interpreters.create()
|
||||||
interp.exec(dedent("""
|
interp.exec(dedent("""
|
||||||
from test.support.interpreters import queues
|
from concurrent.interpreters import _queues as queues
|
||||||
queue = queues.create()
|
queue = queues.create()
|
||||||
"""))
|
"""))
|
||||||
for methname in ('get', 'get_nowait'):
|
for methname in ('get', 'get_nowait'):
|
||||||
@ -352,7 +352,7 @@ class TestQueueOps(TestBase):
|
|||||||
out = _run_output(
|
out = _run_output(
|
||||||
interp,
|
interp,
|
||||||
dedent(f"""
|
dedent(f"""
|
||||||
from test.support.interpreters import queues
|
from concurrent.interpreters import _queues as queues
|
||||||
queue1 = queues.Queue({queue1.id})
|
queue1 = queues.Queue({queue1.id})
|
||||||
queue2 = queues.Queue({queue2.id})
|
queue2 = queues.Queue({queue2.id})
|
||||||
assert queue1.qsize() == 1, 'expected: queue1.qsize() == 1'
|
assert queue1.qsize() == 1, 'expected: queue1.qsize() == 1'
|
||||||
@ -391,7 +391,7 @@ class TestQueueOps(TestBase):
|
|||||||
interp = interpreters.create()
|
interp = interpreters.create()
|
||||||
|
|
||||||
_run_output(interp, dedent(f"""
|
_run_output(interp, dedent(f"""
|
||||||
from test.support.interpreters import queues
|
from concurrent.interpreters import _queues as queues
|
||||||
queue = queues.Queue({queue.id})
|
queue = queues.Queue({queue.id})
|
||||||
obj1 = b'spam'
|
obj1 = b'spam'
|
||||||
obj2 = b'eggs'
|
obj2 = b'eggs'
|
||||||
@ -469,7 +469,7 @@ class TestQueueOps(TestBase):
|
|||||||
queue = queues.create()
|
queue = queues.create()
|
||||||
interp = interpreters.create()
|
interp = interpreters.create()
|
||||||
_run_output(interp, dedent(f"""
|
_run_output(interp, dedent(f"""
|
||||||
from test.support.interpreters import queues
|
from concurrent.interpreters import _queues as queues
|
||||||
queue = queues.Queue({queue.id})
|
queue = queues.Queue({queue.id})
|
||||||
queue.put(1, unbounditems=queues.UNBOUND)
|
queue.put(1, unbounditems=queues.UNBOUND)
|
||||||
queue.put(2, unbounditems=queues.UNBOUND_ERROR)
|
queue.put(2, unbounditems=queues.UNBOUND_ERROR)
|
||||||
@ -505,14 +505,14 @@ class TestQueueOps(TestBase):
|
|||||||
|
|
||||||
queue.put(1)
|
queue.put(1)
|
||||||
_run_output(interp1, dedent(f"""
|
_run_output(interp1, dedent(f"""
|
||||||
from test.support.interpreters import queues
|
from concurrent.interpreters import _queues as queues
|
||||||
queue = queues.Queue({queue.id})
|
queue = queues.Queue({queue.id})
|
||||||
obj1 = queue.get()
|
obj1 = queue.get()
|
||||||
queue.put(2, unbounditems=queues.UNBOUND)
|
queue.put(2, unbounditems=queues.UNBOUND)
|
||||||
queue.put(obj1, unbounditems=queues.UNBOUND_REMOVE)
|
queue.put(obj1, unbounditems=queues.UNBOUND_REMOVE)
|
||||||
"""))
|
"""))
|
||||||
_run_output(interp2, dedent(f"""
|
_run_output(interp2, dedent(f"""
|
||||||
from test.support.interpreters import queues
|
from concurrent.interpreters import _queues as queues
|
||||||
queue = queues.Queue({queue.id})
|
queue = queues.Queue({queue.id})
|
||||||
obj2 = queue.get()
|
obj2 = queue.get()
|
||||||
obj1 = queue.get()
|
obj1 = queue.get()
|
||||||
|
@ -6,7 +6,7 @@ from test.support import import_helper
|
|||||||
from test.support import threading_helper
|
from test.support import threading_helper
|
||||||
# Raise SkipTest if subinterpreters not supported.
|
# Raise SkipTest if subinterpreters not supported.
|
||||||
import_helper.import_module('_interpreters')
|
import_helper.import_module('_interpreters')
|
||||||
from test.support import interpreters
|
from concurrent import interpreters
|
||||||
from .utils import TestBase
|
from .utils import TestBase
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ try:
|
|||||||
import _interpreters
|
import _interpreters
|
||||||
except ImportError as exc:
|
except ImportError as exc:
|
||||||
raise unittest.SkipTest(str(exc))
|
raise unittest.SkipTest(str(exc))
|
||||||
from test.support import interpreters
|
from concurrent import interpreters
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -24,7 +24,7 @@ from test.support import import_helper
|
|||||||
from test.support import force_not_colorized
|
from test.support import force_not_colorized
|
||||||
from test.support import SHORT_TIMEOUT
|
from test.support import SHORT_TIMEOUT
|
||||||
try:
|
try:
|
||||||
from test.support import interpreters
|
from concurrent import interpreters
|
||||||
except ImportError:
|
except ImportError:
|
||||||
interpreters = None
|
interpreters = None
|
||||||
import textwrap
|
import textwrap
|
||||||
|
@ -28,7 +28,7 @@ from test import lock_tests
|
|||||||
from test import support
|
from test import support
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from test.support import interpreters
|
from concurrent import interpreters
|
||||||
except ImportError:
|
except ImportError:
|
||||||
interpreters = None
|
interpreters = None
|
||||||
|
|
||||||
|
@ -2513,15 +2513,16 @@ class SubinterpreterTests(unittest.TestCase):
|
|||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
global interpreters
|
global interpreters
|
||||||
try:
|
try:
|
||||||
from test.support import interpreters
|
from concurrent import interpreters
|
||||||
except ModuleNotFoundError:
|
except ModuleNotFoundError:
|
||||||
raise unittest.SkipTest('subinterpreters required')
|
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
|
@cpython_only
|
||||||
@no_rerun('channels (and queues) might have a refleak; see gh-122199')
|
@no_rerun('channels (and queues) might have a refleak; see gh-122199')
|
||||||
def test_static_types_inherited_slots(self):
|
def test_static_types_inherited_slots(self):
|
||||||
rch, sch = interpreters.channels.create()
|
rch, sch = self.create_channel()
|
||||||
|
|
||||||
script = textwrap.dedent("""
|
script = textwrap.dedent("""
|
||||||
import test.support
|
import test.support
|
||||||
@ -2547,7 +2548,7 @@ class SubinterpreterTests(unittest.TestCase):
|
|||||||
main_results = collate_results(raw)
|
main_results = collate_results(raw)
|
||||||
|
|
||||||
interp = interpreters.create()
|
interp = interpreters.create()
|
||||||
interp.exec('from test.support import interpreters')
|
interp.exec('from concurrent import interpreters')
|
||||||
interp.prepare_main(sch=sch)
|
interp.prepare_main(sch=sch)
|
||||||
interp.exec(script)
|
interp.exec(script)
|
||||||
raw = rch.recv_nowait()
|
raw = rch.recv_nowait()
|
||||||
|
@ -2514,7 +2514,7 @@ XMLLIBSUBDIRS= xml xml/dom xml/etree xml/parsers xml/sax
|
|||||||
LIBSUBDIRS= asyncio \
|
LIBSUBDIRS= asyncio \
|
||||||
collections \
|
collections \
|
||||||
compression compression/_common compression/zstd \
|
compression compression/_common compression/zstd \
|
||||||
concurrent concurrent/futures \
|
concurrent concurrent/futures concurrent/interpreters \
|
||||||
csv \
|
csv \
|
||||||
ctypes ctypes/macholib \
|
ctypes ctypes/macholib \
|
||||||
curses \
|
curses \
|
||||||
@ -2573,7 +2573,6 @@ TESTSUBDIRS= idlelib/idle_test \
|
|||||||
test/subprocessdata \
|
test/subprocessdata \
|
||||||
test/support \
|
test/support \
|
||||||
test/support/_hypothesis_stubs \
|
test/support/_hypothesis_stubs \
|
||||||
test/support/interpreters \
|
|
||||||
test/test_asyncio \
|
test/test_asyncio \
|
||||||
test/test_capi \
|
test/test_capi \
|
||||||
test/test_cext \
|
test/test_cext \
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
Add the :mod:`concurrent.interpreters` module. See :pep:`734`.
|
@ -220,6 +220,22 @@ wait_for_lock(PyThread_type_lock mutex, PY_TIMEOUT_T timeout)
|
|||||||
return 0;
|
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 *************************************************************/
|
/* module state *************************************************************/
|
||||||
|
|
||||||
@ -2742,15 +2758,9 @@ _get_current_channelend_type(int end)
|
|||||||
}
|
}
|
||||||
if (cls == NULL) {
|
if (cls == NULL) {
|
||||||
// Force the module to be loaded, to register the type.
|
// Force the module to be loaded, to register the type.
|
||||||
PyObject *highlevel = PyImport_ImportModule("interpreters.channels");
|
if (ensure_highlevel_module_loaded() < 0) {
|
||||||
if (highlevel == NULL) {
|
return NULL;
|
||||||
PyErr_Clear();
|
|
||||||
highlevel = PyImport_ImportModule("test.support.interpreters.channels");
|
|
||||||
if (highlevel == NULL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Py_DECREF(highlevel);
|
|
||||||
if (end == CHANNEL_SEND) {
|
if (end == CHANNEL_SEND) {
|
||||||
cls = state->send_channel_type;
|
cls = state->send_channel_type;
|
||||||
}
|
}
|
||||||
|
@ -136,13 +136,10 @@ idarg_int64_converter(PyObject *arg, void *ptr)
|
|||||||
static int
|
static int
|
||||||
ensure_highlevel_module_loaded(void)
|
ensure_highlevel_module_loaded(void)
|
||||||
{
|
{
|
||||||
PyObject *highlevel = PyImport_ImportModule("interpreters.queues");
|
PyObject *highlevel =
|
||||||
|
PyImport_ImportModule("concurrent.interpreters._queues");
|
||||||
if (highlevel == NULL) {
|
if (highlevel == NULL) {
|
||||||
PyErr_Clear();
|
return -1;
|
||||||
highlevel = PyImport_ImportModule("test.support.interpreters.queues");
|
|
||||||
if (highlevel == NULL) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Py_DECREF(highlevel);
|
Py_DECREF(highlevel);
|
||||||
return 0;
|
return 0;
|
||||||
@ -299,7 +296,7 @@ add_QueueError(PyObject *mod)
|
|||||||
{
|
{
|
||||||
module_state *state = get_module_state(mod);
|
module_state *state = get_module_state(mod);
|
||||||
|
|
||||||
#define PREFIX "test.support.interpreters."
|
#define PREFIX "concurrent.interpreters."
|
||||||
#define ADD_EXCTYPE(NAME, BASE, DOC) \
|
#define ADD_EXCTYPE(NAME, BASE, DOC) \
|
||||||
assert(state->NAME == NULL); \
|
assert(state->NAME == NULL); \
|
||||||
if (add_exctype(mod, &state->NAME, PREFIX #NAME, DOC, BASE) < 0) { \
|
if (add_exctype(mod, &state->NAME, PREFIX #NAME, DOC, BASE) < 0) { \
|
||||||
|
@ -1788,9 +1788,9 @@ finally:
|
|||||||
|
|
||||||
/* To run some code in a sub-interpreter.
|
/* 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.
|
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 *
|
static PyObject *
|
||||||
run_in_subinterp_with_config(PyObject *self, PyObject *args, PyObject *kwargs)
|
run_in_subinterp_with_config(PyObject *self, PyObject *args, PyObject *kwargs)
|
||||||
|
@ -24,7 +24,7 @@ _ensure_current_cause(PyThreadState *tstate, PyObject *cause)
|
|||||||
|
|
||||||
static PyTypeObject _PyExc_InterpreterError = {
|
static PyTypeObject _PyExc_InterpreterError = {
|
||||||
PyVarObject_HEAD_INIT(NULL, 0)
|
PyVarObject_HEAD_INIT(NULL, 0)
|
||||||
.tp_name = "interpreters.InterpreterError",
|
.tp_name = "concurrent.interpreters.InterpreterError",
|
||||||
.tp_doc = PyDoc_STR("A cross-interpreter operation failed"),
|
.tp_doc = PyDoc_STR("A cross-interpreter operation failed"),
|
||||||
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
|
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
|
||||||
//.tp_traverse = ((PyTypeObject *)PyExc_Exception)->tp_traverse,
|
//.tp_traverse = ((PyTypeObject *)PyExc_Exception)->tp_traverse,
|
||||||
@ -37,7 +37,7 @@ PyObject *PyExc_InterpreterError = (PyObject *)&_PyExc_InterpreterError;
|
|||||||
|
|
||||||
static PyTypeObject _PyExc_InterpreterNotFoundError = {
|
static PyTypeObject _PyExc_InterpreterNotFoundError = {
|
||||||
PyVarObject_HEAD_INIT(NULL, 0)
|
PyVarObject_HEAD_INIT(NULL, 0)
|
||||||
.tp_name = "interpreters.InterpreterNotFoundError",
|
.tp_name = "concurrent.interpreters.InterpreterNotFoundError",
|
||||||
.tp_doc = PyDoc_STR("An interpreter was not found"),
|
.tp_doc = PyDoc_STR("An interpreter was not found"),
|
||||||
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
|
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
|
||||||
//.tp_traverse = ((PyTypeObject *)PyExc_Exception)->tp_traverse,
|
//.tp_traverse = ((PyTypeObject *)PyExc_Exception)->tp_traverse,
|
||||||
@ -51,7 +51,7 @@ PyObject *PyExc_InterpreterNotFoundError = (PyObject *)&_PyExc_InterpreterNotFou
|
|||||||
static int
|
static int
|
||||||
_init_notshareableerror(exceptions_t *state)
|
_init_notshareableerror(exceptions_t *state)
|
||||||
{
|
{
|
||||||
const char *name = "interpreters.NotShareableError";
|
const char *name = "concurrent.interpreters.NotShareableError";
|
||||||
PyObject *base = PyExc_TypeError;
|
PyObject *base = PyExc_TypeError;
|
||||||
PyObject *ns = NULL;
|
PyObject *ns = NULL;
|
||||||
PyObject *exctype = PyErr_NewException(name, base, ns);
|
PyObject *exctype = PyErr_NewException(name, base, ns);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user