• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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