(root)/
Python-3.11.7/
Lib/
test/
test_site.py
       1  """Tests for 'site'.
       2  
       3  Tests assume the initial paths in sys.path once the interpreter has begun
       4  executing have not been removed.
       5  
       6  """
       7  import unittest
       8  import test.support
       9  from test import support
      10  from test.support import os_helper
      11  from test.support import socket_helper
      12  from test.support import captured_stderr
      13  from test.support.os_helper import TESTFN, EnvironmentVarGuard, change_cwd
      14  import ast
      15  import builtins
      16  import encodings
      17  import glob
      18  import io
      19  import os
      20  import re
      21  import shutil
      22  import subprocess
      23  import sys
      24  import sysconfig
      25  import tempfile
      26  import urllib.error
      27  import urllib.request
      28  from unittest import mock
      29  from 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.
      34  if sys.flags.no_site:
      35      raise unittest.SkipTest("Python was invoked with -S")
      36  
      37  import site
      38  
      39  
      40  HAS_USER_SITE = (site.USER_SITE is not None)
      41  OLD_SYS_PATH = None
      42  
      43  
      44  def 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  
      59  def tearDownModule():
      60      sys.path[:] = OLD_SYS_PATH
      61  
      62  
      63  class ESC[4;38;5;81mHelperFunctionsTests(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
      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  
     351  class ESC[4;38;5;81mPthFile(ESC[4;38;5;149mobject):
     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  
     406  class ESC[4;38;5;81mImportSideEffectTests(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     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      @unittest.skipUnless(hasattr(urllib.request, "HTTPSHandler"),
     470                           'need SSL support to download license')
     471      @test.support.requires_resource('network')
     472      @test.support.system_must_validate_cert
     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  
     489  class ESC[4;38;5;81mStartupImportTests(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     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  
     562  class ESC[4;38;5;81m_pthFileTests(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     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', encoding='utf8') 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, '-X', 'utf8', '-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  
     708  if __name__ == "__main__":
     709      unittest.main()