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