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(os.path.normpath(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    @support.requires_subprocess()
215    def test_s_option(self):
216        # (ncoghlan) Change this to use script_helper...
217        usersite = os.path.normpath(site.USER_SITE)
218        self.assertIn(usersite, sys.path)
219
220        env = os.environ.copy()
221        rc = subprocess.call([sys.executable, '-c',
222            'import sys; sys.exit(%r in sys.path)' % usersite],
223            env=env)
224        self.assertEqual(rc, 1)
225
226        env = os.environ.copy()
227        rc = subprocess.call([sys.executable, '-s', '-c',
228            'import sys; sys.exit(%r in sys.path)' % usersite],
229            env=env)
230        if usersite == site.getsitepackages()[0]:
231            self.assertEqual(rc, 1)
232        else:
233            self.assertEqual(rc, 0, "User site still added to path with -s")
234
235        env = os.environ.copy()
236        env["PYTHONNOUSERSITE"] = "1"
237        rc = subprocess.call([sys.executable, '-c',
238            'import sys; sys.exit(%r in sys.path)' % usersite],
239            env=env)
240        if usersite == site.getsitepackages()[0]:
241            self.assertEqual(rc, 1)
242        else:
243            self.assertEqual(rc, 0,
244                        "User site still added to path with PYTHONNOUSERSITE")
245
246        env = os.environ.copy()
247        env["PYTHONUSERBASE"] = "/tmp"
248        rc = subprocess.call([sys.executable, '-c',
249            'import sys, site; sys.exit(site.USER_BASE.startswith("/tmp"))'],
250            env=env)
251        self.assertEqual(rc, 1,
252                        "User base not set by PYTHONUSERBASE")
253
254    @unittest.skipUnless(HAS_USER_SITE, 'need user site')
255    def test_getuserbase(self):
256        site.USER_BASE = None
257        user_base = site.getuserbase()
258
259        # the call sets site.USER_BASE
260        self.assertEqual(site.USER_BASE, user_base)
261
262        # let's set PYTHONUSERBASE and see if it uses it
263        site.USER_BASE = None
264        import sysconfig
265        sysconfig._CONFIG_VARS = None
266
267        with EnvironmentVarGuard() as environ:
268            environ['PYTHONUSERBASE'] = 'xoxo'
269            self.assertTrue(site.getuserbase().startswith('xoxo'),
270                            site.getuserbase())
271
272    @unittest.skipUnless(HAS_USER_SITE, 'need user site')
273    def test_getusersitepackages(self):
274        site.USER_SITE = None
275        site.USER_BASE = None
276        user_site = site.getusersitepackages()
277
278        # the call sets USER_BASE *and* USER_SITE
279        self.assertEqual(site.USER_SITE, user_site)
280        self.assertTrue(user_site.startswith(site.USER_BASE), user_site)
281        self.assertEqual(site.USER_BASE, site.getuserbase())
282
283    def test_getsitepackages(self):
284        site.PREFIXES = ['xoxo']
285        dirs = site.getsitepackages()
286        if os.sep == '/':
287            # OS X, Linux, FreeBSD, etc
288            if sys.platlibdir != "lib":
289                self.assertEqual(len(dirs), 2)
290                wanted = os.path.join('xoxo', sys.platlibdir,
291                                      'python%d.%d' % sys.version_info[:2],
292                                      'site-packages')
293                self.assertEqual(dirs[0], wanted)
294            else:
295                self.assertEqual(len(dirs), 1)
296            wanted = os.path.join('xoxo', 'lib',
297                                  'python%d.%d' % sys.version_info[:2],
298                                  'site-packages')
299            self.assertEqual(dirs[-1], wanted)
300        else:
301            # other platforms
302            self.assertEqual(len(dirs), 2)
303            self.assertEqual(dirs[0], 'xoxo')
304            wanted = os.path.join('xoxo', 'lib', 'site-packages')
305            self.assertEqual(os.path.normcase(dirs[1]),
306                             os.path.normcase(wanted))
307
308    @unittest.skipUnless(HAS_USER_SITE, 'need user site')
309    def test_no_home_directory(self):
310        # bpo-10496: getuserbase() and getusersitepackages() must not fail if
311        # the current user has no home directory (if expanduser() returns the
312        # path unchanged).
313        site.USER_SITE = None
314        site.USER_BASE = None
315
316        with EnvironmentVarGuard() as environ, \
317             mock.patch('os.path.expanduser', lambda path: path):
318
319            del environ['PYTHONUSERBASE']
320            del environ['APPDATA']
321
322            user_base = site.getuserbase()
323            self.assertTrue(user_base.startswith('~' + os.sep),
324                            user_base)
325
326            user_site = site.getusersitepackages()
327            self.assertTrue(user_site.startswith(user_base), user_site)
328
329        with mock.patch('os.path.isdir', return_value=False) as mock_isdir, \
330             mock.patch.object(site, 'addsitedir') as mock_addsitedir, \
331             support.swap_attr(site, 'ENABLE_USER_SITE', True):
332
333            # addusersitepackages() must not add user_site to sys.path
334            # if it is not an existing directory
335            known_paths = set()
336            site.addusersitepackages(known_paths)
337
338            mock_isdir.assert_called_once_with(user_site)
339            mock_addsitedir.assert_not_called()
340            self.assertFalse(known_paths)
341
342    def test_trace(self):
343        message = "bla-bla-bla"
344        for verbose, out in (True, message + "\n"), (False, ""):
345            with mock.patch('sys.flags', mock.Mock(verbose=verbose)), \
346                    mock.patch('sys.stderr', io.StringIO()):
347                site._trace(message)
348                self.assertEqual(sys.stderr.getvalue(), out)
349
350
351class PthFile(object):
352    """Helper class for handling testing of .pth files"""
353
354    def __init__(self, filename_base=TESTFN, imported="time",
355                    good_dirname="__testdir__", bad_dirname="__bad"):
356        """Initialize instance variables"""
357        self.filename = filename_base + ".pth"
358        self.base_dir = os.path.abspath('')
359        self.file_path = os.path.join(self.base_dir, self.filename)
360        self.imported = imported
361        self.good_dirname = good_dirname
362        self.bad_dirname = bad_dirname
363        self.good_dir_path = os.path.join(self.base_dir, self.good_dirname)
364        self.bad_dir_path = os.path.join(self.base_dir, self.bad_dirname)
365
366    def create(self):
367        """Create a .pth file with a comment, blank lines, an ``import
368        <self.imported>``, a line with self.good_dirname, and a line with
369        self.bad_dirname.
370
371        Creation of the directory for self.good_dir_path (based off of
372        self.good_dirname) is also performed.
373
374        Make sure to call self.cleanup() to undo anything done by this method.
375
376        """
377        FILE = open(self.file_path, 'w')
378        try:
379            print("#import @bad module name", file=FILE)
380            print("\n", file=FILE)
381            print("import %s" % self.imported, file=FILE)
382            print(self.good_dirname, file=FILE)
383            print(self.bad_dirname, file=FILE)
384        finally:
385            FILE.close()
386        os.mkdir(self.good_dir_path)
387
388    def cleanup(self, prep=False):
389        """Make sure that the .pth file is deleted, self.imported is not in
390        sys.modules, and that both self.good_dirname and self.bad_dirname are
391        not existing directories."""
392        if os.path.exists(self.file_path):
393            os.remove(self.file_path)
394        if prep:
395            self.imported_module = sys.modules.get(self.imported)
396            if self.imported_module:
397                del sys.modules[self.imported]
398        else:
399            if self.imported_module:
400                sys.modules[self.imported] = self.imported_module
401        if os.path.exists(self.good_dir_path):
402            os.rmdir(self.good_dir_path)
403        if os.path.exists(self.bad_dir_path):
404            os.rmdir(self.bad_dir_path)
405
406class ImportSideEffectTests(unittest.TestCase):
407    """Test side-effects from importing 'site'."""
408
409    def setUp(self):
410        """Make a copy of sys.path"""
411        self.sys_path = sys.path[:]
412
413    def tearDown(self):
414        """Restore sys.path"""
415        sys.path[:] = self.sys_path
416
417    def test_abs_paths_cached_None(self):
418        """Test for __cached__ is None.
419
420        Regarding to PEP 3147, __cached__ can be None.
421
422        See also: https://bugs.python.org/issue30167
423        """
424        sys.modules['test'].__cached__ = None
425        site.abs_paths()
426        self.assertIsNone(sys.modules['test'].__cached__)
427
428    def test_no_duplicate_paths(self):
429        # No duplicate paths should exist in sys.path
430        # Handled by removeduppaths()
431        site.removeduppaths()
432        seen_paths = set()
433        for path in sys.path:
434            self.assertNotIn(path, seen_paths)
435            seen_paths.add(path)
436
437    @unittest.skip('test not implemented')
438    def test_add_build_dir(self):
439        # Test that the build directory's Modules directory is used when it
440        # should be.
441        # XXX: implement
442        pass
443
444    def test_setting_quit(self):
445        # 'quit' and 'exit' should be injected into builtins
446        self.assertTrue(hasattr(builtins, "quit"))
447        self.assertTrue(hasattr(builtins, "exit"))
448
449    def test_setting_copyright(self):
450        # 'copyright', 'credits', and 'license' should be in builtins
451        self.assertTrue(hasattr(builtins, "copyright"))
452        self.assertTrue(hasattr(builtins, "credits"))
453        self.assertTrue(hasattr(builtins, "license"))
454
455    def test_setting_help(self):
456        # 'help' should be set in builtins
457        self.assertTrue(hasattr(builtins, "help"))
458
459    def test_sitecustomize_executed(self):
460        # If sitecustomize is available, it should have been imported.
461        if "sitecustomize" not in sys.modules:
462            try:
463                import sitecustomize
464            except ImportError:
465                pass
466            else:
467                self.fail("sitecustomize not imported automatically")
468
469    @test.support.requires_resource('network')
470    @test.support.system_must_validate_cert
471    @unittest.skipUnless(hasattr(urllib.request, "HTTPSHandler"),
472                         'need SSL support to download license')
473    def test_license_exists_at_url(self):
474        # This test is a bit fragile since it depends on the format of the
475        # string displayed by license in the absence of a LICENSE file.
476        url = license._Printer__data.split()[1]
477        req = urllib.request.Request(url, method='HEAD')
478        # Reset global urllib.request._opener
479        self.addCleanup(urllib.request.urlcleanup)
480        try:
481            with socket_helper.transient_internet(url):
482                with urllib.request.urlopen(req) as data:
483                    code = data.getcode()
484        except urllib.error.HTTPError as e:
485            code = e.code
486        self.assertEqual(code, 200, msg="Can't find " + url)
487
488
489class StartupImportTests(unittest.TestCase):
490
491    @support.requires_subprocess()
492    def test_startup_imports(self):
493        # Get sys.path in isolated mode (python3 -I)
494        popen = subprocess.Popen([sys.executable, '-X', 'utf8', '-I',
495                                  '-c', 'import sys; print(repr(sys.path))'],
496                                 stdout=subprocess.PIPE,
497                                 encoding='utf-8',
498                                 errors='surrogateescape')
499        stdout = popen.communicate()[0]
500        self.assertEqual(popen.returncode, 0, repr(stdout))
501        isolated_paths = ast.literal_eval(stdout)
502
503        # bpo-27807: Even with -I, the site module executes all .pth files
504        # found in sys.path (see site.addpackage()). Skip the test if at least
505        # one .pth file is found.
506        for path in isolated_paths:
507            pth_files = glob.glob(os.path.join(glob.escape(path), "*.pth"))
508            if pth_files:
509                self.skipTest(f"found {len(pth_files)} .pth files in: {path}")
510
511        # This tests checks which modules are loaded by Python when it
512        # initially starts upon startup.
513        popen = subprocess.Popen([sys.executable, '-X', 'utf8', '-I', '-v',
514                                  '-c', 'import sys; print(set(sys.modules))'],
515                                 stdout=subprocess.PIPE,
516                                 stderr=subprocess.PIPE,
517                                 encoding='utf-8',
518                                 errors='surrogateescape')
519        stdout, stderr = popen.communicate()
520        self.assertEqual(popen.returncode, 0, (stdout, stderr))
521        modules = ast.literal_eval(stdout)
522
523        self.assertIn('site', modules)
524
525        # http://bugs.python.org/issue19205
526        re_mods = {'re', '_sre', 're._compiler', 're._constants', 're._parser'}
527        self.assertFalse(modules.intersection(re_mods), stderr)
528
529        # http://bugs.python.org/issue9548
530        self.assertNotIn('locale', modules, stderr)
531
532        # http://bugs.python.org/issue19209
533        self.assertNotIn('copyreg', modules, stderr)
534
535        # http://bugs.python.org/issue19218
536        collection_mods = {'_collections', 'collections', 'functools',
537                           'heapq', 'itertools', 'keyword', 'operator',
538                           'reprlib', 'types', 'weakref'
539                          }.difference(sys.builtin_module_names)
540        self.assertFalse(modules.intersection(collection_mods), stderr)
541
542    @support.requires_subprocess()
543    def test_startup_interactivehook(self):
544        r = subprocess.Popen([sys.executable, '-c',
545            'import sys; sys.exit(hasattr(sys, "__interactivehook__"))']).wait()
546        self.assertTrue(r, "'__interactivehook__' not added by site")
547
548    @support.requires_subprocess()
549    def test_startup_interactivehook_isolated(self):
550        # issue28192 readline is not automatically enabled in isolated mode
551        r = subprocess.Popen([sys.executable, '-I', '-c',
552            'import sys; sys.exit(hasattr(sys, "__interactivehook__"))']).wait()
553        self.assertFalse(r, "'__interactivehook__' added in isolated mode")
554
555    @support.requires_subprocess()
556    def test_startup_interactivehook_isolated_explicit(self):
557        # issue28192 readline can be explicitly enabled in isolated mode
558        r = subprocess.Popen([sys.executable, '-I', '-c',
559            'import site, sys; site.enablerlcompleter(); sys.exit(hasattr(sys, "__interactivehook__"))']).wait()
560        self.assertTrue(r, "'__interactivehook__' not added by enablerlcompleter()")
561
562class _pthFileTests(unittest.TestCase):
563
564    if sys.platform == 'win32':
565        def _create_underpth_exe(self, lines, exe_pth=True):
566            import _winapi
567            temp_dir = tempfile.mkdtemp()
568            self.addCleanup(os_helper.rmtree, temp_dir)
569            exe_file = os.path.join(temp_dir, os.path.split(sys.executable)[1])
570            dll_src_file = _winapi.GetModuleFileName(sys.dllhandle)
571            dll_file = os.path.join(temp_dir, os.path.split(dll_src_file)[1])
572            shutil.copy(sys.executable, exe_file)
573            shutil.copy(dll_src_file, dll_file)
574            for fn in glob.glob(os.path.join(os.path.split(dll_src_file)[0], "vcruntime*.dll")):
575                shutil.copy(fn, os.path.join(temp_dir, os.path.split(fn)[1]))
576            if exe_pth:
577                _pth_file = os.path.splitext(exe_file)[0] + '._pth'
578            else:
579                _pth_file = os.path.splitext(dll_file)[0] + '._pth'
580            with open(_pth_file, 'w') as f:
581                for line in lines:
582                    print(line, file=f)
583            return exe_file
584    else:
585        def _create_underpth_exe(self, lines, exe_pth=True):
586            if not exe_pth:
587                raise unittest.SkipTest("library ._pth file not supported on this platform")
588            temp_dir = tempfile.mkdtemp()
589            self.addCleanup(os_helper.rmtree, temp_dir)
590            exe_file = os.path.join(temp_dir, os.path.split(sys.executable)[1])
591            os.symlink(sys.executable, exe_file)
592            _pth_file = exe_file + '._pth'
593            with open(_pth_file, 'w') as f:
594                for line in lines:
595                    print(line, file=f)
596            return exe_file
597
598    def _calc_sys_path_for_underpth_nosite(self, sys_prefix, lines):
599        sys_path = []
600        for line in lines:
601            if not line or line[0] == '#':
602                continue
603            abs_path = os.path.abspath(os.path.join(sys_prefix, line))
604            sys_path.append(abs_path)
605        return sys_path
606
607    @support.requires_subprocess()
608    def test_underpth_basic(self):
609        libpath = test.support.STDLIB_DIR
610        exe_prefix = os.path.dirname(sys.executable)
611        pth_lines = ['#.', '# ..', *sys.path, '.', '..']
612        exe_file = self._create_underpth_exe(pth_lines)
613        sys_path = self._calc_sys_path_for_underpth_nosite(
614            os.path.dirname(exe_file),
615            pth_lines)
616
617        output = subprocess.check_output([exe_file, '-c',
618            'import sys; print("\\n".join(sys.path) if sys.flags.no_site else "")'
619        ], encoding='utf-8', errors='surrogateescape')
620        actual_sys_path = output.rstrip().split('\n')
621        self.assertTrue(actual_sys_path, "sys.flags.no_site was False")
622        self.assertEqual(
623            actual_sys_path,
624            sys_path,
625            "sys.path is incorrect"
626        )
627
628    @support.requires_subprocess()
629    def test_underpth_nosite_file(self):
630        libpath = test.support.STDLIB_DIR
631        exe_prefix = os.path.dirname(sys.executable)
632        pth_lines = [
633            'fake-path-name',
634            *[libpath for _ in range(200)],
635            '',
636            '# comment',
637        ]
638        exe_file = self._create_underpth_exe(pth_lines)
639        sys_path = self._calc_sys_path_for_underpth_nosite(
640            os.path.dirname(exe_file),
641            pth_lines)
642
643        env = os.environ.copy()
644        env['PYTHONPATH'] = 'from-env'
645        env['PATH'] = '{}{}{}'.format(exe_prefix, os.pathsep, os.getenv('PATH'))
646        output = subprocess.check_output([exe_file, '-c',
647            'import sys; print("\\n".join(sys.path) if sys.flags.no_site else "")'
648        ], env=env, encoding='utf-8', errors='surrogateescape')
649        actual_sys_path = output.rstrip().split('\n')
650        self.assertTrue(actual_sys_path, "sys.flags.no_site was False")
651        self.assertEqual(
652            actual_sys_path,
653            sys_path,
654            "sys.path is incorrect"
655        )
656
657    @support.requires_subprocess()
658    def test_underpth_file(self):
659        libpath = test.support.STDLIB_DIR
660        exe_prefix = os.path.dirname(sys.executable)
661        exe_file = self._create_underpth_exe([
662            'fake-path-name',
663            *[libpath for _ in range(200)],
664            '',
665            '# comment',
666            'import site'
667        ])
668        sys_prefix = os.path.dirname(exe_file)
669        env = os.environ.copy()
670        env['PYTHONPATH'] = 'from-env'
671        env['PATH'] = '{};{}'.format(exe_prefix, os.getenv('PATH'))
672        rc = subprocess.call([exe_file, '-c',
673            'import sys; sys.exit(not sys.flags.no_site and '
674            '%r in sys.path and %r in sys.path and %r not in sys.path and '
675            'all("\\r" not in p and "\\n" not in p for p in sys.path))' % (
676                os.path.join(sys_prefix, 'fake-path-name'),
677                libpath,
678                os.path.join(sys_prefix, 'from-env'),
679            )], env=env)
680        self.assertTrue(rc, "sys.path is incorrect")
681
682    @support.requires_subprocess()
683    def test_underpth_dll_file(self):
684        libpath = test.support.STDLIB_DIR
685        exe_prefix = os.path.dirname(sys.executable)
686        exe_file = self._create_underpth_exe([
687            'fake-path-name',
688            *[libpath for _ in range(200)],
689            '',
690            '# comment',
691            'import site'
692        ], exe_pth=False)
693        sys_prefix = os.path.dirname(exe_file)
694        env = os.environ.copy()
695        env['PYTHONPATH'] = 'from-env'
696        env['PATH'] = '{};{}'.format(exe_prefix, os.getenv('PATH'))
697        rc = subprocess.call([exe_file, '-c',
698            'import sys; sys.exit(not sys.flags.no_site and '
699            '%r in sys.path and %r in sys.path and %r not in sys.path and '
700            'all("\\r" not in p and "\\n" not in p for p in sys.path))' % (
701                os.path.join(sys_prefix, 'fake-path-name'),
702                libpath,
703                os.path.join(sys_prefix, 'from-env'),
704            )], env=env)
705        self.assertTrue(rc, "sys.path is incorrect")
706
707
708if __name__ == "__main__":
709    unittest.main()
710