• 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
8import test.support
9from test import support
10from test.support import socket_helper
11from test.support import (captured_stderr, TESTFN, EnvironmentVarGuard,
12                          change_cwd)
13import builtins
14import encodings
15import glob
16import os
17import re
18import shutil
19import subprocess
20import sys
21import sysconfig
22import tempfile
23import urllib.error
24import urllib.request
25from unittest import mock
26from copy import copy
27
28# These tests are not particularly useful if Python was invoked with -S.
29# If you add tests that are useful under -S, this skip should be moved
30# to the class level.
31if sys.flags.no_site:
32    raise unittest.SkipTest("Python was invoked with -S")
33
34import site
35
36
37OLD_SYS_PATH = None
38
39
40def setUpModule():
41    global OLD_SYS_PATH
42    OLD_SYS_PATH = sys.path[:]
43
44    if site.ENABLE_USER_SITE and not os.path.isdir(site.USER_SITE):
45        # need to add user site directory for tests
46        try:
47            os.makedirs(site.USER_SITE)
48            # modify sys.path: will be restored by tearDownModule()
49            site.addsitedir(site.USER_SITE)
50        except PermissionError as exc:
51            raise unittest.SkipTest('unable to create user site directory (%r): %s'
52                                    % (site.USER_SITE, exc))
53
54
55def tearDownModule():
56    sys.path[:] = OLD_SYS_PATH
57
58
59class HelperFunctionsTests(unittest.TestCase):
60    """Tests for helper functions.
61    """
62
63    def setUp(self):
64        """Save a copy of sys.path"""
65        self.sys_path = sys.path[:]
66        self.old_base = site.USER_BASE
67        self.old_site = site.USER_SITE
68        self.old_prefixes = site.PREFIXES
69        self.original_vars = sysconfig._CONFIG_VARS
70        self.old_vars = copy(sysconfig._CONFIG_VARS)
71
72    def tearDown(self):
73        """Restore sys.path"""
74        sys.path[:] = self.sys_path
75        site.USER_BASE = self.old_base
76        site.USER_SITE = self.old_site
77        site.PREFIXES = self.old_prefixes
78        sysconfig._CONFIG_VARS = self.original_vars
79        sysconfig._CONFIG_VARS.clear()
80        sysconfig._CONFIG_VARS.update(self.old_vars)
81
82    def test_makepath(self):
83        # Test makepath() have an absolute path for its first return value
84        # and a case-normalized version of the absolute path for its
85        # second value.
86        path_parts = ("Beginning", "End")
87        original_dir = os.path.join(*path_parts)
88        abs_dir, norm_dir = site.makepath(*path_parts)
89        self.assertEqual(os.path.abspath(original_dir), abs_dir)
90        if original_dir == os.path.normcase(original_dir):
91            self.assertEqual(abs_dir, norm_dir)
92        else:
93            self.assertEqual(os.path.normcase(abs_dir), norm_dir)
94
95    def test_init_pathinfo(self):
96        dir_set = site._init_pathinfo()
97        for entry in [site.makepath(path)[1] for path in sys.path
98                        if path and os.path.exists(path)]:
99            self.assertIn(entry, dir_set,
100                          "%s from sys.path not found in set returned "
101                          "by _init_pathinfo(): %s" % (entry, dir_set))
102
103    def pth_file_tests(self, pth_file):
104        """Contain common code for testing results of reading a .pth file"""
105        self.assertIn(pth_file.imported, sys.modules,
106                      "%s not in sys.modules" % pth_file.imported)
107        self.assertIn(site.makepath(pth_file.good_dir_path)[0], sys.path)
108        self.assertFalse(os.path.exists(pth_file.bad_dir_path))
109
110    def test_addpackage(self):
111        # Make sure addpackage() imports if the line starts with 'import',
112        # adds directories to sys.path for any line in the file that is not a
113        # comment or import that is a valid directory name for where the .pth
114        # file resides; invalid directories are not added
115        pth_file = PthFile()
116        pth_file.cleanup(prep=True)  # to make sure that nothing is
117                                      # pre-existing that shouldn't be
118        try:
119            pth_file.create()
120            site.addpackage(pth_file.base_dir, pth_file.filename, set())
121            self.pth_file_tests(pth_file)
122        finally:
123            pth_file.cleanup()
124
125    def make_pth(self, contents, pth_dir='.', pth_name=TESTFN):
126        # Create a .pth file and return its (abspath, basename).
127        pth_dir = os.path.abspath(pth_dir)
128        pth_basename = pth_name + '.pth'
129        pth_fn = os.path.join(pth_dir, pth_basename)
130        with open(pth_fn, 'w', encoding='utf-8') as pth_file:
131            self.addCleanup(lambda: os.remove(pth_fn))
132            pth_file.write(contents)
133        return pth_dir, pth_basename
134
135    def test_addpackage_import_bad_syntax(self):
136        # Issue 10642
137        pth_dir, pth_fn = self.make_pth("import bad-syntax\n")
138        with captured_stderr() as err_out:
139            site.addpackage(pth_dir, pth_fn, set())
140        self.assertRegex(err_out.getvalue(), "line 1")
141        self.assertRegex(err_out.getvalue(),
142            re.escape(os.path.join(pth_dir, pth_fn)))
143        # XXX: the previous two should be independent checks so that the
144        # order doesn't matter.  The next three could be a single check
145        # but my regex foo isn't good enough to write it.
146        self.assertRegex(err_out.getvalue(), 'Traceback')
147        self.assertRegex(err_out.getvalue(), r'import bad-syntax')
148        self.assertRegex(err_out.getvalue(), 'SyntaxError')
149
150    def test_addpackage_import_bad_exec(self):
151        # Issue 10642
152        pth_dir, pth_fn = self.make_pth("randompath\nimport nosuchmodule\n")
153        with captured_stderr() as err_out:
154            site.addpackage(pth_dir, pth_fn, set())
155        self.assertRegex(err_out.getvalue(), "line 2")
156        self.assertRegex(err_out.getvalue(),
157            re.escape(os.path.join(pth_dir, pth_fn)))
158        # XXX: ditto previous XXX comment.
159        self.assertRegex(err_out.getvalue(), 'Traceback')
160        self.assertRegex(err_out.getvalue(), 'ModuleNotFoundError')
161
162    def test_addpackage_import_bad_pth_file(self):
163        # Issue 5258
164        pth_dir, pth_fn = self.make_pth("abc\x00def\n")
165        with captured_stderr() as err_out:
166            self.assertFalse(site.addpackage(pth_dir, pth_fn, set()))
167        self.assertEqual(err_out.getvalue(), "")
168        for path in sys.path:
169            if isinstance(path, str):
170                self.assertNotIn("abc\x00def", path)
171
172    def test_addsitedir(self):
173        # Same tests for test_addpackage since addsitedir() essentially just
174        # calls addpackage() for every .pth file in the directory
175        pth_file = PthFile()
176        pth_file.cleanup(prep=True) # Make sure that nothing is pre-existing
177                                    # that is tested for
178        try:
179            pth_file.create()
180            site.addsitedir(pth_file.base_dir, set())
181            self.pth_file_tests(pth_file)
182        finally:
183            pth_file.cleanup()
184
185    # This tests _getuserbase, hence the double underline
186    # to distinguish from a test for getuserbase
187    def test__getuserbase(self):
188        self.assertEqual(site._getuserbase(), sysconfig._getuserbase())
189
190    def test_get_path(self):
191        if sys.platform == 'darwin' and sys._framework:
192            scheme = 'osx_framework_user'
193        else:
194            scheme = os.name + '_user'
195        self.assertEqual(site._get_path(site._getuserbase()),
196                         sysconfig.get_path('purelib', scheme))
197
198    @unittest.skipUnless(site.ENABLE_USER_SITE, "requires access to PEP 370 "
199                          "user-site (site.ENABLE_USER_SITE)")
200    def test_s_option(self):
201        # (ncoghlan) Change this to use script_helper...
202        usersite = site.USER_SITE
203        self.assertIn(usersite, sys.path)
204
205        env = os.environ.copy()
206        rc = subprocess.call([sys.executable, '-c',
207            'import sys; sys.exit(%r in sys.path)' % usersite],
208            env=env)
209        self.assertEqual(rc, 1)
210
211        env = os.environ.copy()
212        rc = subprocess.call([sys.executable, '-s', '-c',
213            'import sys; sys.exit(%r in sys.path)' % usersite],
214            env=env)
215        if usersite == site.getsitepackages()[0]:
216            self.assertEqual(rc, 1)
217        else:
218            self.assertEqual(rc, 0, "User site still added to path with -s")
219
220        env = os.environ.copy()
221        env["PYTHONNOUSERSITE"] = "1"
222        rc = subprocess.call([sys.executable, '-c',
223            'import sys; sys.exit(%r in sys.path)' % usersite],
224            env=env)
225        if usersite == site.getsitepackages()[0]:
226            self.assertEqual(rc, 1)
227        else:
228            self.assertEqual(rc, 0,
229                        "User site still added to path with PYTHONNOUSERSITE")
230
231        env = os.environ.copy()
232        env["PYTHONUSERBASE"] = "/tmp"
233        rc = subprocess.call([sys.executable, '-c',
234            'import sys, site; sys.exit(site.USER_BASE.startswith("/tmp"))'],
235            env=env)
236        self.assertEqual(rc, 1,
237                        "User base not set by PYTHONUSERBASE")
238
239    def test_getuserbase(self):
240        site.USER_BASE = None
241        user_base = site.getuserbase()
242
243        # the call sets site.USER_BASE
244        self.assertEqual(site.USER_BASE, user_base)
245
246        # let's set PYTHONUSERBASE and see if it uses it
247        site.USER_BASE = None
248        import sysconfig
249        sysconfig._CONFIG_VARS = None
250
251        with EnvironmentVarGuard() as environ:
252            environ['PYTHONUSERBASE'] = 'xoxo'
253            self.assertTrue(site.getuserbase().startswith('xoxo'),
254                            site.getuserbase())
255
256    def test_getusersitepackages(self):
257        site.USER_SITE = None
258        site.USER_BASE = None
259        user_site = site.getusersitepackages()
260
261        # the call sets USER_BASE *and* USER_SITE
262        self.assertEqual(site.USER_SITE, user_site)
263        self.assertTrue(user_site.startswith(site.USER_BASE), user_site)
264        self.assertEqual(site.USER_BASE, site.getuserbase())
265
266    def test_getsitepackages(self):
267        site.PREFIXES = ['xoxo']
268        dirs = site.getsitepackages()
269        if os.sep == '/':
270            # OS X, Linux, FreeBSD, etc
271            if sys.platlibdir != "lib":
272                self.assertEqual(len(dirs), 2)
273                wanted = os.path.join('xoxo', sys.platlibdir,
274                                      'python%d.%d' % sys.version_info[:2],
275                                      'site-packages')
276                self.assertEqual(dirs[0], wanted)
277            else:
278                self.assertEqual(len(dirs), 1)
279            wanted = os.path.join('xoxo', 'lib',
280                                  'python%d.%d' % sys.version_info[:2],
281                                  'site-packages')
282            self.assertEqual(dirs[-1], wanted)
283        else:
284            # other platforms
285            self.assertEqual(len(dirs), 2)
286            self.assertEqual(dirs[0], 'xoxo')
287            wanted = os.path.join('xoxo', 'lib', 'site-packages')
288            self.assertEqual(dirs[1], wanted)
289
290    def test_no_home_directory(self):
291        # bpo-10496: getuserbase() and getusersitepackages() must not fail if
292        # the current user has no home directory (if expanduser() returns the
293        # path unchanged).
294        site.USER_SITE = None
295        site.USER_BASE = None
296
297        with EnvironmentVarGuard() as environ, \
298             mock.patch('os.path.expanduser', lambda path: path):
299
300            del environ['PYTHONUSERBASE']
301            del environ['APPDATA']
302
303            user_base = site.getuserbase()
304            self.assertTrue(user_base.startswith('~' + os.sep),
305                            user_base)
306
307            user_site = site.getusersitepackages()
308            self.assertTrue(user_site.startswith(user_base), user_site)
309
310        with mock.patch('os.path.isdir', return_value=False) as mock_isdir, \
311             mock.patch.object(site, 'addsitedir') as mock_addsitedir, \
312             support.swap_attr(site, 'ENABLE_USER_SITE', True):
313
314            # addusersitepackages() must not add user_site to sys.path
315            # if it is not an existing directory
316            known_paths = set()
317            site.addusersitepackages(known_paths)
318
319            mock_isdir.assert_called_once_with(user_site)
320            mock_addsitedir.assert_not_called()
321            self.assertFalse(known_paths)
322
323
324class PthFile(object):
325    """Helper class for handling testing of .pth files"""
326
327    def __init__(self, filename_base=TESTFN, imported="time",
328                    good_dirname="__testdir__", bad_dirname="__bad"):
329        """Initialize instance variables"""
330        self.filename = filename_base + ".pth"
331        self.base_dir = os.path.abspath('')
332        self.file_path = os.path.join(self.base_dir, self.filename)
333        self.imported = imported
334        self.good_dirname = good_dirname
335        self.bad_dirname = bad_dirname
336        self.good_dir_path = os.path.join(self.base_dir, self.good_dirname)
337        self.bad_dir_path = os.path.join(self.base_dir, self.bad_dirname)
338
339    def create(self):
340        """Create a .pth file with a comment, blank lines, an ``import
341        <self.imported>``, a line with self.good_dirname, and a line with
342        self.bad_dirname.
343
344        Creation of the directory for self.good_dir_path (based off of
345        self.good_dirname) is also performed.
346
347        Make sure to call self.cleanup() to undo anything done by this method.
348
349        """
350        FILE = open(self.file_path, 'w')
351        try:
352            print("#import @bad module name", file=FILE)
353            print("\n", file=FILE)
354            print("import %s" % self.imported, file=FILE)
355            print(self.good_dirname, file=FILE)
356            print(self.bad_dirname, file=FILE)
357        finally:
358            FILE.close()
359        os.mkdir(self.good_dir_path)
360
361    def cleanup(self, prep=False):
362        """Make sure that the .pth file is deleted, self.imported is not in
363        sys.modules, and that both self.good_dirname and self.bad_dirname are
364        not existing directories."""
365        if os.path.exists(self.file_path):
366            os.remove(self.file_path)
367        if prep:
368            self.imported_module = sys.modules.get(self.imported)
369            if self.imported_module:
370                del sys.modules[self.imported]
371        else:
372            if self.imported_module:
373                sys.modules[self.imported] = self.imported_module
374        if os.path.exists(self.good_dir_path):
375            os.rmdir(self.good_dir_path)
376        if os.path.exists(self.bad_dir_path):
377            os.rmdir(self.bad_dir_path)
378
379class ImportSideEffectTests(unittest.TestCase):
380    """Test side-effects from importing 'site'."""
381
382    def setUp(self):
383        """Make a copy of sys.path"""
384        self.sys_path = sys.path[:]
385
386    def tearDown(self):
387        """Restore sys.path"""
388        sys.path[:] = self.sys_path
389
390    def test_abs_paths(self):
391        # Make sure all imported modules have their __file__ and __cached__
392        # attributes as absolute paths.  Arranging to put the Lib directory on
393        # PYTHONPATH would cause the os module to have a relative path for
394        # __file__ if abs_paths() does not get run.  sys and builtins (the
395        # only other modules imported before site.py runs) do not have
396        # __file__ or __cached__ because they are built-in.
397        try:
398            parent = os.path.relpath(os.path.dirname(os.__file__))
399            cwd = os.getcwd()
400        except ValueError:
401            # Failure to get relpath probably means we need to chdir
402            # to the same drive.
403            cwd, parent = os.path.split(os.path.dirname(os.__file__))
404        with change_cwd(cwd):
405            env = os.environ.copy()
406            env['PYTHONPATH'] = parent
407            code = ('import os, sys',
408                # use ASCII to avoid locale issues with non-ASCII directories
409                'os_file = os.__file__.encode("ascii", "backslashreplace")',
410                r'sys.stdout.buffer.write(os_file + b"\n")',
411                'os_cached = os.__cached__.encode("ascii", "backslashreplace")',
412                r'sys.stdout.buffer.write(os_cached + b"\n")')
413            command = '\n'.join(code)
414            # First, prove that with -S (no 'import site'), the paths are
415            # relative.
416            proc = subprocess.Popen([sys.executable, '-S', '-c', command],
417                                    env=env,
418                                    stdout=subprocess.PIPE)
419            stdout, stderr = proc.communicate()
420
421            self.assertEqual(proc.returncode, 0)
422            os__file__, os__cached__ = stdout.splitlines()[:2]
423            self.assertFalse(os.path.isabs(os__file__))
424            self.assertFalse(os.path.isabs(os__cached__))
425            # Now, with 'import site', it works.
426            proc = subprocess.Popen([sys.executable, '-c', command],
427                                    env=env,
428                                    stdout=subprocess.PIPE)
429            stdout, stderr = proc.communicate()
430            self.assertEqual(proc.returncode, 0)
431            os__file__, os__cached__ = stdout.splitlines()[:2]
432            self.assertTrue(os.path.isabs(os__file__),
433                            "expected absolute path, got {}"
434                            .format(os__file__.decode('ascii')))
435            self.assertTrue(os.path.isabs(os__cached__),
436                            "expected absolute path, got {}"
437                            .format(os__cached__.decode('ascii')))
438
439    def test_abs_paths_cached_None(self):
440        """Test for __cached__ is None.
441
442        Regarding to PEP 3147, __cached__ can be None.
443
444        See also: https://bugs.python.org/issue30167
445        """
446        sys.modules['test'].__cached__ = None
447        site.abs_paths()
448        self.assertIsNone(sys.modules['test'].__cached__)
449
450    def test_no_duplicate_paths(self):
451        # No duplicate paths should exist in sys.path
452        # Handled by removeduppaths()
453        site.removeduppaths()
454        seen_paths = set()
455        for path in sys.path:
456            self.assertNotIn(path, seen_paths)
457            seen_paths.add(path)
458
459    @unittest.skip('test not implemented')
460    def test_add_build_dir(self):
461        # Test that the build directory's Modules directory is used when it
462        # should be.
463        # XXX: implement
464        pass
465
466    def test_setting_quit(self):
467        # 'quit' and 'exit' should be injected into builtins
468        self.assertTrue(hasattr(builtins, "quit"))
469        self.assertTrue(hasattr(builtins, "exit"))
470
471    def test_setting_copyright(self):
472        # 'copyright', 'credits', and 'license' should be in builtins
473        self.assertTrue(hasattr(builtins, "copyright"))
474        self.assertTrue(hasattr(builtins, "credits"))
475        self.assertTrue(hasattr(builtins, "license"))
476
477    def test_setting_help(self):
478        # 'help' should be set in builtins
479        self.assertTrue(hasattr(builtins, "help"))
480
481    def test_aliasing_mbcs(self):
482        if sys.platform == "win32":
483            import locale
484            if locale.getdefaultlocale()[1].startswith('cp'):
485                for value in encodings.aliases.aliases.values():
486                    if value == "mbcs":
487                        break
488                else:
489                    self.fail("did not alias mbcs")
490
491    def test_sitecustomize_executed(self):
492        # If sitecustomize is available, it should have been imported.
493        if "sitecustomize" not in sys.modules:
494            try:
495                import sitecustomize
496            except ImportError:
497                pass
498            else:
499                self.fail("sitecustomize not imported automatically")
500
501    @test.support.requires_resource('network')
502    @test.support.system_must_validate_cert
503    @unittest.skipUnless(hasattr(urllib.request, "HTTPSHandler"),
504                         'need SSL support to download license')
505    def test_license_exists_at_url(self):
506        # This test is a bit fragile since it depends on the format of the
507        # string displayed by license in the absence of a LICENSE file.
508        url = license._Printer__data.split()[1]
509        req = urllib.request.Request(url, method='HEAD')
510        # Reset global urllib.request._opener
511        self.addCleanup(urllib.request.urlcleanup)
512        try:
513            with socket_helper.transient_internet(url):
514                with urllib.request.urlopen(req) as data:
515                    code = data.getcode()
516        except urllib.error.HTTPError as e:
517            code = e.code
518        self.assertEqual(code, 200, msg="Can't find " + url)
519
520
521class StartupImportTests(unittest.TestCase):
522
523    def test_startup_imports(self):
524        # Get sys.path in isolated mode (python3 -I)
525        popen = subprocess.Popen([sys.executable, '-I', '-c',
526                                  'import sys; print(repr(sys.path))'],
527                                 stdout=subprocess.PIPE,
528                                 encoding='utf-8')
529        stdout = popen.communicate()[0]
530        self.assertEqual(popen.returncode, 0, repr(stdout))
531        isolated_paths = eval(stdout)
532
533        # bpo-27807: Even with -I, the site module executes all .pth files
534        # found in sys.path (see site.addpackage()). Skip the test if at least
535        # one .pth file is found.
536        for path in isolated_paths:
537            pth_files = glob.glob(os.path.join(glob.escape(path), "*.pth"))
538            if pth_files:
539                self.skipTest(f"found {len(pth_files)} .pth files in: {path}")
540
541        # This tests checks which modules are loaded by Python when it
542        # initially starts upon startup.
543        popen = subprocess.Popen([sys.executable, '-I', '-v', '-c',
544                                  'import sys; print(set(sys.modules))'],
545                                 stdout=subprocess.PIPE,
546                                 stderr=subprocess.PIPE,
547                                 encoding='utf-8')
548        stdout, stderr = popen.communicate()
549        self.assertEqual(popen.returncode, 0, (stdout, stderr))
550        modules = eval(stdout)
551
552        self.assertIn('site', modules)
553
554        # http://bugs.python.org/issue19205
555        re_mods = {'re', '_sre', 'sre_compile', 'sre_constants', 'sre_parse'}
556        self.assertFalse(modules.intersection(re_mods), stderr)
557
558        # http://bugs.python.org/issue9548
559        self.assertNotIn('locale', modules, stderr)
560
561        # http://bugs.python.org/issue19209
562        self.assertNotIn('copyreg', modules, stderr)
563
564        # http://bugs.python.org/issue19218
565        collection_mods = {'_collections', 'collections', 'functools',
566                           'heapq', 'itertools', 'keyword', 'operator',
567                           'reprlib', 'types', 'weakref'
568                          }.difference(sys.builtin_module_names)
569        self.assertFalse(modules.intersection(collection_mods), stderr)
570
571    def test_startup_interactivehook(self):
572        r = subprocess.Popen([sys.executable, '-c',
573            'import sys; sys.exit(hasattr(sys, "__interactivehook__"))']).wait()
574        self.assertTrue(r, "'__interactivehook__' not added by site")
575
576    def test_startup_interactivehook_isolated(self):
577        # issue28192 readline is not automatically enabled in isolated mode
578        r = subprocess.Popen([sys.executable, '-I', '-c',
579            'import sys; sys.exit(hasattr(sys, "__interactivehook__"))']).wait()
580        self.assertFalse(r, "'__interactivehook__' added in isolated mode")
581
582    def test_startup_interactivehook_isolated_explicit(self):
583        # issue28192 readline can be explicitly enabled in isolated mode
584        r = subprocess.Popen([sys.executable, '-I', '-c',
585            'import site, sys; site.enablerlcompleter(); sys.exit(hasattr(sys, "__interactivehook__"))']).wait()
586        self.assertTrue(r, "'__interactivehook__' not added by enablerlcompleter()")
587
588
589@unittest.skipUnless(sys.platform == 'win32', "only supported on Windows")
590class _pthFileTests(unittest.TestCase):
591
592    def _create_underpth_exe(self, lines, exe_pth=True):
593        import _winapi
594        temp_dir = tempfile.mkdtemp()
595        self.addCleanup(test.support.rmtree, temp_dir)
596        exe_file = os.path.join(temp_dir, os.path.split(sys.executable)[1])
597        dll_src_file = _winapi.GetModuleFileName(sys.dllhandle)
598        dll_file = os.path.join(temp_dir, os.path.split(dll_src_file)[1])
599        shutil.copy(sys.executable, exe_file)
600        shutil.copy(dll_src_file, dll_file)
601        if exe_pth:
602            _pth_file = os.path.splitext(exe_file)[0] + '._pth'
603        else:
604            _pth_file = os.path.splitext(dll_file)[0] + '._pth'
605        with open(_pth_file, 'w') as f:
606            for line in lines:
607                print(line, file=f)
608        return exe_file
609
610    def _calc_sys_path_for_underpth_nosite(self, sys_prefix, lines):
611        sys_path = []
612        for line in lines:
613            if not line or line[0] == '#':
614                continue
615            abs_path = os.path.abspath(os.path.join(sys_prefix, line))
616            sys_path.append(abs_path)
617        return sys_path
618
619    def test_underpth_nosite_file(self):
620        libpath = os.path.dirname(os.path.dirname(encodings.__file__))
621        exe_prefix = os.path.dirname(sys.executable)
622        pth_lines = [
623            'fake-path-name',
624            *[libpath for _ in range(200)],
625            '',
626            '# comment',
627        ]
628        exe_file = self._create_underpth_exe(pth_lines)
629        sys_path = self._calc_sys_path_for_underpth_nosite(
630            os.path.dirname(exe_file),
631            pth_lines)
632
633        env = os.environ.copy()
634        env['PYTHONPATH'] = 'from-env'
635        env['PATH'] = '{};{}'.format(exe_prefix, os.getenv('PATH'))
636        output = subprocess.check_output([exe_file, '-c',
637            'import sys; print("\\n".join(sys.path) if sys.flags.no_site else "")'
638        ], env=env, encoding='ansi')
639        actual_sys_path = output.rstrip().split('\n')
640        self.assertTrue(actual_sys_path, "sys.flags.no_site was False")
641        self.assertEqual(
642            actual_sys_path,
643            sys_path,
644            "sys.path is incorrect"
645        )
646
647    def test_underpth_file(self):
648        libpath = os.path.dirname(os.path.dirname(encodings.__file__))
649        exe_prefix = os.path.dirname(sys.executable)
650        exe_file = self._create_underpth_exe([
651            'fake-path-name',
652            *[libpath for _ in range(200)],
653            '',
654            '# comment',
655            'import site'
656        ])
657        sys_prefix = os.path.dirname(exe_file)
658        env = os.environ.copy()
659        env['PYTHONPATH'] = 'from-env'
660        env['PATH'] = '{};{}'.format(exe_prefix, os.getenv('PATH'))
661        rc = subprocess.call([exe_file, '-c',
662            'import sys; sys.exit(not sys.flags.no_site and '
663            '%r in sys.path and %r in sys.path and %r not in sys.path and '
664            'all("\\r" not in p and "\\n" not in p for p in sys.path))' % (
665                os.path.join(sys_prefix, 'fake-path-name'),
666                libpath,
667                os.path.join(sys_prefix, 'from-env'),
668            )], env=env)
669        self.assertTrue(rc, "sys.path is incorrect")
670
671
672    def test_underpth_dll_file(self):
673        libpath = os.path.dirname(os.path.dirname(encodings.__file__))
674        exe_prefix = os.path.dirname(sys.executable)
675        exe_file = self._create_underpth_exe([
676            'fake-path-name',
677            *[libpath for _ in range(200)],
678            '',
679            '# comment',
680            'import site'
681        ], exe_pth=False)
682        sys_prefix = os.path.dirname(exe_file)
683        env = os.environ.copy()
684        env['PYTHONPATH'] = 'from-env'
685        env['PATH'] = '{};{}'.format(exe_prefix, os.getenv('PATH'))
686        rc = subprocess.call([exe_file, '-c',
687            'import sys; sys.exit(not sys.flags.no_site and '
688            '%r in sys.path and %r in sys.path and %r not in sys.path and '
689            'all("\\r" not in p and "\\n" not in p for p in sys.path))' % (
690                os.path.join(sys_prefix, 'fake-path-name'),
691                libpath,
692                os.path.join(sys_prefix, 'from-env'),
693            )], env=env)
694        self.assertTrue(rc, "sys.path is incorrect")
695
696
697if __name__ == "__main__":
698    unittest.main()
699