Skip to content

Commit abaf89d

Browse files
[3.12] gh-104621: Check for Incompatible Extensions in import_find_extension() (gh-107184) (gh-107360)
gh-104621: Check for Incompatible Extensions in import_find_extension() (gh-107184) This fixes a bug where incompatible modules could still be imported if attempted multiple times. (cherry picked from commit 75c974f) Co-authored-by: Eric Snow <[email protected]>
1 parent 5daf19d commit abaf89d

File tree

4 files changed

+50
-16
lines changed

4 files changed

+50
-16
lines changed

Lib/test/test_capi/check_config.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ def import_singlephase():
1212
try:
1313
import _testsinglephase
1414
except ImportError:
15-
sys.modules.pop('_testsinglephase')
15+
sys.modules.pop('_testsinglephase', None)
1616
return False
1717
else:
1818
del sys.modules['_testsinglephase']

Lib/test/test_import/__init__.py

+37-5
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,6 @@ def require_frozen(module, *, skip=True):
9797
def require_pure_python(module, *, skip=False):
9898
_require_loader(module, SourceFileLoader, skip)
9999

100-
101100
def remove_files(name):
102101
for f in (name + ".py",
103102
name + ".pyc",
@@ -128,19 +127,34 @@ def _ready_to_import(name=None, source=""):
128127
del sys.modules[name]
129128

130129

131-
def requires_subinterpreters(meth):
132-
"""Decorator to skip a test if subinterpreters are not supported."""
133-
return unittest.skipIf(_interpreters is None,
134-
'subinterpreters required')(meth)
130+
if _testsinglephase is not None:
131+
def restore__testsinglephase(*, _orig=_testsinglephase):
132+
# We started with the module imported and want to restore
133+
# it to its nominal state.
134+
_orig._clear_globals()
135+
_testinternalcapi.clear_extension('_testsinglephase', _orig.__file__)
136+
import _testsinglephase
135137

136138

137139
def requires_singlephase_init(meth):
138140
"""Decorator to skip if single-phase init modules are not supported."""
141+
if not isinstance(meth, type):
142+
def meth(self, _meth=meth):
143+
try:
144+
return _meth(self)
145+
finally:
146+
restore__testsinglephase()
139147
meth = cpython_only(meth)
140148
return unittest.skipIf(_testsinglephase is None,
141149
'test requires _testsinglephase module')(meth)
142150

143151

152+
def requires_subinterpreters(meth):
153+
"""Decorator to skip a test if subinterpreters are not supported."""
154+
return unittest.skipIf(_interpreters is None,
155+
'subinterpreters required')(meth)
156+
157+
144158
class ModuleSnapshot(types.SimpleNamespace):
145159
"""A representation of a module for testing.
146160
@@ -1943,6 +1957,20 @@ def test_isolated_config(self):
19431957
with self.subTest(f'{module}: strict, fresh'):
19441958
self.check_compatible_fresh(module, strict=True, isolated=True)
19451959

1960+
@requires_subinterpreters
1961+
@requires_singlephase_init
1962+
def test_disallowed_reimport(self):
1963+
# See https://github.com/python/cpython/issues/104621.
1964+
script = textwrap.dedent('''
1965+
import _testsinglephase
1966+
print(_testsinglephase)
1967+
''')
1968+
interpid = _interpreters.create()
1969+
with self.assertRaises(_interpreters.RunFailedError):
1970+
_interpreters.run_string(interpid, script)
1971+
with self.assertRaises(_interpreters.RunFailedError):
1972+
_interpreters.run_string(interpid, script)
1973+
19461974

19471975
class TestSinglePhaseSnapshot(ModuleSnapshot):
19481976

@@ -2002,6 +2030,10 @@ def setUpClass(cls):
20022030
# Start fresh.
20032031
cls.clean_up()
20042032

2033+
@classmethod
2034+
def tearDownClass(cls):
2035+
restore__testsinglephase()
2036+
20052037
def tearDown(self):
20062038
# Clean up the module.
20072039
self.clean_up()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Unsupported modules now always fail to be imported.

Python/import.c

+11-10
Original file line numberDiff line numberDiff line change
@@ -1222,6 +1222,15 @@ import_find_extension(PyThreadState *tstate, PyObject *name,
12221222
return NULL;
12231223
}
12241224

1225+
/* It may have been successfully imported previously
1226+
in an interpreter that allows legacy modules
1227+
but is not allowed in the current interpreter. */
1228+
const char *name_buf = PyUnicode_AsUTF8(name);
1229+
assert(name_buf != NULL);
1230+
if (_PyImport_CheckSubinterpIncompatibleExtensionAllowed(name_buf) < 0) {
1231+
return NULL;
1232+
}
1233+
12251234
PyObject *mod, *mdict;
12261235
PyObject *modules = MODULES(tstate->interp);
12271236

@@ -3712,16 +3721,8 @@ _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file)
37123721

37133722
PyThreadState *tstate = _PyThreadState_GET();
37143723
mod = import_find_extension(tstate, name, path);
3715-
if (mod != NULL) {
3716-
const char *name_buf = PyUnicode_AsUTF8(name);
3717-
assert(name_buf != NULL);
3718-
if (_PyImport_CheckSubinterpIncompatibleExtensionAllowed(name_buf) < 0) {
3719-
Py_DECREF(mod);
3720-
mod = NULL;
3721-
}
3722-
goto finally;
3723-
}
3724-
else if (PyErr_Occurred()) {
3724+
if (mod != NULL || _PyErr_Occurred(tstate)) {
3725+
assert(mod == NULL || !_PyErr_Occurred(tstate));
37253726
goto finally;
37263727
}
37273728

0 commit comments

Comments
 (0)