| 
									
										
										
										
											2012-05-26 20:36:12 +01:00
										 |  |  | """
 | 
					
						
							|  |  |  | Virtual environment (venv) package for Python. Based on PEP 405. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-04-15 11:18:10 +01:00
										 |  |  | Copyright (C) 2011-2014 Vinay Sajip. | 
					
						
							| 
									
										
										
										
											2012-06-25 00:47:46 +01:00
										 |  |  | Licensed to the PSF under a contributor agreement. | 
					
						
							| 
									
										
										
										
											2012-05-26 20:36:12 +01:00
										 |  |  | """
 | 
					
						
							| 
									
										
										
										
											2012-05-26 03:45:29 +01:00
										 |  |  | import logging | 
					
						
							|  |  |  | import os | 
					
						
							|  |  |  | import shutil | 
					
						
							| 
									
										
										
										
											2013-11-23 00:30:34 +10:00
										 |  |  | import subprocess | 
					
						
							| 
									
										
										
										
											2012-05-26 03:45:29 +01:00
										 |  |  | import sys | 
					
						
							| 
									
										
										
										
											2018-07-20 17:07:38 +01:00
										 |  |  | import sysconfig | 
					
						
							| 
									
										
										
										
											2013-07-12 21:10:19 +01:00
										 |  |  | import types | 
					
						
							| 
									
										
										
										
											2012-05-26 03:45:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-17 11:18:14 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-17 23:43:34 -05:00
										 |  |  | CORE_VENV_DEPS = ('pip',) | 
					
						
							| 
									
										
										
										
											2012-05-26 03:45:29 +01:00
										 |  |  | logger = logging.getLogger(__name__) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class EnvBuilder: | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     This class exists to allow virtual environment creation to be | 
					
						
							| 
									
										
										
										
											2013-11-15 20:58:13 +00:00
										 |  |  |     customized. The constructor parameters determine the builder's | 
					
						
							| 
									
										
										
										
											2012-05-26 03:45:29 +01:00
										 |  |  |     behaviour when called upon to create a virtual environment. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     By default, the builder makes the system (global) site-packages dir | 
					
						
							| 
									
										
										
										
											2012-11-14 11:18:35 +00:00
										 |  |  |     *un*available to the created environment. | 
					
						
							| 
									
										
										
										
											2012-05-26 03:45:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-14 11:18:35 +00:00
										 |  |  |     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. | 
					
						
							| 
									
										
										
										
											2012-05-26 03:45:29 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     :param system_site_packages: If True, the system (global) site-packages | 
					
						
							|  |  |  |                                  dir is available to created environments. | 
					
						
							| 
									
										
										
										
											2016-01-28 09:13:14 +02:00
										 |  |  |     :param clear: If True, delete the contents of the environment directory if | 
					
						
							|  |  |  |                   it already exists, before environment creation. | 
					
						
							| 
									
										
										
										
											2012-05-26 03:45:29 +01:00
										 |  |  |     :param symlinks: If True, attempt to symlink rather than copy files into | 
					
						
							|  |  |  |                      virtual environment. | 
					
						
							|  |  |  |     :param upgrade: If True, upgrade an existing virtual environment. | 
					
						
							| 
									
										
										
										
											2013-11-23 00:30:34 +10:00
										 |  |  |     :param with_pip: If True, ensure pip is installed in the virtual | 
					
						
							|  |  |  |                      environment | 
					
						
							| 
									
										
										
										
											2016-08-06 10:43:44 +01:00
										 |  |  |     :param prompt: Alternative terminal prefix for the environment. | 
					
						
							| 
									
										
										
										
											2019-06-17 11:18:14 -07:00
										 |  |  |     :param upgrade_deps: Update the base venv modules to the latest on PyPI | 
					
						
							| 
									
										
										
										
											2023-09-15 15:38:08 -07:00
										 |  |  |     :param scm_ignore_files: Create ignore files for the SCMs specified by the | 
					
						
							|  |  |  |                              iterable. | 
					
						
							| 
									
										
										
										
											2012-05-26 03:45:29 +01:00
										 |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, system_site_packages=False, clear=False, | 
					
						
							| 
									
										
										
										
											2019-06-17 11:18:14 -07:00
										 |  |  |                  symlinks=False, upgrade=False, with_pip=False, prompt=None, | 
					
						
							| 
									
										
										
										
											2023-09-15 15:38:08 -07:00
										 |  |  |                  upgrade_deps=False, *, scm_ignore_files=frozenset()): | 
					
						
							| 
									
										
										
										
											2012-05-26 03:45:29 +01:00
										 |  |  |         self.system_site_packages = system_site_packages | 
					
						
							|  |  |  |         self.clear = clear | 
					
						
							|  |  |  |         self.symlinks = symlinks | 
					
						
							|  |  |  |         self.upgrade = upgrade | 
					
						
							| 
									
										
										
										
											2013-11-23 00:30:34 +10:00
										 |  |  |         self.with_pip = with_pip | 
					
						
							| 
									
										
										
										
											2022-01-07 01:50:30 -05:00
										 |  |  |         self.orig_prompt = prompt | 
					
						
							| 
									
										
										
										
											2020-01-14 20:49:30 +00:00
										 |  |  |         if prompt == '.':  # see bpo-38901 | 
					
						
							|  |  |  |             prompt = os.path.basename(os.getcwd()) | 
					
						
							| 
									
										
										
										
											2016-08-06 10:43:44 +01:00
										 |  |  |         self.prompt = prompt | 
					
						
							| 
									
										
										
										
											2019-06-17 11:18:14 -07:00
										 |  |  |         self.upgrade_deps = upgrade_deps | 
					
						
							| 
									
										
										
										
											2023-09-15 15:38:08 -07:00
										 |  |  |         self.scm_ignore_files = frozenset(map(str.lower, scm_ignore_files)) | 
					
						
							| 
									
										
										
										
											2012-05-26 03:45:29 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     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) | 
					
						
							| 
									
										
										
										
											2023-09-15 15:38:08 -07:00
										 |  |  |         for scm in self.scm_ignore_files: | 
					
						
							|  |  |  |             getattr(self, f"create_{scm}_ignore_file")(context) | 
					
						
							| 
									
										
										
										
											2017-02-02 19:05:19 +00:00
										 |  |  |         # 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 | 
					
						
							| 
									
										
										
										
											2012-05-26 03:45:29 +01:00
										 |  |  |         self.create_configuration(context) | 
					
						
							|  |  |  |         self.setup_python(context) | 
					
						
							| 
									
										
										
										
											2013-11-23 00:30:34 +10:00
										 |  |  |         if self.with_pip: | 
					
						
							|  |  |  |             self._setup_pip(context) | 
					
						
							| 
									
										
										
										
											2012-05-26 03:45:29 +01:00
										 |  |  |         if not self.upgrade: | 
					
						
							| 
									
										
										
										
											2019-01-30 13:49:14 -08:00
										 |  |  |             self.setup_scripts(context) | 
					
						
							| 
									
										
										
										
											2012-05-26 03:45:29 +01:00
										 |  |  |             self.post_setup(context) | 
					
						
							| 
									
										
										
										
											2017-02-02 19:05:19 +00:00
										 |  |  |         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) | 
					
						
							| 
									
										
										
										
											2019-06-17 11:18:14 -07:00
										 |  |  |         if self.upgrade_deps: | 
					
						
							|  |  |  |             self.upgrade_dependencies(context) | 
					
						
							| 
									
										
										
										
											2012-05-26 03:45:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-11 17:22:45 +01:00
										 |  |  |     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) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-18 10:53:29 +01:00
										 |  |  |     def _venv_path(self, env_dir, name): | 
					
						
							|  |  |  |         vars = { | 
					
						
							|  |  |  |             'base': env_dir, | 
					
						
							|  |  |  |             'platbase': env_dir, | 
					
						
							|  |  |  |             'installed_base': env_dir, | 
					
						
							|  |  |  |             'installed_platbase': env_dir, | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return sysconfig.get_path(name, scheme='venv', vars=vars) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-15 15:36:06 +01:00
										 |  |  |     @classmethod | 
					
						
							|  |  |  |     def _same_path(cls, path1, path2): | 
					
						
							|  |  |  |         """Check whether two paths appear the same.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Whether they refer to the same file is irrelevant; we're testing for | 
					
						
							|  |  |  |         whether a human reader would look at the path string and easily tell | 
					
						
							|  |  |  |         that they're the same file. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         if sys.platform == 'win32': | 
					
						
							|  |  |  |             if os.path.normcase(path1) == os.path.normcase(path2): | 
					
						
							|  |  |  |                 return True | 
					
						
							|  |  |  |             # gh-90329: Don't display a warning for short/long names | 
					
						
							|  |  |  |             import _winapi | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 path1 = _winapi.GetLongPathName(os.fsdecode(path1)) | 
					
						
							|  |  |  |             except OSError: | 
					
						
							|  |  |  |                 pass | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 path2 = _winapi.GetLongPathName(os.fsdecode(path2)) | 
					
						
							|  |  |  |             except OSError: | 
					
						
							|  |  |  |                 pass | 
					
						
							|  |  |  |             if os.path.normcase(path1) == os.path.normcase(path2): | 
					
						
							|  |  |  |                 return True | 
					
						
							|  |  |  |             return False | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             return path1 == path2 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-26 03:45:29 +01:00
										 |  |  |     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) | 
					
						
							| 
									
										
										
										
											2012-10-11 17:22:45 +01:00
										 |  |  |             elif os.path.islink(d) or os.path.isfile(d): | 
					
						
							|  |  |  |                 raise ValueError('Unable to create directory %r' % d) | 
					
						
							| 
									
										
										
										
											2012-05-26 03:45:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-19 08:47:16 -06:00
										 |  |  |         if os.pathsep in os.fspath(env_dir): | 
					
						
							| 
									
										
										
										
											2022-04-13 04:07:10 -04:00
										 |  |  |             raise ValueError(f'Refusing to create a venv in {env_dir} because ' | 
					
						
							|  |  |  |                              f'it contains the PATH separator {os.pathsep}.') | 
					
						
							| 
									
										
										
										
											2012-05-26 03:45:29 +01:00
										 |  |  |         if os.path.exists(env_dir) and self.clear: | 
					
						
							| 
									
										
										
										
											2012-10-11 17:22:45 +01:00
										 |  |  |             self.clear_directory(env_dir) | 
					
						
							| 
									
										
										
										
											2013-07-12 21:10:19 +01:00
										 |  |  |         context = types.SimpleNamespace() | 
					
						
							| 
									
										
										
										
											2012-05-26 03:45:29 +01:00
										 |  |  |         context.env_dir = env_dir | 
					
						
							|  |  |  |         context.env_name = os.path.split(env_dir)[1] | 
					
						
							| 
									
										
										
										
											2024-01-23 00:53:04 -08:00
										 |  |  |         context.prompt = self.prompt if self.prompt is not None else context.env_name | 
					
						
							| 
									
										
										
										
											2012-05-26 03:45:29 +01:00
										 |  |  |         create_if_needed(env_dir) | 
					
						
							| 
									
										
										
										
											2019-06-29 10:34:11 -07:00
										 |  |  |         executable = sys._base_executable | 
					
						
							| 
									
										
										
										
											2022-09-17 14:58:31 +03:00
										 |  |  |         if not executable:  # see gh-96861 | 
					
						
							|  |  |  |             raise ValueError('Unable to determine path to the running ' | 
					
						
							|  |  |  |                              'Python interpreter. Provide an explicit path or ' | 
					
						
							|  |  |  |                              'check that your PATH environment variable is ' | 
					
						
							|  |  |  |                              'correctly set.') | 
					
						
							| 
									
										
										
										
											2012-05-26 03:45:29 +01:00
										 |  |  |         dirname, exename = os.path.split(os.path.abspath(executable)) | 
					
						
							| 
									
										
										
										
											2024-01-17 21:52:23 +00:00
										 |  |  |         if sys.platform == 'win32': | 
					
						
							|  |  |  |             # Always create the simplest name in the venv. It will either be a | 
					
						
							|  |  |  |             # link back to executable, or a copy of the appropriate launcher | 
					
						
							|  |  |  |             _d = '_d' if os.path.splitext(exename)[0].endswith('_d') else '' | 
					
						
							|  |  |  |             exename = f'python{_d}.exe' | 
					
						
							| 
									
										
										
										
											2012-05-26 03:45:29 +01:00
										 |  |  |         context.executable = executable | 
					
						
							|  |  |  |         context.python_dir = dirname | 
					
						
							|  |  |  |         context.python_exe = exename | 
					
						
							| 
									
										
										
										
											2022-03-18 10:53:29 +01:00
										 |  |  |         binpath = self._venv_path(env_dir, 'scripts') | 
					
						
							|  |  |  |         incpath = self._venv_path(env_dir, 'include') | 
					
						
							|  |  |  |         libpath = self._venv_path(env_dir, 'purelib') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         context.inc_path = incpath | 
					
						
							|  |  |  |         create_if_needed(incpath) | 
					
						
							| 
									
										
										
										
											2022-06-26 17:49:03 +01:00
										 |  |  |         context.lib_path = libpath | 
					
						
							| 
									
										
										
										
											2012-05-26 03:45:29 +01:00
										 |  |  |         create_if_needed(libpath) | 
					
						
							| 
									
										
										
										
											2014-04-15 11:18:10 +01:00
										 |  |  |         # Issue 21197: create lib64 as a symlink to lib on 64-bit non-OS X POSIX | 
					
						
							| 
									
										
										
										
											2014-06-03 16:47:51 +01:00
										 |  |  |         if ((sys.maxsize > 2**32) and (os.name == 'posix') and | 
					
						
							| 
									
										
										
										
											2014-04-15 11:18:10 +01:00
										 |  |  |             (sys.platform != 'darwin')): | 
					
						
							|  |  |  |             link_path = os.path.join(env_dir, 'lib64') | 
					
						
							| 
									
										
										
										
											2014-06-03 16:47:51 +01:00
										 |  |  |             if not os.path.exists(link_path):   # Issue #21643 | 
					
						
							| 
									
										
										
										
											2015-02-06 11:23:58 -05:00
										 |  |  |                 os.symlink('lib', link_path) | 
					
						
							| 
									
										
										
										
											2022-03-18 10:53:29 +01:00
										 |  |  |         context.bin_path = binpath | 
					
						
							|  |  |  |         context.bin_name = os.path.relpath(binpath, env_dir) | 
					
						
							| 
									
										
										
										
											2012-05-26 03:45:29 +01:00
										 |  |  |         context.env_exe = os.path.join(binpath, exename) | 
					
						
							|  |  |  |         create_if_needed(binpath) | 
					
						
							| 
									
										
										
										
											2021-10-07 21:26:12 +01:00
										 |  |  |         # Assign and update the command to use when launching the newly created | 
					
						
							|  |  |  |         # environment, in case it isn't simply the executable script (e.g. bpo-45337) | 
					
						
							|  |  |  |         context.env_exec_cmd = context.env_exe | 
					
						
							|  |  |  |         if sys.platform == 'win32': | 
					
						
							|  |  |  |             # bpo-45337: Fix up env_exec_cmd to account for file system redirections. | 
					
						
							|  |  |  |             # Some redirects only apply to CreateFile and not CreateProcess | 
					
						
							|  |  |  |             real_env_exe = os.path.realpath(context.env_exe) | 
					
						
							| 
									
										
										
										
											2024-04-15 15:36:06 +01:00
										 |  |  |             if not self._same_path(real_env_exe, context.env_exe): | 
					
						
							| 
									
										
										
										
											2021-10-07 21:26:12 +01:00
										 |  |  |                 logger.warning('Actual environment location may have moved due to ' | 
					
						
							|  |  |  |                                'redirects, links or junctions.\n' | 
					
						
							|  |  |  |                                '  Requested location: "%s"\n' | 
					
						
							|  |  |  |                                '  Actual location:    "%s"', | 
					
						
							|  |  |  |                                context.env_exe, real_env_exe) | 
					
						
							|  |  |  |                 context.env_exec_cmd = real_env_exe | 
					
						
							| 
									
										
										
										
											2012-05-26 03:45:29 +01:00
										 |  |  |         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]) | 
					
						
							| 
									
										
										
										
											2019-03-08 17:01:27 -05:00
										 |  |  |             if self.prompt is not None: | 
					
						
							|  |  |  |                 f.write(f'prompt = {self.prompt!r}\n') | 
					
						
							| 
									
										
										
										
											2022-01-07 01:50:30 -05:00
										 |  |  |             f.write('executable = %s\n' % os.path.realpath(sys.executable)) | 
					
						
							|  |  |  |             args = [] | 
					
						
							|  |  |  |             nt = os.name == 'nt' | 
					
						
							|  |  |  |             if nt and self.symlinks: | 
					
						
							|  |  |  |                 args.append('--symlinks') | 
					
						
							|  |  |  |             if not nt and not self.symlinks: | 
					
						
							|  |  |  |                 args.append('--copies') | 
					
						
							|  |  |  |             if not self.with_pip: | 
					
						
							|  |  |  |                 args.append('--without-pip') | 
					
						
							|  |  |  |             if self.system_site_packages: | 
					
						
							|  |  |  |                 args.append('--system-site-packages') | 
					
						
							|  |  |  |             if self.clear: | 
					
						
							|  |  |  |                 args.append('--clear') | 
					
						
							|  |  |  |             if self.upgrade: | 
					
						
							|  |  |  |                 args.append('--upgrade') | 
					
						
							|  |  |  |             if self.upgrade_deps: | 
					
						
							|  |  |  |                 args.append('--upgrade-deps') | 
					
						
							|  |  |  |             if self.orig_prompt is not None: | 
					
						
							|  |  |  |                 args.append(f'--prompt="{self.orig_prompt}"') | 
					
						
							| 
									
										
										
										
											2023-09-15 15:38:08 -07:00
										 |  |  |             if not self.scm_ignore_files: | 
					
						
							|  |  |  |                 args.append('--without-scm-ignore-files') | 
					
						
							| 
									
										
										
										
											2022-01-07 01:50:30 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |             args.append(context.env_dir) | 
					
						
							|  |  |  |             args = ' '.join(args) | 
					
						
							|  |  |  |             f.write(f'command = {sys.executable} -m venv {args}\n') | 
					
						
							| 
									
										
										
										
											2012-05-26 03:45:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-17 21:52:23 +00:00
										 |  |  |     def symlink_or_copy(self, src, dst, relative_symlinks_ok=False): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Try symlinking a file, and if that fails, fall back to copying. | 
					
						
							|  |  |  |         (Unused on Windows, because we can't just copy a failed symlink file: we | 
					
						
							|  |  |  |         switch to a different set of files instead.) | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         assert os.name != 'nt' | 
					
						
							|  |  |  |         force_copy = not self.symlinks | 
					
						
							|  |  |  |         if not force_copy: | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 if not os.path.islink(dst):  # can't link to itself! | 
					
						
							| 
									
										
										
										
											2015-02-06 11:23:58 -05:00
										 |  |  |                     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) | 
					
						
							| 
									
										
										
										
											2024-01-17 21:52:23 +00:00
										 |  |  |             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: | 
					
						
							| 
									
										
										
										
											2012-05-26 03:45:29 +01:00
										 |  |  |             shutil.copyfile(src, dst) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-15 15:38:08 -07:00
										 |  |  |     def create_git_ignore_file(self, context): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Create a .gitignore file in the environment directory. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         The contents of the file cause the entire environment directory to be | 
					
						
							|  |  |  |         ignored by git. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         gitignore_path = os.path.join(context.env_dir, '.gitignore') | 
					
						
							|  |  |  |         with open(gitignore_path, 'w', encoding='utf-8') as file: | 
					
						
							|  |  |  |             file.write('# Created by venv; ' | 
					
						
							|  |  |  |                        'see https://docs.python.org/3/library/venv.html\n') | 
					
						
							|  |  |  |             file.write('*\n') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-17 21:52:23 +00:00
										 |  |  |     if os.name != 'nt': | 
					
						
							|  |  |  |         def setup_python(self, context): | 
					
						
							|  |  |  |             """
 | 
					
						
							|  |  |  |             Set up a Python executable in the environment. | 
					
						
							| 
									
										
										
										
											2012-05-26 03:45:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-17 21:52:23 +00:00
										 |  |  |             :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 | 
					
						
							| 
									
										
										
										
											2018-12-10 08:11:21 -08:00
										 |  |  |             copier(context.executable, path) | 
					
						
							| 
									
										
										
										
											2012-05-26 03:45:29 +01:00
										 |  |  |             if not os.path.islink(path): | 
					
						
							|  |  |  |                 os.chmod(path, 0o755) | 
					
						
							| 
									
										
										
										
											2024-01-17 21:52:23 +00:00
										 |  |  |             for suffix in ('python', 'python3', | 
					
						
							|  |  |  |                            f'python3.{sys.version_info[1]}'): | 
					
						
							| 
									
										
										
										
											2012-05-28 16:33:01 +01:00
										 |  |  |                 path = os.path.join(binpath, suffix) | 
					
						
							|  |  |  |                 if not os.path.exists(path): | 
					
						
							| 
									
										
										
										
											2014-05-28 08:06:24 +01:00
										 |  |  |                     # Issue 18807: make copies if | 
					
						
							|  |  |  |                     # symlinks are not wanted | 
					
						
							| 
									
										
										
										
											2015-02-06 11:23:58 -05:00
										 |  |  |                     copier(context.env_exe, path, relative_symlinks_ok=True) | 
					
						
							| 
									
										
										
										
											2014-06-04 14:11:46 -04:00
										 |  |  |                     if not os.path.islink(path): | 
					
						
							|  |  |  |                         os.chmod(path, 0o755) | 
					
						
							| 
									
										
										
										
											2024-01-17 21:52:23 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         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 | 
					
						
							|  |  |  |             dirname = context.python_dir | 
					
						
							|  |  |  |             exename = os.path.basename(context.env_exe) | 
					
						
							|  |  |  |             exe_stem = os.path.splitext(exename)[0] | 
					
						
							|  |  |  |             exe_d = '_d' if os.path.normcase(exe_stem).endswith('_d') else '' | 
					
						
							|  |  |  |             if sysconfig.is_python_build(): | 
					
						
							|  |  |  |                 scripts = dirname | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 scripts = os.path.join(os.path.dirname(__file__), | 
					
						
							|  |  |  |                                        'scripts', 'nt') | 
					
						
							|  |  |  |             if not sysconfig.get_config_var("Py_GIL_DISABLED"): | 
					
						
							|  |  |  |                 python_exe = os.path.join(dirname, f'python{exe_d}.exe') | 
					
						
							|  |  |  |                 pythonw_exe = os.path.join(dirname, f'pythonw{exe_d}.exe') | 
					
						
							|  |  |  |                 link_sources = { | 
					
						
							|  |  |  |                     'python.exe': python_exe, | 
					
						
							|  |  |  |                     f'python{exe_d}.exe': python_exe, | 
					
						
							|  |  |  |                     'pythonw.exe': pythonw_exe, | 
					
						
							|  |  |  |                     f'pythonw{exe_d}.exe': pythonw_exe, | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 python_exe = os.path.join(scripts, f'venvlauncher{exe_d}.exe') | 
					
						
							|  |  |  |                 pythonw_exe = os.path.join(scripts, f'venvwlauncher{exe_d}.exe') | 
					
						
							|  |  |  |                 copy_sources = { | 
					
						
							|  |  |  |                     'python.exe': python_exe, | 
					
						
							|  |  |  |                     f'python{exe_d}.exe': python_exe, | 
					
						
							|  |  |  |                     'pythonw.exe': pythonw_exe, | 
					
						
							|  |  |  |                     f'pythonw{exe_d}.exe': pythonw_exe, | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2019-01-30 13:49:14 -08:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2024-01-17 21:52:23 +00:00
										 |  |  |                 exe_t = f'3.{sys.version_info[1]}t' | 
					
						
							|  |  |  |                 python_exe = os.path.join(dirname, f'python{exe_t}{exe_d}.exe') | 
					
						
							|  |  |  |                 pythonw_exe = os.path.join(dirname, f'pythonw{exe_t}{exe_d}.exe') | 
					
						
							|  |  |  |                 link_sources = { | 
					
						
							|  |  |  |                     'python.exe': python_exe, | 
					
						
							|  |  |  |                     f'python{exe_d}.exe': python_exe, | 
					
						
							|  |  |  |                     f'python{exe_t}.exe': python_exe, | 
					
						
							|  |  |  |                     f'python{exe_t}{exe_d}.exe': python_exe, | 
					
						
							|  |  |  |                     'pythonw.exe': pythonw_exe, | 
					
						
							|  |  |  |                     f'pythonw{exe_d}.exe': pythonw_exe, | 
					
						
							|  |  |  |                     f'pythonw{exe_t}.exe': pythonw_exe, | 
					
						
							|  |  |  |                     f'pythonw{exe_t}{exe_d}.exe': pythonw_exe, | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 python_exe = os.path.join(scripts, f'venvlaunchert{exe_d}.exe') | 
					
						
							|  |  |  |                 pythonw_exe = os.path.join(scripts, f'venvwlaunchert{exe_d}.exe') | 
					
						
							|  |  |  |                 copy_sources = { | 
					
						
							|  |  |  |                     'python.exe': python_exe, | 
					
						
							|  |  |  |                     f'python{exe_d}.exe': python_exe, | 
					
						
							|  |  |  |                     f'python{exe_t}.exe': python_exe, | 
					
						
							|  |  |  |                     f'python{exe_t}{exe_d}.exe': python_exe, | 
					
						
							|  |  |  |                     'pythonw.exe': pythonw_exe, | 
					
						
							|  |  |  |                     f'pythonw{exe_d}.exe': pythonw_exe, | 
					
						
							|  |  |  |                     f'pythonw{exe_t}.exe': pythonw_exe, | 
					
						
							|  |  |  |                     f'pythonw{exe_t}{exe_d}.exe': pythonw_exe, | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             do_copies = True | 
					
						
							|  |  |  |             if self.symlinks: | 
					
						
							|  |  |  |                 do_copies = False | 
					
						
							|  |  |  |                 # For symlinking, we need all the DLLs to be available alongside | 
					
						
							|  |  |  |                 # the executables. | 
					
						
							|  |  |  |                 link_sources.update({ | 
					
						
							|  |  |  |                     f: os.path.join(dirname, f) for f in os.listdir(dirname) | 
					
						
							|  |  |  |                     if os.path.normcase(f).startswith(('python', 'vcruntime')) | 
					
						
							|  |  |  |                     and os.path.normcase(os.path.splitext(f)[1]) == '.dll' | 
					
						
							|  |  |  |                 }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 to_unlink = [] | 
					
						
							|  |  |  |                 for dest, src in link_sources.items(): | 
					
						
							|  |  |  |                     dest = os.path.join(binpath, dest) | 
					
						
							|  |  |  |                     try: | 
					
						
							|  |  |  |                         os.symlink(src, dest) | 
					
						
							|  |  |  |                         to_unlink.append(dest) | 
					
						
							|  |  |  |                     except OSError: | 
					
						
							| 
									
										
										
										
											2024-09-30 03:11:22 +02:00
										 |  |  |                         logger.warning('Unable to symlink %r to %r', src, dest) | 
					
						
							| 
									
										
										
										
											2024-01-17 21:52:23 +00:00
										 |  |  |                         do_copies = True | 
					
						
							|  |  |  |                         for f in to_unlink: | 
					
						
							|  |  |  |                             try: | 
					
						
							|  |  |  |                                 os.unlink(f) | 
					
						
							|  |  |  |                             except OSError: | 
					
						
							|  |  |  |                                 logger.warning('Failed to clean up symlink %r', | 
					
						
							|  |  |  |                                                f) | 
					
						
							|  |  |  |                         logger.warning('Retrying with copies') | 
					
						
							|  |  |  |                         break | 
					
						
							| 
									
										
										
										
											2018-12-10 08:11:21 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-17 21:52:23 +00:00
										 |  |  |             if do_copies: | 
					
						
							|  |  |  |                 for dest, src in copy_sources.items(): | 
					
						
							|  |  |  |                     dest = os.path.join(binpath, dest) | 
					
						
							|  |  |  |                     try: | 
					
						
							|  |  |  |                         shutil.copy2(src, dest) | 
					
						
							|  |  |  |                     except OSError: | 
					
						
							|  |  |  |                         logger.warning('Unable to copy %r to %r', src, dest) | 
					
						
							| 
									
										
										
										
											2018-12-10 08:11:21 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-26 17:40:47 -04:00
										 |  |  |             if sysconfig.is_python_build(): | 
					
						
							| 
									
										
										
										
											2019-01-30 13:49:14 -08:00
										 |  |  |                 # copy init.tcl | 
					
						
							| 
									
										
										
										
											2018-09-20 13:38:34 -07:00
										 |  |  |                 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 | 
					
						
							| 
									
										
										
										
											2012-05-26 03:45:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-14 16:58:54 +01:00
										 |  |  |     def _call_new_python(self, context, *py_args, **kwargs): | 
					
						
							|  |  |  |         """Executes the newly created Python using safe-ish options""" | 
					
						
							|  |  |  |         # gh-98251: We do not want to just use '-I' because that masks | 
					
						
							|  |  |  |         # legitimate user preferences (such as not writing bytecode). All we | 
					
						
							|  |  |  |         # really need is to ensure that the path variables do not overrule | 
					
						
							|  |  |  |         # normal venv handling. | 
					
						
							|  |  |  |         args = [context.env_exec_cmd, *py_args] | 
					
						
							|  |  |  |         kwargs['env'] = env = os.environ.copy() | 
					
						
							|  |  |  |         env['VIRTUAL_ENV'] = context.env_dir | 
					
						
							|  |  |  |         env.pop('PYTHONHOME', None) | 
					
						
							|  |  |  |         env.pop('PYTHONPATH', None) | 
					
						
							|  |  |  |         kwargs['cwd'] = context.env_dir | 
					
						
							|  |  |  |         kwargs['executable'] = context.env_exec_cmd | 
					
						
							|  |  |  |         subprocess.check_output(args, **kwargs) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-23 00:30:34 +10:00
										 |  |  |     def _setup_pip(self, context): | 
					
						
							|  |  |  |         """Installs or upgrades pip in a virtual environment""" | 
					
						
							| 
									
										
										
										
											2022-10-14 16:58:54 +01:00
										 |  |  |         self._call_new_python(context, '-m', 'ensurepip', '--upgrade', | 
					
						
							|  |  |  |                               '--default-pip', stderr=subprocess.STDOUT) | 
					
						
							| 
									
										
										
										
											2013-11-23 00:30:34 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-26 03:45:29 +01:00
										 |  |  |     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) | 
					
						
							| 
									
										
										
										
											2013-10-02 11:36:16 +01:00
										 |  |  |         text = text.replace('__VENV_NAME__', context.env_name) | 
					
						
							|  |  |  |         text = text.replace('__VENV_PROMPT__', context.prompt) | 
					
						
							| 
									
										
										
										
											2012-05-26 03:45:29 +01:00
										 |  |  |         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) | 
					
						
							| 
									
										
										
										
											2024-01-17 21:52:23 +00:00
										 |  |  |         if os.name == 'nt': | 
					
						
							|  |  |  |             def skip_file(f): | 
					
						
							|  |  |  |                 f = os.path.normcase(f) | 
					
						
							|  |  |  |                 return (f.startswith(('python', 'venv')) | 
					
						
							|  |  |  |                         and f.endswith(('.exe', '.pdb'))) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             def skip_file(f): | 
					
						
							|  |  |  |                 return False | 
					
						
							| 
									
										
										
										
											2012-05-26 03:45:29 +01:00
										 |  |  |         for root, dirs, files in os.walk(path): | 
					
						
							| 
									
										
										
										
											2022-12-14 13:37:11 +02:00
										 |  |  |             if root == path:  # at top-level, remove irrelevant dirs | 
					
						
							| 
									
										
										
										
											2012-05-26 03:45:29 +01:00
										 |  |  |                 for d in dirs[:]: | 
					
						
							|  |  |  |                     if d not in ('common', os.name): | 
					
						
							|  |  |  |                         dirs.remove(d) | 
					
						
							| 
									
										
										
										
											2022-12-14 13:37:11 +02:00
										 |  |  |                 continue  # ignore files in top level | 
					
						
							| 
									
										
										
										
											2012-05-26 03:45:29 +01:00
										 |  |  |             for f in files: | 
					
						
							| 
									
										
										
										
											2024-01-17 21:52:23 +00:00
										 |  |  |                 if skip_file(f): | 
					
						
							| 
									
										
										
										
											2019-01-30 13:49:14 -08:00
										 |  |  |                     continue | 
					
						
							| 
									
										
										
										
											2012-05-26 03:45:29 +01:00
										 |  |  |                 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) | 
					
						
							| 
									
										
										
										
											2024-01-17 21:52:23 +00:00
										 |  |  |                 if os.name == 'nt' and srcfile.endswith(('.exe', '.pdb')): | 
					
						
							|  |  |  |                     shutil.copy2(srcfile, dstfile) | 
					
						
							|  |  |  |                     continue | 
					
						
							| 
									
										
										
										
											2012-05-26 03:45:29 +01:00
										 |  |  |                 with open(srcfile, 'rb') as f: | 
					
						
							|  |  |  |                     data = f.read() | 
					
						
							| 
									
										
										
										
											2024-01-17 21:52:23 +00:00
										 |  |  |                 try: | 
					
						
							|  |  |  |                     new_data = ( | 
					
						
							|  |  |  |                         self.replace_variables(data.decode('utf-8'), context) | 
					
						
							|  |  |  |                             .encode('utf-8') | 
					
						
							|  |  |  |                     ) | 
					
						
							|  |  |  |                 except UnicodeError as e: | 
					
						
							|  |  |  |                     logger.warning('unable to copy script %r, ' | 
					
						
							|  |  |  |                                    'may be binary: %s', srcfile, e) | 
					
						
							|  |  |  |                     continue | 
					
						
							|  |  |  |                 if new_data == data: | 
					
						
							|  |  |  |                     shutil.copy2(srcfile, dstfile) | 
					
						
							|  |  |  |                 else: | 
					
						
							| 
									
										
										
										
											2017-02-02 19:33:43 +00:00
										 |  |  |                     with open(dstfile, 'wb') as f: | 
					
						
							| 
									
										
										
										
											2024-01-17 21:52:23 +00:00
										 |  |  |                         f.write(new_data) | 
					
						
							| 
									
										
										
										
											2012-10-28 12:39:39 +00:00
										 |  |  |                     shutil.copymode(srcfile, dstfile) | 
					
						
							| 
									
										
										
										
											2012-05-26 03:45:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-17 11:18:14 -07:00
										 |  |  |     def upgrade_dependencies(self, context): | 
					
						
							|  |  |  |         logger.debug( | 
					
						
							|  |  |  |             f'Upgrading {CORE_VENV_DEPS} packages in {context.bin_path}' | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2022-10-14 16:58:54 +01:00
										 |  |  |         self._call_new_python(context, '-m', 'pip', 'install', '--upgrade', | 
					
						
							|  |  |  |                               *CORE_VENV_DEPS) | 
					
						
							| 
									
										
										
										
											2019-06-17 11:18:14 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-26 03:45:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-23 00:30:34 +10:00
										 |  |  | def create(env_dir, system_site_packages=False, clear=False, | 
					
						
							| 
									
										
										
										
											2023-09-15 15:38:08 -07:00
										 |  |  |            symlinks=False, with_pip=False, prompt=None, upgrade_deps=False, | 
					
						
							|  |  |  |            *, scm_ignore_files=frozenset()): | 
					
						
							| 
									
										
										
										
											2016-01-30 12:34:12 +02:00
										 |  |  |     """Create a virtual environment in a directory.""" | 
					
						
							| 
									
										
										
										
											2012-05-28 16:33:01 +01:00
										 |  |  |     builder = EnvBuilder(system_site_packages=system_site_packages, | 
					
						
							| 
									
										
										
										
											2016-08-06 10:43:44 +01:00
										 |  |  |                          clear=clear, symlinks=symlinks, with_pip=with_pip, | 
					
						
							| 
									
										
										
										
											2023-09-15 15:38:08 -07:00
										 |  |  |                          prompt=prompt, upgrade_deps=upgrade_deps, | 
					
						
							|  |  |  |                          scm_ignore_files=scm_ignore_files) | 
					
						
							| 
									
										
										
										
											2012-05-26 03:45:29 +01:00
										 |  |  |     builder.create(env_dir) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-14 13:37:11 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-26 03:45:29 +01:00
										 |  |  | def main(args=None): | 
					
						
							| 
									
										
										
										
											2022-12-14 13:37:11 +02:00
										 |  |  |     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 | 
					
						
							| 
									
										
										
										
											2012-05-26 03:45:29 +01:00
										 |  |  |     else: | 
					
						
							| 
									
										
										
										
											2022-12-14 13:37:11 +02:00
										 |  |  |         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', | 
					
						
							| 
									
										
										
										
											2023-04-17 23:43:34 -05:00
										 |  |  |                         help=f'Upgrade core dependencies ({", ".join(CORE_VENV_DEPS)}) ' | 
					
						
							| 
									
										
										
										
											2022-12-14 13:37:11 +02:00
										 |  |  |                              'to the latest version in PyPI') | 
					
						
							| 
									
										
										
										
											2023-09-15 15:38:08 -07:00
										 |  |  |     parser.add_argument('--without-scm-ignore-files', dest='scm_ignore_files', | 
					
						
							|  |  |  |                         action='store_const', const=frozenset(), | 
					
						
							|  |  |  |                         default=frozenset(['git']), | 
					
						
							|  |  |  |                         help='Skips adding SCM ignore files to the environment ' | 
					
						
							|  |  |  |                              'directory (Git is supported by default).') | 
					
						
							| 
									
										
										
										
											2022-12-14 13:37:11 +02:00
										 |  |  |     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, | 
					
						
							| 
									
										
										
										
											2023-09-15 15:38:08 -07:00
										 |  |  |                          upgrade_deps=options.upgrade_deps, | 
					
						
							|  |  |  |                          scm_ignore_files=options.scm_ignore_files) | 
					
						
							| 
									
										
										
										
											2022-12-14 13:37:11 +02:00
										 |  |  |     for d in options.dirs: | 
					
						
							|  |  |  |         builder.create(d) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-26 03:45:29 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | if __name__ == '__main__': | 
					
						
							|  |  |  |     rc = 1 | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         main() | 
					
						
							|  |  |  |         rc = 0 | 
					
						
							|  |  |  |     except Exception as e: | 
					
						
							|  |  |  |         print('Error: %s' % e, file=sys.stderr) | 
					
						
							|  |  |  |     sys.exit(rc) |