1import builtins 2import errno 3import glob 4import json 5import importlib.util 6from importlib._bootstrap_external import _get_sourcefile 7from importlib.machinery import ( 8 AppleFrameworkLoader, 9 BuiltinImporter, 10 ExtensionFileLoader, 11 FrozenImporter, 12 SourceFileLoader, 13) 14import marshal 15import os 16import py_compile 17import random 18import shutil 19import stat 20import subprocess 21import sys 22import textwrap 23import threading 24import time 25import types 26import unittest 27from unittest import mock 28import _imp 29 30from test.support import os_helper 31from test.support import ( 32 STDLIB_DIR, swap_attr, swap_item, cpython_only, is_apple_mobile, is_emscripten, 33 is_wasi, run_in_subinterp, run_in_subinterp_with_config, Py_TRACE_REFS, 34 requires_gil_enabled, Py_GIL_DISABLED) 35from test.support.import_helper import ( 36 forget, make_legacy_pyc, unlink, unload, ready_to_import, 37 DirsOnSysPath, CleanImport, import_module) 38from test.support.os_helper import ( 39 TESTFN, rmtree, temp_umask, TESTFN_UNENCODABLE) 40from test.support import script_helper 41from test.support import threading_helper 42from test.test_importlib.util import uncache 43from types import ModuleType 44try: 45 import _testsinglephase 46except ImportError: 47 _testsinglephase = None 48try: 49 import _testmultiphase 50except ImportError: 51 _testmultiphase = None 52try: 53 import _interpreters 54except ModuleNotFoundError: 55 _interpreters = None 56try: 57 import _testinternalcapi 58except ImportError: 59 _testinternalcapi = None 60 61 62skip_if_dont_write_bytecode = unittest.skipIf( 63 sys.dont_write_bytecode, 64 "test meaningful only when writing bytecode") 65 66 67def _require_loader(module, loader, skip): 68 if isinstance(module, str): 69 module = __import__(module) 70 71 MODULE_KINDS = { 72 BuiltinImporter: 'built-in', 73 ExtensionFileLoader: 'extension', 74 AppleFrameworkLoader: 'framework extension', 75 FrozenImporter: 'frozen', 76 SourceFileLoader: 'pure Python', 77 } 78 79 expected = loader 80 assert isinstance(expected, type), expected 81 expected = MODULE_KINDS[expected] 82 83 actual = module.__spec__.loader 84 if not isinstance(actual, type): 85 actual = type(actual) 86 actual = MODULE_KINDS[actual] 87 88 if actual != expected: 89 err = f'expected module to be {expected}, got {module.__spec__}' 90 if skip: 91 raise unittest.SkipTest(err) 92 raise Exception(err) 93 return module 94 95def require_builtin(module, *, skip=False): 96 module = _require_loader(module, BuiltinImporter, skip) 97 assert module.__spec__.origin == 'built-in', module.__spec__ 98 99def require_extension(module, *, skip=False): 100 # Apple extensions must be distributed as frameworks. This requires 101 # a specialist loader. 102 if is_apple_mobile: 103 _require_loader(module, AppleFrameworkLoader, skip) 104 else: 105 _require_loader(module, ExtensionFileLoader, skip) 106 107def require_frozen(module, *, skip=True): 108 module = _require_loader(module, FrozenImporter, skip) 109 assert module.__spec__.origin == 'frozen', module.__spec__ 110 111def require_pure_python(module, *, skip=False): 112 _require_loader(module, SourceFileLoader, skip) 113 114def create_extension_loader(modname, filename): 115 # Apple extensions must be distributed as frameworks. This requires 116 # a specialist loader. 117 if is_apple_mobile: 118 return AppleFrameworkLoader(modname, filename) 119 else: 120 return ExtensionFileLoader(modname, filename) 121 122def import_extension_from_file(modname, filename, *, put_in_sys_modules=True): 123 loader = create_extension_loader(modname, filename) 124 spec = importlib.util.spec_from_loader(modname, loader) 125 module = importlib.util.module_from_spec(spec) 126 loader.exec_module(module) 127 if put_in_sys_modules: 128 sys.modules[modname] = module 129 return module 130 131 132def remove_files(name): 133 for f in (name + ".py", 134 name + ".pyc", 135 name + ".pyw", 136 name + "$py.class"): 137 unlink(f) 138 rmtree('__pycache__') 139 140 141def no_rerun(reason): 142 """Skip rerunning for a particular test. 143 144 WARNING: Use this decorator with care; skipping rerunning makes it 145 impossible to find reference leaks. Provide a clear reason for skipping the 146 test using the 'reason' parameter. 147 """ 148 def deco(func): 149 _has_run = False 150 def wrapper(self): 151 nonlocal _has_run 152 if _has_run: 153 self.skipTest(reason) 154 func(self) 155 _has_run = True 156 return wrapper 157 return deco 158 159 160if _testsinglephase is not None: 161 def restore__testsinglephase(*, _orig=_testsinglephase): 162 # We started with the module imported and want to restore 163 # it to its nominal state. 164 sys.modules.pop('_testsinglephase', None) 165 _orig._clear_globals() 166 origin = _orig.__spec__.origin 167 _testinternalcapi.clear_extension('_testsinglephase', origin) 168 import _testsinglephase 169 170 171def requires_singlephase_init(meth): 172 """Decorator to skip if single-phase init modules are not supported.""" 173 if not isinstance(meth, type): 174 def meth(self, _meth=meth): 175 try: 176 return _meth(self) 177 finally: 178 restore__testsinglephase() 179 meth = cpython_only(meth) 180 msg = "gh-117694: free-threaded build does not currently support single-phase init modules in sub-interpreters" 181 meth = requires_gil_enabled(msg)(meth) 182 return unittest.skipIf(_testsinglephase is None, 183 'test requires _testsinglephase module')(meth) 184 185 186def requires_subinterpreters(meth): 187 """Decorator to skip a test if subinterpreters are not supported.""" 188 return unittest.skipIf(_interpreters is None, 189 'subinterpreters required')(meth) 190 191 192class ModuleSnapshot(types.SimpleNamespace): 193 """A representation of a module for testing. 194 195 Fields: 196 197 * id - the module's object ID 198 * module - the actual module or an adequate substitute 199 * __file__ 200 * __spec__ 201 * name 202 * origin 203 * ns - a copy (dict) of the module's __dict__ (or None) 204 * ns_id - the object ID of the module's __dict__ 205 * cached - the sys.modules[mod.__spec__.name] entry (or None) 206 * cached_id - the object ID of the sys.modules entry (or None) 207 208 In cases where the value is not available (e.g. due to serialization), 209 the value will be None. 210 """ 211 _fields = tuple('id module ns ns_id cached cached_id'.split()) 212 213 @classmethod 214 def from_module(cls, mod): 215 name = mod.__spec__.name 216 cached = sys.modules.get(name) 217 return cls( 218 id=id(mod), 219 module=mod, 220 ns=types.SimpleNamespace(**mod.__dict__), 221 ns_id=id(mod.__dict__), 222 cached=cached, 223 cached_id=id(cached), 224 ) 225 226 SCRIPT = textwrap.dedent(''' 227 {imports} 228 229 name = {name!r} 230 231 {prescript} 232 233 mod = {name} 234 235 {body} 236 237 {postscript} 238 ''') 239 IMPORTS = textwrap.dedent(''' 240 import sys 241 ''').strip() 242 SCRIPT_BODY = textwrap.dedent(''' 243 # Capture the snapshot data. 244 cached = sys.modules.get(name) 245 snapshot = dict( 246 id=id(mod), 247 module=dict( 248 __file__=mod.__file__, 249 __spec__=dict( 250 name=mod.__spec__.name, 251 origin=mod.__spec__.origin, 252 ), 253 ), 254 ns=None, 255 ns_id=id(mod.__dict__), 256 cached=None, 257 cached_id=id(cached) if cached else None, 258 ) 259 ''').strip() 260 CLEANUP_SCRIPT = textwrap.dedent(''' 261 # Clean up the module. 262 sys.modules.pop(name, None) 263 ''').strip() 264 265 @classmethod 266 def build_script(cls, name, *, 267 prescript=None, 268 import_first=False, 269 postscript=None, 270 postcleanup=False, 271 ): 272 if postcleanup is True: 273 postcleanup = cls.CLEANUP_SCRIPT 274 elif isinstance(postcleanup, str): 275 postcleanup = textwrap.dedent(postcleanup).strip() 276 postcleanup = cls.CLEANUP_SCRIPT + os.linesep + postcleanup 277 else: 278 postcleanup = '' 279 prescript = textwrap.dedent(prescript).strip() if prescript else '' 280 postscript = textwrap.dedent(postscript).strip() if postscript else '' 281 282 if postcleanup: 283 if postscript: 284 postscript = postscript + os.linesep * 2 + postcleanup 285 else: 286 postscript = postcleanup 287 288 if import_first: 289 prescript += textwrap.dedent(f''' 290 291 # Now import the module. 292 assert name not in sys.modules 293 import {name}''') 294 295 return cls.SCRIPT.format( 296 imports=cls.IMPORTS.strip(), 297 name=name, 298 prescript=prescript.strip(), 299 body=cls.SCRIPT_BODY.strip(), 300 postscript=postscript, 301 ) 302 303 @classmethod 304 def parse(cls, text): 305 raw = json.loads(text) 306 mod = raw['module'] 307 mod['__spec__'] = types.SimpleNamespace(**mod['__spec__']) 308 raw['module'] = types.SimpleNamespace(**mod) 309 return cls(**raw) 310 311 @classmethod 312 def from_subinterp(cls, name, interpid=None, *, pipe=None, **script_kwds): 313 if pipe is not None: 314 return cls._from_subinterp(name, interpid, pipe, script_kwds) 315 pipe = os.pipe() 316 try: 317 return cls._from_subinterp(name, interpid, pipe, script_kwds) 318 finally: 319 r, w = pipe 320 os.close(r) 321 os.close(w) 322 323 @classmethod 324 def _from_subinterp(cls, name, interpid, pipe, script_kwargs): 325 r, w = pipe 326 327 # Build the script. 328 postscript = textwrap.dedent(f''' 329 # Send the result over the pipe. 330 import json 331 import os 332 os.write({w}, json.dumps(snapshot).encode()) 333 334 ''') 335 _postscript = script_kwargs.get('postscript') 336 if _postscript: 337 _postscript = textwrap.dedent(_postscript).lstrip() 338 postscript += _postscript 339 script_kwargs['postscript'] = postscript.strip() 340 script = cls.build_script(name, **script_kwargs) 341 342 # Run the script. 343 if interpid is None: 344 ret = run_in_subinterp(script) 345 if ret != 0: 346 raise AssertionError(f'{ret} != 0') 347 else: 348 _interpreters.run_string(interpid, script) 349 350 # Parse the results. 351 text = os.read(r, 1000) 352 return cls.parse(text.decode()) 353 354 355class ImportTests(unittest.TestCase): 356 357 def setUp(self): 358 remove_files(TESTFN) 359 importlib.invalidate_caches() 360 361 def tearDown(self): 362 unload(TESTFN) 363 364 def test_import_raises_ModuleNotFoundError(self): 365 with self.assertRaises(ModuleNotFoundError): 366 import something_that_should_not_exist_anywhere 367 368 def test_from_import_missing_module_raises_ModuleNotFoundError(self): 369 with self.assertRaises(ModuleNotFoundError): 370 from something_that_should_not_exist_anywhere import blah 371 372 def test_from_import_missing_attr_raises_ImportError(self): 373 with self.assertRaises(ImportError): 374 from importlib import something_that_should_not_exist_anywhere 375 376 def test_from_import_missing_attr_has_name_and_path(self): 377 with CleanImport('os'): 378 import os 379 with self.assertRaises(ImportError) as cm: 380 from os import i_dont_exist 381 self.assertEqual(cm.exception.name, 'os') 382 self.assertEqual(cm.exception.path, os.__file__) 383 self.assertRegex(str(cm.exception), r"cannot import name 'i_dont_exist' from 'os' \(.*os.py\)") 384 385 @cpython_only 386 def test_from_import_missing_attr_has_name_and_so_path(self): 387 _testcapi = import_module("_testcapi") 388 with self.assertRaises(ImportError) as cm: 389 from _testcapi import i_dont_exist 390 self.assertEqual(cm.exception.name, '_testcapi') 391 if hasattr(_testcapi, "__file__"): 392 # The path on the exception is strictly the spec origin, not the 393 # module's __file__. For most cases, these are the same; but on 394 # iOS, the Framework relocation process results in the exception 395 # being raised from the spec location. 396 self.assertEqual(cm.exception.path, _testcapi.__spec__.origin) 397 self.assertRegex( 398 str(cm.exception), 399 r"cannot import name 'i_dont_exist' from '_testcapi' \(.*(\.(so|pyd))?\)" 400 ) 401 else: 402 self.assertEqual( 403 str(cm.exception), 404 "cannot import name 'i_dont_exist' from '_testcapi' (unknown location)" 405 ) 406 407 def test_from_import_missing_attr_has_name(self): 408 with self.assertRaises(ImportError) as cm: 409 # _warning has no path as it's a built-in module. 410 from _warning import i_dont_exist 411 self.assertEqual(cm.exception.name, '_warning') 412 self.assertIsNone(cm.exception.path) 413 414 def test_from_import_missing_attr_path_is_canonical(self): 415 with self.assertRaises(ImportError) as cm: 416 from os.path import i_dont_exist 417 self.assertIn(cm.exception.name, {'posixpath', 'ntpath'}) 418 self.assertIsNotNone(cm.exception) 419 420 def test_from_import_star_invalid_type(self): 421 import re 422 with ready_to_import() as (name, path): 423 with open(path, 'w', encoding='utf-8') as f: 424 f.write("__all__ = [b'invalid_type']") 425 globals = {} 426 with self.assertRaisesRegex( 427 TypeError, f"{re.escape(name)}\\.__all__ must be str" 428 ): 429 exec(f"from {name} import *", globals) 430 self.assertNotIn(b"invalid_type", globals) 431 with ready_to_import() as (name, path): 432 with open(path, 'w', encoding='utf-8') as f: 433 f.write("globals()[b'invalid_type'] = object()") 434 globals = {} 435 with self.assertRaisesRegex( 436 TypeError, f"{re.escape(name)}\\.__dict__ must be str" 437 ): 438 exec(f"from {name} import *", globals) 439 self.assertNotIn(b"invalid_type", globals) 440 441 def test_case_sensitivity(self): 442 # Brief digression to test that import is case-sensitive: if we got 443 # this far, we know for sure that "random" exists. 444 with self.assertRaises(ImportError): 445 import RAnDoM 446 447 def test_double_const(self): 448 # Importing double_const checks that float constants 449 # serialiazed by marshal as PYC files don't lose precision 450 # (SF bug 422177). 451 from test.test_import.data import double_const 452 unload('test.test_import.data.double_const') 453 from test.test_import.data import double_const 454 455 def test_import(self): 456 def test_with_extension(ext): 457 # The extension is normally ".py", perhaps ".pyw". 458 source = TESTFN + ext 459 pyc = TESTFN + ".pyc" 460 461 with open(source, "w", encoding='utf-8') as f: 462 print("# This tests Python's ability to import a", 463 ext, "file.", file=f) 464 a = random.randrange(1000) 465 b = random.randrange(1000) 466 print("a =", a, file=f) 467 print("b =", b, file=f) 468 469 if TESTFN in sys.modules: 470 del sys.modules[TESTFN] 471 importlib.invalidate_caches() 472 try: 473 try: 474 mod = __import__(TESTFN) 475 except ImportError as err: 476 self.fail("import from %s failed: %s" % (ext, err)) 477 478 self.assertEqual(mod.a, a, 479 "module loaded (%s) but contents invalid" % mod) 480 self.assertEqual(mod.b, b, 481 "module loaded (%s) but contents invalid" % mod) 482 finally: 483 forget(TESTFN) 484 unlink(source) 485 unlink(pyc) 486 487 sys.path.insert(0, os.curdir) 488 try: 489 test_with_extension(".py") 490 if sys.platform.startswith("win"): 491 for ext in [".PY", ".Py", ".pY", ".pyw", ".PYW", ".pYw"]: 492 test_with_extension(ext) 493 finally: 494 del sys.path[0] 495 496 def test_module_with_large_stack(self, module='longlist'): 497 # Regression test for http://bugs.python.org/issue561858. 498 filename = module + '.py' 499 500 # Create a file with a list of 65000 elements. 501 with open(filename, 'w', encoding='utf-8') as f: 502 f.write('d = [\n') 503 for i in range(65000): 504 f.write('"",\n') 505 f.write(']') 506 507 try: 508 # Compile & remove .py file; we only need .pyc. 509 # Bytecode must be relocated from the PEP 3147 bytecode-only location. 510 py_compile.compile(filename) 511 finally: 512 unlink(filename) 513 514 # Need to be able to load from current dir. 515 sys.path.append('') 516 importlib.invalidate_caches() 517 518 namespace = {} 519 try: 520 make_legacy_pyc(filename) 521 # This used to crash. 522 exec('import ' + module, None, namespace) 523 finally: 524 # Cleanup. 525 del sys.path[-1] 526 unlink(filename + 'c') 527 unlink(filename + 'o') 528 529 # Remove references to the module (unload the module) 530 namespace.clear() 531 try: 532 del sys.modules[module] 533 except KeyError: 534 pass 535 536 def test_failing_import_sticks(self): 537 source = TESTFN + ".py" 538 with open(source, "w", encoding='utf-8') as f: 539 print("a = 1/0", file=f) 540 541 # New in 2.4, we shouldn't be able to import that no matter how often 542 # we try. 543 sys.path.insert(0, os.curdir) 544 importlib.invalidate_caches() 545 if TESTFN in sys.modules: 546 del sys.modules[TESTFN] 547 try: 548 for i in [1, 2, 3]: 549 self.assertRaises(ZeroDivisionError, __import__, TESTFN) 550 self.assertNotIn(TESTFN, sys.modules, 551 "damaged module in sys.modules on %i try" % i) 552 finally: 553 del sys.path[0] 554 remove_files(TESTFN) 555 556 def test_import_name_binding(self): 557 # import x.y.z binds x in the current namespace 558 import test as x 559 import test.support 560 self.assertIs(x, test, x.__name__) 561 self.assertTrue(hasattr(test.support, "__file__")) 562 563 # import x.y.z as w binds z as w 564 import test.support as y 565 self.assertIs(y, test.support, y.__name__) 566 567 def test_issue31286(self): 568 # import in a 'finally' block resulted in SystemError 569 try: 570 x = ... 571 finally: 572 import test.support.script_helper as x 573 574 # import in a 'while' loop resulted in stack overflow 575 i = 0 576 while i < 10: 577 import test.support.script_helper as x 578 i += 1 579 580 # import in a 'for' loop resulted in segmentation fault 581 for i in range(2): 582 import test.support.script_helper as x 583 584 def test_failing_reload(self): 585 # A failing reload should leave the module object in sys.modules. 586 source = TESTFN + os.extsep + "py" 587 with open(source, "w", encoding='utf-8') as f: 588 f.write("a = 1\nb=2\n") 589 590 sys.path.insert(0, os.curdir) 591 try: 592 mod = __import__(TESTFN) 593 self.assertIn(TESTFN, sys.modules) 594 self.assertEqual(mod.a, 1, "module has wrong attribute values") 595 self.assertEqual(mod.b, 2, "module has wrong attribute values") 596 597 # On WinXP, just replacing the .py file wasn't enough to 598 # convince reload() to reparse it. Maybe the timestamp didn't 599 # move enough. We force it to get reparsed by removing the 600 # compiled file too. 601 remove_files(TESTFN) 602 603 # Now damage the module. 604 with open(source, "w", encoding='utf-8') as f: 605 f.write("a = 10\nb=20//0\n") 606 607 self.assertRaises(ZeroDivisionError, importlib.reload, mod) 608 # But we still expect the module to be in sys.modules. 609 mod = sys.modules.get(TESTFN) 610 self.assertIsNotNone(mod, "expected module to be in sys.modules") 611 612 # We should have replaced a w/ 10, but the old b value should 613 # stick. 614 self.assertEqual(mod.a, 10, "module has wrong attribute values") 615 self.assertEqual(mod.b, 2, "module has wrong attribute values") 616 617 finally: 618 del sys.path[0] 619 remove_files(TESTFN) 620 unload(TESTFN) 621 622 @skip_if_dont_write_bytecode 623 def test_file_to_source(self): 624 # check if __file__ points to the source file where available 625 source = TESTFN + ".py" 626 with open(source, "w", encoding='utf-8') as f: 627 f.write("test = None\n") 628 629 sys.path.insert(0, os.curdir) 630 try: 631 mod = __import__(TESTFN) 632 self.assertTrue(mod.__file__.endswith('.py')) 633 os.remove(source) 634 del sys.modules[TESTFN] 635 make_legacy_pyc(source) 636 importlib.invalidate_caches() 637 mod = __import__(TESTFN) 638 base, ext = os.path.splitext(mod.__file__) 639 self.assertEqual(ext, '.pyc') 640 finally: 641 del sys.path[0] 642 remove_files(TESTFN) 643 if TESTFN in sys.modules: 644 del sys.modules[TESTFN] 645 646 def test_import_by_filename(self): 647 path = os.path.abspath(TESTFN) 648 encoding = sys.getfilesystemencoding() 649 try: 650 path.encode(encoding) 651 except UnicodeEncodeError: 652 self.skipTest('path is not encodable to {}'.format(encoding)) 653 with self.assertRaises(ImportError) as c: 654 __import__(path) 655 656 def test_import_in_del_does_not_crash(self): 657 # Issue 4236 658 testfn = script_helper.make_script('', TESTFN, textwrap.dedent("""\ 659 import sys 660 class C: 661 def __del__(self): 662 import importlib 663 sys.argv.insert(0, C()) 664 """)) 665 script_helper.assert_python_ok(testfn) 666 667 @skip_if_dont_write_bytecode 668 def test_timestamp_overflow(self): 669 # A modification timestamp larger than 2**32 should not be a problem 670 # when importing a module (issue #11235). 671 sys.path.insert(0, os.curdir) 672 try: 673 source = TESTFN + ".py" 674 compiled = importlib.util.cache_from_source(source) 675 with open(source, 'w', encoding='utf-8') as f: 676 pass 677 try: 678 os.utime(source, (2 ** 33 - 5, 2 ** 33 - 5)) 679 except OverflowError: 680 self.skipTest("cannot set modification time to large integer") 681 except OSError as e: 682 if e.errno not in (getattr(errno, 'EOVERFLOW', None), 683 getattr(errno, 'EINVAL', None)): 684 raise 685 self.skipTest("cannot set modification time to large integer ({})".format(e)) 686 __import__(TESTFN) 687 # The pyc file was created. 688 os.stat(compiled) 689 finally: 690 del sys.path[0] 691 remove_files(TESTFN) 692 693 def test_bogus_fromlist(self): 694 try: 695 __import__('http', fromlist=['blah']) 696 except ImportError: 697 self.fail("fromlist must allow bogus names") 698 699 @cpython_only 700 def test_delete_builtins_import(self): 701 args = ["-c", "del __builtins__.__import__; import os"] 702 popen = script_helper.spawn_python(*args) 703 stdout, stderr = popen.communicate() 704 self.assertIn(b"ImportError", stdout) 705 706 def test_from_import_message_for_nonexistent_module(self): 707 with self.assertRaisesRegex(ImportError, "^No module named 'bogus'"): 708 from bogus import foo 709 710 def test_from_import_message_for_existing_module(self): 711 with self.assertRaisesRegex(ImportError, "^cannot import name 'bogus'"): 712 from re import bogus 713 714 def test_from_import_AttributeError(self): 715 # Issue #24492: trying to import an attribute that raises an 716 # AttributeError should lead to an ImportError. 717 class AlwaysAttributeError: 718 def __getattr__(self, _): 719 raise AttributeError 720 721 module_name = 'test_from_import_AttributeError' 722 self.addCleanup(unload, module_name) 723 sys.modules[module_name] = AlwaysAttributeError() 724 with self.assertRaises(ImportError) as cm: 725 from test_from_import_AttributeError import does_not_exist 726 727 self.assertEqual(str(cm.exception), 728 "cannot import name 'does_not_exist' from '<unknown module name>' (unknown location)") 729 730 @cpython_only 731 def test_issue31492(self): 732 # There shouldn't be an assertion failure in case of failing to import 733 # from a module with a bad __name__ attribute, or in case of failing 734 # to access an attribute of such a module. 735 with swap_attr(os, '__name__', None): 736 with self.assertRaises(ImportError): 737 from os import does_not_exist 738 739 with self.assertRaises(AttributeError): 740 os.does_not_exist 741 742 @threading_helper.requires_working_threading() 743 def test_concurrency(self): 744 # bpo 38091: this is a hack to slow down the code that calls 745 # has_deadlock(); the logic was itself sometimes deadlocking. 746 def delay_has_deadlock(frame, event, arg): 747 if event == 'call' and frame.f_code.co_name == 'has_deadlock': 748 time.sleep(0.1) 749 750 sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'data')) 751 try: 752 exc = None 753 def run(): 754 sys.settrace(delay_has_deadlock) 755 event.wait() 756 try: 757 import package 758 except BaseException as e: 759 nonlocal exc 760 exc = e 761 sys.settrace(None) 762 763 for i in range(10): 764 event = threading.Event() 765 threads = [threading.Thread(target=run) for x in range(2)] 766 try: 767 with threading_helper.start_threads(threads, event.set): 768 time.sleep(0) 769 finally: 770 sys.modules.pop('package', None) 771 sys.modules.pop('package.submodule', None) 772 if exc is not None: 773 raise exc 774 finally: 775 del sys.path[0] 776 777 @unittest.skipUnless(sys.platform == "win32", "Windows-specific") 778 def test_dll_dependency_import(self): 779 from _winapi import GetModuleFileName 780 dllname = GetModuleFileName(sys.dllhandle) 781 pydname = importlib.util.find_spec("_sqlite3").origin 782 depname = os.path.join( 783 os.path.dirname(pydname), 784 "sqlite3{}.dll".format("_d" if "_d" in pydname else "")) 785 786 with os_helper.temp_dir() as tmp: 787 tmp2 = os.path.join(tmp, "DLLs") 788 os.mkdir(tmp2) 789 790 pyexe = os.path.join(tmp, os.path.basename(sys.executable)) 791 shutil.copy(sys.executable, pyexe) 792 shutil.copy(dllname, tmp) 793 for f in glob.glob(os.path.join(glob.escape(sys.prefix), "vcruntime*.dll")): 794 shutil.copy(f, tmp) 795 796 shutil.copy(pydname, tmp2) 797 798 env = None 799 env = {k.upper(): os.environ[k] for k in os.environ} 800 env["PYTHONPATH"] = tmp2 + ";" + STDLIB_DIR 801 802 # Test 1: import with added DLL directory 803 subprocess.check_call([ 804 pyexe, "-Sc", ";".join([ 805 "import os", 806 "p = os.add_dll_directory({!r})".format( 807 os.path.dirname(depname)), 808 "import _sqlite3", 809 "p.close" 810 ])], 811 stderr=subprocess.STDOUT, 812 env=env, 813 cwd=os.path.dirname(pyexe)) 814 815 # Test 2: import with DLL adjacent to PYD 816 shutil.copy(depname, tmp2) 817 subprocess.check_call([pyexe, "-Sc", "import _sqlite3"], 818 stderr=subprocess.STDOUT, 819 env=env, 820 cwd=os.path.dirname(pyexe)) 821 822 def test_issue105979(self): 823 # this used to crash 824 with self.assertRaises(ImportError) as cm: 825 _imp.get_frozen_object("x", b"6\'\xd5Cu\x12") 826 self.assertIn("Frozen object named 'x' is invalid", 827 str(cm.exception)) 828 829 def test_script_shadowing_stdlib(self): 830 script_errors = [ 831 ( 832 "import fractions\nfractions.Fraction", 833 rb"AttributeError: module 'fractions' has no attribute 'Fraction'" 834 ), 835 ( 836 "from fractions import Fraction", 837 rb"ImportError: cannot import name 'Fraction' from 'fractions'" 838 ) 839 ] 840 for script, error in script_errors: 841 with self.subTest(script=script), os_helper.temp_dir() as tmp: 842 with open(os.path.join(tmp, "fractions.py"), "w", encoding='utf-8') as f: 843 f.write(script) 844 845 expected_error = error + ( 846 rb" \(consider renaming '.*fractions.py' since it has the " 847 rb"same name as the standard library module named 'fractions' " 848 rb"and prevents importing that standard library module\)" 849 ) 850 851 popen = script_helper.spawn_python(os.path.join(tmp, "fractions.py"), cwd=tmp) 852 stdout, stderr = popen.communicate() 853 self.assertRegex(stdout, expected_error) 854 855 popen = script_helper.spawn_python('-m', 'fractions', cwd=tmp) 856 stdout, stderr = popen.communicate() 857 self.assertRegex(stdout, expected_error) 858 859 popen = script_helper.spawn_python('-c', 'import fractions', cwd=tmp) 860 stdout, stderr = popen.communicate() 861 self.assertRegex(stdout, expected_error) 862 863 # and there's no error at all when using -P 864 popen = script_helper.spawn_python('-P', 'fractions.py', cwd=tmp) 865 stdout, stderr = popen.communicate() 866 self.assertEqual(stdout, b'') 867 868 tmp_child = os.path.join(tmp, "child") 869 os.mkdir(tmp_child) 870 871 # test the logic with different cwd 872 popen = script_helper.spawn_python(os.path.join(tmp, "fractions.py"), cwd=tmp_child) 873 stdout, stderr = popen.communicate() 874 self.assertRegex(stdout, expected_error) 875 876 popen = script_helper.spawn_python('-m', 'fractions', cwd=tmp_child) 877 stdout, stderr = popen.communicate() 878 self.assertEqual(stdout, b'') # no error 879 880 popen = script_helper.spawn_python('-c', 'import fractions', cwd=tmp_child) 881 stdout, stderr = popen.communicate() 882 self.assertEqual(stdout, b'') # no error 883 884 def test_package_shadowing_stdlib_module(self): 885 script_errors = [ 886 ( 887 "fractions.Fraction", 888 rb"AttributeError: module 'fractions' has no attribute 'Fraction'" 889 ), 890 ( 891 "from fractions import Fraction", 892 rb"ImportError: cannot import name 'Fraction' from 'fractions'" 893 ) 894 ] 895 for script, error in script_errors: 896 with self.subTest(script=script), os_helper.temp_dir() as tmp: 897 os.mkdir(os.path.join(tmp, "fractions")) 898 with open( 899 os.path.join(tmp, "fractions", "__init__.py"), "w", encoding='utf-8' 900 ) as f: 901 f.write("shadowing_module = True") 902 with open(os.path.join(tmp, "main.py"), "w", encoding='utf-8') as f: 903 f.write("import fractions; fractions.shadowing_module\n") 904 f.write(script) 905 906 expected_error = error + ( 907 rb" \(consider renaming '.*[\\/]fractions[\\/]+__init__.py' since it has the " 908 rb"same name as the standard library module named 'fractions' " 909 rb"and prevents importing that standard library module\)" 910 ) 911 912 popen = script_helper.spawn_python(os.path.join(tmp, "main.py"), cwd=tmp) 913 stdout, stderr = popen.communicate() 914 self.assertRegex(stdout, expected_error) 915 916 popen = script_helper.spawn_python('-m', 'main', cwd=tmp) 917 stdout, stderr = popen.communicate() 918 self.assertRegex(stdout, expected_error) 919 920 # and there's no shadowing at all when using -P 921 popen = script_helper.spawn_python('-P', 'main.py', cwd=tmp) 922 stdout, stderr = popen.communicate() 923 self.assertRegex(stdout, b"module 'fractions' has no attribute 'shadowing_module'") 924 925 def test_script_shadowing_third_party(self): 926 script_errors = [ 927 ( 928 "import numpy\nnumpy.array", 929 rb"AttributeError: module 'numpy' has no attribute 'array'" 930 ), 931 ( 932 "from numpy import array", 933 rb"ImportError: cannot import name 'array' from 'numpy'" 934 ) 935 ] 936 for script, error in script_errors: 937 with self.subTest(script=script), os_helper.temp_dir() as tmp: 938 with open(os.path.join(tmp, "numpy.py"), "w", encoding='utf-8') as f: 939 f.write(script) 940 941 expected_error = error + ( 942 rb" \(consider renaming '.*numpy.py' if it has the " 943 rb"same name as a library you intended to import\)\s+\Z" 944 ) 945 946 popen = script_helper.spawn_python(os.path.join(tmp, "numpy.py")) 947 stdout, stderr = popen.communicate() 948 self.assertRegex(stdout, expected_error) 949 950 popen = script_helper.spawn_python('-m', 'numpy', cwd=tmp) 951 stdout, stderr = popen.communicate() 952 self.assertRegex(stdout, expected_error) 953 954 popen = script_helper.spawn_python('-c', 'import numpy', cwd=tmp) 955 stdout, stderr = popen.communicate() 956 self.assertRegex(stdout, expected_error) 957 958 def test_script_maybe_not_shadowing_third_party(self): 959 with os_helper.temp_dir() as tmp: 960 with open(os.path.join(tmp, "numpy.py"), "w", encoding='utf-8') as f: 961 f.write("this_script_does_not_attempt_to_import_numpy = True") 962 963 expected_error = ( 964 rb"AttributeError: module 'numpy' has no attribute 'attr'\s+\Z" 965 ) 966 popen = script_helper.spawn_python('-c', 'import numpy; numpy.attr', cwd=tmp) 967 stdout, stderr = popen.communicate() 968 self.assertRegex(stdout, expected_error) 969 970 expected_error = ( 971 rb"ImportError: cannot import name 'attr' from 'numpy' \(.*\)\s+\Z" 972 ) 973 popen = script_helper.spawn_python('-c', 'from numpy import attr', cwd=tmp) 974 stdout, stderr = popen.communicate() 975 self.assertRegex(stdout, expected_error) 976 977 def test_script_shadowing_stdlib_edge_cases(self): 978 with os_helper.temp_dir() as tmp: 979 with open(os.path.join(tmp, "fractions.py"), "w", encoding='utf-8') as f: 980 f.write("shadowing_module = True") 981 982 # Unhashable str subclass 983 with open(os.path.join(tmp, "main.py"), "w", encoding='utf-8') as f: 984 f.write(""" 985import fractions 986fractions.shadowing_module 987class substr(str): 988 __hash__ = None 989fractions.__name__ = substr('fractions') 990try: 991 fractions.Fraction 992except TypeError as e: 993 print(str(e)) 994""") 995 popen = script_helper.spawn_python("main.py", cwd=tmp) 996 stdout, stderr = popen.communicate() 997 self.assertEqual(stdout.rstrip(), b"unhashable type: 'substr'") 998 999 with open(os.path.join(tmp, "main.py"), "w", encoding='utf-8') as f: 1000 f.write(""" 1001import fractions 1002fractions.shadowing_module 1003class substr(str): 1004 __hash__ = None 1005fractions.__name__ = substr('fractions') 1006try: 1007 from fractions import Fraction 1008except TypeError as e: 1009 print(str(e)) 1010""") 1011 1012 popen = script_helper.spawn_python("main.py", cwd=tmp) 1013 stdout, stderr = popen.communicate() 1014 self.assertEqual(stdout.rstrip(), b"unhashable type: 'substr'") 1015 1016 # Various issues with sys module 1017 with open(os.path.join(tmp, "main.py"), "w", encoding='utf-8') as f: 1018 f.write(""" 1019import fractions 1020fractions.shadowing_module 1021 1022import sys 1023sys.stdlib_module_names = None 1024try: 1025 fractions.Fraction 1026except AttributeError as e: 1027 print(str(e)) 1028 1029del sys.stdlib_module_names 1030try: 1031 fractions.Fraction 1032except AttributeError as e: 1033 print(str(e)) 1034 1035sys.path = [0] 1036try: 1037 fractions.Fraction 1038except AttributeError as e: 1039 print(str(e)) 1040""") 1041 popen = script_helper.spawn_python("main.py", cwd=tmp) 1042 stdout, stderr = popen.communicate() 1043 lines = stdout.splitlines() 1044 self.assertEqual(len(lines), 3) 1045 for line in lines: 1046 self.assertEqual(line, b"module 'fractions' has no attribute 'Fraction'") 1047 1048 with open(os.path.join(tmp, "main.py"), "w", encoding='utf-8') as f: 1049 f.write(""" 1050import fractions 1051fractions.shadowing_module 1052 1053import sys 1054sys.stdlib_module_names = None 1055try: 1056 from fractions import Fraction 1057except ImportError as e: 1058 print(str(e)) 1059 1060del sys.stdlib_module_names 1061try: 1062 from fractions import Fraction 1063except ImportError as e: 1064 print(str(e)) 1065 1066sys.path = [0] 1067try: 1068 from fractions import Fraction 1069except ImportError as e: 1070 print(str(e)) 1071""") 1072 popen = script_helper.spawn_python("main.py", cwd=tmp) 1073 stdout, stderr = popen.communicate() 1074 lines = stdout.splitlines() 1075 self.assertEqual(len(lines), 3) 1076 for line in lines: 1077 self.assertRegex(line, rb"cannot import name 'Fraction' from 'fractions' \(.*\)") 1078 1079 # Various issues with origin 1080 with open(os.path.join(tmp, "main.py"), "w", encoding='utf-8') as f: 1081 f.write(""" 1082import fractions 1083fractions.shadowing_module 1084del fractions.__spec__.origin 1085try: 1086 fractions.Fraction 1087except AttributeError as e: 1088 print(str(e)) 1089 1090fractions.__spec__.origin = 0 1091try: 1092 fractions.Fraction 1093except AttributeError as e: 1094 print(str(e)) 1095""") 1096 1097 popen = script_helper.spawn_python("main.py", cwd=tmp) 1098 stdout, stderr = popen.communicate() 1099 lines = stdout.splitlines() 1100 self.assertEqual(len(lines), 2) 1101 for line in lines: 1102 self.assertEqual(line, b"module 'fractions' has no attribute 'Fraction'") 1103 1104 with open(os.path.join(tmp, "main.py"), "w", encoding='utf-8') as f: 1105 f.write(""" 1106import fractions 1107fractions.shadowing_module 1108del fractions.__spec__.origin 1109try: 1110 from fractions import Fraction 1111except ImportError as e: 1112 print(str(e)) 1113 1114fractions.__spec__.origin = 0 1115try: 1116 from fractions import Fraction 1117except ImportError as e: 1118 print(str(e)) 1119""") 1120 popen = script_helper.spawn_python("main.py", cwd=tmp) 1121 stdout, stderr = popen.communicate() 1122 lines = stdout.splitlines() 1123 self.assertEqual(len(lines), 2) 1124 for line in lines: 1125 self.assertRegex(line, rb"cannot import name 'Fraction' from 'fractions' \(.*\)") 1126 1127 def test_script_shadowing_stdlib_sys_path_modification(self): 1128 script_errors = [ 1129 ( 1130 "import fractions\nfractions.Fraction", 1131 rb"AttributeError: module 'fractions' has no attribute 'Fraction'" 1132 ), 1133 ( 1134 "from fractions import Fraction", 1135 rb"ImportError: cannot import name 'Fraction' from 'fractions'" 1136 ) 1137 ] 1138 for script, error in script_errors: 1139 with self.subTest(script=script), os_helper.temp_dir() as tmp: 1140 with open(os.path.join(tmp, "fractions.py"), "w", encoding='utf-8') as f: 1141 f.write("shadowing_module = True") 1142 with open(os.path.join(tmp, "main.py"), "w", encoding='utf-8') as f: 1143 f.write('import sys; sys.path.insert(0, "this_folder_does_not_exist")\n') 1144 f.write(script) 1145 expected_error = error + ( 1146 rb" \(consider renaming '.*fractions.py' since it has the " 1147 rb"same name as the standard library module named 'fractions' " 1148 rb"and prevents importing that standard library module\)" 1149 ) 1150 1151 popen = script_helper.spawn_python("main.py", cwd=tmp) 1152 stdout, stderr = popen.communicate() 1153 self.assertRegex(stdout, expected_error) 1154 1155 def test_create_dynamic_null(self): 1156 with self.assertRaisesRegex(ValueError, 'embedded null character'): 1157 class Spec: 1158 name = "a\x00b" 1159 origin = "abc" 1160 _imp.create_dynamic(Spec()) 1161 1162 with self.assertRaisesRegex(ValueError, 'embedded null character'): 1163 class Spec2: 1164 name = "abc" 1165 origin = "a\x00b" 1166 _imp.create_dynamic(Spec2()) 1167 1168 1169@skip_if_dont_write_bytecode 1170class FilePermissionTests(unittest.TestCase): 1171 # tests for file mode on cached .pyc files 1172 1173 @unittest.skipUnless(os.name == 'posix', 1174 "test meaningful only on posix systems") 1175 @unittest.skipIf( 1176 is_emscripten or is_wasi, 1177 "Emscripten's/WASI's umask is a stub." 1178 ) 1179 def test_creation_mode(self): 1180 mask = 0o022 1181 with temp_umask(mask), ready_to_import() as (name, path): 1182 cached_path = importlib.util.cache_from_source(path) 1183 module = __import__(name) 1184 if not os.path.exists(cached_path): 1185 self.fail("__import__ did not result in creation of " 1186 "a .pyc file") 1187 stat_info = os.stat(cached_path) 1188 1189 # Check that the umask is respected, and the executable bits 1190 # aren't set. 1191 self.assertEqual(oct(stat.S_IMODE(stat_info.st_mode)), 1192 oct(0o666 & ~mask)) 1193 1194 @unittest.skipUnless(os.name == 'posix', 1195 "test meaningful only on posix systems") 1196 @os_helper.skip_unless_working_chmod 1197 def test_cached_mode_issue_2051(self): 1198 # permissions of .pyc should match those of .py, regardless of mask 1199 mode = 0o600 1200 with temp_umask(0o022), ready_to_import() as (name, path): 1201 cached_path = importlib.util.cache_from_source(path) 1202 os.chmod(path, mode) 1203 __import__(name) 1204 if not os.path.exists(cached_path): 1205 self.fail("__import__ did not result in creation of " 1206 "a .pyc file") 1207 stat_info = os.stat(cached_path) 1208 1209 self.assertEqual(oct(stat.S_IMODE(stat_info.st_mode)), oct(mode)) 1210 1211 @unittest.skipUnless(os.name == 'posix', 1212 "test meaningful only on posix systems") 1213 @os_helper.skip_unless_working_chmod 1214 def test_cached_readonly(self): 1215 mode = 0o400 1216 with temp_umask(0o022), ready_to_import() as (name, path): 1217 cached_path = importlib.util.cache_from_source(path) 1218 os.chmod(path, mode) 1219 __import__(name) 1220 if not os.path.exists(cached_path): 1221 self.fail("__import__ did not result in creation of " 1222 "a .pyc file") 1223 stat_info = os.stat(cached_path) 1224 1225 expected = mode | 0o200 # Account for fix for issue #6074 1226 self.assertEqual(oct(stat.S_IMODE(stat_info.st_mode)), oct(expected)) 1227 1228 def test_pyc_always_writable(self): 1229 # Initially read-only .pyc files on Windows used to cause problems 1230 # with later updates, see issue #6074 for details 1231 with ready_to_import() as (name, path): 1232 # Write a Python file, make it read-only and import it 1233 with open(path, 'w', encoding='utf-8') as f: 1234 f.write("x = 'original'\n") 1235 # Tweak the mtime of the source to ensure pyc gets updated later 1236 s = os.stat(path) 1237 os.utime(path, (s.st_atime, s.st_mtime-100000000)) 1238 os.chmod(path, 0o400) 1239 m = __import__(name) 1240 self.assertEqual(m.x, 'original') 1241 # Change the file and then reimport it 1242 os.chmod(path, 0o600) 1243 with open(path, 'w', encoding='utf-8') as f: 1244 f.write("x = 'rewritten'\n") 1245 unload(name) 1246 importlib.invalidate_caches() 1247 m = __import__(name) 1248 self.assertEqual(m.x, 'rewritten') 1249 # Now delete the source file and check the pyc was rewritten 1250 unlink(path) 1251 unload(name) 1252 importlib.invalidate_caches() 1253 bytecode_only = path + "c" 1254 os.rename(importlib.util.cache_from_source(path), bytecode_only) 1255 m = __import__(name) 1256 self.assertEqual(m.x, 'rewritten') 1257 1258 1259class PycRewritingTests(unittest.TestCase): 1260 # Test that the `co_filename` attribute on code objects always points 1261 # to the right file, even when various things happen (e.g. both the .py 1262 # and the .pyc file are renamed). 1263 1264 module_name = "unlikely_module_name" 1265 module_source = """ 1266import sys 1267code_filename = sys._getframe().f_code.co_filename 1268module_filename = __file__ 1269constant = 1 1270def func(): 1271 pass 1272func_filename = func.__code__.co_filename 1273""" 1274 dir_name = os.path.abspath(TESTFN) 1275 file_name = os.path.join(dir_name, module_name) + os.extsep + "py" 1276 compiled_name = importlib.util.cache_from_source(file_name) 1277 1278 def setUp(self): 1279 self.sys_path = sys.path[:] 1280 self.orig_module = sys.modules.pop(self.module_name, None) 1281 os.mkdir(self.dir_name) 1282 with open(self.file_name, "w", encoding='utf-8') as f: 1283 f.write(self.module_source) 1284 sys.path.insert(0, self.dir_name) 1285 importlib.invalidate_caches() 1286 1287 def tearDown(self): 1288 sys.path[:] = self.sys_path 1289 if self.orig_module is not None: 1290 sys.modules[self.module_name] = self.orig_module 1291 else: 1292 unload(self.module_name) 1293 unlink(self.file_name) 1294 unlink(self.compiled_name) 1295 rmtree(self.dir_name) 1296 1297 def import_module(self): 1298 ns = globals() 1299 __import__(self.module_name, ns, ns) 1300 return sys.modules[self.module_name] 1301 1302 def test_basics(self): 1303 mod = self.import_module() 1304 self.assertEqual(mod.module_filename, self.file_name) 1305 self.assertEqual(mod.code_filename, self.file_name) 1306 self.assertEqual(mod.func_filename, self.file_name) 1307 del sys.modules[self.module_name] 1308 mod = self.import_module() 1309 self.assertEqual(mod.module_filename, self.file_name) 1310 self.assertEqual(mod.code_filename, self.file_name) 1311 self.assertEqual(mod.func_filename, self.file_name) 1312 1313 def test_incorrect_code_name(self): 1314 py_compile.compile(self.file_name, dfile="another_module.py") 1315 mod = self.import_module() 1316 self.assertEqual(mod.module_filename, self.file_name) 1317 self.assertEqual(mod.code_filename, self.file_name) 1318 self.assertEqual(mod.func_filename, self.file_name) 1319 1320 def test_module_without_source(self): 1321 target = "another_module.py" 1322 py_compile.compile(self.file_name, dfile=target) 1323 os.remove(self.file_name) 1324 pyc_file = make_legacy_pyc(self.file_name) 1325 importlib.invalidate_caches() 1326 mod = self.import_module() 1327 self.assertEqual(mod.module_filename, pyc_file) 1328 self.assertEqual(mod.code_filename, target) 1329 self.assertEqual(mod.func_filename, target) 1330 1331 def test_foreign_code(self): 1332 py_compile.compile(self.file_name) 1333 with open(self.compiled_name, "rb") as f: 1334 header = f.read(16) 1335 code = marshal.load(f) 1336 constants = list(code.co_consts) 1337 foreign_code = importlib.import_module.__code__ 1338 pos = constants.index(1) 1339 constants[pos] = foreign_code 1340 code = code.replace(co_consts=tuple(constants)) 1341 with open(self.compiled_name, "wb") as f: 1342 f.write(header) 1343 marshal.dump(code, f) 1344 mod = self.import_module() 1345 self.assertEqual(mod.constant.co_filename, foreign_code.co_filename) 1346 1347 1348class PathsTests(unittest.TestCase): 1349 SAMPLES = ('test', 'test\u00e4\u00f6\u00fc\u00df', 'test\u00e9\u00e8', 1350 'test\u00b0\u00b3\u00b2') 1351 path = TESTFN 1352 1353 def setUp(self): 1354 os.mkdir(self.path) 1355 self.syspath = sys.path[:] 1356 1357 def tearDown(self): 1358 rmtree(self.path) 1359 sys.path[:] = self.syspath 1360 1361 # Regression test for http://bugs.python.org/issue1293. 1362 def test_trailing_slash(self): 1363 with open(os.path.join(self.path, 'test_trailing_slash.py'), 1364 'w', encoding='utf-8') as f: 1365 f.write("testdata = 'test_trailing_slash'") 1366 sys.path.append(self.path+'/') 1367 mod = __import__("test_trailing_slash") 1368 self.assertEqual(mod.testdata, 'test_trailing_slash') 1369 unload("test_trailing_slash") 1370 1371 # Regression test for http://bugs.python.org/issue3677. 1372 @unittest.skipUnless(sys.platform == 'win32', 'Windows-specific') 1373 def test_UNC_path(self): 1374 with open(os.path.join(self.path, 'test_unc_path.py'), 'w') as f: 1375 f.write("testdata = 'test_unc_path'") 1376 importlib.invalidate_caches() 1377 # Create the UNC path, like \\myhost\c$\foo\bar. 1378 path = os.path.abspath(self.path) 1379 import socket 1380 hn = socket.gethostname() 1381 drive = path[0] 1382 unc = "\\\\%s\\%s$"%(hn, drive) 1383 unc += path[2:] 1384 try: 1385 os.listdir(unc) 1386 except OSError as e: 1387 if e.errno in (errno.EPERM, errno.EACCES, errno.ENOENT): 1388 # See issue #15338 1389 self.skipTest("cannot access administrative share %r" % (unc,)) 1390 raise 1391 sys.path.insert(0, unc) 1392 try: 1393 mod = __import__("test_unc_path") 1394 except ImportError as e: 1395 self.fail("could not import 'test_unc_path' from %r: %r" 1396 % (unc, e)) 1397 self.assertEqual(mod.testdata, 'test_unc_path') 1398 self.assertTrue(mod.__file__.startswith(unc), mod.__file__) 1399 unload("test_unc_path") 1400 1401 1402class RelativeImportTests(unittest.TestCase): 1403 1404 def tearDown(self): 1405 unload("test.relimport") 1406 setUp = tearDown 1407 1408 def test_relimport_star(self): 1409 # This will import * from .test_import. 1410 from .. import relimport 1411 self.assertTrue(hasattr(relimport, "RelativeImportTests")) 1412 1413 def test_issue3221(self): 1414 # Note for mergers: the 'absolute' tests from the 2.x branch 1415 # are missing in Py3k because implicit relative imports are 1416 # a thing of the past 1417 # 1418 # Regression test for http://bugs.python.org/issue3221. 1419 def check_relative(): 1420 exec("from . import relimport", ns) 1421 1422 # Check relative import OK with __package__ and __name__ correct 1423 ns = dict(__package__='test', __name__='test.notarealmodule') 1424 check_relative() 1425 1426 # Check relative import OK with only __name__ wrong 1427 ns = dict(__package__='test', __name__='notarealpkg.notarealmodule') 1428 check_relative() 1429 1430 # Check relative import fails with only __package__ wrong 1431 ns = dict(__package__='foo', __name__='test.notarealmodule') 1432 self.assertRaises(ModuleNotFoundError, check_relative) 1433 1434 # Check relative import fails with __package__ and __name__ wrong 1435 ns = dict(__package__='foo', __name__='notarealpkg.notarealmodule') 1436 self.assertRaises(ModuleNotFoundError, check_relative) 1437 1438 # Check relative import fails with package set to a non-string 1439 ns = dict(__package__=object()) 1440 self.assertRaises(TypeError, check_relative) 1441 1442 def test_parentless_import_shadowed_by_global(self): 1443 # Test as if this were done from the REPL where this error most commonly occurs (bpo-37409). 1444 script_helper.assert_python_failure('-W', 'ignore', '-c', 1445 "foo = 1; from . import foo") 1446 1447 def test_absolute_import_without_future(self): 1448 # If explicit relative import syntax is used, then do not try 1449 # to perform an absolute import in the face of failure. 1450 # Issue #7902. 1451 with self.assertRaises(ImportError): 1452 from .os import sep 1453 self.fail("explicit relative import triggered an " 1454 "implicit absolute import") 1455 1456 def test_import_from_non_package(self): 1457 path = os.path.join(os.path.dirname(__file__), 'data', 'package2') 1458 with uncache('submodule1', 'submodule2'), DirsOnSysPath(path): 1459 with self.assertRaises(ImportError): 1460 import submodule1 1461 self.assertNotIn('submodule1', sys.modules) 1462 self.assertNotIn('submodule2', sys.modules) 1463 1464 def test_import_from_unloaded_package(self): 1465 with uncache('package2', 'package2.submodule1', 'package2.submodule2'), \ 1466 DirsOnSysPath(os.path.join(os.path.dirname(__file__), 'data')): 1467 import package2.submodule1 1468 package2.submodule1.submodule2 1469 1470 def test_rebinding(self): 1471 # The same data is also used for testing pkgutil.resolve_name() 1472 # in test_pkgutil and mock.patch in test_unittest. 1473 path = os.path.join(os.path.dirname(__file__), 'data') 1474 with uncache('package3', 'package3.submodule'), DirsOnSysPath(path): 1475 from package3 import submodule 1476 self.assertEqual(submodule.attr, 'rebound') 1477 import package3.submodule as submodule 1478 self.assertEqual(submodule.attr, 'rebound') 1479 with uncache('package3', 'package3.submodule'), DirsOnSysPath(path): 1480 import package3.submodule as submodule 1481 self.assertEqual(submodule.attr, 'rebound') 1482 from package3 import submodule 1483 self.assertEqual(submodule.attr, 'rebound') 1484 1485 def test_rebinding2(self): 1486 path = os.path.join(os.path.dirname(__file__), 'data') 1487 with uncache('package4', 'package4.submodule'), DirsOnSysPath(path): 1488 import package4.submodule as submodule 1489 self.assertEqual(submodule.attr, 'submodule') 1490 from package4 import submodule 1491 self.assertEqual(submodule.attr, 'submodule') 1492 with uncache('package4', 'package4.submodule'), DirsOnSysPath(path): 1493 from package4 import submodule 1494 self.assertEqual(submodule.attr, 'origin') 1495 import package4.submodule as submodule 1496 self.assertEqual(submodule.attr, 'submodule') 1497 1498 1499class OverridingImportBuiltinTests(unittest.TestCase): 1500 def test_override_builtin(self): 1501 # Test that overriding builtins.__import__ can bypass sys.modules. 1502 import os 1503 1504 def foo(): 1505 import os 1506 return os 1507 self.assertEqual(foo(), os) # Quick sanity check. 1508 1509 with swap_attr(builtins, "__import__", lambda *x: 5): 1510 self.assertEqual(foo(), 5) 1511 1512 # Test what happens when we shadow __import__ in globals(); this 1513 # currently does not impact the import process, but if this changes, 1514 # other code will need to change, so keep this test as a tripwire. 1515 with swap_item(globals(), "__import__", lambda *x: 5): 1516 self.assertEqual(foo(), os) 1517 1518 1519class PycacheTests(unittest.TestCase): 1520 # Test the various PEP 3147/488-related behaviors. 1521 1522 def _clean(self): 1523 forget(TESTFN) 1524 rmtree('__pycache__') 1525 unlink(self.source) 1526 1527 def setUp(self): 1528 self.source = TESTFN + '.py' 1529 self._clean() 1530 with open(self.source, 'w', encoding='utf-8') as fp: 1531 print('# This is a test file written by test_import.py', file=fp) 1532 sys.path.insert(0, os.curdir) 1533 importlib.invalidate_caches() 1534 1535 def tearDown(self): 1536 assert sys.path[0] == os.curdir, 'Unexpected sys.path[0]' 1537 del sys.path[0] 1538 self._clean() 1539 1540 @skip_if_dont_write_bytecode 1541 def test_import_pyc_path(self): 1542 self.assertFalse(os.path.exists('__pycache__')) 1543 __import__(TESTFN) 1544 self.assertTrue(os.path.exists('__pycache__')) 1545 pyc_path = importlib.util.cache_from_source(self.source) 1546 self.assertTrue(os.path.exists(pyc_path), 1547 'bytecode file {!r} for {!r} does not ' 1548 'exist'.format(pyc_path, TESTFN)) 1549 1550 @unittest.skipUnless(os.name == 'posix', 1551 "test meaningful only on posix systems") 1552 @skip_if_dont_write_bytecode 1553 @os_helper.skip_unless_working_chmod 1554 @os_helper.skip_if_dac_override 1555 @unittest.skipIf(is_emscripten, "umask is a stub") 1556 def test_unwritable_directory(self): 1557 # When the umask causes the new __pycache__ directory to be 1558 # unwritable, the import still succeeds but no .pyc file is written. 1559 with temp_umask(0o222): 1560 __import__(TESTFN) 1561 self.assertTrue(os.path.exists('__pycache__')) 1562 pyc_path = importlib.util.cache_from_source(self.source) 1563 self.assertFalse(os.path.exists(pyc_path), 1564 'bytecode file {!r} for {!r} ' 1565 'exists'.format(pyc_path, TESTFN)) 1566 1567 @skip_if_dont_write_bytecode 1568 def test_missing_source(self): 1569 # With PEP 3147 cache layout, removing the source but leaving the pyc 1570 # file does not satisfy the import. 1571 __import__(TESTFN) 1572 pyc_file = importlib.util.cache_from_source(self.source) 1573 self.assertTrue(os.path.exists(pyc_file)) 1574 os.remove(self.source) 1575 forget(TESTFN) 1576 importlib.invalidate_caches() 1577 self.assertRaises(ImportError, __import__, TESTFN) 1578 1579 @skip_if_dont_write_bytecode 1580 def test_missing_source_legacy(self): 1581 # Like test_missing_source() except that for backward compatibility, 1582 # when the pyc file lives where the py file would have been (and named 1583 # without the tag), it is importable. The __file__ of the imported 1584 # module is the pyc location. 1585 __import__(TESTFN) 1586 # pyc_file gets removed in _clean() via tearDown(). 1587 pyc_file = make_legacy_pyc(self.source) 1588 os.remove(self.source) 1589 unload(TESTFN) 1590 importlib.invalidate_caches() 1591 m = __import__(TESTFN) 1592 try: 1593 self.assertEqual(m.__file__, 1594 os.path.join(os.getcwd(), os.path.relpath(pyc_file))) 1595 finally: 1596 os.remove(pyc_file) 1597 1598 def test___cached__(self): 1599 # Modules now also have an __cached__ that points to the pyc file. 1600 m = __import__(TESTFN) 1601 pyc_file = importlib.util.cache_from_source(TESTFN + '.py') 1602 self.assertEqual(m.__cached__, os.path.join(os.getcwd(), pyc_file)) 1603 1604 @skip_if_dont_write_bytecode 1605 def test___cached___legacy_pyc(self): 1606 # Like test___cached__() except that for backward compatibility, 1607 # when the pyc file lives where the py file would have been (and named 1608 # without the tag), it is importable. The __cached__ of the imported 1609 # module is the pyc location. 1610 __import__(TESTFN) 1611 # pyc_file gets removed in _clean() via tearDown(). 1612 pyc_file = make_legacy_pyc(self.source) 1613 os.remove(self.source) 1614 unload(TESTFN) 1615 importlib.invalidate_caches() 1616 m = __import__(TESTFN) 1617 self.assertEqual(m.__cached__, 1618 os.path.join(os.getcwd(), os.path.relpath(pyc_file))) 1619 1620 @skip_if_dont_write_bytecode 1621 def test_package___cached__(self): 1622 # Like test___cached__ but for packages. 1623 def cleanup(): 1624 rmtree('pep3147') 1625 unload('pep3147.foo') 1626 unload('pep3147') 1627 os.mkdir('pep3147') 1628 self.addCleanup(cleanup) 1629 # Touch the __init__.py 1630 with open(os.path.join('pep3147', '__init__.py'), 'wb'): 1631 pass 1632 with open(os.path.join('pep3147', 'foo.py'), 'wb'): 1633 pass 1634 importlib.invalidate_caches() 1635 m = __import__('pep3147.foo') 1636 init_pyc = importlib.util.cache_from_source( 1637 os.path.join('pep3147', '__init__.py')) 1638 self.assertEqual(m.__cached__, os.path.join(os.getcwd(), init_pyc)) 1639 foo_pyc = importlib.util.cache_from_source(os.path.join('pep3147', 'foo.py')) 1640 self.assertEqual(sys.modules['pep3147.foo'].__cached__, 1641 os.path.join(os.getcwd(), foo_pyc)) 1642 1643 def test_package___cached___from_pyc(self): 1644 # Like test___cached__ but ensuring __cached__ when imported from a 1645 # PEP 3147 pyc file. 1646 def cleanup(): 1647 rmtree('pep3147') 1648 unload('pep3147.foo') 1649 unload('pep3147') 1650 os.mkdir('pep3147') 1651 self.addCleanup(cleanup) 1652 # Touch the __init__.py 1653 with open(os.path.join('pep3147', '__init__.py'), 'wb'): 1654 pass 1655 with open(os.path.join('pep3147', 'foo.py'), 'wb'): 1656 pass 1657 importlib.invalidate_caches() 1658 m = __import__('pep3147.foo') 1659 unload('pep3147.foo') 1660 unload('pep3147') 1661 importlib.invalidate_caches() 1662 m = __import__('pep3147.foo') 1663 init_pyc = importlib.util.cache_from_source( 1664 os.path.join('pep3147', '__init__.py')) 1665 self.assertEqual(m.__cached__, os.path.join(os.getcwd(), init_pyc)) 1666 foo_pyc = importlib.util.cache_from_source(os.path.join('pep3147', 'foo.py')) 1667 self.assertEqual(sys.modules['pep3147.foo'].__cached__, 1668 os.path.join(os.getcwd(), foo_pyc)) 1669 1670 def test_recompute_pyc_same_second(self): 1671 # Even when the source file doesn't change timestamp, a change in 1672 # source size is enough to trigger recomputation of the pyc file. 1673 __import__(TESTFN) 1674 unload(TESTFN) 1675 with open(self.source, 'a', encoding='utf-8') as fp: 1676 print("x = 5", file=fp) 1677 m = __import__(TESTFN) 1678 self.assertEqual(m.x, 5) 1679 1680 1681class TestSymbolicallyLinkedPackage(unittest.TestCase): 1682 package_name = 'sample' 1683 tagged = package_name + '-tagged' 1684 1685 def setUp(self): 1686 os_helper.rmtree(self.tagged) 1687 os_helper.rmtree(self.package_name) 1688 self.orig_sys_path = sys.path[:] 1689 1690 # create a sample package; imagine you have a package with a tag and 1691 # you want to symbolically link it from its untagged name. 1692 os.mkdir(self.tagged) 1693 self.addCleanup(os_helper.rmtree, self.tagged) 1694 init_file = os.path.join(self.tagged, '__init__.py') 1695 os_helper.create_empty_file(init_file) 1696 assert os.path.exists(init_file) 1697 1698 # now create a symlink to the tagged package 1699 # sample -> sample-tagged 1700 os.symlink(self.tagged, self.package_name, target_is_directory=True) 1701 self.addCleanup(os_helper.unlink, self.package_name) 1702 importlib.invalidate_caches() 1703 1704 self.assertEqual(os.path.isdir(self.package_name), True) 1705 1706 assert os.path.isfile(os.path.join(self.package_name, '__init__.py')) 1707 1708 def tearDown(self): 1709 sys.path[:] = self.orig_sys_path 1710 1711 # regression test for issue6727 1712 @unittest.skipUnless( 1713 not hasattr(sys, 'getwindowsversion') 1714 or sys.getwindowsversion() >= (6, 0), 1715 "Windows Vista or later required") 1716 @os_helper.skip_unless_symlink 1717 def test_symlinked_dir_importable(self): 1718 # make sure sample can only be imported from the current directory. 1719 sys.path[:] = ['.'] 1720 assert os.path.exists(self.package_name) 1721 assert os.path.exists(os.path.join(self.package_name, '__init__.py')) 1722 1723 # Try to import the package 1724 importlib.import_module(self.package_name) 1725 1726 1727@cpython_only 1728class ImportlibBootstrapTests(unittest.TestCase): 1729 # These tests check that importlib is bootstrapped. 1730 1731 def test_frozen_importlib(self): 1732 mod = sys.modules['_frozen_importlib'] 1733 self.assertTrue(mod) 1734 1735 def test_frozen_importlib_is_bootstrap(self): 1736 from importlib import _bootstrap 1737 mod = sys.modules['_frozen_importlib'] 1738 self.assertIs(mod, _bootstrap) 1739 self.assertEqual(mod.__name__, 'importlib._bootstrap') 1740 self.assertEqual(mod.__package__, 'importlib') 1741 self.assertTrue(mod.__file__.endswith('_bootstrap.py'), mod.__file__) 1742 1743 def test_frozen_importlib_external_is_bootstrap_external(self): 1744 from importlib import _bootstrap_external 1745 mod = sys.modules['_frozen_importlib_external'] 1746 self.assertIs(mod, _bootstrap_external) 1747 self.assertEqual(mod.__name__, 'importlib._bootstrap_external') 1748 self.assertEqual(mod.__package__, 'importlib') 1749 self.assertTrue(mod.__file__.endswith('_bootstrap_external.py'), mod.__file__) 1750 1751 def test_there_can_be_only_one(self): 1752 # Issue #15386 revealed a tricky loophole in the bootstrapping 1753 # This test is technically redundant, since the bug caused importing 1754 # this test module to crash completely, but it helps prove the point 1755 from importlib import machinery 1756 mod = sys.modules['_frozen_importlib'] 1757 self.assertIs(machinery.ModuleSpec, mod.ModuleSpec) 1758 1759 1760@cpython_only 1761class GetSourcefileTests(unittest.TestCase): 1762 1763 """Test importlib._bootstrap_external._get_sourcefile() as used by the C API. 1764 1765 Because of the peculiarities of the need of this function, the tests are 1766 knowingly whitebox tests. 1767 1768 """ 1769 1770 def test_get_sourcefile(self): 1771 # Given a valid bytecode path, return the path to the corresponding 1772 # source file if it exists. 1773 with mock.patch('importlib._bootstrap_external._path_isfile') as _path_isfile: 1774 _path_isfile.return_value = True 1775 path = TESTFN + '.pyc' 1776 expect = TESTFN + '.py' 1777 self.assertEqual(_get_sourcefile(path), expect) 1778 1779 def test_get_sourcefile_no_source(self): 1780 # Given a valid bytecode path without a corresponding source path, 1781 # return the original bytecode path. 1782 with mock.patch('importlib._bootstrap_external._path_isfile') as _path_isfile: 1783 _path_isfile.return_value = False 1784 path = TESTFN + '.pyc' 1785 self.assertEqual(_get_sourcefile(path), path) 1786 1787 def test_get_sourcefile_bad_ext(self): 1788 # Given a path with an invalid bytecode extension, return the 1789 # bytecode path passed as the argument. 1790 path = TESTFN + '.bad_ext' 1791 self.assertEqual(_get_sourcefile(path), path) 1792 1793 1794class ImportTracebackTests(unittest.TestCase): 1795 1796 def setUp(self): 1797 os.mkdir(TESTFN) 1798 self.old_path = sys.path[:] 1799 sys.path.insert(0, TESTFN) 1800 1801 def tearDown(self): 1802 sys.path[:] = self.old_path 1803 rmtree(TESTFN) 1804 1805 def create_module(self, mod, contents, ext=".py"): 1806 fname = os.path.join(TESTFN, mod + ext) 1807 with open(fname, "w", encoding='utf-8') as f: 1808 f.write(contents) 1809 self.addCleanup(unload, mod) 1810 importlib.invalidate_caches() 1811 return fname 1812 1813 def assert_traceback(self, tb, files): 1814 deduped_files = [] 1815 while tb: 1816 code = tb.tb_frame.f_code 1817 fn = code.co_filename 1818 if not deduped_files or fn != deduped_files[-1]: 1819 deduped_files.append(fn) 1820 tb = tb.tb_next 1821 self.assertEqual(len(deduped_files), len(files), deduped_files) 1822 for fn, pat in zip(deduped_files, files): 1823 self.assertIn(pat, fn) 1824 1825 def test_nonexistent_module(self): 1826 try: 1827 # assertRaises() clears __traceback__ 1828 import nonexistent_xyzzy 1829 except ImportError as e: 1830 tb = e.__traceback__ 1831 else: 1832 self.fail("ImportError should have been raised") 1833 self.assert_traceback(tb, [__file__]) 1834 1835 def test_nonexistent_module_nested(self): 1836 self.create_module("foo", "import nonexistent_xyzzy") 1837 try: 1838 import foo 1839 except ImportError as e: 1840 tb = e.__traceback__ 1841 else: 1842 self.fail("ImportError should have been raised") 1843 self.assert_traceback(tb, [__file__, 'foo.py']) 1844 1845 def test_exec_failure(self): 1846 self.create_module("foo", "1/0") 1847 try: 1848 import foo 1849 except ZeroDivisionError as e: 1850 tb = e.__traceback__ 1851 else: 1852 self.fail("ZeroDivisionError should have been raised") 1853 self.assert_traceback(tb, [__file__, 'foo.py']) 1854 1855 def test_exec_failure_nested(self): 1856 self.create_module("foo", "import bar") 1857 self.create_module("bar", "1/0") 1858 try: 1859 import foo 1860 except ZeroDivisionError as e: 1861 tb = e.__traceback__ 1862 else: 1863 self.fail("ZeroDivisionError should have been raised") 1864 self.assert_traceback(tb, [__file__, 'foo.py', 'bar.py']) 1865 1866 # A few more examples from issue #15425 1867 def test_syntax_error(self): 1868 self.create_module("foo", "invalid syntax is invalid") 1869 try: 1870 import foo 1871 except SyntaxError as e: 1872 tb = e.__traceback__ 1873 else: 1874 self.fail("SyntaxError should have been raised") 1875 self.assert_traceback(tb, [__file__]) 1876 1877 def _setup_broken_package(self, parent, child): 1878 pkg_name = "_parent_foo" 1879 self.addCleanup(unload, pkg_name) 1880 pkg_path = os.path.join(TESTFN, pkg_name) 1881 os.mkdir(pkg_path) 1882 # Touch the __init__.py 1883 init_path = os.path.join(pkg_path, '__init__.py') 1884 with open(init_path, 'w', encoding='utf-8') as f: 1885 f.write(parent) 1886 bar_path = os.path.join(pkg_path, 'bar.py') 1887 with open(bar_path, 'w', encoding='utf-8') as f: 1888 f.write(child) 1889 importlib.invalidate_caches() 1890 return init_path, bar_path 1891 1892 def test_broken_submodule(self): 1893 init_path, bar_path = self._setup_broken_package("", "1/0") 1894 try: 1895 import _parent_foo.bar 1896 except ZeroDivisionError as e: 1897 tb = e.__traceback__ 1898 else: 1899 self.fail("ZeroDivisionError should have been raised") 1900 self.assert_traceback(tb, [__file__, bar_path]) 1901 1902 def test_broken_from(self): 1903 init_path, bar_path = self._setup_broken_package("", "1/0") 1904 try: 1905 from _parent_foo import bar 1906 except ZeroDivisionError as e: 1907 tb = e.__traceback__ 1908 else: 1909 self.fail("ImportError should have been raised") 1910 self.assert_traceback(tb, [__file__, bar_path]) 1911 1912 def test_broken_parent(self): 1913 init_path, bar_path = self._setup_broken_package("1/0", "") 1914 try: 1915 import _parent_foo.bar 1916 except ZeroDivisionError as e: 1917 tb = e.__traceback__ 1918 else: 1919 self.fail("ZeroDivisionError should have been raised") 1920 self.assert_traceback(tb, [__file__, init_path]) 1921 1922 def test_broken_parent_from(self): 1923 init_path, bar_path = self._setup_broken_package("1/0", "") 1924 try: 1925 from _parent_foo import bar 1926 except ZeroDivisionError as e: 1927 tb = e.__traceback__ 1928 else: 1929 self.fail("ZeroDivisionError should have been raised") 1930 self.assert_traceback(tb, [__file__, init_path]) 1931 1932 @cpython_only 1933 def test_import_bug(self): 1934 # We simulate a bug in importlib and check that it's not stripped 1935 # away from the traceback. 1936 self.create_module("foo", "") 1937 importlib = sys.modules['_frozen_importlib_external'] 1938 if 'load_module' in vars(importlib.SourceLoader): 1939 old_exec_module = importlib.SourceLoader.exec_module 1940 else: 1941 old_exec_module = None 1942 try: 1943 def exec_module(*args): 1944 1/0 1945 importlib.SourceLoader.exec_module = exec_module 1946 try: 1947 import foo 1948 except ZeroDivisionError as e: 1949 tb = e.__traceback__ 1950 else: 1951 self.fail("ZeroDivisionError should have been raised") 1952 self.assert_traceback(tb, [__file__, '<frozen importlib', __file__]) 1953 finally: 1954 if old_exec_module is None: 1955 del importlib.SourceLoader.exec_module 1956 else: 1957 importlib.SourceLoader.exec_module = old_exec_module 1958 1959 @unittest.skipUnless(TESTFN_UNENCODABLE, 'need TESTFN_UNENCODABLE') 1960 def test_unencodable_filename(self): 1961 # Issue #11619: The Python parser and the import machinery must not 1962 # encode filenames, especially on Windows 1963 pyname = script_helper.make_script('', TESTFN_UNENCODABLE, 'pass') 1964 self.addCleanup(unlink, pyname) 1965 name = pyname[:-3] 1966 script_helper.assert_python_ok("-c", "mod = __import__(%a)" % name, 1967 __isolated=False) 1968 1969 1970class CircularImportTests(unittest.TestCase): 1971 1972 """See the docstrings of the modules being imported for the purpose of the 1973 test.""" 1974 1975 def tearDown(self): 1976 """Make sure no modules pre-exist in sys.modules which are being used to 1977 test.""" 1978 for key in list(sys.modules.keys()): 1979 if key.startswith('test.test_import.data.circular_imports'): 1980 del sys.modules[key] 1981 1982 def test_direct(self): 1983 try: 1984 import test.test_import.data.circular_imports.basic 1985 except ImportError: 1986 self.fail('circular import through relative imports failed') 1987 1988 def test_indirect(self): 1989 try: 1990 import test.test_import.data.circular_imports.indirect 1991 except ImportError: 1992 self.fail('relative import in module contributing to circular ' 1993 'import failed') 1994 1995 def test_subpackage(self): 1996 try: 1997 import test.test_import.data.circular_imports.subpackage 1998 except ImportError: 1999 self.fail('circular import involving a subpackage failed') 2000 2001 def test_rebinding(self): 2002 try: 2003 import test.test_import.data.circular_imports.rebinding as rebinding 2004 except ImportError: 2005 self.fail('circular import with rebinding of module attribute failed') 2006 from test.test_import.data.circular_imports.subpkg import util 2007 self.assertIs(util.util, rebinding.util) 2008 2009 def test_binding(self): 2010 try: 2011 import test.test_import.data.circular_imports.binding 2012 except ImportError: 2013 self.fail('circular import with binding a submodule to a name failed') 2014 2015 def test_crossreference1(self): 2016 import test.test_import.data.circular_imports.use 2017 import test.test_import.data.circular_imports.source 2018 2019 def test_crossreference2(self): 2020 with self.assertRaises(AttributeError) as cm: 2021 import test.test_import.data.circular_imports.source 2022 errmsg = str(cm.exception) 2023 self.assertIn('test.test_import.data.circular_imports.source', errmsg) 2024 self.assertIn('spam', errmsg) 2025 self.assertIn('partially initialized module', errmsg) 2026 self.assertIn('circular import', errmsg) 2027 2028 def test_circular_from_import(self): 2029 with self.assertRaises(ImportError) as cm: 2030 import test.test_import.data.circular_imports.from_cycle1 2031 self.assertIn( 2032 "cannot import name 'b' from partially initialized module " 2033 "'test.test_import.data.circular_imports.from_cycle1' " 2034 "(most likely due to a circular import)", 2035 str(cm.exception), 2036 ) 2037 2038 def test_circular_import(self): 2039 with self.assertRaisesRegex( 2040 AttributeError, 2041 r"partially initialized module 'test.test_import.data.circular_imports.import_cycle' " 2042 r"from '.*' has no attribute 'some_attribute' \(most likely due to a circular import\)" 2043 ): 2044 import test.test_import.data.circular_imports.import_cycle 2045 2046 def test_absolute_circular_submodule(self): 2047 with self.assertRaises(AttributeError) as cm: 2048 import test.test_import.data.circular_imports.subpkg2.parent 2049 self.assertIn( 2050 "cannot access submodule 'parent' of module " 2051 "'test.test_import.data.circular_imports.subpkg2' " 2052 "(most likely due to a circular import)", 2053 str(cm.exception), 2054 ) 2055 2056 @requires_singlephase_init 2057 @unittest.skipIf(_testsinglephase is None, "test requires _testsinglephase module") 2058 def test_singlephase_circular(self): 2059 """Regression test for gh-123950 2060 2061 Import a single-phase-init module that imports itself 2062 from the PyInit_* function (before it's added to sys.modules). 2063 Manages its own cache (which is `static`, and so incompatible 2064 with multiple interpreters or interpreter reset). 2065 """ 2066 name = '_testsinglephase_circular' 2067 helper_name = 'test.test_import.data.circular_imports.singlephase' 2068 with uncache(name, helper_name): 2069 filename = _testsinglephase.__file__ 2070 # We don't put the module in sys.modules: that the *inner* 2071 # import should do that. 2072 mod = import_extension_from_file(name, filename, 2073 put_in_sys_modules=False) 2074 2075 self.assertEqual(mod.helper_mod_name, helper_name) 2076 self.assertIn(name, sys.modules) 2077 self.assertIn(helper_name, sys.modules) 2078 2079 self.assertIn(name, sys.modules) 2080 self.assertIn(helper_name, sys.modules) 2081 self.assertNotIn(name, sys.modules) 2082 self.assertNotIn(helper_name, sys.modules) 2083 self.assertIs(mod.clear_static_var(), mod) 2084 _testinternalcapi.clear_extension('_testsinglephase_circular', 2085 mod.__spec__.origin) 2086 2087 def test_unwritable_module(self): 2088 self.addCleanup(unload, "test.test_import.data.unwritable") 2089 self.addCleanup(unload, "test.test_import.data.unwritable.x") 2090 2091 import test.test_import.data.unwritable as unwritable 2092 with self.assertWarns(ImportWarning): 2093 from test.test_import.data.unwritable import x 2094 2095 self.assertNotEqual(type(unwritable), ModuleType) 2096 self.assertEqual(type(x), ModuleType) 2097 with self.assertRaises(AttributeError): 2098 unwritable.x = 42 2099 2100 2101class SubinterpImportTests(unittest.TestCase): 2102 2103 RUN_KWARGS = dict( 2104 allow_fork=False, 2105 allow_exec=False, 2106 allow_threads=True, 2107 allow_daemon_threads=False, 2108 # Isolation-related config values aren't included here. 2109 ) 2110 ISOLATED = dict( 2111 use_main_obmalloc=False, 2112 gil=2, 2113 ) 2114 NOT_ISOLATED = {k: not v for k, v in ISOLATED.items()} 2115 NOT_ISOLATED['gil'] = 1 2116 2117 @unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()") 2118 def pipe(self): 2119 r, w = os.pipe() 2120 self.addCleanup(os.close, r) 2121 self.addCleanup(os.close, w) 2122 if hasattr(os, 'set_blocking'): 2123 os.set_blocking(r, False) 2124 return (r, w) 2125 2126 def import_script(self, name, fd, filename=None, check_override=None): 2127 override_text = '' 2128 if check_override is not None: 2129 override_text = f''' 2130 import _imp 2131 _imp._override_multi_interp_extensions_check({check_override}) 2132 ''' 2133 if filename: 2134 # Apple extensions must be distributed as frameworks. This requires 2135 # a specialist loader. 2136 if is_apple_mobile: 2137 loader = "AppleFrameworkLoader" 2138 else: 2139 loader = "ExtensionFileLoader" 2140 2141 return textwrap.dedent(f''' 2142 from importlib.util import spec_from_loader, module_from_spec 2143 from importlib.machinery import {loader} 2144 import os, sys 2145 {override_text} 2146 loader = {loader}({name!r}, {filename!r}) 2147 spec = spec_from_loader({name!r}, loader) 2148 try: 2149 module = module_from_spec(spec) 2150 loader.exec_module(module) 2151 except ImportError as exc: 2152 text = 'ImportError: ' + str(exc) 2153 else: 2154 text = 'okay' 2155 os.write({fd}, text.encode('utf-8')) 2156 ''') 2157 else: 2158 return textwrap.dedent(f''' 2159 import os, sys 2160 {override_text} 2161 try: 2162 import {name} 2163 except ImportError as exc: 2164 text = 'ImportError: ' + str(exc) 2165 else: 2166 text = 'okay' 2167 os.write({fd}, text.encode('utf-8')) 2168 ''') 2169 2170 def run_here(self, name, filename=None, *, 2171 check_singlephase_setting=False, 2172 check_singlephase_override=None, 2173 isolated=False, 2174 ): 2175 """ 2176 Try importing the named module in a subinterpreter. 2177 2178 The subinterpreter will be in the current process. 2179 The module will have already been imported in the main interpreter. 2180 Thus, for extension/builtin modules, the module definition will 2181 have been loaded already and cached globally. 2182 2183 "check_singlephase_setting" determines whether or not 2184 the interpreter will be configured to check for modules 2185 that are not compatible with use in multiple interpreters. 2186 2187 This should always return "okay" for all modules if the 2188 setting is False (with no override). 2189 """ 2190 __import__(name) 2191 2192 kwargs = dict( 2193 **self.RUN_KWARGS, 2194 **(self.ISOLATED if isolated else self.NOT_ISOLATED), 2195 check_multi_interp_extensions=check_singlephase_setting, 2196 ) 2197 2198 r, w = self.pipe() 2199 script = self.import_script(name, w, filename, 2200 check_singlephase_override) 2201 2202 ret = run_in_subinterp_with_config(script, **kwargs) 2203 self.assertEqual(ret, 0) 2204 return os.read(r, 100) 2205 2206 def check_compatible_here(self, name, filename=None, *, 2207 strict=False, 2208 isolated=False, 2209 ): 2210 # Verify that the named module may be imported in a subinterpreter. 2211 # (See run_here() for more info.) 2212 out = self.run_here(name, filename, 2213 check_singlephase_setting=strict, 2214 isolated=isolated, 2215 ) 2216 self.assertEqual(out, b'okay') 2217 2218 def check_incompatible_here(self, name, filename=None, *, isolated=False): 2219 # Differences from check_compatible_here(): 2220 # * verify that import fails 2221 # * "strict" is always True 2222 out = self.run_here(name, filename, 2223 check_singlephase_setting=True, 2224 isolated=isolated, 2225 ) 2226 self.assertEqual( 2227 out.decode('utf-8'), 2228 f'ImportError: module {name} does not support loading in subinterpreters', 2229 ) 2230 2231 def check_compatible_fresh(self, name, *, strict=False, isolated=False): 2232 # Differences from check_compatible_here(): 2233 # * subinterpreter in a new process 2234 # * module has never been imported before in that process 2235 # * this tests importing the module for the first time 2236 kwargs = dict( 2237 **self.RUN_KWARGS, 2238 **(self.ISOLATED if isolated else self.NOT_ISOLATED), 2239 check_multi_interp_extensions=strict, 2240 ) 2241 gil = kwargs['gil'] 2242 kwargs['gil'] = 'default' if gil == 0 else ( 2243 'shared' if gil == 1 else 'own' if gil == 2 else gil) 2244 _, out, err = script_helper.assert_python_ok('-c', textwrap.dedent(f''' 2245 import _testinternalcapi, sys 2246 assert ( 2247 {name!r} in sys.builtin_module_names or 2248 {name!r} not in sys.modules 2249 ), repr({name!r}) 2250 config = type(sys.implementation)(**{kwargs}) 2251 ret = _testinternalcapi.run_in_subinterp_with_config( 2252 {self.import_script(name, "sys.stdout.fileno()")!r}, 2253 config, 2254 ) 2255 assert ret == 0, ret 2256 ''')) 2257 self.assertEqual(err, b'') 2258 self.assertEqual(out, b'okay') 2259 2260 def check_incompatible_fresh(self, name, *, isolated=False): 2261 # Differences from check_compatible_fresh(): 2262 # * verify that import fails 2263 # * "strict" is always True 2264 kwargs = dict( 2265 **self.RUN_KWARGS, 2266 **(self.ISOLATED if isolated else self.NOT_ISOLATED), 2267 check_multi_interp_extensions=True, 2268 ) 2269 gil = kwargs['gil'] 2270 kwargs['gil'] = 'default' if gil == 0 else ( 2271 'shared' if gil == 1 else 'own' if gil == 2 else gil) 2272 _, out, err = script_helper.assert_python_ok('-c', textwrap.dedent(f''' 2273 import _testinternalcapi, sys 2274 assert {name!r} not in sys.modules, {name!r} 2275 config = type(sys.implementation)(**{kwargs}) 2276 ret = _testinternalcapi.run_in_subinterp_with_config( 2277 {self.import_script(name, "sys.stdout.fileno()")!r}, 2278 config, 2279 ) 2280 assert ret == 0, ret 2281 ''')) 2282 self.assertEqual(err, b'') 2283 self.assertEqual( 2284 out.decode('utf-8'), 2285 f'ImportError: module {name} does not support loading in subinterpreters', 2286 ) 2287 2288 @unittest.skipIf(_testinternalcapi is None, "requires _testinternalcapi") 2289 def test_builtin_compat(self): 2290 # For now we avoid using sys or builtins 2291 # since they still don't implement multi-phase init. 2292 module = '_imp' 2293 require_builtin(module) 2294 if not Py_GIL_DISABLED: 2295 with self.subTest(f'{module}: not strict'): 2296 self.check_compatible_here(module, strict=False) 2297 with self.subTest(f'{module}: strict, not fresh'): 2298 self.check_compatible_here(module, strict=True) 2299 2300 @cpython_only 2301 @unittest.skipIf(_testinternalcapi is None, "requires _testinternalcapi") 2302 def test_frozen_compat(self): 2303 module = '_frozen_importlib' 2304 require_frozen(module, skip=True) 2305 if __import__(module).__spec__.origin != 'frozen': 2306 raise unittest.SkipTest(f'{module} is unexpectedly not frozen') 2307 if not Py_GIL_DISABLED: 2308 with self.subTest(f'{module}: not strict'): 2309 self.check_compatible_here(module, strict=False) 2310 with self.subTest(f'{module}: strict, not fresh'): 2311 self.check_compatible_here(module, strict=True) 2312 2313 @requires_singlephase_init 2314 def test_single_init_extension_compat(self): 2315 module = '_testsinglephase' 2316 require_extension(module) 2317 with self.subTest(f'{module}: not strict'): 2318 self.check_compatible_here(module, strict=False) 2319 with self.subTest(f'{module}: strict, not fresh'): 2320 self.check_incompatible_here(module) 2321 with self.subTest(f'{module}: strict, fresh'): 2322 self.check_incompatible_fresh(module) 2323 with self.subTest(f'{module}: isolated, fresh'): 2324 self.check_incompatible_fresh(module, isolated=True) 2325 2326 @unittest.skipIf(_testmultiphase is None, "test requires _testmultiphase module") 2327 def test_multi_init_extension_compat(self): 2328 module = '_testmultiphase' 2329 require_extension(module) 2330 if not Py_GIL_DISABLED: 2331 with self.subTest(f'{module}: not strict'): 2332 self.check_compatible_here(module, strict=False) 2333 with self.subTest(f'{module}: strict, not fresh'): 2334 self.check_compatible_here(module, strict=True) 2335 with self.subTest(f'{module}: strict, fresh'): 2336 self.check_compatible_fresh(module, strict=True) 2337 2338 @unittest.skipIf(_testmultiphase is None, "test requires _testmultiphase module") 2339 def test_multi_init_extension_non_isolated_compat(self): 2340 modname = '_test_non_isolated' 2341 filename = _testmultiphase.__file__ 2342 module = import_extension_from_file(modname, filename) 2343 2344 require_extension(module) 2345 with self.subTest(f'{modname}: isolated'): 2346 self.check_incompatible_here(modname, filename, isolated=True) 2347 with self.subTest(f'{modname}: not isolated'): 2348 self.check_incompatible_here(modname, filename, isolated=False) 2349 if not Py_GIL_DISABLED: 2350 with self.subTest(f'{modname}: not strict'): 2351 self.check_compatible_here(modname, filename, strict=False) 2352 2353 @unittest.skipIf(_testmultiphase is None, "test requires _testmultiphase module") 2354 def test_multi_init_extension_per_interpreter_gil_compat(self): 2355 modname = '_test_shared_gil_only' 2356 filename = _testmultiphase.__file__ 2357 module = import_extension_from_file(modname, filename) 2358 2359 require_extension(module) 2360 with self.subTest(f'{modname}: isolated, strict'): 2361 self.check_incompatible_here(modname, filename, isolated=True) 2362 with self.subTest(f'{modname}: not isolated, strict'): 2363 self.check_compatible_here(modname, filename, 2364 strict=True, isolated=False) 2365 if not Py_GIL_DISABLED: 2366 with self.subTest(f'{modname}: not isolated, not strict'): 2367 self.check_compatible_here(modname, filename, 2368 strict=False, isolated=False) 2369 2370 @unittest.skipIf(_testinternalcapi is None, "requires _testinternalcapi") 2371 def test_python_compat(self): 2372 module = 'threading' 2373 require_pure_python(module) 2374 if not Py_GIL_DISABLED: 2375 with self.subTest(f'{module}: not strict'): 2376 self.check_compatible_here(module, strict=False) 2377 with self.subTest(f'{module}: strict, not fresh'): 2378 self.check_compatible_here(module, strict=True) 2379 with self.subTest(f'{module}: strict, fresh'): 2380 self.check_compatible_fresh(module, strict=True) 2381 2382 @requires_singlephase_init 2383 def test_singlephase_check_with_setting_and_override(self): 2384 module = '_testsinglephase' 2385 require_extension(module) 2386 2387 def check_compatible(setting, override): 2388 out = self.run_here( 2389 module, 2390 check_singlephase_setting=setting, 2391 check_singlephase_override=override, 2392 ) 2393 self.assertEqual(out, b'okay') 2394 2395 def check_incompatible(setting, override): 2396 out = self.run_here( 2397 module, 2398 check_singlephase_setting=setting, 2399 check_singlephase_override=override, 2400 ) 2401 self.assertNotEqual(out, b'okay') 2402 2403 with self.subTest('config: check enabled; override: enabled'): 2404 check_incompatible(True, 1) 2405 with self.subTest('config: check enabled; override: use config'): 2406 check_incompatible(True, 0) 2407 with self.subTest('config: check enabled; override: disabled'): 2408 check_compatible(True, -1) 2409 2410 with self.subTest('config: check disabled; override: enabled'): 2411 check_incompatible(False, 1) 2412 with self.subTest('config: check disabled; override: use config'): 2413 check_compatible(False, 0) 2414 with self.subTest('config: check disabled; override: disabled'): 2415 check_compatible(False, -1) 2416 2417 @unittest.skipIf(_testinternalcapi is None, "requires _testinternalcapi") 2418 def test_isolated_config(self): 2419 module = 'threading' 2420 require_pure_python(module) 2421 with self.subTest(f'{module}: strict, not fresh'): 2422 self.check_compatible_here(module, strict=True, isolated=True) 2423 with self.subTest(f'{module}: strict, fresh'): 2424 self.check_compatible_fresh(module, strict=True, isolated=True) 2425 2426 @requires_subinterpreters 2427 @requires_singlephase_init 2428 def test_disallowed_reimport(self): 2429 # See https://github.com/python/cpython/issues/104621. 2430 script = textwrap.dedent(''' 2431 import _testsinglephase 2432 print(_testsinglephase) 2433 ''') 2434 interpid = _interpreters.create() 2435 self.addCleanup(lambda: _interpreters.destroy(interpid)) 2436 2437 excsnap = _interpreters.run_string(interpid, script) 2438 self.assertIsNot(excsnap, None) 2439 2440 excsnap = _interpreters.run_string(interpid, script) 2441 self.assertIsNot(excsnap, None) 2442 2443 2444class TestSinglePhaseSnapshot(ModuleSnapshot): 2445 """A representation of a single-phase init module for testing. 2446 2447 Fields from ModuleSnapshot: 2448 2449 * id - id(mod) 2450 * module - mod or a SimpleNamespace with __file__ & __spec__ 2451 * ns - a shallow copy of mod.__dict__ 2452 * ns_id - id(mod.__dict__) 2453 * cached - sys.modules[name] (or None if not there or not snapshotable) 2454 * cached_id - id(sys.modules[name]) (or None if not there) 2455 2456 Extra fields: 2457 2458 * summed - the result of calling "mod.sum(1, 2)" 2459 * lookedup - the result of calling "mod.look_up_self()" 2460 * lookedup_id - the object ID of self.lookedup 2461 * state_initialized - the result of calling "mod.state_initialized()" 2462 * init_count - (optional) the result of calling "mod.initialized_count()" 2463 2464 Overridden methods from ModuleSnapshot: 2465 2466 * from_module() 2467 * parse() 2468 2469 Other methods from ModuleSnapshot: 2470 2471 * build_script() 2472 * from_subinterp() 2473 2474 ---- 2475 2476 There are 5 modules in Modules/_testsinglephase.c: 2477 2478 * _testsinglephase 2479 * has global state 2480 * extra loads skip the init function, copy def.m_base.m_copy 2481 * counts calls to init function 2482 * _testsinglephase_basic_wrapper 2483 * _testsinglephase by another name (and separate init function symbol) 2484 * _testsinglephase_basic_copy 2485 * same as _testsinglephase but with own def (and init func) 2486 * _testsinglephase_with_reinit 2487 * has no global or module state 2488 * mod.state_initialized returns None 2489 * an extra load in the main interpreter calls the cached init func 2490 * an extra load in legacy subinterpreters does a full load 2491 * _testsinglephase_with_state 2492 * has module state 2493 * an extra load in the main interpreter calls the cached init func 2494 * an extra load in legacy subinterpreters does a full load 2495 2496 (See Modules/_testsinglephase.c for more info.) 2497 2498 For all those modules, the snapshot after the initial load (not in 2499 the global extensions cache) would look like the following: 2500 2501 * initial load 2502 * id: ID of nww module object 2503 * ns: exactly what the module init put there 2504 * ns_id: ID of new module's __dict__ 2505 * cached_id: same as self.id 2506 * summed: 3 (never changes) 2507 * lookedup_id: same as self.id 2508 * state_initialized: a timestamp between the time of the load 2509 and the time of the snapshot 2510 * init_count: 1 (None for _testsinglephase_with_reinit) 2511 2512 For the other scenarios it varies. 2513 2514 For the _testsinglephase, _testsinglephase_basic_wrapper, and 2515 _testsinglephase_basic_copy modules, the snapshot should look 2516 like the following: 2517 2518 * reloaded 2519 * id: no change 2520 * ns: matches what the module init function put there, 2521 including the IDs of all contained objects, 2522 plus any extra attributes added before the reload 2523 * ns_id: no change 2524 * cached_id: no change 2525 * lookedup_id: no change 2526 * state_initialized: no change 2527 * init_count: no change 2528 * already loaded 2529 * (same as initial load except for ns and state_initialized) 2530 * ns: matches the initial load, incl. IDs of contained objects 2531 * state_initialized: no change from initial load 2532 2533 For _testsinglephase_with_reinit: 2534 2535 * reloaded: same as initial load (old module & ns is discarded) 2536 * already loaded: same as initial load (old module & ns is discarded) 2537 2538 For _testsinglephase_with_state: 2539 2540 * reloaded 2541 * (same as initial load (old module & ns is discarded), 2542 except init_count) 2543 * init_count: increase by 1 2544 * already loaded: same as reloaded 2545 """ 2546 2547 @classmethod 2548 def from_module(cls, mod): 2549 self = super().from_module(mod) 2550 self.summed = mod.sum(1, 2) 2551 self.lookedup = mod.look_up_self() 2552 self.lookedup_id = id(self.lookedup) 2553 self.state_initialized = mod.state_initialized() 2554 if hasattr(mod, 'initialized_count'): 2555 self.init_count = mod.initialized_count() 2556 return self 2557 2558 SCRIPT_BODY = ModuleSnapshot.SCRIPT_BODY + textwrap.dedent(''' 2559 snapshot['module'].update(dict( 2560 int_const=mod.int_const, 2561 str_const=mod.str_const, 2562 _module_initialized=mod._module_initialized, 2563 )) 2564 snapshot.update(dict( 2565 summed=mod.sum(1, 2), 2566 lookedup_id=id(mod.look_up_self()), 2567 state_initialized=mod.state_initialized(), 2568 init_count=mod.initialized_count(), 2569 has_spam=hasattr(mod, 'spam'), 2570 spam=getattr(mod, 'spam', None), 2571 )) 2572 ''').rstrip() 2573 2574 @classmethod 2575 def parse(cls, text): 2576 self = super().parse(text) 2577 if not self.has_spam: 2578 del self.spam 2579 del self.has_spam 2580 return self 2581 2582 2583@requires_singlephase_init 2584class SinglephaseInitTests(unittest.TestCase): 2585 2586 NAME = '_testsinglephase' 2587 2588 @classmethod 2589 def setUpClass(cls): 2590 spec = importlib.util.find_spec(cls.NAME) 2591 cls.LOADER = type(spec.loader) 2592 2593 # Apple extensions must be distributed as frameworks. This requires 2594 # a specialist loader, and we need to differentiate between the 2595 # spec.origin and the original file location. 2596 if is_apple_mobile: 2597 assert cls.LOADER is AppleFrameworkLoader 2598 2599 cls.ORIGIN = spec.origin 2600 with open(spec.origin + ".origin", "r") as f: 2601 cls.FILE = os.path.join( 2602 os.path.dirname(sys.executable), 2603 f.read().strip() 2604 ) 2605 else: 2606 assert cls.LOADER is ExtensionFileLoader 2607 2608 cls.ORIGIN = spec.origin 2609 cls.FILE = spec.origin 2610 2611 # Start fresh. 2612 cls.clean_up() 2613 2614 def tearDown(self): 2615 # Clean up the module. 2616 self.clean_up() 2617 2618 @classmethod 2619 def clean_up(cls): 2620 name = cls.NAME 2621 if name in sys.modules: 2622 if hasattr(sys.modules[name], '_clear_globals'): 2623 assert sys.modules[name].__file__ == cls.FILE, \ 2624 f"{sys.modules[name].__file__} != {cls.FILE}" 2625 2626 sys.modules[name]._clear_globals() 2627 del sys.modules[name] 2628 # Clear all internally cached data for the extension. 2629 _testinternalcapi.clear_extension(name, cls.ORIGIN) 2630 2631 ######################### 2632 # helpers 2633 2634 def add_module_cleanup(self, name): 2635 def clean_up(): 2636 # Clear all internally cached data for the extension. 2637 _testinternalcapi.clear_extension(name, self.ORIGIN) 2638 self.addCleanup(clean_up) 2639 2640 def _load_dynamic(self, name, path): 2641 """ 2642 Load an extension module. 2643 """ 2644 # This is essentially copied from the old imp module. 2645 from importlib._bootstrap import _load 2646 loader = self.LOADER(name, path) 2647 2648 # Issue bpo-24748: Skip the sys.modules check in _load_module_shim; 2649 # always load new extension. 2650 spec = importlib.util.spec_from_file_location(name, path, 2651 loader=loader) 2652 return _load(spec) 2653 2654 def load(self, name): 2655 try: 2656 already_loaded = self.already_loaded 2657 except AttributeError: 2658 already_loaded = self.already_loaded = {} 2659 assert name not in already_loaded 2660 mod = self._load_dynamic(name, self.ORIGIN) 2661 self.assertNotIn(mod, already_loaded.values()) 2662 already_loaded[name] = mod 2663 return types.SimpleNamespace( 2664 name=name, 2665 module=mod, 2666 snapshot=TestSinglePhaseSnapshot.from_module(mod), 2667 ) 2668 2669 def re_load(self, name, mod): 2670 assert sys.modules[name] is mod 2671 assert mod.__dict__ == mod.__dict__ 2672 reloaded = self._load_dynamic(name, self.ORIGIN) 2673 return types.SimpleNamespace( 2674 name=name, 2675 module=reloaded, 2676 snapshot=TestSinglePhaseSnapshot.from_module(reloaded), 2677 ) 2678 2679 # subinterpreters 2680 2681 def add_subinterpreter(self): 2682 interpid = _interpreters.create('legacy') 2683 def ensure_destroyed(): 2684 try: 2685 _interpreters.destroy(interpid) 2686 except _interpreters.InterpreterNotFoundError: 2687 pass 2688 self.addCleanup(ensure_destroyed) 2689 _interpreters.exec(interpid, textwrap.dedent(''' 2690 import sys 2691 import _testinternalcapi 2692 ''')) 2693 def clean_up(): 2694 _interpreters.exec(interpid, textwrap.dedent(f''' 2695 name = {self.NAME!r} 2696 if name in sys.modules: 2697 sys.modules.pop(name)._clear_globals() 2698 _testinternalcapi.clear_extension(name, {self.ORIGIN!r}) 2699 ''')) 2700 _interpreters.destroy(interpid) 2701 self.addCleanup(clean_up) 2702 return interpid 2703 2704 def import_in_subinterp(self, interpid=None, *, 2705 postscript=None, 2706 postcleanup=False, 2707 ): 2708 name = self.NAME 2709 2710 if postcleanup: 2711 import_ = 'import _testinternalcapi' if interpid is None else '' 2712 postcleanup = f''' 2713 {import_} 2714 mod._clear_globals() 2715 _testinternalcapi.clear_extension(name, {self.ORIGIN!r}) 2716 ''' 2717 2718 try: 2719 pipe = self._pipe 2720 except AttributeError: 2721 r, w = pipe = self._pipe = os.pipe() 2722 self.addCleanup(os.close, r) 2723 self.addCleanup(os.close, w) 2724 2725 snapshot = TestSinglePhaseSnapshot.from_subinterp( 2726 name, 2727 interpid, 2728 pipe=pipe, 2729 import_first=True, 2730 postscript=postscript, 2731 postcleanup=postcleanup, 2732 ) 2733 2734 return types.SimpleNamespace( 2735 name=name, 2736 module=None, 2737 snapshot=snapshot, 2738 ) 2739 2740 # checks 2741 2742 def check_common(self, loaded): 2743 isolated = False 2744 2745 mod = loaded.module 2746 if not mod: 2747 # It came from a subinterpreter. 2748 isolated = True 2749 mod = loaded.snapshot.module 2750 # mod.__name__ might not match, but the spec will. 2751 self.assertEqual(mod.__spec__.name, loaded.name) 2752 self.assertEqual(mod.__file__, self.FILE) 2753 self.assertEqual(mod.__spec__.origin, self.ORIGIN) 2754 if not isolated: 2755 self.assertTrue(issubclass(mod.error, Exception)) 2756 self.assertEqual(mod.int_const, 1969) 2757 self.assertEqual(mod.str_const, 'something different') 2758 self.assertIsInstance(mod._module_initialized, float) 2759 self.assertGreater(mod._module_initialized, 0) 2760 2761 snap = loaded.snapshot 2762 self.assertEqual(snap.summed, 3) 2763 if snap.state_initialized is not None: 2764 self.assertIsInstance(snap.state_initialized, float) 2765 self.assertGreater(snap.state_initialized, 0) 2766 if isolated: 2767 # The "looked up" module is interpreter-specific 2768 # (interp->imports.modules_by_index was set for the module). 2769 self.assertEqual(snap.lookedup_id, snap.id) 2770 self.assertEqual(snap.cached_id, snap.id) 2771 with self.assertRaises(AttributeError): 2772 snap.spam 2773 else: 2774 self.assertIs(snap.lookedup, mod) 2775 self.assertIs(snap.cached, mod) 2776 2777 def check_direct(self, loaded): 2778 # The module has its own PyModuleDef, with a matching name. 2779 self.assertEqual(loaded.module.__name__, loaded.name) 2780 self.assertIs(loaded.snapshot.lookedup, loaded.module) 2781 2782 def check_indirect(self, loaded, orig): 2783 # The module re-uses another's PyModuleDef, with a different name. 2784 assert orig is not loaded.module 2785 assert orig.__name__ != loaded.name 2786 self.assertNotEqual(loaded.module.__name__, loaded.name) 2787 self.assertIs(loaded.snapshot.lookedup, loaded.module) 2788 2789 def check_basic(self, loaded, expected_init_count): 2790 # m_size == -1 2791 # The module loads fresh the first time and copies m_copy after. 2792 snap = loaded.snapshot 2793 self.assertIsNot(snap.state_initialized, None) 2794 self.assertIsInstance(snap.init_count, int) 2795 self.assertGreater(snap.init_count, 0) 2796 self.assertEqual(snap.init_count, expected_init_count) 2797 2798 def check_with_reinit(self, loaded): 2799 # m_size >= 0 2800 # The module loads fresh every time. 2801 pass 2802 2803 def check_fresh(self, loaded): 2804 """ 2805 The module had not been loaded before (at least since fully reset). 2806 """ 2807 snap = loaded.snapshot 2808 # The module's init func was run. 2809 # A copy of the module's __dict__ was stored in def->m_base.m_copy. 2810 # The previous m_copy was deleted first. 2811 # _PyRuntime.imports.extensions was set. 2812 self.assertEqual(snap.init_count, 1) 2813 # The global state was initialized. 2814 # The module attrs were initialized from that state. 2815 self.assertEqual(snap.module._module_initialized, 2816 snap.state_initialized) 2817 2818 def check_semi_fresh(self, loaded, base, prev): 2819 """ 2820 The module had been loaded before and then reset 2821 (but the module global state wasn't). 2822 """ 2823 snap = loaded.snapshot 2824 # The module's init func was run again. 2825 # A copy of the module's __dict__ was stored in def->m_base.m_copy. 2826 # The previous m_copy was deleted first. 2827 # The module globals did not get reset. 2828 self.assertNotEqual(snap.id, base.snapshot.id) 2829 self.assertNotEqual(snap.id, prev.snapshot.id) 2830 self.assertEqual(snap.init_count, prev.snapshot.init_count + 1) 2831 # The global state was updated. 2832 # The module attrs were initialized from that state. 2833 self.assertEqual(snap.module._module_initialized, 2834 snap.state_initialized) 2835 self.assertNotEqual(snap.state_initialized, 2836 base.snapshot.state_initialized) 2837 self.assertNotEqual(snap.state_initialized, 2838 prev.snapshot.state_initialized) 2839 2840 def check_copied(self, loaded, base): 2841 """ 2842 The module had been loaded before and never reset. 2843 """ 2844 snap = loaded.snapshot 2845 # The module's init func was not run again. 2846 # The interpreter copied m_copy, as set by the other interpreter, 2847 # with objects owned by the other interpreter. 2848 # The module globals did not get reset. 2849 self.assertNotEqual(snap.id, base.snapshot.id) 2850 self.assertEqual(snap.init_count, base.snapshot.init_count) 2851 # The global state was not updated since the init func did not run. 2852 # The module attrs were not directly initialized from that state. 2853 # The state and module attrs still match the previous loading. 2854 self.assertEqual(snap.module._module_initialized, 2855 snap.state_initialized) 2856 self.assertEqual(snap.state_initialized, 2857 base.snapshot.state_initialized) 2858 2859 ######################### 2860 # the tests 2861 2862 def test_cleared_globals(self): 2863 loaded = self.load(self.NAME) 2864 _testsinglephase = loaded.module 2865 init_before = _testsinglephase.state_initialized() 2866 2867 _testsinglephase._clear_globals() 2868 init_after = _testsinglephase.state_initialized() 2869 init_count = _testsinglephase.initialized_count() 2870 2871 self.assertGreater(init_before, 0) 2872 self.assertEqual(init_after, 0) 2873 self.assertEqual(init_count, -1) 2874 2875 def test_variants(self): 2876 # Exercise the most meaningful variants described in Python/import.c. 2877 self.maxDiff = None 2878 2879 # Check the "basic" module. 2880 2881 name = self.NAME 2882 expected_init_count = 1 2883 with self.subTest(name): 2884 loaded = self.load(name) 2885 2886 self.check_common(loaded) 2887 self.check_direct(loaded) 2888 self.check_basic(loaded, expected_init_count) 2889 basic = loaded.module 2890 2891 # Check its indirect variants. 2892 2893 name = f'{self.NAME}_basic_wrapper' 2894 self.add_module_cleanup(name) 2895 expected_init_count += 1 2896 with self.subTest(name): 2897 loaded = self.load(name) 2898 2899 self.check_common(loaded) 2900 self.check_indirect(loaded, basic) 2901 self.check_basic(loaded, expected_init_count) 2902 2903 # Currently PyState_AddModule() always replaces the cached module. 2904 self.assertIs(basic.look_up_self(), loaded.module) 2905 self.assertEqual(basic.initialized_count(), expected_init_count) 2906 2907 # The cached module shouldn't change after this point. 2908 basic_lookedup = loaded.module 2909 2910 # Check its direct variant. 2911 2912 name = f'{self.NAME}_basic_copy' 2913 self.add_module_cleanup(name) 2914 expected_init_count += 1 2915 with self.subTest(name): 2916 loaded = self.load(name) 2917 2918 self.check_common(loaded) 2919 self.check_direct(loaded) 2920 self.check_basic(loaded, expected_init_count) 2921 2922 # This should change the cached module for _testsinglephase. 2923 self.assertIs(basic.look_up_self(), basic_lookedup) 2924 self.assertEqual(basic.initialized_count(), expected_init_count) 2925 2926 # Check the non-basic variant that has no state. 2927 2928 name = f'{self.NAME}_with_reinit' 2929 self.add_module_cleanup(name) 2930 with self.subTest(name): 2931 loaded = self.load(name) 2932 2933 self.check_common(loaded) 2934 self.assertIs(loaded.snapshot.state_initialized, None) 2935 self.check_direct(loaded) 2936 self.check_with_reinit(loaded) 2937 2938 # This should change the cached module for _testsinglephase. 2939 self.assertIs(basic.look_up_self(), basic_lookedup) 2940 self.assertEqual(basic.initialized_count(), expected_init_count) 2941 2942 # Check the basic variant that has state. 2943 2944 name = f'{self.NAME}_with_state' 2945 self.add_module_cleanup(name) 2946 with self.subTest(name): 2947 loaded = self.load(name) 2948 self.addCleanup(loaded.module._clear_module_state) 2949 2950 self.check_common(loaded) 2951 self.assertIsNot(loaded.snapshot.state_initialized, None) 2952 self.check_direct(loaded) 2953 self.check_with_reinit(loaded) 2954 2955 # This should change the cached module for _testsinglephase. 2956 self.assertIs(basic.look_up_self(), basic_lookedup) 2957 self.assertEqual(basic.initialized_count(), expected_init_count) 2958 2959 def test_basic_reloaded(self): 2960 # m_copy is copied into the existing module object. 2961 # Global state is not changed. 2962 self.maxDiff = None 2963 2964 for name in [ 2965 self.NAME, # the "basic" module 2966 f'{self.NAME}_basic_wrapper', # the indirect variant 2967 f'{self.NAME}_basic_copy', # the direct variant 2968 ]: 2969 self.add_module_cleanup(name) 2970 with self.subTest(name): 2971 loaded = self.load(name) 2972 reloaded = self.re_load(name, loaded.module) 2973 2974 self.check_common(loaded) 2975 self.check_common(reloaded) 2976 2977 # Make sure the original __dict__ did not get replaced. 2978 self.assertEqual(id(loaded.module.__dict__), 2979 loaded.snapshot.ns_id) 2980 self.assertEqual(loaded.snapshot.ns.__dict__, 2981 loaded.module.__dict__) 2982 2983 self.assertEqual(reloaded.module.__spec__.name, reloaded.name) 2984 self.assertEqual(reloaded.module.__name__, 2985 reloaded.snapshot.ns.__name__) 2986 2987 self.assertIs(reloaded.module, loaded.module) 2988 self.assertIs(reloaded.module.__dict__, loaded.module.__dict__) 2989 # It only happens to be the same but that's good enough here. 2990 # We really just want to verify that the re-loaded attrs 2991 # didn't change. 2992 self.assertIs(reloaded.snapshot.lookedup, 2993 loaded.snapshot.lookedup) 2994 self.assertEqual(reloaded.snapshot.state_initialized, 2995 loaded.snapshot.state_initialized) 2996 self.assertEqual(reloaded.snapshot.init_count, 2997 loaded.snapshot.init_count) 2998 2999 self.assertIs(reloaded.snapshot.cached, reloaded.module) 3000 3001 def test_with_reinit_reloaded(self): 3002 # The module's m_init func is run again. 3003 self.maxDiff = None 3004 3005 # Keep a reference around. 3006 basic = self.load(self.NAME) 3007 3008 for name, has_state in [ 3009 (f'{self.NAME}_with_reinit', False), # m_size == 0 3010 (f'{self.NAME}_with_state', True), # m_size > 0 3011 ]: 3012 self.add_module_cleanup(name) 3013 with self.subTest(name=name, has_state=has_state): 3014 loaded = self.load(name) 3015 if has_state: 3016 self.addCleanup(loaded.module._clear_module_state) 3017 3018 reloaded = self.re_load(name, loaded.module) 3019 if has_state: 3020 self.addCleanup(reloaded.module._clear_module_state) 3021 3022 self.check_common(loaded) 3023 self.check_common(reloaded) 3024 3025 # Make sure the original __dict__ did not get replaced. 3026 self.assertEqual(id(loaded.module.__dict__), 3027 loaded.snapshot.ns_id) 3028 self.assertEqual(loaded.snapshot.ns.__dict__, 3029 loaded.module.__dict__) 3030 3031 self.assertEqual(reloaded.module.__spec__.name, reloaded.name) 3032 self.assertEqual(reloaded.module.__name__, 3033 reloaded.snapshot.ns.__name__) 3034 3035 self.assertIsNot(reloaded.module, loaded.module) 3036 self.assertNotEqual(reloaded.module.__dict__, 3037 loaded.module.__dict__) 3038 self.assertIs(reloaded.snapshot.lookedup, reloaded.module) 3039 if loaded.snapshot.state_initialized is None: 3040 self.assertIs(reloaded.snapshot.state_initialized, None) 3041 else: 3042 self.assertGreater(reloaded.snapshot.state_initialized, 3043 loaded.snapshot.state_initialized) 3044 3045 self.assertIs(reloaded.snapshot.cached, reloaded.module) 3046 3047 @unittest.skipIf(_testinternalcapi is None, "requires _testinternalcapi") 3048 def test_check_state_first(self): 3049 for variant in ['', '_with_reinit', '_with_state']: 3050 name = f'{self.NAME}{variant}_check_cache_first' 3051 with self.subTest(name): 3052 mod = self._load_dynamic(name, self.ORIGIN) 3053 self.assertEqual(mod.__name__, name) 3054 sys.modules.pop(name, None) 3055 _testinternalcapi.clear_extension(name, self.ORIGIN) 3056 3057 # Currently, for every single-phrase init module loaded 3058 # in multiple interpreters, those interpreters share a 3059 # PyModuleDef for that object, which can be a problem. 3060 # Also, we test with a single-phase module that has global state, 3061 # which is shared by all interpreters. 3062 3063 @requires_subinterpreters 3064 def test_basic_multiple_interpreters_main_no_reset(self): 3065 # without resetting; already loaded in main interpreter 3066 3067 # At this point: 3068 # * alive in 0 interpreters 3069 # * module def may or may not be loaded already 3070 # * module def not in _PyRuntime.imports.extensions 3071 # * mod init func has not run yet (since reset, at least) 3072 # * m_copy not set (hasn't been loaded yet or already cleared) 3073 # * module's global state has not been initialized yet 3074 # (or already cleared) 3075 3076 main_loaded = self.load(self.NAME) 3077 _testsinglephase = main_loaded.module 3078 # Attrs set after loading are not in m_copy. 3079 _testsinglephase.spam = 'spam, spam, spam, spam, eggs, and spam' 3080 3081 self.check_common(main_loaded) 3082 self.check_fresh(main_loaded) 3083 3084 interpid1 = self.add_subinterpreter() 3085 interpid2 = self.add_subinterpreter() 3086 3087 # At this point: 3088 # * alive in 1 interpreter (main) 3089 # * module def in _PyRuntime.imports.extensions 3090 # * mod init func ran for the first time (since reset, at least) 3091 # * m_copy was copied from the main interpreter (was NULL) 3092 # * module's global state was initialized 3093 3094 # Use an interpreter that gets destroyed right away. 3095 loaded = self.import_in_subinterp() 3096 self.check_common(loaded) 3097 self.check_copied(loaded, main_loaded) 3098 3099 # At this point: 3100 # * alive in 1 interpreter (main) 3101 # * module def still in _PyRuntime.imports.extensions 3102 # * mod init func ran again 3103 # * m_copy is NULL (claered when the interpreter was destroyed) 3104 # (was from main interpreter) 3105 # * module's global state was updated, not reset 3106 3107 # Use a subinterpreter that sticks around. 3108 loaded = self.import_in_subinterp(interpid1) 3109 self.check_common(loaded) 3110 self.check_copied(loaded, main_loaded) 3111 3112 # At this point: 3113 # * alive in 2 interpreters (main, interp1) 3114 # * module def still in _PyRuntime.imports.extensions 3115 # * mod init func ran again 3116 # * m_copy was copied from interp1 3117 # * module's global state was updated, not reset 3118 3119 # Use a subinterpreter while the previous one is still alive. 3120 loaded = self.import_in_subinterp(interpid2) 3121 self.check_common(loaded) 3122 self.check_copied(loaded, main_loaded) 3123 3124 # At this point: 3125 # * alive in 3 interpreters (main, interp1, interp2) 3126 # * module def still in _PyRuntime.imports.extensions 3127 # * mod init func ran again 3128 # * m_copy was copied from interp2 (was from interp1) 3129 # * module's global state was updated, not reset 3130 3131 @no_rerun(reason="rerun not possible; module state is never cleared (see gh-102251)") 3132 @requires_subinterpreters 3133 def test_basic_multiple_interpreters_deleted_no_reset(self): 3134 # without resetting; already loaded in a deleted interpreter 3135 3136 if Py_TRACE_REFS: 3137 # It's a Py_TRACE_REFS build. 3138 # This test breaks interpreter isolation a little, 3139 # which causes problems on Py_TRACE_REF builds. 3140 raise unittest.SkipTest('crashes on Py_TRACE_REFS builds') 3141 3142 # At this point: 3143 # * alive in 0 interpreters 3144 # * module def may or may not be loaded already 3145 # * module def not in _PyRuntime.imports.extensions 3146 # * mod init func has not run yet (since reset, at least) 3147 # * m_copy not set (hasn't been loaded yet or already cleared) 3148 # * module's global state has not been initialized yet 3149 # (or already cleared) 3150 3151 interpid1 = self.add_subinterpreter() 3152 interpid2 = self.add_subinterpreter() 3153 3154 # First, load in the main interpreter but then completely clear it. 3155 loaded_main = self.load(self.NAME) 3156 loaded_main.module._clear_globals() 3157 _testinternalcapi.clear_extension(self.NAME, self.ORIGIN) 3158 3159 # At this point: 3160 # * alive in 0 interpreters 3161 # * module def loaded already 3162 # * module def was in _PyRuntime.imports.extensions, but cleared 3163 # * mod init func ran for the first time (since reset, at least) 3164 # * m_copy was set, but cleared (was NULL) 3165 # * module's global state was initialized but cleared 3166 3167 # Start with an interpreter that gets destroyed right away. 3168 base = self.import_in_subinterp( 3169 postscript=''' 3170 # Attrs set after loading are not in m_copy. 3171 mod.spam = 'spam, spam, mash, spam, eggs, and spam' 3172 ''') 3173 self.check_common(base) 3174 self.check_fresh(base) 3175 3176 # At this point: 3177 # * alive in 0 interpreters 3178 # * module def in _PyRuntime.imports.extensions 3179 # * mod init func ran for the first time (since reset) 3180 # * m_copy is still set (owned by main interpreter) 3181 # * module's global state was initialized, not reset 3182 3183 # Use a subinterpreter that sticks around. 3184 loaded_interp1 = self.import_in_subinterp(interpid1) 3185 self.check_common(loaded_interp1) 3186 self.check_copied(loaded_interp1, base) 3187 3188 # At this point: 3189 # * alive in 1 interpreter (interp1) 3190 # * module def still in _PyRuntime.imports.extensions 3191 # * mod init func did not run again 3192 # * m_copy was not changed 3193 # * module's global state was not touched 3194 3195 # Use a subinterpreter while the previous one is still alive. 3196 loaded_interp2 = self.import_in_subinterp(interpid2) 3197 self.check_common(loaded_interp2) 3198 self.check_copied(loaded_interp2, loaded_interp1) 3199 3200 # At this point: 3201 # * alive in 2 interpreters (interp1, interp2) 3202 # * module def still in _PyRuntime.imports.extensions 3203 # * mod init func did not run again 3204 # * m_copy was not changed 3205 # * module's global state was not touched 3206 3207 @requires_subinterpreters 3208 def test_basic_multiple_interpreters_reset_each(self): 3209 # resetting between each interpreter 3210 3211 # At this point: 3212 # * alive in 0 interpreters 3213 # * module def may or may not be loaded already 3214 # * module def not in _PyRuntime.imports.extensions 3215 # * mod init func has not run yet (since reset, at least) 3216 # * m_copy not set (hasn't been loaded yet or already cleared) 3217 # * module's global state has not been initialized yet 3218 # (or already cleared) 3219 3220 interpid1 = self.add_subinterpreter() 3221 interpid2 = self.add_subinterpreter() 3222 3223 # Use an interpreter that gets destroyed right away. 3224 loaded = self.import_in_subinterp( 3225 postscript=''' 3226 # Attrs set after loading are not in m_copy. 3227 mod.spam = 'spam, spam, mash, spam, eggs, and spam' 3228 ''', 3229 postcleanup=True, 3230 ) 3231 self.check_common(loaded) 3232 self.check_fresh(loaded) 3233 3234 # At this point: 3235 # * alive in 0 interpreters 3236 # * module def in _PyRuntime.imports.extensions 3237 # * mod init func ran for the first time (since reset, at least) 3238 # * m_copy is NULL (claered when the interpreter was destroyed) 3239 # * module's global state was initialized, not reset 3240 3241 # Use a subinterpreter that sticks around. 3242 loaded = self.import_in_subinterp(interpid1, postcleanup=True) 3243 self.check_common(loaded) 3244 self.check_fresh(loaded) 3245 3246 # At this point: 3247 # * alive in 1 interpreter (interp1) 3248 # * module def still in _PyRuntime.imports.extensions 3249 # * mod init func ran again 3250 # * m_copy was copied from interp1 (was NULL) 3251 # * module's global state was initialized, not reset 3252 3253 # Use a subinterpreter while the previous one is still alive. 3254 loaded = self.import_in_subinterp(interpid2, postcleanup=True) 3255 self.check_common(loaded) 3256 self.check_fresh(loaded) 3257 3258 # At this point: 3259 # * alive in 2 interpreters (interp2, interp2) 3260 # * module def still in _PyRuntime.imports.extensions 3261 # * mod init func ran again 3262 # * m_copy was copied from interp2 (was from interp1) 3263 # * module's global state was initialized, not reset 3264 3265 3266@cpython_only 3267class CAPITests(unittest.TestCase): 3268 def test_pyimport_addmodule(self): 3269 # gh-105922: Test PyImport_AddModuleRef(), PyImport_AddModule() 3270 # and PyImport_AddModuleObject() 3271 _testcapi = import_module("_testcapi") 3272 for name in ( 3273 'sys', # frozen module 3274 'test', # package 3275 __name__, # package.module 3276 ): 3277 _testcapi.check_pyimport_addmodule(name) 3278 3279 def test_pyimport_addmodule_create(self): 3280 # gh-105922: Test PyImport_AddModuleRef(), create a new module 3281 _testcapi = import_module("_testcapi") 3282 name = 'dontexist' 3283 self.assertNotIn(name, sys.modules) 3284 self.addCleanup(unload, name) 3285 3286 mod = _testcapi.check_pyimport_addmodule(name) 3287 self.assertIs(mod, sys.modules[name]) 3288 3289 3290if __name__ == '__main__': 3291 # Test needs to be a package, so we can do relative imports. 3292 unittest.main() 3293