Skip to content

Commit 1ce6ed5

Browse files
committed
tests: use modern interpreter API
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
1 parent 75f2d9c commit 1ce6ed5

1 file changed

Lines changed: 69 additions & 36 deletions

File tree

tests/test_multiple_interpreters.py

Lines changed: 69 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,71 @@
11
from __future__ import annotations
22

3+
import contextlib
34
import os
45
import pickle
56
import sys
67

78
import pytest
89

10+
CONCURRENT_INTERPRETERS_SUPPORT = sys.version_info >= (3, 14) and (
11+
sys.version_info != (3, 14, 0, "beta", 1)
12+
or sys.version_info != (3, 14, 0, "beta", 2)
13+
)
14+
15+
16+
def get_interpreters(*, modern: bool):
17+
if modern and CONCURRENT_INTERPRETERS_SUPPORT:
18+
from concurrent import interpreters
19+
20+
def create():
21+
return contextlib.closing(interpreters.create())
22+
23+
def run_string(
24+
interp: interpreters.Interpreter,
25+
code: str,
26+
*,
27+
shared: dict[str, object] | None = None,
28+
) -> Exception | None:
29+
if shared:
30+
interp.prepare_main(**shared)
31+
try:
32+
interp.exec(code)
33+
return None
34+
except interpreters.ExecutionFailed as err:
35+
return err
36+
37+
return run_string, create
38+
39+
if sys.version_info >= (3, 12):
40+
interpreters = pytest.importorskip(
41+
"_interpreters" if sys.version_info >= (3, 13) else "_xxsubinterpreters"
42+
)
43+
44+
@contextlib.contextmanager
45+
def create(config: str = ""):
46+
try:
47+
if config:
48+
interp = interpreters.create(config)
49+
else:
50+
interp = interpreters.create()
51+
except TypeError:
52+
pytest.skip(f"interpreters module needs to support {config} config")
53+
54+
try:
55+
yield interp
56+
finally:
57+
interpreters.destroy(interp)
58+
59+
def run_string(
60+
interp: int, code: str, shared: dict[str, object] | None = None
61+
) -> Exception | None:
62+
kwargs = {"shared": shared} if shared else {}
63+
return interpreters.run_string(interp, code, **kwargs)
64+
65+
return run_string, create
66+
67+
pytest.skip("Test requires the interpreters stdlib module")
68+
969

1070
@pytest.mark.skipif(
1171
sys.platform.startswith("emscripten"), reason="Requires loadable modules"
@@ -15,15 +75,7 @@ def test_independent_subinterpreters():
1575

1676
sys.path.append(".")
1777

18-
# This is supposed to be added in 3.14.0b3
19-
if sys.version_info >= (3, 15):
20-
import interpreters
21-
elif sys.version_info >= (3, 13):
22-
interpreters = pytest.importorskip("_interpreters")
23-
elif sys.version_info >= (3, 12):
24-
interpreters = pytest.importorskip("_xxsubinterpreters")
25-
else:
26-
pytest.skip("Test requires the interpreters stdlib module")
78+
run_string, create = get_interpreters(modern=True)
2779

2880
m = pytest.importorskip("mod_per_interpreter_gil")
2981

@@ -37,23 +89,21 @@ def test_independent_subinterpreters():
3789
pickle.dump(m.internals_at(), f)
3890
"""
3991

40-
interp1 = interpreters.create()
41-
interp2 = interpreters.create()
42-
try:
92+
with create() as interp1, create() as interp2:
4393
try:
44-
res0 = interpreters.run_string(interp1, "import mod_shared_interpreter_gil")
94+
res0 = run_string(interp1, "import mod_shared_interpreter_gil")
4595
if res0 is not None:
46-
res0 = res0.msg
96+
res0 = str(res0)
4797
except Exception as e:
4898
res0 = str(e)
4999

50100
pipei, pipeo = os.pipe()
51-
interpreters.run_string(interp1, code, shared={"pipeo": pipeo})
101+
run_string(interp1, code, shared={"pipeo": pipeo})
52102
with open(pipei, "rb") as f:
53103
res1 = pickle.load(f)
54104

55105
pipei, pipeo = os.pipe()
56-
interpreters.run_string(interp2, code, shared={"pipeo": pipeo})
106+
run_string(interp2, code, shared={"pipeo": pipeo})
57107
with open(pipei, "rb") as f:
58108
res2 = pickle.load(f)
59109

@@ -63,9 +113,6 @@ def test_independent_subinterpreters():
63113
assert m.internals_at() == m2.internals_at(), (
64114
"internals should be the same within the main interpreter"
65115
)
66-
finally:
67-
interpreters.destroy(interp1)
68-
interpreters.destroy(interp2)
69116

70117
assert "does not support loading in subinterpreters" in res0, (
71118
"cannot use shared_gil in a default subinterpreter"
@@ -90,14 +137,7 @@ def test_dependent_subinterpreters():
90137

91138
sys.path.append(".")
92139

93-
if sys.version_info >= (3, 15):
94-
import interpreters
95-
elif sys.version_info >= (3, 13):
96-
interpreters = pytest.importorskip("_interpreters")
97-
elif sys.version_info >= (3, 12):
98-
interpreters = pytest.importorskip("_xxsubinterpreters")
99-
else:
100-
pytest.skip("Test requires the interpreters stdlib module")
140+
run_string, create = get_interpreters(modern=False)
101141

102142
m = pytest.importorskip("mod_shared_interpreter_gil")
103143

@@ -111,14 +151,9 @@ def test_dependent_subinterpreters():
111151
pickle.dump(m.internals_at(), f)
112152
"""
113153

114-
try:
115-
interp1 = interpreters.create("legacy")
116-
except TypeError:
117-
pytest.skip("interpreters module needs to support legacy config")
118-
119-
try:
154+
with create("legacy") as interp1:
120155
pipei, pipeo = os.pipe()
121-
interpreters.run_string(interp1, code, shared={"pipeo": pipeo})
156+
run_string(interp1, code, shared={"pipeo": pipeo})
122157
with open(pipei, "rb") as f:
123158
res1 = pickle.load(f)
124159

@@ -128,8 +163,6 @@ def test_dependent_subinterpreters():
128163
assert m.internals_at() == m2.internals_at(), (
129164
"internals should be the same within the main interpreter"
130165
)
131-
finally:
132-
interpreters.destroy(interp1)
133166

134167
assert res1 != m.internals_at(), "internals should differ from main interpreter"
135168

0 commit comments

Comments
 (0)