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