|
1 | 1 | import gc
|
2 | 2 | import importlib
|
3 | 3 | import importlib.util
|
| 4 | +import shutil |
4 | 5 | import os
|
5 | 6 | import os.path
|
6 | 7 | import py_compile
|
7 | 8 | import sys
|
| 9 | +import tempfile |
8 | 10 | from test import support
|
9 | 11 | from test.support import import_helper
|
10 | 12 | from test.support import os_helper
|
11 | 13 | from test.support import script_helper
|
12 | 14 | from test.support import warnings_helper
|
| 15 | +import textwrap |
13 | 16 | import unittest
|
14 | 17 | import warnings
|
15 | 18 | imp = warnings_helper.import_deprecated('imp')
|
16 | 19 | import _imp
|
| 20 | +import _xxsubinterpreters as _interpreters |
17 | 21 |
|
18 | 22 |
|
19 | 23 | OS_PATH_NAME = os.path.__name__
|
@@ -67,6 +71,17 @@ def setUp(self):
|
67 | 71 | self.test_strings = mod.test_strings
|
68 | 72 | self.test_path = mod.__path__
|
69 | 73 |
|
| 74 | + def _copy_extension(self, name): |
| 75 | + fileobj, pathname, _ = imp.find_module('_testsinglephase') |
| 76 | + fileobj.close() |
| 77 | + |
| 78 | + dirname = tempfile.mkdtemp() |
| 79 | + self.addCleanup(os_helper.rmtree, dirname) |
| 80 | + |
| 81 | + copied = os.path.join(dirname, os.path.basename(pathname)) |
| 82 | + shutil.copyfile(pathname, copied) |
| 83 | + return copied |
| 84 | + |
70 | 85 | # test_import_encoded_module moved to test_source_encoding.py
|
71 | 86 |
|
72 | 87 | def test_find_module_encoding(self):
|
@@ -251,6 +266,48 @@ def test_issue16421_multiple_modules_in_one_dll(self):
|
251 | 266 | with self.assertRaises(ImportError):
|
252 | 267 | imp.load_dynamic('nonexistent', pathname)
|
253 | 268 |
|
| 269 | + @requires_load_dynamic |
| 270 | + def test_singlephase_multiple_interpreters(self): |
| 271 | + # Currently, for every single-phrase init module loaded |
| 272 | + # in multiple interpreters, those interpreters share a |
| 273 | + # PyModuleDef for that object, which can be a problem. |
| 274 | + |
| 275 | + # This single-phase module has global state, which is shared |
| 276 | + # by the interpreters. |
| 277 | + name = '_testsinglephase' |
| 278 | + filename = self._copy_extension(name) |
| 279 | + |
| 280 | + interp1 = _interpreters.create(isolated=False) |
| 281 | + self.addCleanup(_interpreters.destroy, interp1) |
| 282 | + interp2 = _interpreters.create(isolated=False) |
| 283 | + self.addCleanup(_interpreters.destroy, interp2) |
| 284 | + |
| 285 | + script = textwrap.dedent(f''' |
| 286 | + from test.support import warnings_helper |
| 287 | + imp = warnings_helper.import_deprecated('imp') |
| 288 | + module = imp.load_dynamic({name!r}, {filename!r}) |
| 289 | +
|
| 290 | + init_count = module.initialized_count() |
| 291 | + if init_count != %d: |
| 292 | + raise Exception(init_count) |
| 293 | +
|
| 294 | + lookedup = module.look_up_self() |
| 295 | + if lookedup is not module: |
| 296 | + raise Exception((module, lookedup)) |
| 297 | + ''') |
| 298 | + # Use an interpreter that gets destroyed right away. |
| 299 | + ret = support.run_in_subinterp(script % 1) |
| 300 | + self.assertEqual(ret, 0) |
| 301 | + |
| 302 | + # The module's init func gets run again. |
| 303 | + # The module's globals did not get destroyed. |
| 304 | + _interpreters.run_string(interp1, script % 2) |
| 305 | + |
| 306 | + # The module's init func is not run again. |
| 307 | + # The second interpreter copies the module's m_copy. |
| 308 | + # However, globals are still shared. |
| 309 | + _interpreters.run_string(interp2, script % 2) |
| 310 | + |
254 | 311 | @requires_load_dynamic
|
255 | 312 | def test_singlephase_variants(self):
|
256 | 313 | '''Exercise the most meaningful variants described in Python/import.c.'''
|
|
0 commit comments