summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorEric Snow <ericsnowcurrently@gmail.com>2023-02-15 23:05:07 (GMT)
committerGitHub <noreply@github.com>2023-02-15 23:05:07 (GMT)
commitb365d88465d9228ce4e9e0be20b88e9e4056ad88 (patch)
treec2f746555097abede7a9461cc7ae0cc1022ca272 /Lib
parentb2fc5492789623d656953d458f3eeaac03c1ef56 (diff)
downloadcpython-b365d88465d9228ce4e9e0be20b88e9e4056ad88.zip
cpython-b365d88465d9228ce4e9e0be20b88e9e4056ad88.tar.gz
cpython-b365d88465d9228ce4e9e0be20b88e9e4056ad88.tar.bz2
gh-101758: Add a Test For Single-Phase Init Modules in Multiple Interpreters (gh-101920)
The test verifies the behavior of single-phase init modules when loaded in multiple interpreters. https://github.com/python/cpython/issues/101758
Diffstat (limited to 'Lib')
-rw-r--r--Lib/test/test_imp.py73
1 files changed, 73 insertions, 0 deletions
diff --git a/Lib/test/test_imp.py b/Lib/test/test_imp.py
index c85ab92..e81eb6f 100644
--- a/Lib/test/test_imp.py
+++ b/Lib/test/test_imp.py
@@ -10,10 +10,13 @@ from test.support import import_helper
from test.support import os_helper
from test.support import script_helper
from test.support import warnings_helper
+import textwrap
import unittest
import warnings
imp = warnings_helper.import_deprecated('imp')
import _imp
+import _testinternalcapi
+import _xxsubinterpreters as _interpreters
OS_PATH_NAME = os.path.__name__
@@ -252,6 +255,71 @@ class ImportTests(unittest.TestCase):
imp.load_dynamic('nonexistent', pathname)
@requires_load_dynamic
+ def test_singlephase_multiple_interpreters(self):
+ # Currently, for every single-phrase init module loaded
+ # in multiple interpreters, those interpreters share a
+ # PyModuleDef for that object, which can be a problem.
+
+ # This single-phase module has global state, which is shared
+ # by the interpreters.
+ import _testsinglephase
+ name = _testsinglephase.__name__
+ filename = _testsinglephase.__file__
+
+ del sys.modules[name]
+ _testsinglephase._clear_globals()
+ _testinternalcapi.clear_extension(name, filename)
+ init_count = _testsinglephase.initialized_count()
+ assert init_count == -1, (init_count,)
+
+ def clean_up():
+ _testsinglephase._clear_globals()
+ _testinternalcapi.clear_extension(name, filename)
+ self.addCleanup(clean_up)
+
+ interp1 = _interpreters.create(isolated=False)
+ self.addCleanup(_interpreters.destroy, interp1)
+ interp2 = _interpreters.create(isolated=False)
+ self.addCleanup(_interpreters.destroy, interp2)
+
+ script = textwrap.dedent(f'''
+ import _testsinglephase
+
+ expected = %d
+ init_count = _testsinglephase.initialized_count()
+ if init_count != expected:
+ raise Exception(init_count)
+
+ lookedup = _testsinglephase.look_up_self()
+ if lookedup is not _testsinglephase:
+ raise Exception((_testsinglephase, lookedup))
+
+ # Attrs set in the module init func are in m_copy.
+ _initialized = _testsinglephase._initialized
+ initialized = _testsinglephase.initialized()
+ if _initialized != initialized:
+ raise Exception((_initialized, initialized))
+
+ # Attrs set after loading are not in m_copy.
+ if hasattr(_testsinglephase, 'spam'):
+ raise Exception(_testsinglephase.spam)
+ _testsinglephase.spam = expected
+ ''')
+
+ # Use an interpreter that gets destroyed right away.
+ ret = support.run_in_subinterp(script % 1)
+ self.assertEqual(ret, 0)
+
+ # The module's init func gets run again.
+ # The module's globals did not get destroyed.
+ _interpreters.run_string(interp1, script % 2)
+
+ # The module's init func is not run again.
+ # The second interpreter copies the module's m_copy.
+ # However, globals are still shared.
+ _interpreters.run_string(interp2, script % 2)
+
+ @requires_load_dynamic
def test_singlephase_variants(self):
'''Exercise the most meaningful variants described in Python/import.c.'''
self.maxDiff = None
@@ -260,6 +328,11 @@ class ImportTests(unittest.TestCase):
fileobj, pathname, _ = imp.find_module(basename)
fileobj.close()
+ def clean_up():
+ import _testsinglephase
+ _testsinglephase._clear_globals()
+ self.addCleanup(clean_up)
+
modules = {}
def load(name):
assert name not in modules