summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>2023-07-27 21:51:34 (GMT)
committerGitHub <noreply@github.com>2023-07-27 21:51:34 (GMT)
commitabaf89d908304891c99c6a0450cf7e160c17cdd3 (patch)
treec05549d0e395f57e1a53eb47689b3e7d4ce0bb58
parent5daf19d763826a977a596b6fbc035ee03c0deafc (diff)
downloadcpython-abaf89d908304891c99c6a0450cf7e160c17cdd3.zip
cpython-abaf89d908304891c99c6a0450cf7e160c17cdd3.tar.gz
cpython-abaf89d908304891c99c6a0450cf7e160c17cdd3.tar.bz2
[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 75c974f5353685f338344618ad7344e64c2293d0) Co-authored-by: Eric Snow <ericsnowcurrently@gmail.com>
-rw-r--r--Lib/test/test_capi/check_config.py2
-rw-r--r--Lib/test/test_import/__init__.py42
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2023-07-24-11-11-41.gh-issue-104621.vM8Y_l.rst1
-rw-r--r--Python/import.c21
4 files changed, 50 insertions, 16 deletions
diff --git a/Lib/test/test_capi/check_config.py b/Lib/test/test_capi/check_config.py
index aaedd82..eb99ae1 100644
--- a/Lib/test/test_capi/check_config.py
+++ b/Lib/test/test_capi/check_config.py
@@ -12,7 +12,7 @@ def import_singlephase():
try:
import _testsinglephase
except ImportError:
- sys.modules.pop('_testsinglephase')
+ sys.modules.pop('_testsinglephase', None)
return False
else:
del sys.modules['_testsinglephase']
diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py
index ee9fe4d..cc6e8d4 100644
--- a/Lib/test/test_import/__init__.py
+++ b/Lib/test/test_import/__init__.py
@@ -97,7 +97,6 @@ def require_frozen(module, *, skip=True):
def require_pure_python(module, *, skip=False):
_require_loader(module, SourceFileLoader, skip)
-
def remove_files(name):
for f in (name + ".py",
name + ".pyc",
@@ -128,19 +127,34 @@ def _ready_to_import(name=None, source=""):
del sys.modules[name]
-def requires_subinterpreters(meth):
- """Decorator to skip a test if subinterpreters are not supported."""
- return unittest.skipIf(_interpreters is None,
- 'subinterpreters required')(meth)
+if _testsinglephase is not None:
+ def restore__testsinglephase(*, _orig=_testsinglephase):
+ # We started with the module imported and want to restore
+ # it to its nominal state.
+ _orig._clear_globals()
+ _testinternalcapi.clear_extension('_testsinglephase', _orig.__file__)
+ import _testsinglephase
def requires_singlephase_init(meth):
"""Decorator to skip if single-phase init modules are not supported."""
+ if not isinstance(meth, type):
+ def meth(self, _meth=meth):
+ try:
+ return _meth(self)
+ finally:
+ restore__testsinglephase()
meth = cpython_only(meth)
return unittest.skipIf(_testsinglephase is None,
'test requires _testsinglephase module')(meth)
+def requires_subinterpreters(meth):
+ """Decorator to skip a test if subinterpreters are not supported."""
+ return unittest.skipIf(_interpreters is None,
+ 'subinterpreters required')(meth)
+
+
class ModuleSnapshot(types.SimpleNamespace):
"""A representation of a module for testing.
@@ -1943,6 +1957,20 @@ class SubinterpImportTests(unittest.TestCase):
with self.subTest(f'{module}: strict, fresh'):
self.check_compatible_fresh(module, strict=True, isolated=True)
+ @requires_subinterpreters
+ @requires_singlephase_init
+ def test_disallowed_reimport(self):
+ # See https://github.com/python/cpython/issues/104621.
+ script = textwrap.dedent('''
+ import _testsinglephase
+ print(_testsinglephase)
+ ''')
+ interpid = _interpreters.create()
+ with self.assertRaises(_interpreters.RunFailedError):
+ _interpreters.run_string(interpid, script)
+ with self.assertRaises(_interpreters.RunFailedError):
+ _interpreters.run_string(interpid, script)
+
class TestSinglePhaseSnapshot(ModuleSnapshot):
@@ -2002,6 +2030,10 @@ class SinglephaseInitTests(unittest.TestCase):
# Start fresh.
cls.clean_up()
+ @classmethod
+ def tearDownClass(cls):
+ restore__testsinglephase()
+
def tearDown(self):
# Clean up the module.
self.clean_up()
diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-07-24-11-11-41.gh-issue-104621.vM8Y_l.rst b/Misc/NEWS.d/next/Core and Builtins/2023-07-24-11-11-41.gh-issue-104621.vM8Y_l.rst
new file mode 100644
index 0000000..86c9762
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2023-07-24-11-11-41.gh-issue-104621.vM8Y_l.rst
@@ -0,0 +1 @@
+Unsupported modules now always fail to be imported.
diff --git a/Python/import.c b/Python/import.c
index d10c5ce..a93a645 100644
--- a/Python/import.c
+++ b/Python/import.c
@@ -1222,6 +1222,15 @@ import_find_extension(PyThreadState *tstate, PyObject *name,
return NULL;
}
+ /* It may have been successfully imported previously
+ in an interpreter that allows legacy modules
+ but is not allowed in the current interpreter. */
+ const char *name_buf = PyUnicode_AsUTF8(name);
+ assert(name_buf != NULL);
+ if (_PyImport_CheckSubinterpIncompatibleExtensionAllowed(name_buf) < 0) {
+ return NULL;
+ }
+
PyObject *mod, *mdict;
PyObject *modules = MODULES(tstate->interp);
@@ -3712,16 +3721,8 @@ _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file)
PyThreadState *tstate = _PyThreadState_GET();
mod = import_find_extension(tstate, name, path);
- if (mod != NULL) {
- const char *name_buf = PyUnicode_AsUTF8(name);
- assert(name_buf != NULL);
- if (_PyImport_CheckSubinterpIncompatibleExtensionAllowed(name_buf) < 0) {
- Py_DECREF(mod);
- mod = NULL;
- }
- goto finally;
- }
- else if (PyErr_Occurred()) {
+ if (mod != NULL || _PyErr_Occurred(tstate)) {
+ assert(mod == NULL || !_PyErr_Occurred(tstate));
goto finally;
}