python (3.12.0)

(root)/
lib/
python3.12/
test/
test_venv.py
       1  """
       2  Test harness for the venv module.
       3  
       4  Copyright (C) 2011-2012 Vinay Sajip.
       5  Licensed to the PSF under a contributor agreement.
       6  """
       7  
       8  import contextlib
       9  import ensurepip
      10  import os
      11  import os.path
      12  import pathlib
      13  import re
      14  import shutil
      15  import struct
      16  import subprocess
      17  import sys
      18  import sysconfig
      19  import tempfile
      20  from test.support import (captured_stdout, captured_stderr,
      21                            skip_if_broken_multiprocessing_synchronize, verbose,
      22                            requires_subprocess, is_emscripten, is_wasi,
      23                            requires_venv_with_pip, TEST_HOME_DIR,
      24                            requires_resource)
      25  from test.support.os_helper import (can_symlink, EnvironmentVarGuard, rmtree)
      26  import unittest
      27  import venv
      28  from unittest.mock import patch, Mock
      29  
      30  try:
      31      import ctypes
      32  except ImportError:
      33      ctypes = None
      34  
      35  # Platforms that set sys._base_executable can create venvs from within
      36  # another venv, so no need to skip tests that require venv.create().
      37  requireVenvCreate = unittest.skipUnless(
      38      sys.prefix == sys.base_prefix
      39      or sys._base_executable != sys.executable,
      40      'cannot run venv.create from within a venv on this platform')
      41  
      42  if is_emscripten or is_wasi:
      43      raise unittest.SkipTest("venv is not available on Emscripten/WASI.")
      44  
      45  @requires_subprocess()
      46  def check_output(cmd, encoding=None):
      47      p = subprocess.Popen(cmd,
      48          stdout=subprocess.PIPE,
      49          stderr=subprocess.PIPE,
      50          encoding=encoding)
      51      out, err = p.communicate()
      52      if p.returncode:
      53          if verbose and err:
      54              print(err.decode('utf-8', 'backslashreplace'))
      55          raise subprocess.CalledProcessError(
      56              p.returncode, cmd, out, err)
      57      return out, err
      58  
      59  class ESC[4;38;5;81mBaseTest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
      60      """Base class for venv tests."""
      61      maxDiff = 80 * 50
      62  
      63      def setUp(self):
      64          self.env_dir = os.path.realpath(tempfile.mkdtemp())
      65          if os.name == 'nt':
      66              self.bindir = 'Scripts'
      67              self.lib = ('Lib',)
      68              self.include = 'Include'
      69          else:
      70              self.bindir = 'bin'
      71              self.lib = ('lib', 'python%d.%d' % sys.version_info[:2])
      72              self.include = 'include'
      73          executable = sys._base_executable
      74          self.exe = os.path.split(executable)[-1]
      75          if (sys.platform == 'win32'
      76              and os.path.lexists(executable)
      77              and not os.path.exists(executable)):
      78              self.cannot_link_exe = True
      79          else:
      80              self.cannot_link_exe = False
      81  
      82      def tearDown(self):
      83          rmtree(self.env_dir)
      84  
      85      def run_with_capture(self, func, *args, **kwargs):
      86          with captured_stdout() as output:
      87              with captured_stderr() as error:
      88                  func(*args, **kwargs)
      89          return output.getvalue(), error.getvalue()
      90  
      91      def get_env_file(self, *args):
      92          return os.path.join(self.env_dir, *args)
      93  
      94      def get_text_file_contents(self, *args, encoding='utf-8'):
      95          with open(self.get_env_file(*args), 'r', encoding=encoding) as f:
      96              result = f.read()
      97          return result
      98  
      99  class ESC[4;38;5;81mBasicTest(ESC[4;38;5;149mBaseTest):
     100      """Test venv module functionality."""
     101  
     102      def isdir(self, *args):
     103          fn = self.get_env_file(*args)
     104          self.assertTrue(os.path.isdir(fn))
     105  
     106      def test_defaults_with_str_path(self):
     107          """
     108          Test the create function with default arguments and a str path.
     109          """
     110          rmtree(self.env_dir)
     111          self.run_with_capture(venv.create, self.env_dir)
     112          self._check_output_of_default_create()
     113  
     114      def test_defaults_with_pathlib_path(self):
     115          """
     116          Test the create function with default arguments and a pathlib.Path path.
     117          """
     118          rmtree(self.env_dir)
     119          self.run_with_capture(venv.create, pathlib.Path(self.env_dir))
     120          self._check_output_of_default_create()
     121  
     122      def _check_output_of_default_create(self):
     123          self.isdir(self.bindir)
     124          self.isdir(self.include)
     125          self.isdir(*self.lib)
     126          # Issue 21197
     127          p = self.get_env_file('lib64')
     128          conditions = ((struct.calcsize('P') == 8) and (os.name == 'posix') and
     129                        (sys.platform != 'darwin'))
     130          if conditions:
     131              self.assertTrue(os.path.islink(p))
     132          else:
     133              self.assertFalse(os.path.exists(p))
     134          data = self.get_text_file_contents('pyvenv.cfg')
     135          executable = sys._base_executable
     136          path = os.path.dirname(executable)
     137          self.assertIn('home = %s' % path, data)
     138          self.assertIn('executable = %s' %
     139                        os.path.realpath(sys.executable), data)
     140          copies = '' if os.name=='nt' else ' --copies'
     141          cmd = f'command = {sys.executable} -m venv{copies} --without-pip {self.env_dir}'
     142          self.assertIn(cmd, data)
     143          fn = self.get_env_file(self.bindir, self.exe)
     144          if not os.path.exists(fn):  # diagnostics for Windows buildbot failures
     145              bd = self.get_env_file(self.bindir)
     146              print('Contents of %r:' % bd)
     147              print('    %r' % os.listdir(bd))
     148          self.assertTrue(os.path.exists(fn), 'File %r should exist.' % fn)
     149  
     150      def test_config_file_command_key(self):
     151          attrs = [
     152              (None, None),
     153              ('symlinks', '--copies'),
     154              ('with_pip', '--without-pip'),
     155              ('system_site_packages', '--system-site-packages'),
     156              ('clear', '--clear'),
     157              ('upgrade', '--upgrade'),
     158              ('upgrade_deps', '--upgrade-deps'),
     159              ('prompt', '--prompt'),
     160          ]
     161          for attr, opt in attrs:
     162              rmtree(self.env_dir)
     163              if not attr:
     164                  b = venv.EnvBuilder()
     165              else:
     166                  b = venv.EnvBuilder(
     167                      **{attr: False if attr in ('with_pip', 'symlinks') else True})
     168              b.upgrade_dependencies = Mock() # avoid pip command to upgrade deps
     169              b._setup_pip = Mock() # avoid pip setup
     170              self.run_with_capture(b.create, self.env_dir)
     171              data = self.get_text_file_contents('pyvenv.cfg')
     172              if not attr:
     173                  for opt in ('--system-site-packages', '--clear', '--upgrade',
     174                          '--upgrade-deps', '--prompt'):
     175                      self.assertNotRegex(data, rf'command = .* {opt}')
     176              elif os.name=='nt' and attr=='symlinks':
     177                  pass
     178              else:
     179                  self.assertRegex(data, rf'command = .* {opt}')
     180  
     181      def test_prompt(self):
     182          env_name = os.path.split(self.env_dir)[1]
     183  
     184          rmtree(self.env_dir)
     185          builder = venv.EnvBuilder()
     186          self.run_with_capture(builder.create, self.env_dir)
     187          context = builder.ensure_directories(self.env_dir)
     188          data = self.get_text_file_contents('pyvenv.cfg')
     189          self.assertEqual(context.prompt, '(%s) ' % env_name)
     190          self.assertNotIn("prompt = ", data)
     191  
     192          rmtree(self.env_dir)
     193          builder = venv.EnvBuilder(prompt='My prompt')
     194          self.run_with_capture(builder.create, self.env_dir)
     195          context = builder.ensure_directories(self.env_dir)
     196          data = self.get_text_file_contents('pyvenv.cfg')
     197          self.assertEqual(context.prompt, '(My prompt) ')
     198          self.assertIn("prompt = 'My prompt'\n", data)
     199  
     200          rmtree(self.env_dir)
     201          builder = venv.EnvBuilder(prompt='.')
     202          cwd = os.path.basename(os.getcwd())
     203          self.run_with_capture(builder.create, self.env_dir)
     204          context = builder.ensure_directories(self.env_dir)
     205          data = self.get_text_file_contents('pyvenv.cfg')
     206          self.assertEqual(context.prompt, '(%s) ' % cwd)
     207          self.assertIn("prompt = '%s'\n" % cwd, data)
     208  
     209      def test_upgrade_dependencies(self):
     210          builder = venv.EnvBuilder()
     211          bin_path = 'Scripts' if sys.platform == 'win32' else 'bin'
     212          python_exe = os.path.split(sys.executable)[1]
     213          with tempfile.TemporaryDirectory() as fake_env_dir:
     214              expect_exe = os.path.normcase(
     215                  os.path.join(fake_env_dir, bin_path, python_exe)
     216              )
     217              if sys.platform == 'win32':
     218                  expect_exe = os.path.normcase(os.path.realpath(expect_exe))
     219  
     220              def pip_cmd_checker(cmd, **kwargs):
     221                  cmd[0] = os.path.normcase(cmd[0])
     222                  self.assertEqual(
     223                      cmd,
     224                      [
     225                          expect_exe,
     226                          '-m',
     227                          'pip',
     228                          'install',
     229                          '--upgrade',
     230                          'pip',
     231                      ]
     232                  )
     233  
     234              fake_context = builder.ensure_directories(fake_env_dir)
     235              with patch('venv.subprocess.check_output', pip_cmd_checker):
     236                  builder.upgrade_dependencies(fake_context)
     237  
     238      @requireVenvCreate
     239      def test_prefixes(self):
     240          """
     241          Test that the prefix values are as expected.
     242          """
     243          # check a venv's prefixes
     244          rmtree(self.env_dir)
     245          self.run_with_capture(venv.create, self.env_dir)
     246          envpy = os.path.join(self.env_dir, self.bindir, self.exe)
     247          cmd = [envpy, '-c', None]
     248          for prefix, expected in (
     249              ('prefix', self.env_dir),
     250              ('exec_prefix', self.env_dir),
     251              ('base_prefix', sys.base_prefix),
     252              ('base_exec_prefix', sys.base_exec_prefix)):
     253              cmd[2] = 'import sys; print(sys.%s)' % prefix
     254              out, err = check_output(cmd)
     255              self.assertEqual(out.strip(), expected.encode(), prefix)
     256  
     257      @requireVenvCreate
     258      def test_sysconfig(self):
     259          """
     260          Test that the sysconfig functions work in a virtual environment.
     261          """
     262          rmtree(self.env_dir)
     263          self.run_with_capture(venv.create, self.env_dir, symlinks=False)
     264          envpy = os.path.join(self.env_dir, self.bindir, self.exe)
     265          cmd = [envpy, '-c', None]
     266          for call, expected in (
     267              # installation scheme
     268              ('get_preferred_scheme("prefix")', 'venv'),
     269              ('get_default_scheme()', 'venv'),
     270              # build environment
     271              ('is_python_build()', str(sysconfig.is_python_build())),
     272              ('get_makefile_filename()', sysconfig.get_makefile_filename()),
     273              ('get_config_h_filename()', sysconfig.get_config_h_filename())):
     274              with self.subTest(call):
     275                  cmd[2] = 'import sysconfig; print(sysconfig.%s)' % call
     276                  out, err = check_output(cmd)
     277                  self.assertEqual(out.strip(), expected.encode(), err)
     278  
     279      @requireVenvCreate
     280      @unittest.skipUnless(can_symlink(), 'Needs symlinks')
     281      def test_sysconfig_symlinks(self):
     282          """
     283          Test that the sysconfig functions work in a virtual environment.
     284          """
     285          rmtree(self.env_dir)
     286          self.run_with_capture(venv.create, self.env_dir, symlinks=True)
     287          envpy = os.path.join(self.env_dir, self.bindir, self.exe)
     288          cmd = [envpy, '-c', None]
     289          for call, expected in (
     290              # installation scheme
     291              ('get_preferred_scheme("prefix")', 'venv'),
     292              ('get_default_scheme()', 'venv'),
     293              # build environment
     294              ('is_python_build()', str(sysconfig.is_python_build())),
     295              ('get_makefile_filename()', sysconfig.get_makefile_filename()),
     296              ('get_config_h_filename()', sysconfig.get_config_h_filename())):
     297              with self.subTest(call):
     298                  cmd[2] = 'import sysconfig; print(sysconfig.%s)' % call
     299                  out, err = check_output(cmd)
     300                  self.assertEqual(out.strip(), expected.encode(), err)
     301  
     302      if sys.platform == 'win32':
     303          ENV_SUBDIRS = (
     304              ('Scripts',),
     305              ('Include',),
     306              ('Lib',),
     307              ('Lib', 'site-packages'),
     308          )
     309      else:
     310          ENV_SUBDIRS = (
     311              ('bin',),
     312              ('include',),
     313              ('lib',),
     314              ('lib', 'python%d.%d' % sys.version_info[:2]),
     315              ('lib', 'python%d.%d' % sys.version_info[:2], 'site-packages'),
     316          )
     317  
     318      def create_contents(self, paths, filename):
     319          """
     320          Create some files in the environment which are unrelated
     321          to the virtual environment.
     322          """
     323          for subdirs in paths:
     324              d = os.path.join(self.env_dir, *subdirs)
     325              os.mkdir(d)
     326              fn = os.path.join(d, filename)
     327              with open(fn, 'wb') as f:
     328                  f.write(b'Still here?')
     329  
     330      def test_overwrite_existing(self):
     331          """
     332          Test creating environment in an existing directory.
     333          """
     334          self.create_contents(self.ENV_SUBDIRS, 'foo')
     335          venv.create(self.env_dir)
     336          for subdirs in self.ENV_SUBDIRS:
     337              fn = os.path.join(self.env_dir, *(subdirs + ('foo',)))
     338              self.assertTrue(os.path.exists(fn))
     339              with open(fn, 'rb') as f:
     340                  self.assertEqual(f.read(), b'Still here?')
     341  
     342          builder = venv.EnvBuilder(clear=True)
     343          builder.create(self.env_dir)
     344          for subdirs in self.ENV_SUBDIRS:
     345              fn = os.path.join(self.env_dir, *(subdirs + ('foo',)))
     346              self.assertFalse(os.path.exists(fn))
     347  
     348      def clear_directory(self, path):
     349          for fn in os.listdir(path):
     350              fn = os.path.join(path, fn)
     351              if os.path.islink(fn) or os.path.isfile(fn):
     352                  os.remove(fn)
     353              elif os.path.isdir(fn):
     354                  rmtree(fn)
     355  
     356      def test_unoverwritable_fails(self):
     357          #create a file clashing with directories in the env dir
     358          for paths in self.ENV_SUBDIRS[:3]:
     359              fn = os.path.join(self.env_dir, *paths)
     360              with open(fn, 'wb') as f:
     361                  f.write(b'')
     362              self.assertRaises((ValueError, OSError), venv.create, self.env_dir)
     363              self.clear_directory(self.env_dir)
     364  
     365      def test_upgrade(self):
     366          """
     367          Test upgrading an existing environment directory.
     368          """
     369          # See Issue #21643: the loop needs to run twice to ensure
     370          # that everything works on the upgrade (the first run just creates
     371          # the venv).
     372          for upgrade in (False, True):
     373              builder = venv.EnvBuilder(upgrade=upgrade)
     374              self.run_with_capture(builder.create, self.env_dir)
     375              self.isdir(self.bindir)
     376              self.isdir(self.include)
     377              self.isdir(*self.lib)
     378              fn = self.get_env_file(self.bindir, self.exe)
     379              if not os.path.exists(fn):
     380                  # diagnostics for Windows buildbot failures
     381                  bd = self.get_env_file(self.bindir)
     382                  print('Contents of %r:' % bd)
     383                  print('    %r' % os.listdir(bd))
     384              self.assertTrue(os.path.exists(fn), 'File %r should exist.' % fn)
     385  
     386      def test_isolation(self):
     387          """
     388          Test isolation from system site-packages
     389          """
     390          for ssp, s in ((True, 'true'), (False, 'false')):
     391              builder = venv.EnvBuilder(clear=True, system_site_packages=ssp)
     392              builder.create(self.env_dir)
     393              data = self.get_text_file_contents('pyvenv.cfg')
     394              self.assertIn('include-system-site-packages = %s\n' % s, data)
     395  
     396      @unittest.skipUnless(can_symlink(), 'Needs symlinks')
     397      def test_symlinking(self):
     398          """
     399          Test symlinking works as expected
     400          """
     401          for usl in (False, True):
     402              builder = venv.EnvBuilder(clear=True, symlinks=usl)
     403              builder.create(self.env_dir)
     404              fn = self.get_env_file(self.bindir, self.exe)
     405              # Don't test when False, because e.g. 'python' is always
     406              # symlinked to 'python3.3' in the env, even when symlinking in
     407              # general isn't wanted.
     408              if usl:
     409                  if self.cannot_link_exe:
     410                      # Symlinking is skipped when our executable is already a
     411                      # special app symlink
     412                      self.assertFalse(os.path.islink(fn))
     413                  else:
     414                      self.assertTrue(os.path.islink(fn))
     415  
     416      # If a venv is created from a source build and that venv is used to
     417      # run the test, the pyvenv.cfg in the venv created in the test will
     418      # point to the venv being used to run the test, and we lose the link
     419      # to the source build - so Python can't initialise properly.
     420      @requireVenvCreate
     421      def test_executable(self):
     422          """
     423          Test that the sys.executable value is as expected.
     424          """
     425          rmtree(self.env_dir)
     426          self.run_with_capture(venv.create, self.env_dir)
     427          envpy = os.path.join(os.path.realpath(self.env_dir),
     428                               self.bindir, self.exe)
     429          out, err = check_output([envpy, '-c',
     430              'import sys; print(sys.executable)'])
     431          self.assertEqual(out.strip(), envpy.encode())
     432  
     433      @unittest.skipUnless(can_symlink(), 'Needs symlinks')
     434      def test_executable_symlinks(self):
     435          """
     436          Test that the sys.executable value is as expected.
     437          """
     438          rmtree(self.env_dir)
     439          builder = venv.EnvBuilder(clear=True, symlinks=True)
     440          builder.create(self.env_dir)
     441          envpy = os.path.join(os.path.realpath(self.env_dir),
     442                               self.bindir, self.exe)
     443          out, err = check_output([envpy, '-c',
     444              'import sys; print(sys.executable)'])
     445          self.assertEqual(out.strip(), envpy.encode())
     446  
     447      @unittest.skipUnless(os.name == 'nt', 'only relevant on Windows')
     448      def test_unicode_in_batch_file(self):
     449          """
     450          Test handling of Unicode paths
     451          """
     452          rmtree(self.env_dir)
     453          env_dir = os.path.join(os.path.realpath(self.env_dir), 'ϼўТλФЙ')
     454          builder = venv.EnvBuilder(clear=True)
     455          builder.create(env_dir)
     456          activate = os.path.join(env_dir, self.bindir, 'activate.bat')
     457          envpy = os.path.join(env_dir, self.bindir, self.exe)
     458          out, err = check_output(
     459              [activate, '&', self.exe, '-c', 'print(0)'],
     460              encoding='oem',
     461          )
     462          self.assertEqual(out.strip(), '0')
     463  
     464      @requireVenvCreate
     465      def test_multiprocessing(self):
     466          """
     467          Test that the multiprocessing is able to spawn.
     468          """
     469          # bpo-36342: Instantiation of a Pool object imports the
     470          # multiprocessing.synchronize module. Skip the test if this module
     471          # cannot be imported.
     472          skip_if_broken_multiprocessing_synchronize()
     473  
     474          rmtree(self.env_dir)
     475          self.run_with_capture(venv.create, self.env_dir)
     476          envpy = os.path.join(os.path.realpath(self.env_dir),
     477                               self.bindir, self.exe)
     478          out, err = check_output([envpy, '-c',
     479              'from multiprocessing import Pool; '
     480              'pool = Pool(1); '
     481              'print(pool.apply_async("Python".lower).get(3)); '
     482              'pool.terminate()'])
     483          self.assertEqual(out.strip(), "python".encode())
     484  
     485      @requireVenvCreate
     486      def test_multiprocessing_recursion(self):
     487          """
     488          Test that the multiprocessing is able to spawn itself
     489          """
     490          skip_if_broken_multiprocessing_synchronize()
     491  
     492          rmtree(self.env_dir)
     493          self.run_with_capture(venv.create, self.env_dir)
     494          envpy = os.path.join(os.path.realpath(self.env_dir),
     495                               self.bindir, self.exe)
     496          script = os.path.join(TEST_HOME_DIR, '_test_venv_multiprocessing.py')
     497          subprocess.check_call([envpy, script])
     498  
     499      @unittest.skipIf(os.name == 'nt', 'not relevant on Windows')
     500      def test_deactivate_with_strict_bash_opts(self):
     501          bash = shutil.which("bash")
     502          if bash is None:
     503              self.skipTest("bash required for this test")
     504          rmtree(self.env_dir)
     505          builder = venv.EnvBuilder(clear=True)
     506          builder.create(self.env_dir)
     507          activate = os.path.join(self.env_dir, self.bindir, "activate")
     508          test_script = os.path.join(self.env_dir, "test_strict.sh")
     509          with open(test_script, "w") as f:
     510              f.write("set -euo pipefail\n"
     511                      f"source {activate}\n"
     512                      "deactivate\n")
     513          out, err = check_output([bash, test_script])
     514          self.assertEqual(out, "".encode())
     515          self.assertEqual(err, "".encode())
     516  
     517  
     518      @unittest.skipUnless(sys.platform == 'darwin', 'only relevant on macOS')
     519      def test_macos_env(self):
     520          rmtree(self.env_dir)
     521          builder = venv.EnvBuilder()
     522          builder.create(self.env_dir)
     523  
     524          envpy = os.path.join(os.path.realpath(self.env_dir),
     525                               self.bindir, self.exe)
     526          out, err = check_output([envpy, '-c',
     527              'import os; print("__PYVENV_LAUNCHER__" in os.environ)'])
     528          self.assertEqual(out.strip(), 'False'.encode())
     529  
     530      def test_pathsep_error(self):
     531          """
     532          Test that venv creation fails when the target directory contains
     533          the path separator.
     534          """
     535          rmtree(self.env_dir)
     536          bad_itempath = self.env_dir + os.pathsep
     537          self.assertRaises(ValueError, venv.create, bad_itempath)
     538          self.assertRaises(ValueError, venv.create, pathlib.Path(bad_itempath))
     539  
     540      @unittest.skipIf(os.name == 'nt', 'not relevant on Windows')
     541      @requireVenvCreate
     542      def test_zippath_from_non_installed_posix(self):
     543          """
     544          Test that when create venv from non-installed python, the zip path
     545          value is as expected.
     546          """
     547          rmtree(self.env_dir)
     548          # First try to create a non-installed python. It's not a real full
     549          # functional non-installed python, but enough for this test.
     550          platlibdir = sys.platlibdir
     551          non_installed_dir = os.path.realpath(tempfile.mkdtemp())
     552          self.addCleanup(rmtree, non_installed_dir)
     553          bindir = os.path.join(non_installed_dir, self.bindir)
     554          os.mkdir(bindir)
     555          shutil.copy2(sys.executable, bindir)
     556          libdir = os.path.join(non_installed_dir, platlibdir, self.lib[1])
     557          os.makedirs(libdir)
     558          landmark = os.path.join(libdir, "os.py")
     559          stdlib_zip = "python%d%d.zip" % sys.version_info[:2]
     560          zip_landmark = os.path.join(non_installed_dir,
     561                                      platlibdir,
     562                                      stdlib_zip)
     563          additional_pythonpath_for_non_installed = []
     564          # Copy stdlib files to the non-installed python so venv can
     565          # correctly calculate the prefix.
     566          for eachpath in sys.path:
     567              if eachpath.endswith(".zip"):
     568                  if os.path.isfile(eachpath):
     569                      shutil.copyfile(
     570                          eachpath,
     571                          os.path.join(non_installed_dir, platlibdir))
     572              elif os.path.isfile(os.path.join(eachpath, "os.py")):
     573                  for name in os.listdir(eachpath):
     574                      if name == "site-packages":
     575                          continue
     576                      fn = os.path.join(eachpath, name)
     577                      if os.path.isfile(fn):
     578                          shutil.copy(fn, libdir)
     579                      elif os.path.isdir(fn):
     580                          shutil.copytree(fn, os.path.join(libdir, name))
     581              else:
     582                  additional_pythonpath_for_non_installed.append(
     583                      eachpath)
     584          cmd = [os.path.join(non_installed_dir, self.bindir, self.exe),
     585                 "-m",
     586                 "venv",
     587                 "--without-pip",
     588                 self.env_dir]
     589          # Our fake non-installed python is not fully functional because
     590          # it cannot find the extensions. Set PYTHONPATH so it can run the
     591          # venv module correctly.
     592          pythonpath = os.pathsep.join(
     593              additional_pythonpath_for_non_installed)
     594          # For python built with shared enabled. We need to set
     595          # LD_LIBRARY_PATH so the non-installed python can find and link
     596          # libpython.so
     597          ld_library_path = sysconfig.get_config_var("LIBDIR")
     598          if not ld_library_path or sysconfig.is_python_build():
     599              ld_library_path = os.path.abspath(os.path.dirname(sys.executable))
     600          if sys.platform == 'darwin':
     601              ld_library_path_env = "DYLD_LIBRARY_PATH"
     602          else:
     603              ld_library_path_env = "LD_LIBRARY_PATH"
     604          child_env = {
     605                  "PYTHONPATH": pythonpath,
     606                  ld_library_path_env: ld_library_path,
     607          }
     608          if asan_options := os.environ.get("ASAN_OPTIONS"):
     609              # prevent https://github.com/python/cpython/issues/104839
     610              child_env["ASAN_OPTIONS"] = asan_options
     611          subprocess.check_call(cmd, env=child_env)
     612          envpy = os.path.join(self.env_dir, self.bindir, self.exe)
     613          # Now check the venv created from the non-installed python has
     614          # correct zip path in pythonpath.
     615          cmd = [envpy, '-S', '-c', 'import sys; print(sys.path)']
     616          out, err = check_output(cmd)
     617          self.assertTrue(zip_landmark.encode() in out)
     618  
     619      def test_activate_shell_script_has_no_dos_newlines(self):
     620          """
     621          Test that the `activate` shell script contains no CR LF.
     622          This is relevant for Cygwin, as the Windows build might have
     623          converted line endings accidentally.
     624          """
     625          venv_dir = pathlib.Path(self.env_dir)
     626          rmtree(venv_dir)
     627          [[scripts_dir], *_] = self.ENV_SUBDIRS
     628          script_path = venv_dir / scripts_dir / "activate"
     629          venv.create(venv_dir)
     630          with open(script_path, 'rb') as script:
     631              for i, line in enumerate(script, 1):
     632                  error_message = f"CR LF found in line {i}"
     633                  self.assertFalse(line.endswith(b'\r\n'), error_message)
     634  
     635  @requireVenvCreate
     636  class ESC[4;38;5;81mEnsurePipTest(ESC[4;38;5;149mBaseTest):
     637      """Test venv module installation of pip."""
     638      def assert_pip_not_installed(self):
     639          envpy = os.path.join(os.path.realpath(self.env_dir),
     640                               self.bindir, self.exe)
     641          out, err = check_output([envpy, '-c',
     642              'try:\n import pip\nexcept ImportError:\n print("OK")'])
     643          # We force everything to text, so unittest gives the detailed diff
     644          # if we get unexpected results
     645          err = err.decode("latin-1") # Force to text, prevent decoding errors
     646          self.assertEqual(err, "")
     647          out = out.decode("latin-1") # Force to text, prevent decoding errors
     648          self.assertEqual(out.strip(), "OK")
     649  
     650  
     651      def test_no_pip_by_default(self):
     652          rmtree(self.env_dir)
     653          self.run_with_capture(venv.create, self.env_dir)
     654          self.assert_pip_not_installed()
     655  
     656      def test_explicit_no_pip(self):
     657          rmtree(self.env_dir)
     658          self.run_with_capture(venv.create, self.env_dir, with_pip=False)
     659          self.assert_pip_not_installed()
     660  
     661      def test_devnull(self):
     662          # Fix for issue #20053 uses os.devnull to force a config file to
     663          # appear empty. However http://bugs.python.org/issue20541 means
     664          # that doesn't currently work properly on Windows. Once that is
     665          # fixed, the "win_location" part of test_with_pip should be restored
     666          with open(os.devnull, "rb") as f:
     667              self.assertEqual(f.read(), b"")
     668  
     669          self.assertTrue(os.path.exists(os.devnull))
     670  
     671      def do_test_with_pip(self, system_site_packages):
     672          rmtree(self.env_dir)
     673          with EnvironmentVarGuard() as envvars:
     674              # pip's cross-version compatibility may trigger deprecation
     675              # warnings in current versions of Python. Ensure related
     676              # environment settings don't cause venv to fail.
     677              envvars["PYTHONWARNINGS"] = "ignore"
     678              # ensurepip is different enough from a normal pip invocation
     679              # that we want to ensure it ignores the normal pip environment
     680              # variable settings. We set PIP_NO_INSTALL here specifically
     681              # to check that ensurepip (and hence venv) ignores it.
     682              # See http://bugs.python.org/issue19734
     683              envvars["PIP_NO_INSTALL"] = "1"
     684              # Also check that we ignore the pip configuration file
     685              # See http://bugs.python.org/issue20053
     686              with tempfile.TemporaryDirectory() as home_dir:
     687                  envvars["HOME"] = home_dir
     688                  bad_config = "[global]\nno-install=1"
     689                  # Write to both config file names on all platforms to reduce
     690                  # cross-platform variation in test code behaviour
     691                  win_location = ("pip", "pip.ini")
     692                  posix_location = (".pip", "pip.conf")
     693                  # Skips win_location due to http://bugs.python.org/issue20541
     694                  for dirname, fname in (posix_location,):
     695                      dirpath = os.path.join(home_dir, dirname)
     696                      os.mkdir(dirpath)
     697                      fpath = os.path.join(dirpath, fname)
     698                      with open(fpath, 'w') as f:
     699                          f.write(bad_config)
     700  
     701                  # Actually run the create command with all that unhelpful
     702                  # config in place to ensure we ignore it
     703                  with self.nicer_error():
     704                      self.run_with_capture(venv.create, self.env_dir,
     705                                            system_site_packages=system_site_packages,
     706                                            with_pip=True)
     707          # Ensure pip is available in the virtual environment
     708          envpy = os.path.join(os.path.realpath(self.env_dir), self.bindir, self.exe)
     709          # Ignore DeprecationWarning since pip code is not part of Python
     710          out, err = check_output([envpy, '-W', 'ignore::DeprecationWarning',
     711                 '-W', 'ignore::ImportWarning', '-I',
     712                 '-m', 'pip', '--version'])
     713          # We force everything to text, so unittest gives the detailed diff
     714          # if we get unexpected results
     715          err = err.decode("latin-1") # Force to text, prevent decoding errors
     716          self.assertEqual(err, "")
     717          out = out.decode("latin-1") # Force to text, prevent decoding errors
     718          expected_version = "pip {}".format(ensurepip.version())
     719          self.assertEqual(out[:len(expected_version)], expected_version)
     720          env_dir = os.fsencode(self.env_dir).decode("latin-1")
     721          self.assertIn(env_dir, out)
     722  
     723          # http://bugs.python.org/issue19728
     724          # Check the private uninstall command provided for the Windows
     725          # installers works (at least in a virtual environment)
     726          with EnvironmentVarGuard() as envvars:
     727              with self.nicer_error():
     728                  # It seems ensurepip._uninstall calls subprocesses which do not
     729                  # inherit the interpreter settings.
     730                  envvars["PYTHONWARNINGS"] = "ignore"
     731                  out, err = check_output([envpy,
     732                      '-W', 'ignore::DeprecationWarning',
     733                      '-W', 'ignore::ImportWarning', '-I',
     734                      '-m', 'ensurepip._uninstall'])
     735          # We force everything to text, so unittest gives the detailed diff
     736          # if we get unexpected results
     737          err = err.decode("latin-1") # Force to text, prevent decoding errors
     738          # Ignore the warning:
     739          #   "The directory '$HOME/.cache/pip/http' or its parent directory
     740          #    is not owned by the current user and the cache has been disabled.
     741          #    Please check the permissions and owner of that directory. If
     742          #    executing pip with sudo, you may want sudo's -H flag."
     743          # where $HOME is replaced by the HOME environment variable.
     744          err = re.sub("^(WARNING: )?The directory .* or its parent directory "
     745                       "is not owned or is not writable by the current user.*$", "",
     746                       err, flags=re.MULTILINE)
     747          self.assertEqual(err.rstrip(), "")
     748          # Being fairly specific regarding the expected behaviour for the
     749          # initial bundling phase in Python 3.4. If the output changes in
     750          # future pip versions, this test can likely be relaxed further.
     751          out = out.decode("latin-1") # Force to text, prevent decoding errors
     752          self.assertIn("Successfully uninstalled pip", out)
     753          # Check pip is now gone from the virtual environment. This only
     754          # applies in the system_site_packages=False case, because in the
     755          # other case, pip may still be available in the system site-packages
     756          if not system_site_packages:
     757              self.assert_pip_not_installed()
     758  
     759      @contextlib.contextmanager
     760      def nicer_error(self):
     761          """
     762          Capture output from a failed subprocess for easier debugging.
     763  
     764          The output this handler produces can be a little hard to read,
     765          but at least it has all the details.
     766          """
     767          try:
     768              yield
     769          except subprocess.CalledProcessError as exc:
     770              out = (exc.output or b'').decode(errors="replace")
     771              err = (exc.stderr or b'').decode(errors="replace")
     772              self.fail(
     773                  f"{exc}\n\n"
     774                  f"**Subprocess Output**\n{out}\n\n"
     775                  f"**Subprocess Error**\n{err}"
     776              )
     777  
     778      @requires_venv_with_pip()
     779      @requires_resource('cpu')
     780      def test_with_pip(self):
     781          self.do_test_with_pip(False)
     782          self.do_test_with_pip(True)
     783  
     784  
     785  if __name__ == "__main__":
     786      unittest.main()