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