diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py new file mode 100644 index 000000000..ca003d55d --- /dev/null +++ b/Lib/test/test_venv.py @@ -0,0 +1,541 @@ +""" +Test harness for the venv module. + +Copyright (C) 2011-2012 Vinay Sajip. +Licensed to the PSF under a contributor agreement. +""" + +import ensurepip +import os +import os.path +import re +import shutil +import struct +import subprocess +import sys +import tempfile +from test.support import (captured_stdout, captured_stderr, requires_zlib, + can_symlink, EnvironmentVarGuard, rmtree, + import_module, + skip_if_broken_multiprocessing_synchronize) +import unittest +import venv +from unittest.mock import patch + +try: + import ctypes +except ImportError: + ctypes = None + +# Platforms that set sys._base_executable can create venvs from within +# another venv, so no need to skip tests that require venv.create(). +requireVenvCreate = unittest.skipUnless( + sys.prefix == sys.base_prefix + or sys._base_executable != sys.executable, + 'cannot run venv.create from within a venv on this platform') + +def check_output(cmd, encoding=None): + p = subprocess.Popen(cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + encoding=encoding) + out, err = p.communicate() + if p.returncode: + raise subprocess.CalledProcessError( + p.returncode, cmd, out, err) + return out, err + +class BaseTest(unittest.TestCase): + """Base class for venv tests.""" + maxDiff = 80 * 50 + + def setUp(self): + self.env_dir = os.path.realpath(tempfile.mkdtemp()) + if os.name == 'nt': + self.bindir = 'Scripts' + self.lib = ('Lib',) + self.include = 'Include' + else: + self.bindir = 'bin' + self.lib = ('lib', 'python%d.%d' % sys.version_info[:2]) + self.include = 'include' + executable = sys._base_executable + self.exe = os.path.split(executable)[-1] + if (sys.platform == 'win32' + and os.path.lexists(executable) + and not os.path.exists(executable)): + self.cannot_link_exe = True + else: + self.cannot_link_exe = False + + def tearDown(self): + rmtree(self.env_dir) + + def run_with_capture(self, func, *args, **kwargs): + with captured_stdout() as output: + with captured_stderr() as error: + func(*args, **kwargs) + return output.getvalue(), error.getvalue() + + def get_env_file(self, *args): + return os.path.join(self.env_dir, *args) + + def get_text_file_contents(self, *args, encoding='utf-8'): + with open(self.get_env_file(*args), 'r', encoding=encoding) as f: + result = f.read() + return result + +class BasicTest(BaseTest): + """Test venv module functionality.""" + + def isdir(self, *args): + fn = self.get_env_file(*args) + self.assertTrue(os.path.isdir(fn)) + + def test_defaults(self): + """ + Test the create function with default arguments. + """ + rmtree(self.env_dir) + self.run_with_capture(venv.create, self.env_dir) + self.isdir(self.bindir) + self.isdir(self.include) + self.isdir(*self.lib) + # Issue 21197 + p = self.get_env_file('lib64') + conditions = ((struct.calcsize('P') == 8) and (os.name == 'posix') and + (sys.platform != 'darwin')) + if conditions: + self.assertTrue(os.path.islink(p)) + else: + self.assertFalse(os.path.exists(p)) + data = self.get_text_file_contents('pyvenv.cfg') + executable = sys._base_executable + path = os.path.dirname(executable) + self.assertIn('home = %s' % path, data) + fn = self.get_env_file(self.bindir, self.exe) + if not os.path.exists(fn): # diagnostics for Windows buildbot failures + bd = self.get_env_file(self.bindir) + print('Contents of %r:' % bd) + print(' %r' % os.listdir(bd)) + self.assertTrue(os.path.exists(fn), 'File %r should exist.' % fn) + + def test_prompt(self): + env_name = os.path.split(self.env_dir)[1] + + rmtree(self.env_dir) + builder = venv.EnvBuilder() + self.run_with_capture(builder.create, self.env_dir) + context = builder.ensure_directories(self.env_dir) + data = self.get_text_file_contents('pyvenv.cfg') + self.assertEqual(context.prompt, '(%s) ' % env_name) + self.assertNotIn("prompt = ", data) + + rmtree(self.env_dir) + builder = venv.EnvBuilder(prompt='My prompt') + self.run_with_capture(builder.create, self.env_dir) + context = builder.ensure_directories(self.env_dir) + data = self.get_text_file_contents('pyvenv.cfg') + self.assertEqual(context.prompt, '(My prompt) ') + self.assertIn("prompt = 'My prompt'\n", data) + + rmtree(self.env_dir) + builder = venv.EnvBuilder(prompt='.') + cwd = os.path.basename(os.getcwd()) + self.run_with_capture(builder.create, self.env_dir) + context = builder.ensure_directories(self.env_dir) + data = self.get_text_file_contents('pyvenv.cfg') + self.assertEqual(context.prompt, '(%s) ' % cwd) + self.assertIn("prompt = '%s'\n" % cwd, data) + + def test_upgrade_dependencies(self): + builder = venv.EnvBuilder() + bin_path = 'Scripts' if sys.platform == 'win32' else 'bin' + python_exe = 'python.exe' if sys.platform == 'win32' else 'python' + with tempfile.TemporaryDirectory() as fake_env_dir: + + def pip_cmd_checker(cmd): + self.assertEqual( + cmd, + [ + os.path.join(fake_env_dir, bin_path, python_exe), + '-m', + 'pip', + 'install', + '--upgrade', + 'pip', + 'setuptools' + ] + ) + + fake_context = builder.ensure_directories(fake_env_dir) + with patch('venv.subprocess.check_call', pip_cmd_checker): + builder.upgrade_dependencies(fake_context) + + @requireVenvCreate + def test_prefixes(self): + """ + Test that the prefix values are as expected. + """ + # check a venv's prefixes + rmtree(self.env_dir) + self.run_with_capture(venv.create, self.env_dir) + envpy = os.path.join(self.env_dir, self.bindir, self.exe) + cmd = [envpy, '-c', None] + for prefix, expected in ( + ('prefix', self.env_dir), + ('exec_prefix', self.env_dir), + ('base_prefix', sys.base_prefix), + ('base_exec_prefix', sys.base_exec_prefix)): + cmd[2] = 'import sys; print(sys.%s)' % prefix + out, err = check_output(cmd) + self.assertEqual(out.strip(), expected.encode()) + + if sys.platform == 'win32': + ENV_SUBDIRS = ( + ('Scripts',), + ('Include',), + ('Lib',), + ('Lib', 'site-packages'), + ) + else: + ENV_SUBDIRS = ( + ('bin',), + ('include',), + ('lib',), + ('lib', 'python%d.%d' % sys.version_info[:2]), + ('lib', 'python%d.%d' % sys.version_info[:2], 'site-packages'), + ) + + def create_contents(self, paths, filename): + """ + Create some files in the environment which are unrelated + to the virtual environment. + """ + for subdirs in paths: + d = os.path.join(self.env_dir, *subdirs) + os.mkdir(d) + fn = os.path.join(d, filename) + with open(fn, 'wb') as f: + f.write(b'Still here?') + + def test_overwrite_existing(self): + """ + Test creating environment in an existing directory. + """ + self.create_contents(self.ENV_SUBDIRS, 'foo') + venv.create(self.env_dir) + for subdirs in self.ENV_SUBDIRS: + fn = os.path.join(self.env_dir, *(subdirs + ('foo',))) + self.assertTrue(os.path.exists(fn)) + with open(fn, 'rb') as f: + self.assertEqual(f.read(), b'Still here?') + + builder = venv.EnvBuilder(clear=True) + builder.create(self.env_dir) + for subdirs in self.ENV_SUBDIRS: + fn = os.path.join(self.env_dir, *(subdirs + ('foo',))) + self.assertFalse(os.path.exists(fn)) + + def clear_directory(self, path): + for fn in os.listdir(path): + fn = os.path.join(path, fn) + if os.path.islink(fn) or os.path.isfile(fn): + os.remove(fn) + elif os.path.isdir(fn): + rmtree(fn) + + def test_unoverwritable_fails(self): + #create a file clashing with directories in the env dir + for paths in self.ENV_SUBDIRS[:3]: + fn = os.path.join(self.env_dir, *paths) + with open(fn, 'wb') as f: + f.write(b'') + self.assertRaises((ValueError, OSError), venv.create, self.env_dir) + self.clear_directory(self.env_dir) + + def test_upgrade(self): + """ + Test upgrading an existing environment directory. + """ + # See Issue #21643: the loop needs to run twice to ensure + # that everything works on the upgrade (the first run just creates + # the venv). + for upgrade in (False, True): + builder = venv.EnvBuilder(upgrade=upgrade) + self.run_with_capture(builder.create, self.env_dir) + self.isdir(self.bindir) + self.isdir(self.include) + self.isdir(*self.lib) + fn = self.get_env_file(self.bindir, self.exe) + if not os.path.exists(fn): + # diagnostics for Windows buildbot failures + bd = self.get_env_file(self.bindir) + print('Contents of %r:' % bd) + print(' %r' % os.listdir(bd)) + self.assertTrue(os.path.exists(fn), 'File %r should exist.' % fn) + + def test_isolation(self): + """ + Test isolation from system site-packages + """ + for ssp, s in ((True, 'true'), (False, 'false')): + builder = venv.EnvBuilder(clear=True, system_site_packages=ssp) + builder.create(self.env_dir) + data = self.get_text_file_contents('pyvenv.cfg') + self.assertIn('include-system-site-packages = %s\n' % s, data) + + @unittest.skipUnless(can_symlink(), 'Needs symlinks') + def test_symlinking(self): + """ + Test symlinking works as expected + """ + for usl in (False, True): + builder = venv.EnvBuilder(clear=True, symlinks=usl) + builder.create(self.env_dir) + fn = self.get_env_file(self.bindir, self.exe) + # Don't test when False, because e.g. 'python' is always + # symlinked to 'python3.3' in the env, even when symlinking in + # general isn't wanted. + if usl: + if self.cannot_link_exe: + # Symlinking is skipped when our executable is already a + # special app symlink + self.assertFalse(os.path.islink(fn)) + else: + self.assertTrue(os.path.islink(fn)) + + # If a venv is created from a source build and that venv is used to + # run the test, the pyvenv.cfg in the venv created in the test will + # point to the venv being used to run the test, and we lose the link + # to the source build - so Python can't initialise properly. + @requireVenvCreate + def test_executable(self): + """ + Test that the sys.executable value is as expected. + """ + rmtree(self.env_dir) + self.run_with_capture(venv.create, self.env_dir) + envpy = os.path.join(os.path.realpath(self.env_dir), + self.bindir, self.exe) + out, err = check_output([envpy, '-c', + 'import sys; print(sys.executable)']) + self.assertEqual(out.strip(), envpy.encode()) + + @unittest.skipUnless(can_symlink(), 'Needs symlinks') + def test_executable_symlinks(self): + """ + Test that the sys.executable value is as expected. + """ + rmtree(self.env_dir) + builder = venv.EnvBuilder(clear=True, symlinks=True) + builder.create(self.env_dir) + envpy = os.path.join(os.path.realpath(self.env_dir), + self.bindir, self.exe) + out, err = check_output([envpy, '-c', + 'import sys; print(sys.executable)']) + self.assertEqual(out.strip(), envpy.encode()) + + @unittest.skipUnless(os.name == 'nt', 'only relevant on Windows') + def test_unicode_in_batch_file(self): + """ + Test handling of Unicode paths + """ + rmtree(self.env_dir) + env_dir = os.path.join(os.path.realpath(self.env_dir), 'ϼўТλФЙ') + builder = venv.EnvBuilder(clear=True) + builder.create(env_dir) + activate = os.path.join(env_dir, self.bindir, 'activate.bat') + envpy = os.path.join(env_dir, self.bindir, self.exe) + out, err = check_output( + [activate, '&', self.exe, '-c', 'print(0)'], + encoding='oem', + ) + self.assertEqual(out.strip(), '0') + + @requireVenvCreate + def test_multiprocessing(self): + """ + Test that the multiprocessing is able to spawn. + """ + # bpo-36342: Instantiation of a Pool object imports the + # multiprocessing.synchronize module. Skip the test if this module + # cannot be imported. + skip_if_broken_multiprocessing_synchronize() + + rmtree(self.env_dir) + self.run_with_capture(venv.create, self.env_dir) + envpy = os.path.join(os.path.realpath(self.env_dir), + self.bindir, self.exe) + out, err = check_output([envpy, '-c', + 'from multiprocessing import Pool; ' + 'pool = Pool(1); ' + 'print(pool.apply_async("Python".lower).get(3)); ' + 'pool.terminate()']) + self.assertEqual(out.strip(), "python".encode()) + + @unittest.skipIf(os.name == 'nt', 'not relevant on Windows') + def test_deactivate_with_strict_bash_opts(self): + bash = shutil.which("bash") + if bash is None: + self.skipTest("bash required for this test") + rmtree(self.env_dir) + builder = venv.EnvBuilder(clear=True) + builder.create(self.env_dir) + activate = os.path.join(self.env_dir, self.bindir, "activate") + test_script = os.path.join(self.env_dir, "test_strict.sh") + with open(test_script, "w") as f: + f.write("set -euo pipefail\n" + f"source {activate}\n" + "deactivate\n") + out, err = check_output([bash, test_script]) + self.assertEqual(out, "".encode()) + self.assertEqual(err, "".encode()) + + + @unittest.skipUnless(sys.platform == 'darwin', 'only relevant on macOS') + def test_macos_env(self): + rmtree(self.env_dir) + builder = venv.EnvBuilder() + builder.create(self.env_dir) + + envpy = os.path.join(os.path.realpath(self.env_dir), + self.bindir, self.exe) + out, err = check_output([envpy, '-c', + 'import os; print("__PYVENV_LAUNCHER__" in os.environ)']) + self.assertEqual(out.strip(), 'False'.encode()) + +@requireVenvCreate +class EnsurePipTest(BaseTest): + """Test venv module installation of pip.""" + def assert_pip_not_installed(self): + envpy = os.path.join(os.path.realpath(self.env_dir), + self.bindir, self.exe) + out, err = check_output([envpy, '-c', + 'try:\n import pip\nexcept ImportError:\n print("OK")']) + # We force everything to text, so unittest gives the detailed diff + # if we get unexpected results + err = err.decode("latin-1") # Force to text, prevent decoding errors + self.assertEqual(err, "") + out = out.decode("latin-1") # Force to text, prevent decoding errors + self.assertEqual(out.strip(), "OK") + + + def test_no_pip_by_default(self): + rmtree(self.env_dir) + self.run_with_capture(venv.create, self.env_dir) + self.assert_pip_not_installed() + + def test_explicit_no_pip(self): + rmtree(self.env_dir) + self.run_with_capture(venv.create, self.env_dir, with_pip=False) + self.assert_pip_not_installed() + + def test_devnull(self): + # Fix for issue #20053 uses os.devnull to force a config file to + # appear empty. However http://bugs.python.org/issue20541 means + # that doesn't currently work properly on Windows. Once that is + # fixed, the "win_location" part of test_with_pip should be restored + with open(os.devnull, "rb") as f: + self.assertEqual(f.read(), b"") + + self.assertTrue(os.path.exists(os.devnull)) + + def do_test_with_pip(self, system_site_packages): + rmtree(self.env_dir) + with EnvironmentVarGuard() as envvars: + # pip's cross-version compatibility may trigger deprecation + # warnings in current versions of Python. Ensure related + # environment settings don't cause venv to fail. + envvars["PYTHONWARNINGS"] = "e" + # ensurepip is different enough from a normal pip invocation + # that we want to ensure it ignores the normal pip environment + # variable settings. We set PIP_NO_INSTALL here specifically + # to check that ensurepip (and hence venv) ignores it. + # See http://bugs.python.org/issue19734 + envvars["PIP_NO_INSTALL"] = "1" + # Also check that we ignore the pip configuration file + # See http://bugs.python.org/issue20053 + with tempfile.TemporaryDirectory() as home_dir: + envvars["HOME"] = home_dir + bad_config = "[global]\nno-install=1" + # Write to both config file names on all platforms to reduce + # cross-platform variation in test code behaviour + win_location = ("pip", "pip.ini") + posix_location = (".pip", "pip.conf") + # Skips win_location due to http://bugs.python.org/issue20541 + for dirname, fname in (posix_location,): + dirpath = os.path.join(home_dir, dirname) + os.mkdir(dirpath) + fpath = os.path.join(dirpath, fname) + with open(fpath, 'w') as f: + f.write(bad_config) + + # Actually run the create command with all that unhelpful + # config in place to ensure we ignore it + try: + self.run_with_capture(venv.create, self.env_dir, + system_site_packages=system_site_packages, + with_pip=True) + except subprocess.CalledProcessError as exc: + # The output this produces can be a little hard to read, + # but at least it has all the details + details = exc.output.decode(errors="replace") + msg = "{}\n\n**Subprocess Output**\n{}" + self.fail(msg.format(exc, details)) + # Ensure pip is available in the virtual environment + envpy = os.path.join(os.path.realpath(self.env_dir), self.bindir, self.exe) + # Ignore DeprecationWarning since pip code is not part of Python + out, err = check_output([envpy, '-W', 'ignore::DeprecationWarning', '-I', + '-m', 'pip', '--version']) + # We force everything to text, so unittest gives the detailed diff + # if we get unexpected results + err = err.decode("latin-1") # Force to text, prevent decoding errors + self.assertEqual(err, "") + out = out.decode("latin-1") # Force to text, prevent decoding errors + expected_version = "pip {}".format(ensurepip.version()) + self.assertEqual(out[:len(expected_version)], expected_version) + env_dir = os.fsencode(self.env_dir).decode("latin-1") + self.assertIn(env_dir, out) + + # http://bugs.python.org/issue19728 + # Check the private uninstall command provided for the Windows + # installers works (at least in a virtual environment) + with EnvironmentVarGuard() as envvars: + out, err = check_output([envpy, + '-W', 'ignore::DeprecationWarning', '-I', + '-m', 'ensurepip._uninstall']) + # We force everything to text, so unittest gives the detailed diff + # if we get unexpected results + err = err.decode("latin-1") # Force to text, prevent decoding errors + # Ignore the warning: + # "The directory '$HOME/.cache/pip/http' or its parent directory + # is not owned by the current user and the cache has been disabled. + # Please check the permissions and owner of that directory. If + # executing pip with sudo, you may want sudo's -H flag." + # where $HOME is replaced by the HOME environment variable. + err = re.sub("^(WARNING: )?The directory .* or its parent directory " + "is not owned or is not writable by the current user.*$", "", + err, flags=re.MULTILINE) + self.assertEqual(err.rstrip(), "") + # Being fairly specific regarding the expected behaviour for the + # initial bundling phase in Python 3.4. If the output changes in + # future pip versions, this test can likely be relaxed further. + out = out.decode("latin-1") # Force to text, prevent decoding errors + self.assertIn("Successfully uninstalled pip", out) + self.assertIn("Successfully uninstalled setuptools", out) + # Check pip is now gone from the virtual environment. This only + # applies in the system_site_packages=False case, because in the + # other case, pip may still be available in the system site-packages + if not system_site_packages: + self.assert_pip_not_installed() + + # Issue #26610: pip/pep425tags.py requires ctypes + @unittest.skipUnless(ctypes, 'pip requires ctypes') + @requires_zlib() + def test_with_pip(self): + self.do_test_with_pip(False) + self.do_test_with_pip(True) + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/venv/__init__.py b/Lib/venv/__init__.py new file mode 100644 index 000000000..8009deb3e --- /dev/null +++ b/Lib/venv/__init__.py @@ -0,0 +1,499 @@ +""" +Virtual environment (venv) package for Python. Based on PEP 405. + +Copyright (C) 2011-2014 Vinay Sajip. +Licensed to the PSF under a contributor agreement. +""" +import logging +import os +import shutil +import subprocess +import sys +import sysconfig +import types + + +CORE_VENV_DEPS = ('pip', 'setuptools') +logger = logging.getLogger(__name__) + + +class EnvBuilder: + """ + This class exists to allow virtual environment creation to be + customized. The constructor parameters determine the builder's + behaviour when called upon to create a virtual environment. + + By default, the builder makes the system (global) site-packages dir + *un*available to the created environment. + + If invoked using the Python -m option, the default is to use copying + on Windows platforms but symlinks elsewhere. If instantiated some + other way, the default is to *not* use symlinks. + + :param system_site_packages: If True, the system (global) site-packages + dir is available to created environments. + :param clear: If True, delete the contents of the environment directory if + it already exists, before environment creation. + :param symlinks: If True, attempt to symlink rather than copy files into + virtual environment. + :param upgrade: If True, upgrade an existing virtual environment. + :param with_pip: If True, ensure pip is installed in the virtual + environment + :param prompt: Alternative terminal prefix for the environment. + :param upgrade_deps: Update the base venv modules to the latest on PyPI + """ + + def __init__(self, system_site_packages=False, clear=False, + symlinks=False, upgrade=False, with_pip=False, prompt=None, + upgrade_deps=False): + self.system_site_packages = system_site_packages + self.clear = clear + self.symlinks = symlinks + self.upgrade = upgrade + self.with_pip = with_pip + if prompt == '.': # see bpo-38901 + prompt = os.path.basename(os.getcwd()) + self.prompt = prompt + self.upgrade_deps = upgrade_deps + + def create(self, env_dir): + """ + Create a virtual environment in a directory. + + :param env_dir: The target directory to create an environment in. + + """ + env_dir = os.path.abspath(env_dir) + context = self.ensure_directories(env_dir) + # See issue 24875. We need system_site_packages to be False + # until after pip is installed. + true_system_site_packages = self.system_site_packages + self.system_site_packages = False + self.create_configuration(context) + self.setup_python(context) + if self.with_pip: + self._setup_pip(context) + if not self.upgrade: + self.setup_scripts(context) + self.post_setup(context) + if true_system_site_packages: + # We had set it to False before, now + # restore it and rewrite the configuration + self.system_site_packages = True + self.create_configuration(context) + if self.upgrade_deps: + self.upgrade_dependencies(context) + + def clear_directory(self, path): + for fn in os.listdir(path): + fn = os.path.join(path, fn) + if os.path.islink(fn) or os.path.isfile(fn): + os.remove(fn) + elif os.path.isdir(fn): + shutil.rmtree(fn) + + def ensure_directories(self, env_dir): + """ + Create the directories for the environment. + + Returns a context object which holds paths in the environment, + for use by subsequent logic. + """ + + def create_if_needed(d): + if not os.path.exists(d): + os.makedirs(d) + elif os.path.islink(d) or os.path.isfile(d): + raise ValueError('Unable to create directory %r' % d) + + if os.path.exists(env_dir) and self.clear: + self.clear_directory(env_dir) + context = types.SimpleNamespace() + context.env_dir = env_dir + context.env_name = os.path.split(env_dir)[1] + prompt = self.prompt if self.prompt is not None else context.env_name + context.prompt = '(%s) ' % prompt + create_if_needed(env_dir) + executable = sys._base_executable + dirname, exename = os.path.split(os.path.abspath(executable)) + context.executable = executable + context.python_dir = dirname + context.python_exe = exename + if sys.platform == 'win32': + binname = 'Scripts' + incpath = 'Include' + libpath = os.path.join(env_dir, 'Lib', 'site-packages') + else: + binname = 'bin' + incpath = 'include' + libpath = os.path.join(env_dir, 'lib', + 'python%d.%d' % sys.version_info[:2], + 'site-packages') + context.inc_path = path = os.path.join(env_dir, incpath) + create_if_needed(path) + create_if_needed(libpath) + # Issue 21197: create lib64 as a symlink to lib on 64-bit non-OS X POSIX + if ((sys.maxsize > 2**32) and (os.name == 'posix') and + (sys.platform != 'darwin')): + link_path = os.path.join(env_dir, 'lib64') + if not os.path.exists(link_path): # Issue #21643 + os.symlink('lib', link_path) + context.bin_path = binpath = os.path.join(env_dir, binname) + context.bin_name = binname + context.env_exe = os.path.join(binpath, exename) + create_if_needed(binpath) + return context + + def create_configuration(self, context): + """ + Create a configuration file indicating where the environment's Python + was copied from, and whether the system site-packages should be made + available in the environment. + + :param context: The information for the environment creation request + being processed. + """ + context.cfg_path = path = os.path.join(context.env_dir, 'pyvenv.cfg') + with open(path, 'w', encoding='utf-8') as f: + f.write('home = %s\n' % context.python_dir) + if self.system_site_packages: + incl = 'true' + else: + incl = 'false' + f.write('include-system-site-packages = %s\n' % incl) + f.write('version = %d.%d.%d\n' % sys.version_info[:3]) + if self.prompt is not None: + f.write(f'prompt = {self.prompt!r}\n') + + if os.name != 'nt': + def symlink_or_copy(self, src, dst, relative_symlinks_ok=False): + """ + Try symlinking a file, and if that fails, fall back to copying. + """ + force_copy = not self.symlinks + if not force_copy: + try: + if not os.path.islink(dst): # can't link to itself! + if relative_symlinks_ok: + assert os.path.dirname(src) == os.path.dirname(dst) + os.symlink(os.path.basename(src), dst) + else: + os.symlink(src, dst) + except Exception: # may need to use a more specific exception + logger.warning('Unable to symlink %r to %r', src, dst) + force_copy = True + if force_copy: + shutil.copyfile(src, dst) + else: + def symlink_or_copy(self, src, dst, relative_symlinks_ok=False): + """ + Try symlinking a file, and if that fails, fall back to copying. + """ + bad_src = os.path.lexists(src) and not os.path.exists(src) + if self.symlinks and not bad_src and not os.path.islink(dst): + try: + if relative_symlinks_ok: + assert os.path.dirname(src) == os.path.dirname(dst) + os.symlink(os.path.basename(src), dst) + else: + os.symlink(src, dst) + return + except Exception: # may need to use a more specific exception + logger.warning('Unable to symlink %r to %r', src, dst) + + # On Windows, we rewrite symlinks to our base python.exe into + # copies of venvlauncher.exe + basename, ext = os.path.splitext(os.path.basename(src)) + srcfn = os.path.join(os.path.dirname(__file__), + "scripts", + "nt", + basename + ext) + # Builds or venv's from builds need to remap source file + # locations, as we do not put them into Lib/venv/scripts + if sysconfig.is_python_build(True) or not os.path.isfile(srcfn): + if basename.endswith('_d'): + ext = '_d' + ext + basename = basename[:-2] + if basename == 'python': + basename = 'venvlauncher' + elif basename == 'pythonw': + basename = 'venvwlauncher' + src = os.path.join(os.path.dirname(src), basename + ext) + else: + src = srcfn + if not os.path.exists(src): + if not bad_src: + logger.warning('Unable to copy %r', src) + return + + shutil.copyfile(src, dst) + + def setup_python(self, context): + """ + Set up a Python executable in the environment. + + :param context: The information for the environment creation request + being processed. + """ + binpath = context.bin_path + path = context.env_exe + copier = self.symlink_or_copy + dirname = context.python_dir + if os.name != 'nt': + copier(context.executable, path) + if not os.path.islink(path): + os.chmod(path, 0o755) + for suffix in ('python', 'python3', f'python3.{sys.version_info[1]}'): + path = os.path.join(binpath, suffix) + if not os.path.exists(path): + # Issue 18807: make copies if + # symlinks are not wanted + copier(context.env_exe, path, relative_symlinks_ok=True) + if not os.path.islink(path): + os.chmod(path, 0o755) + else: + if self.symlinks: + # For symlinking, we need a complete copy of the root directory + # If symlinks fail, you'll get unnecessary copies of files, but + # we assume that if you've opted into symlinks on Windows then + # you know what you're doing. + suffixes = [ + f for f in os.listdir(dirname) if + os.path.normcase(os.path.splitext(f)[1]) in ('.exe', '.dll') + ] + if sysconfig.is_python_build(True): + suffixes = [ + f for f in suffixes if + os.path.normcase(f).startswith(('python', 'vcruntime')) + ] + else: + suffixes = ['python.exe', 'python_d.exe', 'pythonw.exe', + 'pythonw_d.exe'] + + for suffix in suffixes: + src = os.path.join(dirname, suffix) + if os.path.lexists(src): + copier(src, os.path.join(binpath, suffix)) + + if sysconfig.is_python_build(True): + # copy init.tcl + for root, dirs, files in os.walk(context.python_dir): + if 'init.tcl' in files: + tcldir = os.path.basename(root) + tcldir = os.path.join(context.env_dir, 'Lib', tcldir) + if not os.path.exists(tcldir): + os.makedirs(tcldir) + src = os.path.join(root, 'init.tcl') + dst = os.path.join(tcldir, 'init.tcl') + shutil.copyfile(src, dst) + break + + def _setup_pip(self, context): + """Installs or upgrades pip in a virtual environment""" + # We run ensurepip in isolated mode to avoid side effects from + # environment vars, the current directory and anything else + # intended for the global Python environment + cmd = [context.env_exe, '-Im', 'ensurepip', '--upgrade', + '--default-pip'] + subprocess.check_output(cmd, stderr=subprocess.STDOUT) + + def setup_scripts(self, context): + """ + Set up scripts into the created environment from a directory. + + This method installs the default scripts into the environment + being created. You can prevent the default installation by overriding + this method if you really need to, or if you need to specify + a different location for the scripts to install. By default, the + 'scripts' directory in the venv package is used as the source of + scripts to install. + """ + path = os.path.abspath(os.path.dirname(__file__)) + path = os.path.join(path, 'scripts') + self.install_scripts(context, path) + + def post_setup(self, context): + """ + Hook for post-setup modification of the venv. Subclasses may install + additional packages or scripts here, add activation shell scripts, etc. + + :param context: The information for the environment creation request + being processed. + """ + pass + + def replace_variables(self, text, context): + """ + Replace variable placeholders in script text with context-specific + variables. + + Return the text passed in , but with variables replaced. + + :param text: The text in which to replace placeholder variables. + :param context: The information for the environment creation request + being processed. + """ + text = text.replace('__VENV_DIR__', context.env_dir) + text = text.replace('__VENV_NAME__', context.env_name) + text = text.replace('__VENV_PROMPT__', context.prompt) + text = text.replace('__VENV_BIN_NAME__', context.bin_name) + text = text.replace('__VENV_PYTHON__', context.env_exe) + return text + + def install_scripts(self, context, path): + """ + Install scripts into the created environment from a directory. + + :param context: The information for the environment creation request + being processed. + :param path: Absolute pathname of a directory containing script. + Scripts in the 'common' subdirectory of this directory, + and those in the directory named for the platform + being run on, are installed in the created environment. + Placeholder variables are replaced with environment- + specific values. + """ + binpath = context.bin_path + plen = len(path) + for root, dirs, files in os.walk(path): + if root == path: # at top-level, remove irrelevant dirs + for d in dirs[:]: + if d not in ('common', os.name): + dirs.remove(d) + continue # ignore files in top level + for f in files: + if (os.name == 'nt' and f.startswith('python') + and f.endswith(('.exe', '.pdb'))): + continue + srcfile = os.path.join(root, f) + suffix = root[plen:].split(os.sep)[2:] + if not suffix: + dstdir = binpath + else: + dstdir = os.path.join(binpath, *suffix) + if not os.path.exists(dstdir): + os.makedirs(dstdir) + dstfile = os.path.join(dstdir, f) + with open(srcfile, 'rb') as f: + data = f.read() + if not srcfile.endswith(('.exe', '.pdb')): + try: + data = data.decode('utf-8') + data = self.replace_variables(data, context) + data = data.encode('utf-8') + except UnicodeError as e: + data = None + logger.warning('unable to copy script %r, ' + 'may be binary: %s', srcfile, e) + if data is not None: + with open(dstfile, 'wb') as f: + f.write(data) + shutil.copymode(srcfile, dstfile) + + def upgrade_dependencies(self, context): + logger.debug( + f'Upgrading {CORE_VENV_DEPS} packages in {context.bin_path}' + ) + if sys.platform == 'win32': + python_exe = os.path.join(context.bin_path, 'python.exe') + else: + python_exe = os.path.join(context.bin_path, 'python') + cmd = [python_exe, '-m', 'pip', 'install', '--upgrade'] + cmd.extend(CORE_VENV_DEPS) + subprocess.check_call(cmd) + + +def create(env_dir, system_site_packages=False, clear=False, + symlinks=False, with_pip=False, prompt=None, upgrade_deps=False): + """Create a virtual environment in a directory.""" + builder = EnvBuilder(system_site_packages=system_site_packages, + clear=clear, symlinks=symlinks, with_pip=with_pip, + prompt=prompt, upgrade_deps=upgrade_deps) + builder.create(env_dir) + +def main(args=None): + compatible = True + if sys.version_info < (3, 3): + compatible = False + elif not hasattr(sys, 'base_prefix'): + compatible = False + if not compatible: + raise ValueError('This script is only for use with Python >= 3.3') + else: + import argparse + + parser = argparse.ArgumentParser(prog=__name__, + description='Creates virtual Python ' + 'environments in one or ' + 'more target ' + 'directories.', + epilog='Once an environment has been ' + 'created, you may wish to ' + 'activate it, e.g. by ' + 'sourcing an activate script ' + 'in its bin directory.') + parser.add_argument('dirs', metavar='ENV_DIR', nargs='+', + help='A directory to create the environment in.') + parser.add_argument('--system-site-packages', default=False, + action='store_true', dest='system_site', + help='Give the virtual environment access to the ' + 'system site-packages dir.') + if os.name == 'nt': + use_symlinks = False + else: + use_symlinks = True + group = parser.add_mutually_exclusive_group() + group.add_argument('--symlinks', default=use_symlinks, + action='store_true', dest='symlinks', + help='Try to use symlinks rather than copies, ' + 'when symlinks are not the default for ' + 'the platform.') + group.add_argument('--copies', default=not use_symlinks, + action='store_false', dest='symlinks', + help='Try to use copies rather than symlinks, ' + 'even when symlinks are the default for ' + 'the platform.') + parser.add_argument('--clear', default=False, action='store_true', + dest='clear', help='Delete the contents of the ' + 'environment directory if it ' + 'already exists, before ' + 'environment creation.') + parser.add_argument('--upgrade', default=False, action='store_true', + dest='upgrade', help='Upgrade the environment ' + 'directory to use this version ' + 'of Python, assuming Python ' + 'has been upgraded in-place.') + parser.add_argument('--without-pip', dest='with_pip', + default=True, action='store_false', + help='Skips installing or upgrading pip in the ' + 'virtual environment (pip is bootstrapped ' + 'by default)') + parser.add_argument('--prompt', + help='Provides an alternative prompt prefix for ' + 'this environment.') + parser.add_argument('--upgrade-deps', default=False, action='store_true', + dest='upgrade_deps', + help='Upgrade core dependencies: {} to the latest ' + 'version in PyPI'.format( + ' '.join(CORE_VENV_DEPS))) + options = parser.parse_args(args) + if options.upgrade and options.clear: + raise ValueError('you cannot supply --upgrade and --clear together.') + builder = EnvBuilder(system_site_packages=options.system_site, + clear=options.clear, + symlinks=options.symlinks, + upgrade=options.upgrade, + with_pip=options.with_pip, + prompt=options.prompt, + upgrade_deps=options.upgrade_deps) + for d in options.dirs: + builder.create(d) + +if __name__ == '__main__': + rc = 1 + try: + main() + rc = 0 + except Exception as e: + print('Error: %s' % e, file=sys.stderr) + sys.exit(rc) diff --git a/Lib/venv/__main__.py b/Lib/venv/__main__.py new file mode 100644 index 000000000..912423e4a --- /dev/null +++ b/Lib/venv/__main__.py @@ -0,0 +1,10 @@ +import sys +from . import main + +rc = 1 +try: + main() + rc = 0 +except Exception as e: + print('Error: %s' % e, file=sys.stderr) +sys.exit(rc) diff --git a/Lib/venv/scripts/common/Activate.ps1 b/Lib/venv/scripts/common/Activate.ps1 new file mode 100644 index 000000000..2fb3852c3 --- /dev/null +++ b/Lib/venv/scripts/common/Activate.ps1 @@ -0,0 +1,241 @@ +<# +.Synopsis +Activate a Python virtual environment for the current PowerShell session. + +.Description +Pushes the python executable for a virtual environment to the front of the +$Env:PATH environment variable and sets the prompt to signify that you are +in a Python virtual environment. Makes use of the command line switches as +well as the `pyvenv.cfg` file values present in the virtual environment. + +.Parameter VenvDir +Path to the directory that contains the virtual environment to activate. The +default value for this is the parent of the directory that the Activate.ps1 +script is located within. + +.Parameter Prompt +The prompt prefix to display when this virtual environment is activated. By +default, this prompt is the name of the virtual environment folder (VenvDir) +surrounded by parentheses and followed by a single space (ie. '(.venv) '). + +.Example +Activate.ps1 +Activates the Python virtual environment that contains the Activate.ps1 script. + +.Example +Activate.ps1 -Verbose +Activates the Python virtual environment that contains the Activate.ps1 script, +and shows extra information about the activation as it executes. + +.Example +Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv +Activates the Python virtual environment located in the specified location. + +.Example +Activate.ps1 -Prompt "MyPython" +Activates the Python virtual environment that contains the Activate.ps1 script, +and prefixes the current prompt with the specified string (surrounded in +parentheses) while the virtual environment is active. + +.Notes +On Windows, it may be required to enable this Activate.ps1 script by setting the +execution policy for the user. You can do this by issuing the following PowerShell +command: + +PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser + +For more information on Execution Policies: +https://go.microsoft.com/fwlink/?LinkID=135170 + +#> +Param( + [Parameter(Mandatory = $false)] + [String] + $VenvDir, + [Parameter(Mandatory = $false)] + [String] + $Prompt +) + +<# Function declarations --------------------------------------------------- #> + +<# +.Synopsis +Remove all shell session elements added by the Activate script, including the +addition of the virtual environment's Python executable from the beginning of +the PATH variable. + +.Parameter NonDestructive +If present, do not remove this function from the global namespace for the +session. + +#> +function global:deactivate ([switch]$NonDestructive) { + # Revert to original values + + # The prior prompt: + if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) { + Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt + Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT + } + + # The prior PYTHONHOME: + if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) { + Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME + Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME + } + + # The prior PATH: + if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) { + Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH + Remove-Item -Path Env:_OLD_VIRTUAL_PATH + } + + # Just remove the VIRTUAL_ENV altogether: + if (Test-Path -Path Env:VIRTUAL_ENV) { + Remove-Item -Path env:VIRTUAL_ENV + } + + # Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether: + if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) { + Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force + } + + # Leave deactivate function in the global namespace if requested: + if (-not $NonDestructive) { + Remove-Item -Path function:deactivate + } +} + +<# +.Description +Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the +given folder, and returns them in a map. + +For each line in the pyvenv.cfg file, if that line can be parsed into exactly +two strings separated by `=` (with any amount of whitespace surrounding the =) +then it is considered a `key = value` line. The left hand string is the key, +the right hand is the value. + +If the value starts with a `'` or a `"` then the first and last character is +stripped from the value before being captured. + +.Parameter ConfigDir +Path to the directory that contains the `pyvenv.cfg` file. +#> +function Get-PyVenvConfig( + [String] + $ConfigDir +) { + Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg" + + # Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue). + $pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue + + # An empty map will be returned if no config file is found. + $pyvenvConfig = @{ } + + if ($pyvenvConfigPath) { + + Write-Verbose "File exists, parse `key = value` lines" + $pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath + + $pyvenvConfigContent | ForEach-Object { + $keyval = $PSItem -split "\s*=\s*", 2 + if ($keyval[0] -and $keyval[1]) { + $val = $keyval[1] + + # Remove extraneous quotations around a string value. + if ("'""".Contains($val.Substring(0, 1))) { + $val = $val.Substring(1, $val.Length - 2) + } + + $pyvenvConfig[$keyval[0]] = $val + Write-Verbose "Adding Key: '$($keyval[0])'='$val'" + } + } + } + return $pyvenvConfig +} + + +<# Begin Activate script --------------------------------------------------- #> + +# Determine the containing directory of this script +$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition +$VenvExecDir = Get-Item -Path $VenvExecPath + +Write-Verbose "Activation script is located in path: '$VenvExecPath'" +Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)" +Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)" + +# Set values required in priority: CmdLine, ConfigFile, Default +# First, get the location of the virtual environment, it might not be +# VenvExecDir if specified on the command line. +if ($VenvDir) { + Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values" +} +else { + Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir." + $VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/") + Write-Verbose "VenvDir=$VenvDir" +} + +# Next, read the `pyvenv.cfg` file to determine any required value such +# as `prompt`. +$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir + +# Next, set the prompt from the command line, or the config file, or +# just use the name of the virtual environment folder. +if ($Prompt) { + Write-Verbose "Prompt specified as argument, using '$Prompt'" +} +else { + Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value" + if ($pyvenvCfg -and $pyvenvCfg['prompt']) { + Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'" + $Prompt = $pyvenvCfg['prompt']; + } + else { + Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virutal environment)" + Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'" + $Prompt = Split-Path -Path $venvDir -Leaf + } +} + +Write-Verbose "Prompt = '$Prompt'" +Write-Verbose "VenvDir='$VenvDir'" + +# Deactivate any currently active virtual environment, but leave the +# deactivate function in place. +deactivate -nondestructive + +# Now set the environment variable VIRTUAL_ENV, used by many tools to determine +# that there is an activated venv. +$env:VIRTUAL_ENV = $VenvDir + +if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) { + + Write-Verbose "Setting prompt to '$Prompt'" + + # Set the prompt to include the env name + # Make sure _OLD_VIRTUAL_PROMPT is global + function global:_OLD_VIRTUAL_PROMPT { "" } + Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT + New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt + + function global:prompt { + Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) " + _OLD_VIRTUAL_PROMPT + } +} + +# Clear PYTHONHOME +if (Test-Path -Path Env:PYTHONHOME) { + Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME + Remove-Item -Path Env:PYTHONHOME +} + +# Add the venv to the PATH +Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH +$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH" diff --git a/Lib/venv/scripts/common/activate b/Lib/venv/scripts/common/activate new file mode 100644 index 000000000..45af3536a --- /dev/null +++ b/Lib/venv/scripts/common/activate @@ -0,0 +1,66 @@ +# This file must be used with "source bin/activate" *from bash* +# you cannot run it directly + +deactivate () { + # reset old environment variables + if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then + PATH="${_OLD_VIRTUAL_PATH:-}" + export PATH + unset _OLD_VIRTUAL_PATH + fi + if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then + PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}" + export PYTHONHOME + unset _OLD_VIRTUAL_PYTHONHOME + fi + + # This should detect bash and zsh, which have a hash command that must + # be called to get it to forget past commands. Without forgetting + # past commands the $PATH changes we made may not be respected + if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then + hash -r 2> /dev/null + fi + + if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then + PS1="${_OLD_VIRTUAL_PS1:-}" + export PS1 + unset _OLD_VIRTUAL_PS1 + fi + + unset VIRTUAL_ENV + if [ ! "${1:-}" = "nondestructive" ] ; then + # Self destruct! + unset -f deactivate + fi +} + +# unset irrelevant variables +deactivate nondestructive + +VIRTUAL_ENV="__VENV_DIR__" +export VIRTUAL_ENV + +_OLD_VIRTUAL_PATH="$PATH" +PATH="$VIRTUAL_ENV/__VENV_BIN_NAME__:$PATH" +export PATH + +# unset PYTHONHOME if set +# this will fail if PYTHONHOME is set to the empty string (which is bad anyway) +# could use `if (set -u; : $PYTHONHOME) ;` in bash +if [ -n "${PYTHONHOME:-}" ] ; then + _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}" + unset PYTHONHOME +fi + +if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then + _OLD_VIRTUAL_PS1="${PS1:-}" + PS1="__VENV_PROMPT__${PS1:-}" + export PS1 +fi + +# This should detect bash and zsh, which have a hash command that must +# be called to get it to forget past commands. Without forgetting +# past commands the $PATH changes we made may not be respected +if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then + hash -r 2> /dev/null +fi diff --git a/Lib/venv/scripts/nt/activate.bat b/Lib/venv/scripts/nt/activate.bat new file mode 100644 index 000000000..f61413e23 --- /dev/null +++ b/Lib/venv/scripts/nt/activate.bat @@ -0,0 +1,33 @@ +@echo off + +rem This file is UTF-8 encoded, so we need to update the current code page while executing it +for /f "tokens=2 delims=:." %%a in ('"%SystemRoot%\System32\chcp.com"') do ( + set _OLD_CODEPAGE=%%a +) +if defined _OLD_CODEPAGE ( + "%SystemRoot%\System32\chcp.com" 65001 > nul +) + +set VIRTUAL_ENV=__VENV_DIR__ + +if not defined PROMPT set PROMPT=$P$G + +if defined _OLD_VIRTUAL_PROMPT set PROMPT=%_OLD_VIRTUAL_PROMPT% +if defined _OLD_VIRTUAL_PYTHONHOME set PYTHONHOME=%_OLD_VIRTUAL_PYTHONHOME% + +set _OLD_VIRTUAL_PROMPT=%PROMPT% +set PROMPT=__VENV_PROMPT__%PROMPT% + +if defined PYTHONHOME set _OLD_VIRTUAL_PYTHONHOME=%PYTHONHOME% +set PYTHONHOME= + +if defined _OLD_VIRTUAL_PATH set PATH=%_OLD_VIRTUAL_PATH% +if not defined _OLD_VIRTUAL_PATH set _OLD_VIRTUAL_PATH=%PATH% + +set PATH=%VIRTUAL_ENV%\__VENV_BIN_NAME__;%PATH% + +:END +if defined _OLD_CODEPAGE ( + "%SystemRoot%\System32\chcp.com" %_OLD_CODEPAGE% > nul + set _OLD_CODEPAGE= +) diff --git a/Lib/venv/scripts/nt/deactivate.bat b/Lib/venv/scripts/nt/deactivate.bat new file mode 100644 index 000000000..313c07911 --- /dev/null +++ b/Lib/venv/scripts/nt/deactivate.bat @@ -0,0 +1,21 @@ +@echo off + +if defined _OLD_VIRTUAL_PROMPT ( + set "PROMPT=%_OLD_VIRTUAL_PROMPT%" +) +set _OLD_VIRTUAL_PROMPT= + +if defined _OLD_VIRTUAL_PYTHONHOME ( + set "PYTHONHOME=%_OLD_VIRTUAL_PYTHONHOME%" + set _OLD_VIRTUAL_PYTHONHOME= +) + +if defined _OLD_VIRTUAL_PATH ( + set "PATH=%_OLD_VIRTUAL_PATH%" +) + +set _OLD_VIRTUAL_PATH= + +set VIRTUAL_ENV= + +:END diff --git a/Lib/venv/scripts/posix/activate.csh b/Lib/venv/scripts/posix/activate.csh new file mode 100644 index 000000000..68a0dc74e --- /dev/null +++ b/Lib/venv/scripts/posix/activate.csh @@ -0,0 +1,25 @@ +# This file must be used with "source bin/activate.csh" *from csh*. +# You cannot run it directly. +# Created by Davide Di Blasi . +# Ported to Python 3.3 venv by Andrew Svetlov + +alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; test "\!:*" != "nondestructive" && unalias deactivate' + +# Unset irrelevant variables. +deactivate nondestructive + +setenv VIRTUAL_ENV "__VENV_DIR__" + +set _OLD_VIRTUAL_PATH="$PATH" +setenv PATH "$VIRTUAL_ENV/__VENV_BIN_NAME__:$PATH" + + +set _OLD_VIRTUAL_PROMPT="$prompt" + +if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then + set prompt = "__VENV_PROMPT__$prompt" +endif + +alias pydoc python -m pydoc + +rehash diff --git a/Lib/venv/scripts/posix/activate.fish b/Lib/venv/scripts/posix/activate.fish new file mode 100644 index 000000000..54b9ea567 --- /dev/null +++ b/Lib/venv/scripts/posix/activate.fish @@ -0,0 +1,64 @@ +# This file must be used with "source /bin/activate.fish" *from fish* +# (https://fishshell.com/); you cannot run it directly. + +function deactivate -d "Exit virtual environment and return to normal shell environment" + # reset old environment variables + if test -n "$_OLD_VIRTUAL_PATH" + set -gx PATH $_OLD_VIRTUAL_PATH + set -e _OLD_VIRTUAL_PATH + end + if test -n "$_OLD_VIRTUAL_PYTHONHOME" + set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME + set -e _OLD_VIRTUAL_PYTHONHOME + end + + if test -n "$_OLD_FISH_PROMPT_OVERRIDE" + functions -e fish_prompt + set -e _OLD_FISH_PROMPT_OVERRIDE + functions -c _old_fish_prompt fish_prompt + functions -e _old_fish_prompt + end + + set -e VIRTUAL_ENV + if test "$argv[1]" != "nondestructive" + # Self-destruct! + functions -e deactivate + end +end + +# Unset irrelevant variables. +deactivate nondestructive + +set -gx VIRTUAL_ENV "__VENV_DIR__" + +set -gx _OLD_VIRTUAL_PATH $PATH +set -gx PATH "$VIRTUAL_ENV/__VENV_BIN_NAME__" $PATH + +# Unset PYTHONHOME if set. +if set -q PYTHONHOME + set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME + set -e PYTHONHOME +end + +if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" + # fish uses a function instead of an env var to generate the prompt. + + # Save the current fish_prompt function as the function _old_fish_prompt. + functions -c fish_prompt _old_fish_prompt + + # With the original prompt function renamed, we can override with our own. + function fish_prompt + # Save the return status of the last command. + set -l old_status $status + + # Output the venv prompt; color taken from the blue of the Python logo. + printf "%s%s%s" (set_color 4B8BBE) "__VENV_PROMPT__" (set_color normal) + + # Restore the return status of the previous command. + echo "exit $old_status" | . + # Output the original/"old" prompt. + _old_fish_prompt + end + + set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" +end