Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 86a77f4

Browse filesBrowse files
pythongh-76785: Fixes for test.support.interpreters (pythongh-112982)
This involves a number of changes for PEP 734.
1 parent f26bfe4 commit 86a77f4
Copy full SHA for 86a77f4
Expand file treeCollapse file tree

30 files changed

+2506
-1507
lines changed

‎.github/CODEOWNERS

Copy file name to clipboardExpand all lines: .github/CODEOWNERS
+14-1Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,11 @@ Python/traceback.c @iritkatriel
7878
**/*importlib/resources/* @jaraco @warsaw @FFY00
7979
**/importlib/metadata/* @jaraco @warsaw
8080

81+
# Subinterpreters
82+
Lib/test/support/interpreters/** @ericsnowcurrently
83+
Modules/_xx*interp*module.c @ericsnowcurrently
84+
Lib/test/test_interpreters/** @ericsnowcurrently
85+
8186
# Dates and times
8287
**/*datetime* @pganssle @abalkin
8388
**/*str*time* @pganssle @abalkin
@@ -148,7 +153,15 @@ Doc/c-api/stable.rst @encukou
148153
**/*itertools* @rhettinger
149154
**/*collections* @rhettinger
150155
**/*random* @rhettinger
151-
**/*queue* @rhettinger
156+
Doc/**/*queue* @rhettinger
157+
PCbuild/**/*queue* @rhettinger
158+
Modules/_queuemodule.c @rhettinger
159+
Lib/*queue*.py @rhettinger
160+
Lib/asyncio/*queue*.py @rhettinger
161+
Lib/multiprocessing/*queue*.py @rhettinger
162+
Lib/test/*queue*.py @rhettinger
163+
Lib/test_asyncio/*queue*.py @rhettinger
164+
Lib/test_multiprocessing/*queue*.py @rhettinger
152165
**/*bisect* @rhettinger
153166
**/*heapq* @rhettinger
154167
**/*functools* @rhettinger

‎Include/internal/pycore_crossinterp.h

Copy file name to clipboardExpand all lines: Include/internal/pycore_crossinterp.h
+10Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@ extern "C" {
1111
#include "pycore_lock.h" // PyMutex
1212
#include "pycore_pyerrors.h"
1313

14+
/**************/
15+
/* exceptions */
16+
/**************/
17+
18+
PyAPI_DATA(PyObject *) PyExc_InterpreterError;
19+
PyAPI_DATA(PyObject *) PyExc_InterpreterNotFoundError;
20+
1421

1522
/***************************/
1623
/* cross-interpreter calls */
@@ -160,6 +167,9 @@ struct _xi_state {
160167
extern PyStatus _PyXI_Init(PyInterpreterState *interp);
161168
extern void _PyXI_Fini(PyInterpreterState *interp);
162169

170+
extern PyStatus _PyXI_InitTypes(PyInterpreterState *interp);
171+
extern void _PyXI_FiniTypes(PyInterpreterState *interp);
172+
163173

164174
/***************************/
165175
/* short-term data sharing */

‎Include/internal/pycore_interp.h

Copy file name to clipboardExpand all lines: Include/internal/pycore_interp.h
+3-3Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -250,9 +250,9 @@ _PyInterpreterState_SetFinalizing(PyInterpreterState *interp, PyThreadState *tst
250250
// Export for the _xxinterpchannels module.
251251
PyAPI_FUNC(PyInterpreterState *) _PyInterpreterState_LookUpID(int64_t);
252252

253-
extern int _PyInterpreterState_IDInitref(PyInterpreterState *);
254-
extern int _PyInterpreterState_IDIncref(PyInterpreterState *);
255-
extern void _PyInterpreterState_IDDecref(PyInterpreterState *);
253+
PyAPI_FUNC(int) _PyInterpreterState_IDInitref(PyInterpreterState *);
254+
PyAPI_FUNC(int) _PyInterpreterState_IDIncref(PyInterpreterState *);
255+
PyAPI_FUNC(void) _PyInterpreterState_IDDecref(PyInterpreterState *);
256256

257257
extern const PyConfig* _PyInterpreterState_GetConfig(PyInterpreterState *interp);
258258

+160Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
"""Subinterpreters High Level Module."""
2+
3+
import threading
4+
import weakref
5+
import _xxsubinterpreters as _interpreters
6+
7+
# aliases:
8+
from _xxsubinterpreters import (
9+
InterpreterError, InterpreterNotFoundError,
10+
is_shareable,
11+
)
12+
13+
14+
__all__ = [
15+
'get_current', 'get_main', 'create', 'list_all', 'is_shareable',
16+
'Interpreter',
17+
'InterpreterError', 'InterpreterNotFoundError', 'ExecFailure',
18+
'create_queue', 'Queue', 'QueueEmpty', 'QueueFull',
19+
]
20+
21+
22+
_queuemod = None
23+
24+
def __getattr__(name):
25+
if name in ('Queue', 'QueueEmpty', 'QueueFull', 'create_queue'):
26+
global create_queue, Queue, QueueEmpty, QueueFull
27+
ns = globals()
28+
from .queues import (
29+
create as create_queue,
30+
Queue, QueueEmpty, QueueFull,
31+
)
32+
return ns[name]
33+
else:
34+
raise AttributeError(name)
35+
36+
37+
class ExecFailure(RuntimeError):
38+
39+
def __init__(self, excinfo):
40+
msg = excinfo.formatted
41+
if not msg:
42+
if excinfo.type and snapshot.msg:
43+
msg = f'{snapshot.type.__name__}: {snapshot.msg}'
44+
else:
45+
msg = snapshot.type.__name__ or snapshot.msg
46+
super().__init__(msg)
47+
self.snapshot = excinfo
48+
49+
50+
def create():
51+
"""Return a new (idle) Python interpreter."""
52+
id = _interpreters.create(isolated=True)
53+
return Interpreter(id)
54+
55+
56+
def list_all():
57+
"""Return all existing interpreters."""
58+
return [Interpreter(id) for id in _interpreters.list_all()]
59+
60+
61+
def get_current():
62+
"""Return the currently running interpreter."""
63+
id = _interpreters.get_current()
64+
return Interpreter(id)
65+
66+
67+
def get_main():
68+
"""Return the main interpreter."""
69+
id = _interpreters.get_main()
70+
return Interpreter(id)
71+
72+
73+
_known = weakref.WeakValueDictionary()
74+
75+
class Interpreter:
76+
"""A single Python interpreter."""
77+
78+
def __new__(cls, id, /):
79+
# There is only one instance for any given ID.
80+
if not isinstance(id, int):
81+
raise TypeError(f'id must be an int, got {id!r}')
82+
id = int(id)
83+
try:
84+
self = _known[id]
85+
assert hasattr(self, '_ownsref')
86+
except KeyError:
87+
# This may raise InterpreterNotFoundError:
88+
_interpreters._incref(id)
89+
try:
90+
self = super().__new__(cls)
91+
self._id = id
92+
self._ownsref = True
93+
except BaseException:
94+
_interpreters._deccref(id)
95+
raise
96+
_known[id] = self
97+
return self
98+
99+
def __repr__(self):
100+
return f'{type(self).__name__}({self.id})'
101+
102+
def __hash__(self):
103+
return hash(self._id)
104+
105+
def __del__(self):
106+
self._decref()
107+
108+
def _decref(self):
109+
if not self._ownsref:
110+
return
111+
self._ownsref = False
112+
try:
113+
_interpreters._decref(self.id)
114+
except InterpreterNotFoundError:
115+
pass
116+
117+
@property
118+
def id(self):
119+
return self._id
120+
121+
def is_running(self):
122+
"""Return whether or not the identified interpreter is running."""
123+
return _interpreters.is_running(self._id)
124+
125+
def close(self):
126+
"""Finalize and destroy the interpreter.
127+
128+
Attempting to destroy the current interpreter results
129+
in a RuntimeError.
130+
"""
131+
return _interpreters.destroy(self._id)
132+
133+
def exec_sync(self, code, /, channels=None):
134+
"""Run the given source code in the interpreter.
135+
136+
This is essentially the same as calling the builtin "exec"
137+
with this interpreter, using the __dict__ of its __main__
138+
module as both globals and locals.
139+
140+
There is no return value.
141+
142+
If the code raises an unhandled exception then an ExecFailure
143+
is raised, which summarizes the unhandled exception. The actual
144+
exception is discarded because objects cannot be shared between
145+
interpreters.
146+
147+
This blocks the current Python thread until done. During
148+
that time, the previous interpreter is allowed to run
149+
in other threads.
150+
"""
151+
excinfo = _interpreters.exec(self._id, code, channels)
152+
if excinfo is not None:
153+
raise ExecFailure(excinfo)
154+
155+
def run(self, code, /, channels=None):
156+
def task():
157+
self.exec_sync(code, channels=channels)
158+
t = threading.Thread(target=task)
159+
t.start()
160+
return t

‎Lib/test/support/interpreters.py renamed to ‎Lib/test/support/interpreters/channels.py

Copy file name to clipboardExpand all lines: Lib/test/support/interpreters/channels.py
+6-118Lines changed: 6 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -1,135 +1,23 @@
1-
"""Subinterpreters High Level Module."""
1+
"""Cross-interpreter Channels High Level Module."""
22

33
import time
4-
import _xxsubinterpreters as _interpreters
54
import _xxinterpchannels as _channels
65

76
# aliases:
8-
from _xxsubinterpreters import is_shareable
97
from _xxinterpchannels import (
108
ChannelError, ChannelNotFoundError, ChannelClosedError,
119
ChannelEmptyError, ChannelNotEmptyError,
1210
)
1311

1412

1513
__all__ = [
16-
'Interpreter', 'get_current', 'get_main', 'create', 'list_all',
17-
'RunFailedError',
14+
'create', 'list_all',
1815
'SendChannel', 'RecvChannel',
19-
'create_channel', 'list_all_channels', 'is_shareable',
20-
'ChannelError', 'ChannelNotFoundError',
21-
'ChannelEmptyError',
22-
]
16+
'ChannelError', 'ChannelNotFoundError', 'ChannelEmptyError',
17+
]
2318

2419

25-
class RunFailedError(RuntimeError):
26-
27-
def __init__(self, excinfo):
28-
msg = excinfo.formatted
29-
if not msg:
30-
if excinfo.type and snapshot.msg:
31-
msg = f'{snapshot.type.__name__}: {snapshot.msg}'
32-
else:
33-
msg = snapshot.type.__name__ or snapshot.msg
34-
super().__init__(msg)
35-
self.snapshot = excinfo
36-
37-
38-
def create(*, isolated=True):
39-
"""Return a new (idle) Python interpreter."""
40-
id = _interpreters.create(isolated=isolated)
41-
return Interpreter(id, isolated=isolated)
42-
43-
44-
def list_all():
45-
"""Return all existing interpreters."""
46-
return [Interpreter(id) for id in _interpreters.list_all()]
47-
48-
49-
def get_current():
50-
"""Return the currently running interpreter."""
51-
id = _interpreters.get_current()
52-
return Interpreter(id)
53-
54-
55-
def get_main():
56-
"""Return the main interpreter."""
57-
id = _interpreters.get_main()
58-
return Interpreter(id)
59-
60-
61-
class Interpreter:
62-
"""A single Python interpreter."""
63-
64-
def __init__(self, id, *, isolated=None):
65-
if not isinstance(id, (int, _interpreters.InterpreterID)):
66-
raise TypeError(f'id must be an int, got {id!r}')
67-
self._id = id
68-
self._isolated = isolated
69-
70-
def __repr__(self):
71-
data = dict(id=int(self._id), isolated=self._isolated)
72-
kwargs = (f'{k}={v!r}' for k, v in data.items())
73-
return f'{type(self).__name__}({", ".join(kwargs)})'
74-
75-
def __hash__(self):
76-
return hash(self._id)
77-
78-
def __eq__(self, other):
79-
if not isinstance(other, Interpreter):
80-
return NotImplemented
81-
else:
82-
return other._id == self._id
83-
84-
@property
85-
def id(self):
86-
return self._id
87-
88-
@property
89-
def isolated(self):
90-
if self._isolated is None:
91-
# XXX The low-level function has not been added yet.
92-
# See bpo-....
93-
self._isolated = _interpreters.is_isolated(self._id)
94-
return self._isolated
95-
96-
def is_running(self):
97-
"""Return whether or not the identified interpreter is running."""
98-
return _interpreters.is_running(self._id)
99-
100-
def close(self):
101-
"""Finalize and destroy the interpreter.
102-
103-
Attempting to destroy the current interpreter results
104-
in a RuntimeError.
105-
"""
106-
return _interpreters.destroy(self._id)
107-
108-
# XXX Rename "run" to "exec"?
109-
def run(self, src_str, /, channels=None):
110-
"""Run the given source code in the interpreter.
111-
112-
This is essentially the same as calling the builtin "exec"
113-
with this interpreter, using the __dict__ of its __main__
114-
module as both globals and locals.
115-
116-
There is no return value.
117-
118-
If the code raises an unhandled exception then a RunFailedError
119-
is raised, which summarizes the unhandled exception. The actual
120-
exception is discarded because objects cannot be shared between
121-
interpreters.
122-
123-
This blocks the current Python thread until done. During
124-
that time, the previous interpreter is allowed to run
125-
in other threads.
126-
"""
127-
excinfo = _interpreters.exec(self._id, src_str, channels)
128-
if excinfo is not None:
129-
raise RunFailedError(excinfo)
130-
131-
132-
def create_channel():
20+
def create():
13321
"""Return (recv, send) for a new cross-interpreter channel.
13422
13523
The channel may be used to pass data safely between interpreters.
@@ -139,7 +27,7 @@ def create_channel():
13927
return recv, send
14028

14129

142-
def list_all_channels():
30+
def list_all():
14331
"""Return a list of (recv, send) for all open channels."""
14432
return [(RecvChannel(cid), SendChannel(cid))
14533
for cid in _channels.list_all()]

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.