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