Home | History | Annotate | Download | only in test
      1 """Tests for 'site'.
      2 
      3 Tests assume the initial paths in sys.path once the interpreter has begun
      4 executing have not been removed.
      5 
      6 """
      7 import unittest
      8 from test.test_support import run_unittest, TESTFN, EnvironmentVarGuard
      9 from test.test_support import captured_output
     10 import __builtin__
     11 import os
     12 import sys
     13 import re
     14 import encodings
     15 import subprocess
     16 import sysconfig
     17 from copy import copy
     18 
     19 # Need to make sure to not import 'site' if someone specified ``-S`` at the
     20 # command-line.  Detect this by just making sure 'site' has not been imported
     21 # already.
     22 if "site" in sys.modules:
     23     import site
     24 else:
     25     raise unittest.SkipTest("importation of site.py suppressed")
     26 
     27 if site.ENABLE_USER_SITE and not os.path.isdir(site.USER_SITE):
     28     # need to add user site directory for tests
     29     try:
     30         os.makedirs(site.USER_SITE)
     31         site.addsitedir(site.USER_SITE)
     32     except OSError as exc:
     33         raise unittest.SkipTest('unable to create user site directory (%r): %s'
     34                                 % (site.USER_SITE, exc))
     35 
     36 
     37 class HelperFunctionsTests(unittest.TestCase):
     38     """Tests for helper functions.
     39 
     40     The setting of the encoding (set using sys.setdefaultencoding) used by
     41     the Unicode implementation is not tested.
     42 
     43     """
     44 
     45     def setUp(self):
     46         """Save a copy of sys.path"""
     47         self.sys_path = sys.path[:]
     48         self.old_base = site.USER_BASE
     49         self.old_site = site.USER_SITE
     50         self.old_prefixes = site.PREFIXES
     51         self.old_vars = copy(sysconfig._CONFIG_VARS)
     52 
     53     def tearDown(self):
     54         """Restore sys.path"""
     55         sys.path[:] = self.sys_path
     56         site.USER_BASE = self.old_base
     57         site.USER_SITE = self.old_site
     58         site.PREFIXES = self.old_prefixes
     59         sysconfig._CONFIG_VARS = self.old_vars
     60 
     61     def test_makepath(self):
     62         # Test makepath() have an absolute path for its first return value
     63         # and a case-normalized version of the absolute path for its
     64         # second value.
     65         path_parts = ("Beginning", "End")
     66         original_dir = os.path.join(*path_parts)
     67         abs_dir, norm_dir = site.makepath(*path_parts)
     68         self.assertEqual(os.path.abspath(original_dir), abs_dir)
     69         if original_dir == os.path.normcase(original_dir):
     70             self.assertEqual(abs_dir, norm_dir)
     71         else:
     72             self.assertEqual(os.path.normcase(abs_dir), norm_dir)
     73 
     74     def test_init_pathinfo(self):
     75         dir_set = site._init_pathinfo()
     76         for entry in [site.makepath(path)[1] for path in sys.path
     77                         if path and os.path.isdir(path)]:
     78             self.assertIn(entry, dir_set,
     79                           "%s from sys.path not found in set returned "
     80                           "by _init_pathinfo(): %s" % (entry, dir_set))
     81 
     82     def pth_file_tests(self, pth_file):
     83         """Contain common code for testing results of reading a .pth file"""
     84         self.assertIn(pth_file.imported, sys.modules,
     85                       "%s not in sys.modules" % pth_file.imported)
     86         self.assertIn(site.makepath(pth_file.good_dir_path)[0], sys.path)
     87         self.assertFalse(os.path.exists(pth_file.bad_dir_path))
     88 
     89     def test_addpackage(self):
     90         # Make sure addpackage() imports if the line starts with 'import',
     91         # adds directories to sys.path for any line in the file that is not a
     92         # comment or import that is a valid directory name for where the .pth
     93         # file resides; invalid directories are not added
     94         pth_file = PthFile()
     95         pth_file.cleanup(prep=True)  # to make sure that nothing is
     96                                       # pre-existing that shouldn't be
     97         try:
     98             pth_file.create()
     99             site.addpackage(pth_file.base_dir, pth_file.filename, set())
    100             self.pth_file_tests(pth_file)
    101         finally:
    102             pth_file.cleanup()
    103 
    104     def make_pth(self, contents, pth_dir='.', pth_name=TESTFN):
    105         # Create a .pth file and return its (abspath, basename).
    106         pth_dir = os.path.abspath(pth_dir)
    107         pth_basename = pth_name + '.pth'
    108         pth_fn = os.path.join(pth_dir, pth_basename)
    109         pth_file = open(pth_fn, 'w')
    110         self.addCleanup(lambda: os.remove(pth_fn))
    111         pth_file.write(contents)
    112         pth_file.close()
    113         return pth_dir, pth_basename
    114 
    115     def test_addpackage_import_bad_syntax(self):
    116         # Issue 10642
    117         pth_dir, pth_fn = self.make_pth("import bad)syntax\n")
    118         with captured_output("stderr") as err_out:
    119             site.addpackage(pth_dir, pth_fn, set())
    120         self.assertRegexpMatches(err_out.getvalue(), "line 1")
    121         self.assertRegexpMatches(err_out.getvalue(),
    122             re.escape(os.path.join(pth_dir, pth_fn)))
    123         # XXX: the previous two should be independent checks so that the
    124         # order doesn't matter.  The next three could be a single check
    125         # but my regex foo isn't good enough to write it.
    126         self.assertRegexpMatches(err_out.getvalue(), 'Traceback')
    127         self.assertRegexpMatches(err_out.getvalue(), r'import bad\)syntax')
    128         self.assertRegexpMatches(err_out.getvalue(), 'SyntaxError')
    129 
    130     def test_addpackage_import_bad_exec(self):
    131         # Issue 10642
    132         pth_dir, pth_fn = self.make_pth("randompath\nimport nosuchmodule\n")
    133         with captured_output("stderr") as err_out:
    134             site.addpackage(pth_dir, pth_fn, set())
    135         self.assertRegexpMatches(err_out.getvalue(), "line 2")
    136         self.assertRegexpMatches(err_out.getvalue(),
    137             re.escape(os.path.join(pth_dir, pth_fn)))
    138         # XXX: ditto previous XXX comment.
    139         self.assertRegexpMatches(err_out.getvalue(), 'Traceback')
    140         self.assertRegexpMatches(err_out.getvalue(), 'ImportError')
    141 
    142     @unittest.skipIf(sys.platform == "win32", "Windows does not raise an "
    143                       "error for file paths containing null characters")
    144     def test_addpackage_import_bad_pth_file(self):
    145         # Issue 5258
    146         pth_dir, pth_fn = self.make_pth("abc\x00def\n")
    147         with captured_output("stderr") as err_out:
    148             site.addpackage(pth_dir, pth_fn, set())
    149         self.assertRegexpMatches(err_out.getvalue(), "line 1")
    150         self.assertRegexpMatches(err_out.getvalue(),
    151             re.escape(os.path.join(pth_dir, pth_fn)))
    152         # XXX: ditto previous XXX comment.
    153         self.assertRegexpMatches(err_out.getvalue(), 'Traceback')
    154         self.assertRegexpMatches(err_out.getvalue(), 'TypeError')
    155 
    156     def test_addsitedir(self):
    157         # Same tests for test_addpackage since addsitedir() essentially just
    158         # calls addpackage() for every .pth file in the directory
    159         pth_file = PthFile()
    160         pth_file.cleanup(prep=True) # Make sure that nothing is pre-existing
    161                                     # that is tested for
    162         try:
    163             pth_file.create()
    164             site.addsitedir(pth_file.base_dir, set())
    165             self.pth_file_tests(pth_file)
    166         finally:
    167             pth_file.cleanup()
    168 
    169     @unittest.skipUnless(site.ENABLE_USER_SITE, "requires access to PEP 370 "
    170                           "user-site (site.ENABLE_USER_SITE)")
    171     def test_s_option(self):
    172         usersite = site.USER_SITE
    173         self.assertIn(usersite, sys.path)
    174 
    175         env = os.environ.copy()
    176         rc = subprocess.call([sys.executable, '-c',
    177             'import sys; sys.exit(%r in sys.path)' % usersite],
    178             env=env)
    179         self.assertEqual(rc, 1, "%r is not in sys.path (sys.exit returned %r)"
    180                 % (usersite, rc))
    181 
    182         env = os.environ.copy()
    183         rc = subprocess.call([sys.executable, '-s', '-c',
    184             'import sys; sys.exit(%r in sys.path)' % usersite],
    185             env=env)
    186         self.assertEqual(rc, 0)
    187 
    188         env = os.environ.copy()
    189         env["PYTHONNOUSERSITE"] = "1"
    190         rc = subprocess.call([sys.executable, '-c',
    191             'import sys; sys.exit(%r in sys.path)' % usersite],
    192             env=env)
    193         self.assertEqual(rc, 0)
    194 
    195         env = os.environ.copy()
    196         env["PYTHONUSERBASE"] = "/tmp"
    197         rc = subprocess.call([sys.executable, '-c',
    198             'import sys, site; sys.exit(site.USER_BASE.startswith("/tmp"))'],
    199             env=env)
    200         self.assertEqual(rc, 1)
    201 
    202     def test_getuserbase(self):
    203         site.USER_BASE = None
    204         user_base = site.getuserbase()
    205 
    206         # the call sets site.USER_BASE
    207         self.assertEqual(site.USER_BASE, user_base)
    208 
    209         # let's set PYTHONUSERBASE and see if it uses it
    210         site.USER_BASE = None
    211         import sysconfig
    212         sysconfig._CONFIG_VARS = None
    213 
    214         with EnvironmentVarGuard() as environ:
    215             environ['PYTHONUSERBASE'] = 'xoxo'
    216             self.assertTrue(site.getuserbase().startswith('xoxo'),
    217                             site.getuserbase())
    218 
    219     def test_getusersitepackages(self):
    220         site.USER_SITE = None
    221         site.USER_BASE = None
    222         user_site = site.getusersitepackages()
    223 
    224         # the call sets USER_BASE *and* USER_SITE
    225         self.assertEqual(site.USER_SITE, user_site)
    226         self.assertTrue(user_site.startswith(site.USER_BASE), user_site)
    227 
    228     def test_getsitepackages(self):
    229         site.PREFIXES = ['xoxo']
    230         dirs = site.getsitepackages()
    231 
    232         if sys.platform in ('os2emx', 'riscos'):
    233             self.assertEqual(len(dirs), 1)
    234             wanted = os.path.join('xoxo', 'Lib', 'site-packages')
    235             self.assertEqual(dirs[0], wanted)
    236         elif os.sep == '/':
    237             # OS X, Linux, FreeBSD, etc
    238             self.assertEqual(len(dirs), 2)
    239             wanted = os.path.join('xoxo', 'lib', 'python' + sys.version[:3],
    240                                   'site-packages')
    241             self.assertEqual(dirs[0], wanted)
    242             wanted = os.path.join('xoxo', 'lib', 'site-python')
    243             self.assertEqual(dirs[1], wanted)
    244         else:
    245             # other platforms
    246             self.assertEqual(len(dirs), 2)
    247             self.assertEqual(dirs[0], 'xoxo')
    248             wanted = os.path.join('xoxo', 'lib', 'site-packages')
    249             self.assertEqual(dirs[1], wanted)
    250 
    251 class PthFile(object):
    252     """Helper class for handling testing of .pth files"""
    253 
    254     def __init__(self, filename_base=TESTFN, imported="time",
    255                     good_dirname="__testdir__", bad_dirname="__bad"):
    256         """Initialize instance variables"""
    257         self.filename = filename_base + ".pth"
    258         self.base_dir = os.path.abspath('')
    259         self.file_path = os.path.join(self.base_dir, self.filename)
    260         self.imported = imported
    261         self.good_dirname = good_dirname
    262         self.bad_dirname = bad_dirname
    263         self.good_dir_path = os.path.join(self.base_dir, self.good_dirname)
    264         self.bad_dir_path = os.path.join(self.base_dir, self.bad_dirname)
    265 
    266     def create(self):
    267         """Create a .pth file with a comment, blank lines, an ``import
    268         <self.imported>``, a line with self.good_dirname, and a line with
    269         self.bad_dirname.
    270 
    271         Creation of the directory for self.good_dir_path (based off of
    272         self.good_dirname) is also performed.
    273 
    274         Make sure to call self.cleanup() to undo anything done by this method.
    275 
    276         """
    277         FILE = open(self.file_path, 'w')
    278         try:
    279             print>>FILE, "#import @bad module name"
    280             print>>FILE, "\n"
    281             print>>FILE, "import %s" % self.imported
    282             print>>FILE, self.good_dirname
    283             print>>FILE, self.bad_dirname
    284         finally:
    285             FILE.close()
    286         os.mkdir(self.good_dir_path)
    287 
    288     def cleanup(self, prep=False):
    289         """Make sure that the .pth file is deleted, self.imported is not in
    290         sys.modules, and that both self.good_dirname and self.bad_dirname are
    291         not existing directories."""
    292         if os.path.exists(self.file_path):
    293             os.remove(self.file_path)
    294         if prep:
    295             self.imported_module = sys.modules.get(self.imported)
    296             if self.imported_module:
    297                 del sys.modules[self.imported]
    298         else:
    299             if self.imported_module:
    300                 sys.modules[self.imported] = self.imported_module
    301         if os.path.exists(self.good_dir_path):
    302             os.rmdir(self.good_dir_path)
    303         if os.path.exists(self.bad_dir_path):
    304             os.rmdir(self.bad_dir_path)
    305 
    306 class ImportSideEffectTests(unittest.TestCase):
    307     """Test side-effects from importing 'site'."""
    308 
    309     def setUp(self):
    310         """Make a copy of sys.path"""
    311         self.sys_path = sys.path[:]
    312 
    313     def tearDown(self):
    314         """Restore sys.path"""
    315         sys.path[:] = self.sys_path
    316 
    317     def test_abs__file__(self):
    318         # Make sure all imported modules have their __file__ attribute
    319         # as an absolute path.
    320         # Handled by abs__file__()
    321         site.abs__file__()
    322         for module in (sys, os, __builtin__):
    323             try:
    324                 self.assertTrue(os.path.isabs(module.__file__), repr(module))
    325             except AttributeError:
    326                 continue
    327         # We could try everything in sys.modules; however, when regrtest.py
    328         # runs something like test_frozen before test_site, then we will
    329         # be testing things loaded *after* test_site did path normalization
    330 
    331     def test_no_duplicate_paths(self):
    332         # No duplicate paths should exist in sys.path
    333         # Handled by removeduppaths()
    334         site.removeduppaths()
    335         seen_paths = set()
    336         for path in sys.path:
    337             self.assertNotIn(path, seen_paths)
    338             seen_paths.add(path)
    339 
    340     @unittest.skip('test not implemented')
    341     def test_add_build_dir(self):
    342         # Test that the build directory's Modules directory is used when it
    343         # should be.
    344         # XXX: implement
    345         pass
    346 
    347     def test_setting_quit(self):
    348         # 'quit' and 'exit' should be injected into __builtin__
    349         self.assertTrue(hasattr(__builtin__, "quit"))
    350         self.assertTrue(hasattr(__builtin__, "exit"))
    351 
    352     def test_setting_copyright(self):
    353         # 'copyright' and 'credits' should be in __builtin__
    354         self.assertTrue(hasattr(__builtin__, "copyright"))
    355         self.assertTrue(hasattr(__builtin__, "credits"))
    356 
    357     def test_setting_help(self):
    358         # 'help' should be set in __builtin__
    359         self.assertTrue(hasattr(__builtin__, "help"))
    360 
    361     def test_aliasing_mbcs(self):
    362         if sys.platform == "win32":
    363             import locale
    364             if locale.getdefaultlocale()[1].startswith('cp'):
    365                 for value in encodings.aliases.aliases.itervalues():
    366                     if value == "mbcs":
    367                         break
    368                 else:
    369                     self.fail("did not alias mbcs")
    370 
    371     def test_setdefaultencoding_removed(self):
    372         # Make sure sys.setdefaultencoding is gone
    373         self.assertTrue(not hasattr(sys, "setdefaultencoding"))
    374 
    375     def test_sitecustomize_executed(self):
    376         # If sitecustomize is available, it should have been imported.
    377         if "sitecustomize" not in sys.modules:
    378             try:
    379                 import sitecustomize
    380             except ImportError:
    381                 pass
    382             else:
    383                 self.fail("sitecustomize not imported automatically")
    384 
    385 def test_main():
    386     run_unittest(HelperFunctionsTests, ImportSideEffectTests)
    387 
    388 if __name__ == "__main__":
    389     test_main()
    390