1import errno
2import imp
3import marshal
4import os
5import py_compile
6import random
7import stat
8import struct
9import sys
10import unittest
11import textwrap
12import shutil
13
14from test.test_support import (unlink, TESTFN, unload, run_unittest, rmtree,
15                               is_jython, check_warnings, EnvironmentVarGuard)
16from test import symlink_support
17from test import script_helper
18
19def _files(name):
20    return (name + os.extsep + "py",
21            name + os.extsep + "pyc",
22            name + os.extsep + "pyo",
23            name + os.extsep + "pyw",
24            name + "$py.class")
25
26def chmod_files(name):
27    for f in _files(name):
28        try:
29            os.chmod(f, 0600)
30        except OSError as exc:
31            if exc.errno != errno.ENOENT:
32                raise
33
34def remove_files(name):
35    for f in _files(name):
36        unlink(f)
37
38
39class ImportTests(unittest.TestCase):
40
41    def tearDown(self):
42        unload(TESTFN)
43    setUp = tearDown
44
45    def test_case_sensitivity(self):
46        # Brief digression to test that import is case-sensitive:  if we got
47        # this far, we know for sure that "random" exists.
48        try:
49            import RAnDoM
50        except ImportError:
51            pass
52        else:
53            self.fail("import of RAnDoM should have failed (case mismatch)")
54
55    def test_double_const(self):
56        # Another brief digression to test the accuracy of manifest float
57        # constants.
58        from test import double_const  # don't blink -- that *was* the test
59
60    def test_import(self):
61        def test_with_extension(ext):
62            # The extension is normally ".py", perhaps ".pyw".
63            source = TESTFN + ext
64            pyo = TESTFN + os.extsep + "pyo"
65            if is_jython:
66                pyc = TESTFN + "$py.class"
67            else:
68                pyc = TESTFN + os.extsep + "pyc"
69
70            with open(source, "w") as f:
71                print >> f, ("# This tests Python's ability to import a", ext,
72                             "file.")
73                a = random.randrange(1000)
74                b = random.randrange(1000)
75                print >> f, "a =", a
76                print >> f, "b =", b
77
78            try:
79                mod = __import__(TESTFN)
80            except ImportError, err:
81                self.fail("import from %s failed: %s" % (ext, err))
82            else:
83                self.assertEqual(mod.a, a,
84                    "module loaded (%s) but contents invalid" % mod)
85                self.assertEqual(mod.b, b,
86                    "module loaded (%s) but contents invalid" % mod)
87            finally:
88                unlink(source)
89
90            try:
91                if not sys.dont_write_bytecode:
92                    imp.reload(mod)
93            except ImportError, err:
94                self.fail("import from .pyc/.pyo failed: %s" % err)
95            finally:
96                unlink(pyc)
97                unlink(pyo)
98                unload(TESTFN)
99
100        sys.path.insert(0, os.curdir)
101        try:
102            test_with_extension(os.extsep + "py")
103            if sys.platform.startswith("win"):
104                for ext in [".PY", ".Py", ".pY", ".pyw", ".PYW", ".pYw"]:
105                    test_with_extension(ext)
106        finally:
107            del sys.path[0]
108
109    @unittest.skipUnless(os.name == 'posix',
110        "test meaningful only on posix systems")
111    @unittest.skipIf(sys.dont_write_bytecode,
112        "test meaningful only when writing bytecode")
113    def test_execute_bit_not_copied(self):
114        # Issue 6070: under posix .pyc files got their execute bit set if
115        # the .py file had the execute bit set, but they aren't executable.
116        oldmask = os.umask(022)
117        sys.path.insert(0, os.curdir)
118        try:
119            fname = TESTFN + os.extsep + "py"
120            f = open(fname, 'w').close()
121            os.chmod(fname, (stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH |
122                             stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH))
123            __import__(TESTFN)
124            fn = fname + 'c'
125            if not os.path.exists(fn):
126                fn = fname + 'o'
127                if not os.path.exists(fn):
128                    self.fail("__import__ did not result in creation of "
129                              "either a .pyc or .pyo file")
130            s = os.stat(fn)
131            self.assertEqual(stat.S_IMODE(s.st_mode),
132                             stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH)
133        finally:
134            os.umask(oldmask)
135            remove_files(TESTFN)
136            unload(TESTFN)
137            del sys.path[0]
138
139    @unittest.skipIf(sys.dont_write_bytecode,
140        "test meaningful only when writing bytecode")
141    def test_rewrite_pyc_with_read_only_source(self):
142        # Issue 6074: a long time ago on posix, and more recently on Windows,
143        # a read only source file resulted in a read only pyc file, which
144        # led to problems with updating it later
145        sys.path.insert(0, os.curdir)
146        fname = TESTFN + os.extsep + "py"
147        try:
148            # Write a Python file, make it read-only and import it
149            with open(fname, 'w') as f:
150                f.write("x = 'original'\n")
151            # Tweak the mtime of the source to ensure pyc gets updated later
152            s = os.stat(fname)
153            os.utime(fname, (s.st_atime, s.st_mtime-100000000))
154            os.chmod(fname, 0400)
155            m1 = __import__(TESTFN)
156            self.assertEqual(m1.x, 'original')
157            # Change the file and then reimport it
158            os.chmod(fname, 0600)
159            with open(fname, 'w') as f:
160                f.write("x = 'rewritten'\n")
161            unload(TESTFN)
162            m2 = __import__(TESTFN)
163            self.assertEqual(m2.x, 'rewritten')
164            # Now delete the source file and check the pyc was rewritten
165            unlink(fname)
166            unload(TESTFN)
167            m3 = __import__(TESTFN)
168            self.assertEqual(m3.x, 'rewritten')
169        finally:
170            chmod_files(TESTFN)
171            remove_files(TESTFN)
172            unload(TESTFN)
173            del sys.path[0]
174
175    def test_imp_module(self):
176        # Verify that the imp module can correctly load and find .py files
177
178        # XXX (ncoghlan): It would be nice to use test_support.CleanImport
179        # here, but that breaks because the os module registers some
180        # handlers in copy_reg on import. Since CleanImport doesn't
181        # revert that registration, the module is left in a broken
182        # state after reversion. Reinitialising the module contents
183        # and just reverting os.environ to its previous state is an OK
184        # workaround
185        orig_path = os.path
186        orig_getenv = os.getenv
187        with EnvironmentVarGuard():
188            x = imp.find_module("os")
189            new_os = imp.load_module("os", *x)
190            self.assertIs(os, new_os)
191            self.assertIs(orig_path, new_os.path)
192            self.assertIsNot(orig_getenv, new_os.getenv)
193
194    def test_module_with_large_stack(self, module='longlist'):
195        # Regression test for http://bugs.python.org/issue561858.
196        filename = module + os.extsep + 'py'
197
198        # Create a file with a list of 65000 elements.
199        with open(filename, 'w+') as f:
200            f.write('d = [\n')
201            for i in range(65000):
202                f.write('"",\n')
203            f.write(']')
204
205        # Compile & remove .py file, we only need .pyc (or .pyo).
206        with open(filename, 'r') as f:
207            py_compile.compile(filename)
208        unlink(filename)
209
210        # Need to be able to load from current dir.
211        sys.path.append('')
212
213        # This used to crash.
214        exec 'import ' + module
215
216        # Cleanup.
217        del sys.path[-1]
218        unlink(filename + 'c')
219        unlink(filename + 'o')
220
221    def test_failing_import_sticks(self):
222        source = TESTFN + os.extsep + "py"
223        with open(source, "w") as f:
224            print >> f, "a = 1 // 0"
225
226        # New in 2.4, we shouldn't be able to import that no matter how often
227        # we try.
228        sys.path.insert(0, os.curdir)
229        try:
230            for i in [1, 2, 3]:
231                self.assertRaises(ZeroDivisionError, __import__, TESTFN)
232                self.assertNotIn(TESTFN, sys.modules,
233                                 "damaged module in sys.modules on %i try" % i)
234        finally:
235            del sys.path[0]
236            remove_files(TESTFN)
237
238    def test_failing_reload(self):
239        # A failing reload should leave the module object in sys.modules.
240        source = TESTFN + os.extsep + "py"
241        with open(source, "w") as f:
242            print >> f, "a = 1"
243            print >> f, "b = 2"
244
245        sys.path.insert(0, os.curdir)
246        try:
247            mod = __import__(TESTFN)
248            self.assertIn(TESTFN, sys.modules)
249            self.assertEqual(mod.a, 1, "module has wrong attribute values")
250            self.assertEqual(mod.b, 2, "module has wrong attribute values")
251
252            # On WinXP, just replacing the .py file wasn't enough to
253            # convince reload() to reparse it.  Maybe the timestamp didn't
254            # move enough.  We force it to get reparsed by removing the
255            # compiled file too.
256            remove_files(TESTFN)
257
258            # Now damage the module.
259            with open(source, "w") as f:
260                print >> f, "a = 10"
261                print >> f, "b = 20//0"
262
263            self.assertRaises(ZeroDivisionError, imp.reload, mod)
264
265            # But we still expect the module to be in sys.modules.
266            mod = sys.modules.get(TESTFN)
267            self.assertIsNot(mod, None, "expected module to be in sys.modules")
268
269            # We should have replaced a w/ 10, but the old b value should
270            # stick.
271            self.assertEqual(mod.a, 10, "module has wrong attribute values")
272            self.assertEqual(mod.b, 2, "module has wrong attribute values")
273
274        finally:
275            del sys.path[0]
276            remove_files(TESTFN)
277            unload(TESTFN)
278
279    def test_infinite_reload(self):
280        # http://bugs.python.org/issue742342 reports that Python segfaults
281        # (infinite recursion in C) when faced with self-recursive reload()ing.
282
283        sys.path.insert(0, os.path.dirname(__file__))
284        try:
285            import infinite_reload
286        finally:
287            del sys.path[0]
288
289    def test_import_name_binding(self):
290        # import x.y.z binds x in the current namespace.
291        import test as x
292        import test.test_support
293        self.assertIs(x, test, x.__name__)
294        self.assertTrue(hasattr(test.test_support, "__file__"))
295
296        # import x.y.z as w binds z as w.
297        import test.test_support as y
298        self.assertIs(y, test.test_support, y.__name__)
299
300    def test_import_initless_directory_warning(self):
301        with check_warnings(('', ImportWarning)):
302            # Just a random non-package directory we always expect to be
303            # somewhere in sys.path...
304            self.assertRaises(ImportError, __import__, "site-packages")
305
306    def test_import_by_filename(self):
307        path = os.path.abspath(TESTFN)
308        with self.assertRaises(ImportError) as c:
309            __import__(path)
310        self.assertEqual("Import by filename is not supported.",
311                         c.exception.args[0])
312
313    def test_import_in_del_does_not_crash(self):
314        # Issue 4236
315        testfn = script_helper.make_script('', TESTFN, textwrap.dedent("""\
316            import sys
317            class C:
318               def __del__(self):
319                  import imp
320            sys.argv.insert(0, C())
321            """))
322        try:
323            script_helper.assert_python_ok(testfn)
324        finally:
325            unlink(testfn)
326
327    def test_bug7732(self):
328        source = TESTFN + '.py'
329        os.mkdir(source)
330        try:
331            self.assertRaises((ImportError, IOError),
332                              imp.find_module, TESTFN, ["."])
333        finally:
334            os.rmdir(source)
335
336    def test_timestamp_overflow(self):
337        # A modification timestamp larger than 2**32 should not be a problem
338        # when importing a module (issue #11235).
339        sys.path.insert(0, os.curdir)
340        try:
341            source = TESTFN + ".py"
342            compiled = source + ('c' if __debug__ else 'o')
343            with open(source, 'w') as f:
344                pass
345            try:
346                os.utime(source, (2 ** 33 - 5, 2 ** 33 - 5))
347            except OverflowError:
348                self.skipTest("cannot set modification time to large integer")
349            except OSError as e:
350                if e.errno != getattr(errno, 'EOVERFLOW', None):
351                    raise
352                self.skipTest("cannot set modification time to large integer ({})".format(e))
353            __import__(TESTFN)
354            # The pyc file was created.
355            os.stat(compiled)
356        finally:
357            del sys.path[0]
358            remove_files(TESTFN)
359
360    def test_pyc_mtime(self):
361        # Test for issue #13863: .pyc timestamp sometimes incorrect on Windows.
362        sys.path.insert(0, os.curdir)
363        try:
364            # Jan 1, 2012; Jul 1, 2012.
365            mtimes = 1325376000, 1341100800
366
367            # Different names to avoid running into import caching.
368            tails = "spam", "eggs"
369            for mtime, tail in zip(mtimes, tails):
370                module = TESTFN + tail
371                source = module + ".py"
372                compiled = source + ('c' if __debug__ else 'o')
373
374                # Create a new Python file with the given mtime.
375                with open(source, 'w') as f:
376                    f.write("# Just testing\nx=1, 2, 3\n")
377                os.utime(source, (mtime, mtime))
378
379                # Generate the .pyc/o file; if it couldn't be created
380                # for some reason, skip the test.
381                m = __import__(module)
382                if not os.path.exists(compiled):
383                    unlink(source)
384                    self.skipTest("Couldn't create .pyc/.pyo file.")
385
386                # Actual modification time of .py file.
387                mtime1 = int(os.stat(source).st_mtime) & 0xffffffff
388
389                # mtime that was encoded in the .pyc file.
390                with open(compiled, 'rb') as f:
391                    mtime2 = struct.unpack('<L', f.read(8)[4:])[0]
392
393                unlink(compiled)
394                unlink(source)
395
396                self.assertEqual(mtime1, mtime2)
397        finally:
398            sys.path.pop(0)
399
400
401class PycRewritingTests(unittest.TestCase):
402    # Test that the `co_filename` attribute on code objects always points
403    # to the right file, even when various things happen (e.g. both the .py
404    # and the .pyc file are renamed).
405
406    module_name = "unlikely_module_name"
407    module_source = """
408import sys
409code_filename = sys._getframe().f_code.co_filename
410module_filename = __file__
411constant = 1
412def func():
413    pass
414func_filename = func.func_code.co_filename
415"""
416    dir_name = os.path.abspath(TESTFN)
417    file_name = os.path.join(dir_name, module_name) + os.extsep + "py"
418    compiled_name = file_name + ("c" if __debug__ else "o")
419
420    def setUp(self):
421        self.sys_path = sys.path[:]
422        self.orig_module = sys.modules.pop(self.module_name, None)
423        os.mkdir(self.dir_name)
424        with open(self.file_name, "w") as f:
425            f.write(self.module_source)
426        sys.path.insert(0, self.dir_name)
427
428    def tearDown(self):
429        sys.path[:] = self.sys_path
430        if self.orig_module is not None:
431            sys.modules[self.module_name] = self.orig_module
432        else:
433            unload(self.module_name)
434        unlink(self.file_name)
435        unlink(self.compiled_name)
436        rmtree(self.dir_name)
437
438    def import_module(self):
439        ns = globals()
440        __import__(self.module_name, ns, ns)
441        return sys.modules[self.module_name]
442
443    def test_basics(self):
444        mod = self.import_module()
445        self.assertEqual(mod.module_filename, self.file_name)
446        self.assertEqual(mod.code_filename, self.file_name)
447        self.assertEqual(mod.func_filename, self.file_name)
448        del sys.modules[self.module_name]
449        mod = self.import_module()
450        if not sys.dont_write_bytecode:
451            self.assertEqual(mod.module_filename, self.compiled_name)
452        self.assertEqual(mod.code_filename, self.file_name)
453        self.assertEqual(mod.func_filename, self.file_name)
454
455    def test_incorrect_code_name(self):
456        py_compile.compile(self.file_name, dfile="another_module.py")
457        mod = self.import_module()
458        self.assertEqual(mod.module_filename, self.compiled_name)
459        self.assertEqual(mod.code_filename, self.file_name)
460        self.assertEqual(mod.func_filename, self.file_name)
461
462    def test_module_without_source(self):
463        target = "another_module.py"
464        py_compile.compile(self.file_name, dfile=target)
465        os.remove(self.file_name)
466        mod = self.import_module()
467        self.assertEqual(mod.module_filename, self.compiled_name)
468        self.assertEqual(mod.code_filename, target)
469        self.assertEqual(mod.func_filename, target)
470
471    def test_foreign_code(self):
472        py_compile.compile(self.file_name)
473        with open(self.compiled_name, "rb") as f:
474            header = f.read(8)
475            code = marshal.load(f)
476        constants = list(code.co_consts)
477        foreign_code = test_main.func_code
478        pos = constants.index(1)
479        constants[pos] = foreign_code
480        code = type(code)(code.co_argcount, code.co_nlocals, code.co_stacksize,
481                          code.co_flags, code.co_code, tuple(constants),
482                          code.co_names, code.co_varnames, code.co_filename,
483                          code.co_name, code.co_firstlineno, code.co_lnotab,
484                          code.co_freevars, code.co_cellvars)
485        with open(self.compiled_name, "wb") as f:
486            f.write(header)
487            marshal.dump(code, f)
488        mod = self.import_module()
489        self.assertEqual(mod.constant.co_filename, foreign_code.co_filename)
490
491
492class PathsTests(unittest.TestCase):
493    path = TESTFN
494
495    def setUp(self):
496        os.mkdir(self.path)
497        self.syspath = sys.path[:]
498
499    def tearDown(self):
500        rmtree(self.path)
501        sys.path[:] = self.syspath
502
503    # Regression test for http://bugs.python.org/issue1293.
504    def test_trailing_slash(self):
505        with open(os.path.join(self.path, 'test_trailing_slash.py'), 'w') as f:
506            f.write("testdata = 'test_trailing_slash'")
507        sys.path.append(self.path+'/')
508        mod = __import__("test_trailing_slash")
509        self.assertEqual(mod.testdata, 'test_trailing_slash')
510        unload("test_trailing_slash")
511
512    # Regression test for http://bugs.python.org/issue3677.
513    def _test_UNC_path(self):
514        with open(os.path.join(self.path, 'test_trailing_slash.py'), 'w') as f:
515            f.write("testdata = 'test_trailing_slash'")
516        # Create the UNC path, like \\myhost\c$\foo\bar.
517        path = os.path.abspath(self.path)
518        import socket
519        hn = socket.gethostname()
520        drive = path[0]
521        unc = "\\\\%s\\%s$"%(hn, drive)
522        unc += path[2:]
523        try:
524            os.listdir(unc)
525        except OSError as e:
526            if e.errno in (errno.EPERM, errno.EACCES):
527                # See issue #15338
528                self.skipTest("cannot access administrative share %r" % (unc,))
529            raise
530        sys.path.append(path)
531        mod = __import__("test_trailing_slash")
532        self.assertEqual(mod.testdata, 'test_trailing_slash')
533        unload("test_trailing_slash")
534
535    if sys.platform == "win32":
536        test_UNC_path = _test_UNC_path
537
538
539class RelativeImportTests(unittest.TestCase):
540
541    def tearDown(self):
542        unload("test.relimport")
543    setUp = tearDown
544
545    def test_relimport_star(self):
546        # This will import * from .test_import.
547        from . import relimport
548        self.assertTrue(hasattr(relimport, "RelativeImportTests"))
549
550    def test_issue3221(self):
551        # Regression test for http://bugs.python.org/issue3221.
552        def check_absolute():
553            exec "from os import path" in ns
554        def check_relative():
555            exec "from . import relimport" in ns
556
557        # Check both OK with __package__ and __name__ correct
558        ns = dict(__package__='test', __name__='test.notarealmodule')
559        check_absolute()
560        check_relative()
561
562        # Check both OK with only __name__ wrong
563        ns = dict(__package__='test', __name__='notarealpkg.notarealmodule')
564        check_absolute()
565        check_relative()
566
567        # Check relative fails with only __package__ wrong
568        ns = dict(__package__='foo', __name__='test.notarealmodule')
569        with check_warnings(('.+foo', RuntimeWarning)):
570            check_absolute()
571        self.assertRaises(SystemError, check_relative)
572
573        # Check relative fails with __package__ and __name__ wrong
574        ns = dict(__package__='foo', __name__='notarealpkg.notarealmodule')
575        with check_warnings(('.+foo', RuntimeWarning)):
576            check_absolute()
577        self.assertRaises(SystemError, check_relative)
578
579        # Check both fail with package set to a non-string
580        ns = dict(__package__=object())
581        self.assertRaises(ValueError, check_absolute)
582        self.assertRaises(ValueError, check_relative)
583
584    def test_absolute_import_without_future(self):
585        # If explicit relative import syntax is used, then do not try
586        # to perform an absolute import in the face of failure.
587        # Issue #7902.
588        with self.assertRaises(ImportError):
589            from .os import sep
590            self.fail("explicit relative import triggered an "
591                      "implicit absolute import")
592
593
594class TestSymbolicallyLinkedPackage(unittest.TestCase):
595    package_name = 'sample'
596
597    def setUp(self):
598        if os.path.exists(self.tagged):
599            shutil.rmtree(self.tagged)
600        if os.path.exists(self.package_name):
601            symlink_support.remove_symlink(self.package_name)
602        self.orig_sys_path = sys.path[:]
603
604        # create a sample package; imagine you have a package with a tag and
605        #  you want to symbolically link it from its untagged name.
606        os.mkdir(self.tagged)
607        init_file = os.path.join(self.tagged, '__init__.py')
608        open(init_file, 'w').close()
609        assert os.path.exists(init_file)
610
611        # now create a symlink to the tagged package
612        # sample -> sample-tagged
613        symlink_support.symlink(self.tagged, self.package_name)
614
615        assert os.path.isdir(self.package_name)
616        assert os.path.isfile(os.path.join(self.package_name, '__init__.py'))
617
618    @property
619    def tagged(self):
620        return self.package_name + '-tagged'
621
622    # regression test for issue6727
623    @unittest.skipUnless(
624        not hasattr(sys, 'getwindowsversion')
625        or sys.getwindowsversion() >= (6, 0),
626        "Windows Vista or later required")
627    @symlink_support.skip_unless_symlink
628    def test_symlinked_dir_importable(self):
629        # make sure sample can only be imported from the current directory.
630        sys.path[:] = ['.']
631
632        # and try to import the package
633        __import__(self.package_name)
634
635    def tearDown(self):
636        # now cleanup
637        if os.path.exists(self.package_name):
638            symlink_support.remove_symlink(self.package_name)
639        if os.path.exists(self.tagged):
640            shutil.rmtree(self.tagged)
641        sys.path[:] = self.orig_sys_path
642
643def test_main(verbose=None):
644    run_unittest(ImportTests, PycRewritingTests, PathsTests,
645        RelativeImportTests, TestSymbolicallyLinkedPackage)
646
647if __name__ == '__main__':
648    # Test needs to be a package, so we can do relative imports.
649    from test.test_import import test_main
650    test_main()
651