Skip to content

Commit 92ca90b

Browse files
gh-76785: Support Running Some Functions in Subinterpreters (gh-110251)
This specifically refers to `test.support.interpreters.Interpreter.run()`.
1 parent de10522 commit 92ca90b

File tree

3 files changed

+439
-26
lines changed

3 files changed

+439
-26
lines changed

Lib/test/support/interpreters.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,12 +91,26 @@ def close(self):
9191
"""
9292
return _interpreters.destroy(self._id)
9393

94+
# XXX Rename "run" to "exec"?
9495
def run(self, src_str, /, *, channels=None):
9596
"""Run the given source code in the interpreter.
9697
97-
This blocks the current Python thread until done.
98+
This is essentially the same as calling the builtin "exec"
99+
with this interpreter, using the __dict__ of its __main__
100+
module as both globals and locals.
101+
102+
There is no return value.
103+
104+
If the code raises an unhandled exception then a RunFailedError
105+
is raised, which summarizes the unhandled exception. The actual
106+
exception is discarded because objects cannot be shared between
107+
interpreters.
108+
109+
This blocks the current Python thread until done. During
110+
that time, the previous interpreter is allowed to run
111+
in other threads.
98112
"""
99-
_interpreters.run_string(self._id, src_str, channels)
113+
_interpreters.exec(self._id, src_str, channels)
100114

101115

102116
def create_channel():

Lib/test/test__xxsubinterpreters.py

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -925,5 +925,110 @@ def f():
925925
self.assertEqual(retcode, 0)
926926

927927

928+
class RunFuncTests(TestBase):
929+
930+
def setUp(self):
931+
super().setUp()
932+
self.id = interpreters.create()
933+
934+
def test_success(self):
935+
r, w = os.pipe()
936+
def script():
937+
global w
938+
import contextlib
939+
with open(w, 'w', encoding="utf-8") as spipe:
940+
with contextlib.redirect_stdout(spipe):
941+
print('it worked!', end='')
942+
interpreters.run_func(self.id, script, shared=dict(w=w))
943+
944+
with open(r, encoding="utf-8") as outfile:
945+
out = outfile.read()
946+
947+
self.assertEqual(out, 'it worked!')
948+
949+
def test_in_thread(self):
950+
r, w = os.pipe()
951+
def script():
952+
global w
953+
import contextlib
954+
with open(w, 'w', encoding="utf-8") as spipe:
955+
with contextlib.redirect_stdout(spipe):
956+
print('it worked!', end='')
957+
def f():
958+
interpreters.run_func(self.id, script, shared=dict(w=w))
959+
t = threading.Thread(target=f)
960+
t.start()
961+
t.join()
962+
963+
with open(r, encoding="utf-8") as outfile:
964+
out = outfile.read()
965+
966+
self.assertEqual(out, 'it worked!')
967+
968+
def test_code_object(self):
969+
r, w = os.pipe()
970+
971+
def script():
972+
global w
973+
import contextlib
974+
with open(w, 'w', encoding="utf-8") as spipe:
975+
with contextlib.redirect_stdout(spipe):
976+
print('it worked!', end='')
977+
code = script.__code__
978+
interpreters.run_func(self.id, code, shared=dict(w=w))
979+
980+
with open(r, encoding="utf-8") as outfile:
981+
out = outfile.read()
982+
983+
self.assertEqual(out, 'it worked!')
984+
985+
def test_closure(self):
986+
spam = True
987+
def script():
988+
assert spam
989+
990+
with self.assertRaises(ValueError):
991+
interpreters.run_func(self.id, script)
992+
993+
# XXX This hasn't been fixed yet.
994+
@unittest.expectedFailure
995+
def test_return_value(self):
996+
def script():
997+
return 'spam'
998+
with self.assertRaises(ValueError):
999+
interpreters.run_func(self.id, script)
1000+
1001+
def test_args(self):
1002+
with self.subTest('args'):
1003+
def script(a, b=0):
1004+
assert a == b
1005+
with self.assertRaises(ValueError):
1006+
interpreters.run_func(self.id, script)
1007+
1008+
with self.subTest('*args'):
1009+
def script(*args):
1010+
assert not args
1011+
with self.assertRaises(ValueError):
1012+
interpreters.run_func(self.id, script)
1013+
1014+
with self.subTest('**kwargs'):
1015+
def script(**kwargs):
1016+
assert not kwargs
1017+
with self.assertRaises(ValueError):
1018+
interpreters.run_func(self.id, script)
1019+
1020+
with self.subTest('kwonly'):
1021+
def script(*, spam=True):
1022+
assert spam
1023+
with self.assertRaises(ValueError):
1024+
interpreters.run_func(self.id, script)
1025+
1026+
with self.subTest('posonly'):
1027+
def script(spam, /):
1028+
assert spam
1029+
with self.assertRaises(ValueError):
1030+
interpreters.run_func(self.id, script)
1031+
1032+
9281033
if __name__ == '__main__':
9291034
unittest.main()

0 commit comments

Comments
 (0)