Skip to content

Commit 9d17cbf

Browse files
nanjekyejoannahnanjekyejoannahblurb-it[bot]
authored
bpo-32604: PEP 554 for use in test suite (GH-19985)
* PEP 554 for use in test suite * πŸ“œπŸ€– Added by blurb_it. * Fix space * Add doc to doc tree * Move to modules doc tree * Fix suspicious doc errors * Fix test__all * Docs docs docs * Support isolated and fix wait * Fix white space * Remove undefined from __all__ * Fix recv and add exceptions * Remove unused exceptions, fix pep 8 formatting errors and fix _NOT_SET in recv_nowait() Co-authored-by: nanjekyejoannah <[email protected]> Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
1 parent c105f7d commit 9d17cbf

File tree

4 files changed

+865
-0
lines changed

4 files changed

+865
-0
lines changed

β€ŽLib/test/support/interpreters.py

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
"""Subinterpreters High Level Module."""
2+
3+
import _xxsubinterpreters as _interpreters
4+
5+
# aliases:
6+
from _xxsubinterpreters import (
7+
ChannelError, ChannelNotFoundError, ChannelEmptyError,
8+
is_shareable,
9+
)
10+
11+
12+
__all__ = [
13+
'Interpreter', 'get_current', 'get_main', 'create', 'list_all',
14+
'SendChannel', 'RecvChannel',
15+
'create_channel', 'list_all_channels', 'is_shareable',
16+
'ChannelError', 'ChannelNotFoundError',
17+
'ChannelEmptyError',
18+
]
19+
20+
21+
def create(*, isolated=True):
22+
"""
23+
Initialize a new (idle) Python interpreter.
24+
"""
25+
id = _interpreters.create(isolated=isolated)
26+
return Interpreter(id, isolated=isolated)
27+
28+
29+
def list_all():
30+
"""
31+
Get all existing interpreters.
32+
"""
33+
return [Interpreter(id) for id in
34+
_interpreters.list_all()]
35+
36+
37+
def get_current():
38+
"""
39+
Get the currently running interpreter.
40+
"""
41+
id = _interpreters.get_current()
42+
return Interpreter(id)
43+
44+
45+
def get_main():
46+
"""
47+
Get the main interpreter.
48+
"""
49+
id = _interpreters.get_main()
50+
return Interpreter(id)
51+
52+
53+
class Interpreter:
54+
"""
55+
The Interpreter object represents
56+
a single interpreter.
57+
"""
58+
59+
def __init__(self, id, *, isolated=None):
60+
self._id = id
61+
self._isolated = isolated
62+
63+
@property
64+
def id(self):
65+
return self._id
66+
67+
@property
68+
def isolated(self):
69+
if self._isolated is None:
70+
self._isolated = _interpreters.is_isolated(self._id)
71+
return self._isolated
72+
73+
def is_running(self):
74+
"""
75+
Return whether or not the identified
76+
interpreter is running.
77+
"""
78+
return _interpreters.is_running(self._id)
79+
80+
def close(self):
81+
"""
82+
Finalize and destroy the interpreter.
83+
84+
Attempting to destroy the current
85+
interpreter results in a RuntimeError.
86+
"""
87+
return _interpreters.destroy(self._id)
88+
89+
def run(self, src_str, /, *, channels=None):
90+
"""
91+
Run the given source code in the interpreter.
92+
This blocks the current Python thread until done.
93+
"""
94+
_interpreters.run_string(self._id, src_str)
95+
96+
97+
def create_channel():
98+
"""
99+
Create a new channel for passing data between
100+
interpreters.
101+
"""
102+
103+
cid = _interpreters.channel_create()
104+
return (RecvChannel(cid), SendChannel(cid))
105+
106+
107+
def list_all_channels():
108+
"""
109+
Get all open channels.
110+
"""
111+
return [(RecvChannel(cid), SendChannel(cid))
112+
for cid in _interpreters.channel_list_all()]
113+
114+
115+
_NOT_SET = object()
116+
117+
118+
class RecvChannel:
119+
"""
120+
The RecvChannel object represents
121+
a recieving channel.
122+
"""
123+
124+
def __init__(self, id):
125+
self._id = id
126+
127+
def recv(self, *, _delay=10 / 1000): # 10 milliseconds
128+
"""
129+
Get the next object from the channel,
130+
and wait if none have been sent.
131+
Associate the interpreter with the channel.
132+
"""
133+
import time
134+
sentinel = object()
135+
obj = _interpreters.channel_recv(self._id, sentinel)
136+
while obj is sentinel:
137+
time.sleep(_delay)
138+
obj = _interpreters.channel_recv(self._id, sentinel)
139+
return obj
140+
141+
def recv_nowait(self, default=_NOT_SET):
142+
"""
143+
Like recv(), but return the default
144+
instead of waiting.
145+
146+
This function is blocked by a missing low-level
147+
implementation of channel_recv_wait().
148+
"""
149+
if default is _NOT_SET:
150+
return _interpreters.channel_recv(self._id)
151+
else:
152+
return _interpreters.channel_recv(self._id, default)
153+
154+
155+
class SendChannel:
156+
"""
157+
The SendChannel object represents
158+
a sending channel.
159+
"""
160+
161+
def __init__(self, id):
162+
self._id = id
163+
164+
def send(self, obj):
165+
"""
166+
Send the object (i.e. its data) to the receiving
167+
end of the channel and wait. Associate the interpreter
168+
with the channel.
169+
"""
170+
import time
171+
_interpreters.channel_send(self._id, obj)
172+
time.sleep(2)
173+
174+
def send_nowait(self, obj):
175+
"""
176+
Like send(), but return False if not received.
177+
178+
This function is blocked by a missing low-level
179+
implementation of channel_send_wait().
180+
"""
181+
182+
_interpreters.channel_send(self._id, obj)
183+
return False

β€ŽLib/test/support/interpreters.rst

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
High-level implementation of Subinterpreters
2+
============================================
3+
4+
**Source code:** :source:`Lib/test/support/_interpreters.py`
5+
6+
--------------
7+
8+
This module provides high-level tools for working with sub-interpreters,
9+
such as creating them, running code in them, or sending data between them.
10+
It is a wrapper around the low-level ``__xxsubinterpreters`` module.
11+
12+
.. versionchanged:: added in 3.9
13+
14+
Interpreter Objects
15+
-------------------
16+
17+
The ``Interpreter`` object represents a single interpreter.
18+
19+
.. class:: Interpreter(id)
20+
21+
The class implementing a subinterpreter object.
22+
23+
.. method:: is_running()
24+
25+
Return ``True`` if the identified interpreter is running.
26+
27+
.. method:: close()
28+
29+
Destroy the interpreter. Attempting to destroy the current
30+
interpreter results in a `RuntimeError`.
31+
32+
.. method:: run(self, src_str, /, *, channels=None):
33+
34+
Run the given source code in the interpreter. This blocks
35+
the current thread until done. ``channels`` should be in
36+
the form : `(RecvChannel, SendChannel)`.
37+
38+
RecvChannel Objects
39+
-------------------
40+
41+
The ``RecvChannel`` object represents a recieving channel.
42+
43+
.. class:: RecvChannel(id)
44+
45+
This class represents the receiving end of a channel.
46+
47+
.. method:: recv()
48+
49+
Get the next object from the channel, and wait if
50+
none have been sent. Associate the interpreter
51+
with the channel.
52+
53+
.. method:: recv_nowait(default=None)
54+
55+
Like ``recv()``, but return the default result
56+
instead of waiting.
57+
58+
59+
SendChannel Objects
60+
--------------------
61+
62+
The ``SendChannel`` object represents a sending channel.
63+
64+
.. class:: SendChannel(id)
65+
66+
This class represents the sending end of a channel.
67+
68+
.. method:: send(obj)
69+
70+
Send the object ``obj`` to the receiving end of the channel
71+
and wait. Associate the interpreter with the channel.
72+
73+
.. method:: send_nowait(obj)
74+
75+
Similar to ``send()``, but returns ``False`` if
76+
*obj* is not immediately received instead of blocking.
77+
78+
79+
This module defines the following global functions:
80+
81+
82+
.. function:: is_shareable(obj)
83+
84+
Return ``True`` if the object's data can be shared between
85+
interpreters.
86+
87+
.. function:: create_channel()
88+
89+
Create a new channel for passing data between interpreters.
90+
91+
.. function:: list_all_channels()
92+
93+
Return all open channels.
94+
95+
.. function:: create(*, isolated=True)
96+
97+
Initialize a new (idle) Python interpreter. Get the currently
98+
running interpreter. This method returns an ``Interpreter`` object.
99+
100+
.. function:: get_current()
101+
102+
Get the currently running interpreter. This method returns
103+
an ``Interpreter`` object.
104+
105+
.. function:: get_main()
106+
107+
Get the main interpreter. This method returns
108+
an ``Interpreter`` object.
109+
110+
.. function:: list_all()
111+
112+
Get all existing interpreters. Returns a list
113+
of ``Interpreter`` objects.
114+
115+
This module also defines the following exceptions.
116+
117+
.. exception:: RunFailedError
118+
119+
This exception, a subclass of :exc:`RuntimeError`, is raised when the
120+
``Interpreter.run()`` results in an uncaught exception.
121+
122+
.. exception:: ChannelError
123+
124+
This exception is a subclass of :exc:`Exception`, and is the base
125+
class for all channel-related exceptions.
126+
127+
.. exception:: ChannelNotFoundError
128+
129+
This exception is a subclass of :exc:`ChannelError`, and is raised
130+
when the the identified channel is not found.
131+
132+
.. exception:: ChannelEmptyError
133+
134+
This exception is a subclass of :exc:`ChannelError`, and is raised when
135+
the channel is unexpectedly empty.
136+
137+
.. exception:: ChannelNotEmptyError
138+
139+
This exception is a subclass of :exc:`ChannelError`, and is raised when
140+
the channel is unexpectedly not empty.
141+
142+
.. exception:: NotReceivedError
143+
144+
This exception is a subclass of :exc:`ChannelError`, and is raised when
145+
nothing was waiting to receive a sent object.

0 commit comments

Comments
Β (0)