test_import.py 16.3 KB
Newer Older
1
import unittest
2
import os
3
import stat
4
import random
5
import shutil
6
import sys
7
import py_compile
8
import warnings
9
import imp
10
import marshal
11 12
from test.support import (unlink, TESTFN, unload, run_unittest,
                          TestFailed, EnvironmentVarGuard)
13

14

Tim Peters's avatar
Tim Peters committed
15
def remove_files(name):
Skip Montanaro's avatar
Skip Montanaro committed
16 17 18 19
    for f in (name + ".py",
              name + ".pyc",
              name + ".pyo",
              name + ".pyw",
Tim Peters's avatar
Tim Peters committed
20 21 22 23
              name + "$py.class"):
        if os.path.exists(f):
            os.remove(f)

24

25
class ImportTest(unittest.TestCase):
26

27 28 29
    def testCaseSensitivity(self):
        # Brief digression to test that import is case-sensitive:  if we got this
        # far, we know for sure that "random" exists.
30
        try:
31 32
            import RAnDoM
        except ImportError:
33
            pass
34 35 36 37 38 39 40 41 42 43 44
        else:
            self.fail("import of RAnDoM should have failed (case mismatch)")

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

    def testImport(self):
        def test_with_extension(ext):
            # ext normally ".py"; perhaps ".pyw"
            source = TESTFN + ext
Skip Montanaro's avatar
Skip Montanaro committed
45
            pyo = TESTFN + ".pyo"
46 47
            if sys.platform.startswith('java'):
                pyc = TESTFN + "$py.class"
Tim Peters's avatar
Tim Peters committed
48
            else:
Skip Montanaro's avatar
Skip Montanaro committed
49
                pyc = TESTFN + ".pyc"
50

51 52 53 54 55 56
            with open(source, "w") as f:
                print("# This tests Python's ability to import a", ext, "file.", file=f)
                a = random.randrange(1000)
                b = random.randrange(1000)
                print("a =", a, file=f)
                print("b =", b, file=f)
57

58 59
            if TESTFN in sys.modules:
                del sys.modules[TESTFN]
60 61 62
            try:
                try:
                    mod = __import__(TESTFN)
63
                except ImportError as err:
64 65 66 67 68 69 70
                    self.fail("import from %s failed: %s" % (ext, err))

                self.assertEquals(mod.a, a,
                    "module loaded (%s) but contents invalid" % mod)
                self.assertEquals(mod.b, b,
                    "module loaded (%s) but contents invalid" % mod)
            finally:
71 72 73
                unlink(source)
                unlink(pyc)
                unlink(pyo)
74 75 76 77
                del sys.modules[TESTFN]

        sys.path.insert(0, os.curdir)
        try:
Skip Montanaro's avatar
Skip Montanaro committed
78
            test_with_extension(".py")
79 80 81 82 83 84
            if sys.platform.startswith("win"):
                for ext in ".PY", ".Py", ".pY", ".pyw", ".PYW", ".pYw":
                    test_with_extension(ext)
        finally:
            del sys.path[0]

85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
    @unittest.skipUnless(os.name == 'posix', "test meaningful only on posix systems")
    def test_execute_bit_not_copied(self):
        # Issue 6070: under posix .pyc files got their execute bit set if
        # the .py file had the execute bit set, but they aren't executable.
        oldmask = os.umask(0o022)
        sys.path.insert(0, os.curdir)
        try:
            fname = TESTFN + os.extsep + "py"
            f = open(fname, 'w').close()
            os.chmod(fname, (stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH |
                             stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH))
            __import__(TESTFN)
            fn = fname + 'c'
            if not os.path.exists(fn):
                fn = fname + 'o'
                if not os.path.exists(fn): raise TestFailed("__import__ did "
                    "not result in creation of either a .pyc or .pyo file")
            s = os.stat(fn)
            self.assertEquals(stat.S_IMODE(s.st_mode),
                              stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH)
        finally:
            os.umask(oldmask)
            remove_files(TESTFN)
            if TESTFN in sys.modules: del sys.modules[TESTFN]
            del sys.path[0]

111 112
    def testImpModule(self):
        # Verify that the imp module can correctly load and find .py files
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
        import imp, os
        # XXX (ncoghlan): It would be nice to use test_support.CleanImport
        # here, but that breaks because the os module registers some
        # handlers in copy_reg on import. Since CleanImport doesn't
        # revert that registration, the module is left in a broken
        # state after reversion. Reinitialising the module contents
        # and just reverting os.environ to its previous state is an OK
        # workaround
        orig_path = os.path
        orig_getenv = os.getenv
        with EnvironmentVarGuard():
            x = imp.find_module("os")
            new_os = imp.load_module("os", *x)
            self.assertIs(os, new_os)
            self.assertIs(orig_path, new_os.path)
            self.assertIsNot(orig_getenv, new_os.getenv)
129 130 131

    def test_module_with_large_stack(self, module='longlist'):
        # create module w/list of 65000 elements to test bug #561858
Skip Montanaro's avatar
Skip Montanaro committed
132
        filename = module + '.py'
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155

        # create a file with a list of 65000 elements
        f = open(filename, 'w+')
        f.write('d = [\n')
        for i in range(65000):
            f.write('"",\n')
        f.write(']')
        f.close()

        # compile & remove .py file, we only need .pyc (or .pyo)
        f = open(filename, 'r')
        py_compile.compile(filename)
        f.close()
        os.unlink(filename)

        # need to be able to load from current dir
        sys.path.append('')

        # this used to crash
        exec('import ' + module)

        # cleanup
        del sys.path[-1]
Skip Montanaro's avatar
Skip Montanaro committed
156 157
        for ext in '.pyc', '.pyo':
            fname = module + ext
158 159 160 161
            if os.path.exists(fname):
                os.unlink(fname)

    def test_failing_import_sticks(self):
Skip Montanaro's avatar
Skip Montanaro committed
162
        source = TESTFN + ".py"
Tim Peters's avatar
Tim Peters committed
163
        f = open(source, "w")
164
        print("a = 1/0", file=f)
Tim Peters's avatar
Tim Peters committed
165
        f.close()
166 167 168 169

        # New in 2.4, we shouldn't be able to import that no matter how often
        # we try.
        sys.path.insert(0, os.curdir)
170 171
        if TESTFN in sys.modules:
            del sys.modules[TESTFN]
Tim Peters's avatar
Tim Peters committed
172
        try:
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
            for i in 1, 2, 3:
                try:
                    mod = __import__(TESTFN)
                except ZeroDivisionError:
                    if TESTFN in sys.modules:
                        self.fail("damaged module in sys.modules on %i. try" % i)
                else:
                    self.fail("was able to import a damaged module on %i. try" % i)
        finally:
            sys.path.pop(0)
            remove_files(TESTFN)

    def test_import_name_binding(self):
        # import x.y.z binds x in the current namespace
        import test as x
188
        import test.support
189 190
        self.assertTrue(x is test, x.__name__)
        self.assertTrue(hasattr(test.support, "__file__"))
191 192

        # import x.y.z as w binds z as w
193
        import test.support as y
194
        self.assertTrue(y is test.support, y.__name__)
195 196

    def test_import_initless_directory_warning(self):
197
        with warnings.catch_warnings():
198 199
            # Just a random non-package directory we always expect to be
            # somewhere in sys.path...
200
            warnings.simplefilter('error', ImportWarning)
201 202
            self.assertRaises(ImportWarning, __import__, "site-packages")

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

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

            # 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")

            self.assertRaises(ZeroDivisionError, imp.reload, mod)
            # But we still expect the module to be in sys.modules.
            mod = sys.modules.get(TESTFN)
229
            self.assertFalse(mod is None, "expected module to still be in sys.modules")
230 231 232 233 234 235 236 237 238 239 240 241

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

        finally:
            sys.path.pop(0)
            remove_files(TESTFN)
            if TESTFN in sys.modules:
                del sys.modules[TESTFN]

242 243 244 245 246 247 248 249 250
    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)
251
            self.assertTrue(mod.__file__.endswith('.py'))
252 253 254
            os.remove(source)
            del sys.modules[TESTFN]
            mod = __import__(TESTFN)
Christian Heimes's avatar
Christian Heimes committed
255
            ext = mod.__file__[-4:]
256
            self.assertIn(ext, ('.pyc', '.pyo'))
257 258 259 260 261 262 263
        finally:
            sys.path.pop(0)
            remove_files(TESTFN)
            if TESTFN in sys.modules:
                del sys.modules[TESTFN]


264 265 266 267 268
    def test_importbyfilename(self):
        path = os.path.abspath(TESTFN)
        try:
            __import__(path)
        except ImportError as err:
269
            pass
270 271 272
        else:
            self.fail("import by path didn't raise an exception")

273

274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365
class TestPycRewriting(unittest.TestCase):
    # 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"
    compiled_name = file_name + ("c" if __debug__ else "o")

    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)

    def tearDown(self):
        sys.path[:] = self.sys_path
        if self.orig_module is not None:
            sys.modules[self.module_name] = self.orig_module
        else:
            del sys.modules[self.module_name]
        for file_name in self.file_name, self.compiled_name:
            if os.path.exists(file_name):
                os.remove(file_name)
        if os.path.exists(self.dir_name):
            shutil.rmtree(self.dir_name)

    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)
        mod = self.import_module()
        self.assertEqual(mod.module_filename, self.compiled_name)
        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:
            header = f.read(8)
            code = marshal.load(f)
        constants = list(code.co_consts)
        foreign_code = test_main.__code__
        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)

366 367 368
class PathsTests(unittest.TestCase):
    SAMPLES = ('test', 'test\u00e4\u00f6\u00fc\u00df', 'test\u00e9\u00e8',
               'test\u00b0\u00b3\u00b2')
369 370 371 372 373 374 375 376
    path = TESTFN

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

    def tearDown(self):
        shutil.rmtree(self.path)
377
        sys.path[:] = self.syspath
378

379 380 381 382 383 384 385 386 387 388
    # http://bugs.python.org/issue1293
    def test_trailing_slash(self):
        f = open(os.path.join(self.path, 'test_trailing_slash.py'), 'w')
        f.write("testdata = 'test_trailing_slash'")
        f.close()
        sys.path.append(self.path+'/')
        mod = __import__("test_trailing_slash")
        self.assertEqual(mod.testdata, 'test_trailing_slash')
        unload("test_trailing_slash")

389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409
    # http://bugs.python.org/issue3677
    def _test_UNC_path(self):
        f = open(os.path.join(self.path, 'test_trailing_slash.py'), 'w')
        f.write("testdata = 'test_trailing_slash'")
        f.close()
        #create the UNC path, like \\myhost\c$\foo\bar
        path = os.path.abspath(self.path)
        import socket
        hn = socket.gethostname()
        drive = path[0]
        unc = "\\\\%s\\%s$"%(hn, drive)
        unc += path[2:]
        sys.path.append(path)
        mod = __import__("test_trailing_slash")
        self.assertEqual(mod.testdata, 'test_trailing_slash')
        unload("test_trailing_slash")

    if sys.platform == "win32":
        test_UNC_path = _test_UNC_path


Christian Heimes's avatar
Christian Heimes committed
410 411 412 413 414 415 416 417 418 419 420 421
class RelativeImport(unittest.TestCase):
    def tearDown(self):
        try:
            del sys.modules["test.relimport"]
        except:
            pass

    def test_relimport_star(self):
        # This will import * from .test_import.
        from . import relimport
        self.assertTrue(hasattr(relimport, "RelativeImport"))

Georg Brandl's avatar
Georg Brandl committed
422
    def test_issue3221(self):
Benjamin Peterson's avatar
Benjamin Peterson committed
423 424 425
        # 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
Georg Brandl's avatar
Georg Brandl committed
426 427
        def check_relative():
            exec("from . import relimport", ns)
Benjamin Peterson's avatar
Benjamin Peterson committed
428
        # Check relative import OK with __package__ and __name__ correct
Georg Brandl's avatar
Georg Brandl committed
429 430
        ns = dict(__package__='test', __name__='test.notarealmodule')
        check_relative()
Benjamin Peterson's avatar
Benjamin Peterson committed
431
        # Check relative import OK with only __name__ wrong
Georg Brandl's avatar
Georg Brandl committed
432 433
        ns = dict(__package__='test', __name__='notarealpkg.notarealmodule')
        check_relative()
Benjamin Peterson's avatar
Benjamin Peterson committed
434
        # Check relative import fails with only __package__ wrong
Georg Brandl's avatar
Georg Brandl committed
435 436
        ns = dict(__package__='foo', __name__='test.notarealmodule')
        self.assertRaises(SystemError, check_relative)
Benjamin Peterson's avatar
Benjamin Peterson committed
437
        # Check relative import fails with __package__ and __name__ wrong
Georg Brandl's avatar
Georg Brandl committed
438 439
        ns = dict(__package__='foo', __name__='notarealpkg.notarealmodule')
        self.assertRaises(SystemError, check_relative)
Benjamin Peterson's avatar
Benjamin Peterson committed
440
        # Check relative import fails with package set to a non-string
Georg Brandl's avatar
Georg Brandl committed
441 442 443
        ns = dict(__package__=object())
        self.assertRaises(ValueError, check_relative)

444
def test_main(verbose=None):
445
    run_unittest(ImportTest, TestPycRewriting, PathsTests, RelativeImport)
446 447

if __name__ == '__main__':
Christian Heimes's avatar
Christian Heimes committed
448 449
    # test needs to be a package, so we can do relative import
    from test.test_import import test_main
450
    test_main()