Skip to content

Commit b0dcb10

Browse files
ericsnowcurrentlyGlyphack
authored andcommitted
pythongh-76785: Support Running Some Functions in Subinterpreters (pythongh-110251)
This specifically refers to `test.support.interpreters.Interpreter.run()`.
1 parent 56a89cb commit b0dcb10

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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy