test_import.py 37.4 KB
Newer Older
1 2
# We import importlib *ASAP* in order to test #15386
import importlib
3
import importlib.util
4
import builtins
5
from test.test_importlib.import_ import util as importlib_util
6
import marshal
7
import os
8
import platform
9
import py_compile
10
import random
11
import stat
12
import sys
13
import unittest
14
import textwrap
15
import errno
16
import shutil
17
import contextlib
Barry Warsaw's avatar
Barry Warsaw committed
18

19
import test.support
Barry Warsaw's avatar
Barry Warsaw committed
20 21 22
from test.support import (
    EnvironmentVarGuard, TESTFN, check_warnings, forget, is_jython,
    make_legacy_pyc, rmtree, run_unittest, swap_attr, swap_item, temp_umask,
23
    unlink, unload, create_empty_file, cpython_only)
24
from test import script_helper
25

26

27 28 29 30
skip_if_dont_write_bytecode = unittest.skipIf(
        sys.dont_write_bytecode,
        "test meaningful only when writing bytecode")

Tim Peters's avatar
Tim Peters committed
31
def remove_files(name):
Skip Montanaro's avatar
Skip Montanaro committed
32 33 34 35
    for f in (name + ".py",
              name + ".pyc",
              name + ".pyo",
              name + ".pyw",
Tim Peters's avatar
Tim Peters committed
36
              name + "$py.class"):
37
        unlink(f)
38
    rmtree('__pycache__')
Tim Peters's avatar
Tim Peters committed
39

40

41 42 43 44 45
@contextlib.contextmanager
def _ready_to_import(name=None, source=""):
    # sets up a temporary directory and removes it
    # creates the module file
    # temporarily clears the module from sys.modules (if any)
46
    # reverts or removes the module when cleaning up
47 48 49 50 51 52 53 54 55 56 57
    name = name or "spam"
    with script_helper.temp_dir() as tempdir:
        path = script_helper.make_script(tempdir, name, source)
        old_module = sys.modules.pop(name, None)
        try:
            sys.path.insert(0, tempdir)
            yield name, path
            sys.path.remove(tempdir)
        finally:
            if old_module is not None:
                sys.modules[name] = old_module
58 59
            elif name in sys.modules:
                del sys.modules[name]
60 61


62
class ImportTests(unittest.TestCase):
63

64 65
    def setUp(self):
        remove_files(TESTFN)
66
        importlib.invalidate_caches()
67

68 69
    def tearDown(self):
        unload(TESTFN)
Barry Warsaw's avatar
Barry Warsaw committed
70

71 72 73 74 75 76 77 78 79
    def test_import_raises_ModuleNotFoundError(self):
        with self.assertRaises(ModuleNotFoundError):
            import something_that_should_not_exist_anywhere

    def test_from_import_raises_ModuleNotFoundError(self):
        with self.assertRaises(ModuleNotFoundError):
            from something_that_should_not_exist_anywhere import blah
        with self.assertRaises(ModuleNotFoundError):
            from importlib import something_that_should_not_exist_anywhere
80

81 82 83
    def test_case_sensitivity(self):
        # Brief digression to test that import is case-sensitive:  if we got
        # this far, we know for sure that "random" exists.
Benjamin Peterson's avatar
Benjamin Peterson committed
84
        with self.assertRaises(ImportError):
85 86
            import RAnDoM

87 88 89
    def test_double_const(self):
        # Another brief digression to test the accuracy of manifest float
        # constants.
90 91
        from test import double_const  # don't blink -- that *was* the test

92
    def test_import(self):
93
        def test_with_extension(ext):
94
            # The extension is normally ".py", perhaps ".pyw".
95
            source = TESTFN + ext
Skip Montanaro's avatar
Skip Montanaro committed
96
            pyo = TESTFN + ".pyo"
97
            if is_jython:
98
                pyc = TESTFN + "$py.class"
Tim Peters's avatar
Tim Peters committed
99
            else:
Skip Montanaro's avatar
Skip Montanaro committed
100
                pyc = TESTFN + ".pyc"
101

102
            with open(source, "w") as f:
Barry Warsaw's avatar
Barry Warsaw committed
103 104
                print("# This tests Python's ability to import a",
                      ext, "file.", file=f)
105 106 107 108
                a = random.randrange(1000)
                b = random.randrange(1000)
                print("a =", a, file=f)
                print("b =", b, file=f)
109

110 111
            if TESTFN in sys.modules:
                del sys.modules[TESTFN]
112
            importlib.invalidate_caches()
113 114 115
            try:
                try:
                    mod = __import__(TESTFN)
116
                except ImportError as err:
117 118
                    self.fail("import from %s failed: %s" % (ext, err))

119
                self.assertEqual(mod.a, a,
120
                    "module loaded (%s) but contents invalid" % mod)
121
                self.assertEqual(mod.b, b,
122 123
                    "module loaded (%s) but contents invalid" % mod)
            finally:
Barry Warsaw's avatar
Barry Warsaw committed
124
                forget(TESTFN)
125 126 127
                unlink(source)
                unlink(pyc)
                unlink(pyo)
128 129 130

        sys.path.insert(0, os.curdir)
        try:
Skip Montanaro's avatar
Skip Montanaro committed
131
            test_with_extension(".py")
132
            if sys.platform.startswith("win"):
133
                for ext in [".PY", ".Py", ".pY", ".pyw", ".PYW", ".pYw"]:
134 135 136 137 138
                    test_with_extension(ext)
        finally:
            del sys.path[0]

    def test_module_with_large_stack(self, module='longlist'):
139
        # Regression test for http://bugs.python.org/issue561858.
Skip Montanaro's avatar
Skip Montanaro committed
140
        filename = module + '.py'
141

142
        # Create a file with a list of 65000 elements.
143
        with open(filename, 'w') as f:
144 145 146 147 148
            f.write('d = [\n')
            for i in range(65000):
                f.write('"",\n')
            f.write(']')

149 150 151 152 153 154
        try:
            # Compile & remove .py file; we only need .pyc (or .pyo).
            # Bytecode must be relocated from the PEP 3147 bytecode-only location.
            py_compile.compile(filename)
        finally:
            unlink(filename)
155

156
        # Need to be able to load from current dir.
157
        sys.path.append('')
158
        importlib.invalidate_caches()
159

160
        try:
161
            make_legacy_pyc(filename)
162 163 164 165 166 167 168
            # This used to crash.
            exec('import ' + module)
        finally:
            # Cleanup.
            del sys.path[-1]
            unlink(filename + 'c')
            unlink(filename + 'o')
169 170

    def test_failing_import_sticks(self):
Skip Montanaro's avatar
Skip Montanaro committed
171
        source = TESTFN + ".py"
172 173
        with open(source, "w") as f:
            print("a = 1/0", file=f)
174 175 176 177

        # New in 2.4, we shouldn't be able to import that no matter how often
        # we try.
        sys.path.insert(0, os.curdir)
178
        importlib.invalidate_caches()
179 180
        if TESTFN in sys.modules:
            del sys.modules[TESTFN]
Tim Peters's avatar
Tim Peters committed
181
        try:
182
            for i in [1, 2, 3]:
183 184 185
                self.assertRaises(ZeroDivisionError, __import__, TESTFN)
                self.assertNotIn(TESTFN, sys.modules,
                                 "damaged module in sys.modules on %i try" % i)
186
        finally:
187
            del sys.path[0]
188 189 190 191 192
            remove_files(TESTFN)

    def test_import_name_binding(self):
        # import x.y.z binds x in the current namespace
        import test as x
193
        import test.support
194 195
        self.assertTrue(x is test, x.__name__)
        self.assertTrue(hasattr(test.support, "__file__"))
196 197

        # import x.y.z as w binds z as w
198
        import test.support as y
199
        self.assertTrue(y is test.support, y.__name__)
200

201 202
    def test_failing_reload(self):
        # A failing reload should leave the module object in sys.modules.
203
        source = TESTFN + os.extsep + "py"
204 205 206 207 208 209
        with open(source, "w") as f:
            f.write("a = 1\nb=2\n")

        sys.path.insert(0, os.curdir)
        try:
            mod = __import__(TESTFN)
210
            self.assertIn(TESTFN, sys.modules)
211 212
            self.assertEqual(mod.a, 1, "module has wrong attribute values")
            self.assertEqual(mod.b, 2, "module has wrong attribute values")
213 214 215 216 217 218 219 220 221 222 223

            # On WinXP, just replacing the .py file wasn't enough to
            # convince reload() to reparse it.  Maybe the timestamp didn't
            # move enough.  We force it to get reparsed by removing the
            # compiled file too.
            remove_files(TESTFN)

            # Now damage the module.
            with open(source, "w") as f:
                f.write("a = 10\nb=20//0\n")

224
            self.assertRaises(ZeroDivisionError, importlib.reload, mod)
225 226
            # But we still expect the module to be in sys.modules.
            mod = sys.modules.get(TESTFN)
227
            self.assertIsNot(mod, None, "expected module to be in sys.modules")
228 229 230

            # We should have replaced a w/ 10, but the old b value should
            # stick.
231 232
            self.assertEqual(mod.a, 10, "module has wrong attribute values")
            self.assertEqual(mod.b, 2, "module has wrong attribute values")
233 234

        finally:
235
            del sys.path[0]
236
            remove_files(TESTFN)
237
            unload(TESTFN)
238

239
    @skip_if_dont_write_bytecode
240 241 242 243 244 245 246 247 248
    def test_file_to_source(self):
        # check if __file__ points to the source file where available
        source = TESTFN + ".py"
        with open(source, "w") as f:
            f.write("test = None\n")

        sys.path.insert(0, os.curdir)
        try:
            mod = __import__(TESTFN)
249
            self.assertTrue(mod.__file__.endswith('.py'))
250 251
            os.remove(source)
            del sys.modules[TESTFN]
Barry Warsaw's avatar
Barry Warsaw committed
252
            make_legacy_pyc(source)
253
            importlib.invalidate_caches()
254
            mod = __import__(TESTFN)
Barry Warsaw's avatar
Barry Warsaw committed
255
            base, ext = os.path.splitext(mod.__file__)
256
            self.assertIn(ext, ('.pyc', '.pyo'))
257
        finally:
258
            del sys.path[0]
259 260 261 262
            remove_files(TESTFN)
            if TESTFN in sys.modules:
                del sys.modules[TESTFN]

263 264 265 266 267 268 269 270 271 272 273
    def test_import_name_binding(self):
        # import x.y.z binds x in the current namespace.
        import test as x
        import test.support
        self.assertIs(x, test, x.__name__)
        self.assertTrue(hasattr(test.support, "__file__"))

        # import x.y.z as w binds z as w.
        import test.support as y
        self.assertIs(y, test.support, y.__name__)

274
    def test_import_by_filename(self):
275
        path = os.path.abspath(TESTFN)
276 277 278 279 280
        encoding = sys.getfilesystemencoding()
        try:
            path.encode(encoding)
        except UnicodeEncodeError:
            self.skipTest('path is not encodable to {}'.format(encoding))
281
        with self.assertRaises(ImportError) as c:
282 283
            __import__(path)

284 285 286 287 288 289
    def test_import_in_del_does_not_crash(self):
        # Issue 4236
        testfn = script_helper.make_script('', TESTFN, textwrap.dedent("""\
            import sys
            class C:
               def __del__(self):
290
                  import importlib
291 292 293 294
            sys.argv.insert(0, C())
            """))
        script_helper.assert_python_ok(testfn)

295 296 297
    def test_timestamp_overflow(self):
        # A modification timestamp larger than 2**32 should not be a problem
        # when importing a module (issue #11235).
298 299 300
        sys.path.insert(0, os.curdir)
        try:
            source = TESTFN + ".py"
301
            compiled = importlib.util.cache_from_source(source)
302 303 304
            with open(source, 'w') as f:
                pass
            try:
305
                os.utime(source, (2 ** 33 - 5, 2 ** 33 - 5))
306 307
            except OverflowError:
                self.skipTest("cannot set modification time to large integer")
308 309 310 311
            except OSError as e:
                if e.errno != getattr(errno, 'EOVERFLOW', None):
                    raise
                self.skipTest("cannot set modification time to large integer ({})".format(e))
312 313 314 315 316 317
            __import__(TESTFN)
            # The pyc file was created.
            os.stat(compiled)
        finally:
            del sys.path[0]
            remove_files(TESTFN)
318

319 320 321 322 323 324
    def test_bogus_fromlist(self):
        try:
            __import__('http', fromlist=['blah'])
        except ImportError:
            self.fail("fromlist must allow bogus names")

325 326 327 328 329 330 331
    @cpython_only
    def test_delete_builtins_import(self):
        args = ["-c", "del __builtins__.__import__; import os"]
        popen = script_helper.spawn_python(*args)
        stdout, stderr = popen.communicate()
        self.assertIn(b"ImportError", stdout)

332

Ezio Melotti's avatar
Ezio Melotti committed
333
@skip_if_dont_write_bytecode
334 335 336 337 338 339 340 341
class FilePermissionTests(unittest.TestCase):
    # tests for file mode on cached .pyc/.pyo files

    @unittest.skipUnless(os.name == 'posix',
                         "test meaningful only on posix systems")
    def test_creation_mode(self):
        mask = 0o022
        with temp_umask(mask), _ready_to_import() as (name, path):
342
            cached_path = importlib.util.cache_from_source(path)
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
            module = __import__(name)
            if not os.path.exists(cached_path):
                self.fail("__import__ did not result in creation of "
                          "either a .pyc or .pyo file")
            stat_info = os.stat(cached_path)

        # Check that the umask is respected, and the executable bits
        # aren't set.
        self.assertEqual(oct(stat.S_IMODE(stat_info.st_mode)),
                         oct(0o666 & ~mask))

    @unittest.skipUnless(os.name == 'posix',
                         "test meaningful only on posix systems")
    def test_cached_mode_issue_2051(self):
        # permissions of .pyc should match those of .py, regardless of mask
        mode = 0o600
        with temp_umask(0o022), _ready_to_import() as (name, path):
360
            cached_path = importlib.util.cache_from_source(path)
361 362 363 364 365 366 367 368 369 370 371 372 373 374
            os.chmod(path, mode)
            __import__(name)
            if not os.path.exists(cached_path):
                self.fail("__import__ did not result in creation of "
                          "either a .pyc or .pyo file")
            stat_info = os.stat(cached_path)

        self.assertEqual(oct(stat.S_IMODE(stat_info.st_mode)), oct(mode))

    @unittest.skipUnless(os.name == 'posix',
                         "test meaningful only on posix systems")
    def test_cached_readonly(self):
        mode = 0o400
        with temp_umask(0o022), _ready_to_import() as (name, path):
375
            cached_path = importlib.util.cache_from_source(path)
376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414
            os.chmod(path, mode)
            __import__(name)
            if not os.path.exists(cached_path):
                self.fail("__import__ did not result in creation of "
                          "either a .pyc or .pyo file")
            stat_info = os.stat(cached_path)

        expected = mode | 0o200 # Account for fix for issue #6074
        self.assertEqual(oct(stat.S_IMODE(stat_info.st_mode)), oct(expected))

    def test_pyc_always_writable(self):
        # Initially read-only .pyc files on Windows used to cause problems
        # with later updates, see issue #6074 for details
        with _ready_to_import() as (name, path):
            # Write a Python file, make it read-only and import it
            with open(path, 'w') as f:
                f.write("x = 'original'\n")
            # Tweak the mtime of the source to ensure pyc gets updated later
            s = os.stat(path)
            os.utime(path, (s.st_atime, s.st_mtime-100000000))
            os.chmod(path, 0o400)
            m = __import__(name)
            self.assertEqual(m.x, 'original')
            # Change the file and then reimport it
            os.chmod(path, 0o600)
            with open(path, 'w') as f:
                f.write("x = 'rewritten'\n")
            unload(name)
            importlib.invalidate_caches()
            m = __import__(name)
            self.assertEqual(m.x, 'rewritten')
            # Now delete the source file and check the pyc was rewritten
            unlink(path)
            unload(name)
            importlib.invalidate_caches()
            if __debug__:
                bytecode_only = path + "c"
            else:
                bytecode_only = path + "o"
415
            os.rename(importlib.util.cache_from_source(path), bytecode_only)
416 417 418 419
            m = __import__(name)
            self.assertEqual(m.x, 'rewritten')


420
class PycRewritingTests(unittest.TestCase):
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436
    # Test that the `co_filename` attribute on code objects always points
    # to the right file, even when various things happen (e.g. both the .py
    # and the .pyc file are renamed).

    module_name = "unlikely_module_name"
    module_source = """
import sys
code_filename = sys._getframe().f_code.co_filename
module_filename = __file__
constant = 1
def func():
    pass
func_filename = func.__code__.co_filename
"""
    dir_name = os.path.abspath(TESTFN)
    file_name = os.path.join(dir_name, module_name) + os.extsep + "py"
437
    compiled_name = importlib.util.cache_from_source(file_name)
438 439 440 441 442 443 444 445

    def setUp(self):
        self.sys_path = sys.path[:]
        self.orig_module = sys.modules.pop(self.module_name, None)
        os.mkdir(self.dir_name)
        with open(self.file_name, "w") as f:
            f.write(self.module_source)
        sys.path.insert(0, self.dir_name)
446
        importlib.invalidate_caches()
447 448 449 450 451 452

    def tearDown(self):
        sys.path[:] = self.sys_path
        if self.orig_module is not None:
            sys.modules[self.module_name] = self.orig_module
        else:
453 454 455
            unload(self.module_name)
        unlink(self.file_name)
        unlink(self.compiled_name)
456
        rmtree(self.dir_name)
457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484

    def import_module(self):
        ns = globals()
        __import__(self.module_name, ns, ns)
        return sys.modules[self.module_name]

    def test_basics(self):
        mod = self.import_module()
        self.assertEqual(mod.module_filename, self.file_name)
        self.assertEqual(mod.code_filename, self.file_name)
        self.assertEqual(mod.func_filename, self.file_name)
        del sys.modules[self.module_name]
        mod = self.import_module()
        self.assertEqual(mod.module_filename, self.file_name)
        self.assertEqual(mod.code_filename, self.file_name)
        self.assertEqual(mod.func_filename, self.file_name)

    def test_incorrect_code_name(self):
        py_compile.compile(self.file_name, dfile="another_module.py")
        mod = self.import_module()
        self.assertEqual(mod.module_filename, self.file_name)
        self.assertEqual(mod.code_filename, self.file_name)
        self.assertEqual(mod.func_filename, self.file_name)

    def test_module_without_source(self):
        target = "another_module.py"
        py_compile.compile(self.file_name, dfile=target)
        os.remove(self.file_name)
Barry Warsaw's avatar
Barry Warsaw committed
485
        pyc_file = make_legacy_pyc(self.file_name)
486
        importlib.invalidate_caches()
487
        mod = self.import_module()
Barry Warsaw's avatar
Barry Warsaw committed
488
        self.assertEqual(mod.module_filename, pyc_file)
489 490 491 492 493 494
        self.assertEqual(mod.code_filename, target)
        self.assertEqual(mod.func_filename, target)

    def test_foreign_code(self):
        py_compile.compile(self.file_name)
        with open(self.compiled_name, "rb") as f:
495
            header = f.read(12)
496 497
            code = marshal.load(f)
        constants = list(code.co_consts)
498
        foreign_code = importlib.import_module.__code__
499 500 501 502 503 504 505 506 507 508 509 510 511 512
        pos = constants.index(1)
        constants[pos] = foreign_code
        code = type(code)(code.co_argcount, code.co_kwonlyargcount,
                          code.co_nlocals, code.co_stacksize,
                          code.co_flags, code.co_code, tuple(constants),
                          code.co_names, code.co_varnames, code.co_filename,
                          code.co_name, code.co_firstlineno, code.co_lnotab,
                          code.co_freevars, code.co_cellvars)
        with open(self.compiled_name, "wb") as f:
            f.write(header)
            marshal.dump(code, f)
        mod = self.import_module()
        self.assertEqual(mod.constant.co_filename, foreign_code.co_filename)

513

514 515 516
class PathsTests(unittest.TestCase):
    SAMPLES = ('test', 'test\u00e4\u00f6\u00fc\u00df', 'test\u00e9\u00e8',
               'test\u00b0\u00b3\u00b2')
517 518 519 520 521 522 523
    path = TESTFN

    def setUp(self):
        os.mkdir(self.path)
        self.syspath = sys.path[:]

    def tearDown(self):
524
        rmtree(self.path)
525
        sys.path[:] = self.syspath
526

527
    # Regression test for http://bugs.python.org/issue1293.
528
    def test_trailing_slash(self):
529 530
        with open(os.path.join(self.path, 'test_trailing_slash.py'), 'w') as f:
            f.write("testdata = 'test_trailing_slash'")
531 532 533 534 535
        sys.path.append(self.path+'/')
        mod = __import__("test_trailing_slash")
        self.assertEqual(mod.testdata, 'test_trailing_slash')
        unload("test_trailing_slash")

536
    # Regression test for http://bugs.python.org/issue3677.
537
    @unittest.skipUnless(sys.platform == 'win32', 'Windows-specific')
Brett Cannon's avatar
Brett Cannon committed
538
    def test_UNC_path(self):
539 540
        with open(os.path.join(self.path, 'test_unc_path.py'), 'w') as f:
            f.write("testdata = 'test_unc_path'")
541
        importlib.invalidate_caches()
542
        # Create the UNC path, like \\myhost\c$\foo\bar.
543 544 545 546 547 548
        path = os.path.abspath(self.path)
        import socket
        hn = socket.gethostname()
        drive = path[0]
        unc = "\\\\%s\\%s$"%(hn, drive)
        unc += path[2:]
549
        try:
550
            os.listdir(unc)
551 552 553 554 555
        except OSError as e:
            if e.errno in (errno.EPERM, errno.EACCES):
                # See issue #15338
                self.skipTest("cannot access administrative share %r" % (unc,))
            raise
556 557 558 559 560 561 562 563 564
        sys.path.insert(0, unc)
        try:
            mod = __import__("test_unc_path")
        except ImportError as e:
            self.fail("could not import 'test_unc_path' from %r: %r"
                      % (unc, e))
        self.assertEqual(mod.testdata, 'test_unc_path')
        self.assertTrue(mod.__file__.startswith(unc), mod.__file__)
        unload("test_unc_path")
565 566


567
class RelativeImportTests(unittest.TestCase):
568

Christian Heimes's avatar
Christian Heimes committed
569
    def tearDown(self):
570 571
        unload("test.relimport")
    setUp = tearDown
Christian Heimes's avatar
Christian Heimes committed
572 573 574 575

    def test_relimport_star(self):
        # This will import * from .test_import.
        from . import relimport
576
        self.assertTrue(hasattr(relimport, "RelativeImportTests"))
Christian Heimes's avatar
Christian Heimes committed
577

Georg Brandl's avatar
Georg Brandl committed
578
    def test_issue3221(self):
Benjamin Peterson's avatar
Benjamin Peterson committed
579 580 581
        # Note for mergers: the 'absolute' tests from the 2.x branch
        # are missing in Py3k because implicit relative imports are
        # a thing of the past
582 583
        #
        # Regression test for http://bugs.python.org/issue3221.
Georg Brandl's avatar
Georg Brandl committed
584 585
        def check_relative():
            exec("from . import relimport", ns)
586

Benjamin Peterson's avatar
Benjamin Peterson committed
587
        # Check relative import OK with __package__ and __name__ correct
Georg Brandl's avatar
Georg Brandl committed
588 589
        ns = dict(__package__='test', __name__='test.notarealmodule')
        check_relative()
590

Benjamin Peterson's avatar
Benjamin Peterson committed
591
        # Check relative import OK with only __name__ wrong
Georg Brandl's avatar
Georg Brandl committed
592 593
        ns = dict(__package__='test', __name__='notarealpkg.notarealmodule')
        check_relative()
594

Benjamin Peterson's avatar
Benjamin Peterson committed
595
        # Check relative import fails with only __package__ wrong
Georg Brandl's avatar
Georg Brandl committed
596 597
        ns = dict(__package__='foo', __name__='test.notarealmodule')
        self.assertRaises(SystemError, check_relative)
598

Benjamin Peterson's avatar
Benjamin Peterson committed
599
        # Check relative import fails with __package__ and __name__ wrong
Georg Brandl's avatar
Georg Brandl committed
600 601
        ns = dict(__package__='foo', __name__='notarealpkg.notarealmodule')
        self.assertRaises(SystemError, check_relative)
602

Benjamin Peterson's avatar
Benjamin Peterson committed
603
        # Check relative import fails with package set to a non-string
Georg Brandl's avatar
Georg Brandl committed
604
        ns = dict(__package__=object())
605
        self.assertRaises(TypeError, check_relative)
Georg Brandl's avatar
Georg Brandl committed
606

607
    def test_absolute_import_without_future(self):
608
        # If explicit relative import syntax is used, then do not try
609
        # to perform an absolute import in the face of failure.
610
        # Issue #7902.
611
        with self.assertRaises(ImportError):
612 613
            from .os import sep
            self.fail("explicit relative import triggered an "
614
                      "implicit absolute import")
615

616

617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636
class OverridingImportBuiltinTests(unittest.TestCase):
    def test_override_builtin(self):
        # Test that overriding builtins.__import__ can bypass sys.modules.
        import os

        def foo():
            import os
            return os
        self.assertEqual(foo(), os)  # Quick sanity check.

        with swap_attr(builtins, "__import__", lambda *x: 5):
            self.assertEqual(foo(), 5)

        # Test what happens when we shadow __import__ in globals(); this
        # currently does not impact the import process, but if this changes,
        # other code will need to change, so keep this test as a tripwire.
        with swap_item(globals(), "__import__", lambda *x: 5):
            self.assertEqual(foo(), os)


Barry Warsaw's avatar
Barry Warsaw committed
637 638 639
class PycacheTests(unittest.TestCase):
    # Test the various PEP 3147 related behaviors.

640
    tag = sys.implementation.cache_tag
Barry Warsaw's avatar
Barry Warsaw committed
641 642 643 644 645 646 647 648 649 650 651 652

    def _clean(self):
        forget(TESTFN)
        rmtree('__pycache__')
        unlink(self.source)

    def setUp(self):
        self.source = TESTFN + '.py'
        self._clean()
        with open(self.source, 'w') as fp:
            print('# This is a test file written by test_import.py', file=fp)
        sys.path.insert(0, os.curdir)
653
        importlib.invalidate_caches()
Barry Warsaw's avatar
Barry Warsaw committed
654 655 656 657 658 659

    def tearDown(self):
        assert sys.path[0] == os.curdir, 'Unexpected sys.path[0]'
        del sys.path[0]
        self._clean()

660
    @skip_if_dont_write_bytecode
Barry Warsaw's avatar
Barry Warsaw committed
661 662 663 664 665
    def test_import_pyc_path(self):
        self.assertFalse(os.path.exists('__pycache__'))
        __import__(TESTFN)
        self.assertTrue(os.path.exists('__pycache__'))
        self.assertTrue(os.path.exists(os.path.join(
666
            '__pycache__', '{}.{}.py{}'.format(
667
            TESTFN, self.tag, 'c' if __debug__ else 'o'))))
Barry Warsaw's avatar
Barry Warsaw committed
668 669 670

    @unittest.skipUnless(os.name == 'posix',
                         "test meaningful only on posix systems")
671
    @unittest.skipIf(hasattr(os, 'geteuid') and os.geteuid() == 0,
672
            "due to varying filesystem permission semantics (issue #11956)")
673
    @skip_if_dont_write_bytecode
Barry Warsaw's avatar
Barry Warsaw committed
674 675 676 677 678 679 680 681 682
    def test_unwritable_directory(self):
        # When the umask causes the new __pycache__ directory to be
        # unwritable, the import still succeeds but no .pyc file is written.
        with temp_umask(0o222):
            __import__(TESTFN)
        self.assertTrue(os.path.exists('__pycache__'))
        self.assertFalse(os.path.exists(os.path.join(
            '__pycache__', '{}.{}.pyc'.format(TESTFN, self.tag))))

683
    @skip_if_dont_write_bytecode
Barry Warsaw's avatar
Barry Warsaw committed
684 685 686 687
    def test_missing_source(self):
        # With PEP 3147 cache layout, removing the source but leaving the pyc
        # file does not satisfy the import.
        __import__(TESTFN)
688
        pyc_file = importlib.util.cache_from_source(self.source)
Barry Warsaw's avatar
Barry Warsaw committed
689 690 691 692 693
        self.assertTrue(os.path.exists(pyc_file))
        os.remove(self.source)
        forget(TESTFN)
        self.assertRaises(ImportError, __import__, TESTFN)

694
    @skip_if_dont_write_bytecode
Barry Warsaw's avatar
Barry Warsaw committed
695 696 697 698 699 700 701 702 703 704
    def test_missing_source_legacy(self):
        # Like test_missing_source() except that for backward compatibility,
        # when the pyc file lives where the py file would have been (and named
        # without the tag), it is importable.  The __file__ of the imported
        # module is the pyc location.
        __import__(TESTFN)
        # pyc_file gets removed in _clean() via tearDown().
        pyc_file = make_legacy_pyc(self.source)
        os.remove(self.source)
        unload(TESTFN)
705
        importlib.invalidate_caches()
Barry Warsaw's avatar
Barry Warsaw committed
706 707 708 709 710 711 712
        m = __import__(TESTFN)
        self.assertEqual(m.__file__,
                         os.path.join(os.curdir, os.path.relpath(pyc_file)))

    def test___cached__(self):
        # Modules now also have an __cached__ that points to the pyc file.
        m = __import__(TESTFN)
713
        pyc_file = importlib.util.cache_from_source(TESTFN + '.py')
Barry Warsaw's avatar
Barry Warsaw committed
714 715
        self.assertEqual(m.__cached__, os.path.join(os.curdir, pyc_file))

716
    @skip_if_dont_write_bytecode
Barry Warsaw's avatar
Barry Warsaw committed
717 718 719 720 721 722 723 724 725 726
    def test___cached___legacy_pyc(self):
        # Like test___cached__() except that for backward compatibility,
        # when the pyc file lives where the py file would have been (and named
        # without the tag), it is importable.  The __cached__ of the imported
        # module is the pyc location.
        __import__(TESTFN)
        # pyc_file gets removed in _clean() via tearDown().
        pyc_file = make_legacy_pyc(self.source)
        os.remove(self.source)
        unload(TESTFN)
727
        importlib.invalidate_caches()
Barry Warsaw's avatar
Barry Warsaw committed
728 729 730 731
        m = __import__(TESTFN)
        self.assertEqual(m.__cached__,
                         os.path.join(os.curdir, os.path.relpath(pyc_file)))

732
    @skip_if_dont_write_bytecode
Barry Warsaw's avatar
Barry Warsaw committed
733 734 735
    def test_package___cached__(self):
        # Like test___cached__ but for packages.
        def cleanup():
736
            rmtree('pep3147')
737 738
            unload('pep3147.foo')
            unload('pep3147')
Barry Warsaw's avatar
Barry Warsaw committed
739 740 741 742 743 744 745
        os.mkdir('pep3147')
        self.addCleanup(cleanup)
        # Touch the __init__.py
        with open(os.path.join('pep3147', '__init__.py'), 'w'):
            pass
        with open(os.path.join('pep3147', 'foo.py'), 'w'):
            pass
746
        importlib.invalidate_caches()
Barry Warsaw's avatar
Barry Warsaw committed
747
        m = __import__('pep3147.foo')
748
        init_pyc = importlib.util.cache_from_source(
Barry Warsaw's avatar
Barry Warsaw committed
749 750
            os.path.join('pep3147', '__init__.py'))
        self.assertEqual(m.__cached__, os.path.join(os.curdir, init_pyc))
751
        foo_pyc = importlib.util.cache_from_source(os.path.join('pep3147', 'foo.py'))
Barry Warsaw's avatar
Barry Warsaw committed
752 753 754 755 756 757 758
        self.assertEqual(sys.modules['pep3147.foo'].__cached__,
                         os.path.join(os.curdir, foo_pyc))

    def test_package___cached___from_pyc(self):
        # Like test___cached__ but ensuring __cached__ when imported from a
        # PEP 3147 pyc file.
        def cleanup():
759
            rmtree('pep3147')
760 761
            unload('pep3147.foo')
            unload('pep3147')
Barry Warsaw's avatar
Barry Warsaw committed
762 763 764 765 766 767 768
        os.mkdir('pep3147')
        self.addCleanup(cleanup)
        # Touch the __init__.py
        with open(os.path.join('pep3147', '__init__.py'), 'w'):
            pass
        with open(os.path.join('pep3147', 'foo.py'), 'w'):
            pass
769
        importlib.invalidate_caches()
Barry Warsaw's avatar
Barry Warsaw committed
770 771 772
        m = __import__('pep3147.foo')
        unload('pep3147.foo')
        unload('pep3147')
773
        importlib.invalidate_caches()
Barry Warsaw's avatar
Barry Warsaw committed
774
        m = __import__('pep3147.foo')
775
        init_pyc = importlib.util.cache_from_source(
Barry Warsaw's avatar
Barry Warsaw committed
776 777
            os.path.join('pep3147', '__init__.py'))
        self.assertEqual(m.__cached__, os.path.join(os.curdir, init_pyc))
778
        foo_pyc = importlib.util.cache_from_source(os.path.join('pep3147', 'foo.py'))
Barry Warsaw's avatar
Barry Warsaw committed
779 780 781
        self.assertEqual(sys.modules['pep3147.foo'].__cached__,
                         os.path.join(os.curdir, foo_pyc))

782 783 784 785 786 787 788 789 790 791
    def test_recompute_pyc_same_second(self):
        # Even when the source file doesn't change timestamp, a change in
        # source size is enough to trigger recomputation of the pyc file.
        __import__(TESTFN)
        unload(TESTFN)
        with open(self.source, 'a') as fp:
            print("x = 5", file=fp)
        m = __import__(TESTFN)
        self.assertEqual(m.x, 5)

Barry Warsaw's avatar
Barry Warsaw committed
792

793 794
class TestSymbolicallyLinkedPackage(unittest.TestCase):
    package_name = 'sample'
795
    tagged = package_name + '-tagged'
796 797

    def setUp(self):
798 799
        test.support.rmtree(self.tagged)
        test.support.rmtree(self.package_name)
800 801 802 803 804
        self.orig_sys_path = sys.path[:]

        # create a sample package; imagine you have a package with a tag and
        #  you want to symbolically link it from its untagged name.
        os.mkdir(self.tagged)
805
        self.addCleanup(test.support.rmtree, self.tagged)
806
        init_file = os.path.join(self.tagged, '__init__.py')
807 808
        test.support.create_empty_file(init_file)
        assert os.path.exists(init_file)
809 810 811

        # now create a symlink to the tagged package
        # sample -> sample-tagged
812
        os.symlink(self.tagged, self.package_name, target_is_directory=True)
813 814
        self.addCleanup(test.support.unlink, self.package_name)
        importlib.invalidate_caches()
815

816
        self.assertEqual(os.path.isdir(self.package_name), True)
817

818
        assert os.path.isfile(os.path.join(self.package_name, '__init__.py'))
819

820 821
    def tearDown(self):
        sys.path[:] = self.orig_sys_path
822 823 824 825 826 827 828 829 830 831

    # regression test for issue6727
    @unittest.skipUnless(
        not hasattr(sys, 'getwindowsversion')
        or sys.getwindowsversion() >= (6, 0),
        "Windows Vista or later required")
    @test.support.skip_unless_symlink
    def test_symlinked_dir_importable(self):
        # make sure sample can only be imported from the current directory.
        sys.path[:] = ['.']
832 833
        assert os.path.exists(self.package_name)
        assert os.path.exists(os.path.join(self.package_name, '__init__.py'))
834

835 836
        # Try to import the package
        importlib.import_module(self.package_name)
837 838


839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854
@cpython_only
class ImportlibBootstrapTests(unittest.TestCase):
    # These tests check that importlib is bootstrapped.

    def test_frozen_importlib(self):
        mod = sys.modules['_frozen_importlib']
        self.assertTrue(mod)

    def test_frozen_importlib_is_bootstrap(self):
        from importlib import _bootstrap
        mod = sys.modules['_frozen_importlib']
        self.assertIs(mod, _bootstrap)
        self.assertEqual(mod.__name__, 'importlib._bootstrap')
        self.assertEqual(mod.__package__, 'importlib')
        self.assertTrue(mod.__file__.endswith('_bootstrap.py'), mod.__file__)

855 856 857 858 859 860 861 862
    def test_there_can_be_only_one(self):
        # Issue #15386 revealed a tricky loophole in the bootstrapping
        # This test is technically redundant, since the bug caused importing
        # this test module to crash completely, but it helps prove the point
        from importlib import machinery
        mod = sys.modules['_frozen_importlib']
        self.assertIs(machinery.FileFinder, mod.FileFinder)

863

864 865 866 867 868 869 870 871 872 873 874
class ImportTracebackTests(unittest.TestCase):

    def setUp(self):
        os.mkdir(TESTFN)
        self.old_path = sys.path[:]
        sys.path.insert(0, TESTFN)

    def tearDown(self):
        sys.path[:] = self.old_path
        rmtree(TESTFN)

875 876 877
    def create_module(self, mod, contents, ext=".py"):
        fname = os.path.join(TESTFN, mod + ext)
        with open(fname, "w") as f:
878 879 880
            f.write(contents)
        self.addCleanup(unload, mod)
        importlib.invalidate_caches()
881
        return fname
882 883 884 885 886 887 888 889 890 891 892

    def assert_traceback(self, tb, files):
        deduped_files = []
        while tb:
            code = tb.tb_frame.f_code
            fn = code.co_filename
            if not deduped_files or fn != deduped_files[-1]:
                deduped_files.append(fn)
            tb = tb.tb_next
        self.assertEqual(len(deduped_files), len(files), deduped_files)
        for fn, pat in zip(deduped_files, files):
893
            self.assertIn(pat, fn)
894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935

    def test_nonexistent_module(self):
        try:
            # assertRaises() clears __traceback__
            import nonexistent_xyzzy
        except ImportError as e:
            tb = e.__traceback__
        else:
            self.fail("ImportError should have been raised")
        self.assert_traceback(tb, [__file__])

    def test_nonexistent_module_nested(self):
        self.create_module("foo", "import nonexistent_xyzzy")
        try:
            import foo
        except ImportError as e:
            tb = e.__traceback__
        else:
            self.fail("ImportError should have been raised")
        self.assert_traceback(tb, [__file__, 'foo.py'])

    def test_exec_failure(self):
        self.create_module("foo", "1/0")
        try:
            import foo
        except ZeroDivisionError as e:
            tb = e.__traceback__
        else:
            self.fail("ZeroDivisionError should have been raised")
        self.assert_traceback(tb, [__file__, 'foo.py'])

    def test_exec_failure_nested(self):
        self.create_module("foo", "import bar")
        self.create_module("bar", "1/0")
        try:
            import foo
        except ZeroDivisionError as e:
            tb = e.__traceback__
        else:
            self.fail("ZeroDivisionError should have been raised")
        self.assert_traceback(tb, [__file__, 'foo.py', 'bar.py'])

936 937 938 939 940 941 942 943 944 945 946 947 948
    # A few more examples from issue #15425
    def test_syntax_error(self):
        self.create_module("foo", "invalid syntax is invalid")
        try:
            import foo
        except SyntaxError as e:
            tb = e.__traceback__
        else:
            self.fail("SyntaxError should have been raised")
        self.assert_traceback(tb, [__file__])

    def _setup_broken_package(self, parent, child):
        pkg_name = "_parent_foo"
949 950 951
        self.addCleanup(unload, pkg_name)
        pkg_path = os.path.join(TESTFN, pkg_name)
        os.mkdir(pkg_path)
952
        # Touch the __init__.py
953
        init_path = os.path.join(pkg_path, '__init__.py')
954 955
        with open(init_path, 'w') as f:
            f.write(parent)
956
        bar_path = os.path.join(pkg_path, 'bar.py')
957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001
        with open(bar_path, 'w') as f:
            f.write(child)
        importlib.invalidate_caches()
        return init_path, bar_path

    def test_broken_submodule(self):
        init_path, bar_path = self._setup_broken_package("", "1/0")
        try:
            import _parent_foo.bar
        except ZeroDivisionError as e:
            tb = e.__traceback__
        else:
            self.fail("ZeroDivisionError should have been raised")
        self.assert_traceback(tb, [__file__, bar_path])

    def test_broken_from(self):
        init_path, bar_path = self._setup_broken_package("", "1/0")
        try:
            from _parent_foo import bar
        except ZeroDivisionError as e:
            tb = e.__traceback__
        else:
            self.fail("ImportError should have been raised")
        self.assert_traceback(tb, [__file__, bar_path])

    def test_broken_parent(self):
        init_path, bar_path = self._setup_broken_package("1/0", "")
        try:
            import _parent_foo.bar
        except ZeroDivisionError as e:
            tb = e.__traceback__
        else:
            self.fail("ZeroDivisionError should have been raised")
        self.assert_traceback(tb, [__file__, init_path])

    def test_broken_parent_from(self):
        init_path, bar_path = self._setup_broken_package("1/0", "")
        try:
            from _parent_foo import bar
        except ZeroDivisionError as e:
            tb = e.__traceback__
        else:
            self.fail("ZeroDivisionError should have been raised")
        self.assert_traceback(tb, [__file__, init_path])

1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023
    @cpython_only
    def test_import_bug(self):
        # We simulate a bug in importlib and check that it's not stripped
        # away from the traceback.
        self.create_module("foo", "")
        importlib = sys.modules['_frozen_importlib']
        old_load_module = importlib.SourceLoader.load_module
        try:
            def load_module(*args):
                1/0
            importlib.SourceLoader.load_module = load_module
            try:
                import foo
            except ZeroDivisionError as e:
                tb = e.__traceback__
            else:
                self.fail("ZeroDivisionError should have been raised")
            self.assert_traceback(tb, [__file__, '<frozen importlib', __file__])
        finally:
            importlib.SourceLoader.load_module = old_load_module


1024
if __name__ == '__main__':
1025
    unittest.main()