ยปCore Development>Code coverage>Lib/venv/__init__.py

Python code coverage for Lib/venv/__init__.py

#countcontent
1n/a"""
2n/aVirtual environment (venv) package for Python. Based on PEP 405.
3n/a
4n/aCopyright (C) 2011-2014 Vinay Sajip.
5n/aLicensed to the PSF under a contributor agreement.
6n/a"""
7n/aimport logging
8n/aimport os
9n/aimport shutil
10n/aimport subprocess
11n/aimport sys
12n/aimport types
13n/a
14n/alogger = logging.getLogger(__name__)
15n/a
16n/a
17n/aclass EnvBuilder:
18n/a """
19n/a This class exists to allow virtual environment creation to be
20n/a customized. The constructor parameters determine the builder's
21n/a behaviour when called upon to create a virtual environment.
22n/a
23n/a By default, the builder makes the system (global) site-packages dir
24n/a *un*available to the created environment.
25n/a
26n/a If invoked using the Python -m option, the default is to use copying
27n/a on Windows platforms but symlinks elsewhere. If instantiated some
28n/a other way, the default is to *not* use symlinks.
29n/a
30n/a :param system_site_packages: If True, the system (global) site-packages
31n/a dir is available to created environments.
32n/a :param clear: If True, delete the contents of the environment directory if
33n/a it already exists, before environment creation.
34n/a :param symlinks: If True, attempt to symlink rather than copy files into
35n/a virtual environment.
36n/a :param upgrade: If True, upgrade an existing virtual environment.
37n/a :param with_pip: If True, ensure pip is installed in the virtual
38n/a environment
39n/a :param prompt: Alternative terminal prefix for the environment.
40n/a """
41n/a
42n/a def __init__(self, system_site_packages=False, clear=False,
43n/a symlinks=False, upgrade=False, with_pip=False, prompt=None):
44n/a self.system_site_packages = system_site_packages
45n/a self.clear = clear
46n/a self.symlinks = symlinks
47n/a self.upgrade = upgrade
48n/a self.with_pip = with_pip
49n/a self.prompt = prompt
50n/a
51n/a def create(self, env_dir):
52n/a """
53n/a Create a virtual environment in a directory.
54n/a
55n/a :param env_dir: The target directory to create an environment in.
56n/a
57n/a """
58n/a env_dir = os.path.abspath(env_dir)
59n/a context = self.ensure_directories(env_dir)
60n/a # See issue 24875. We need system_site_packages to be False
61n/a # until after pip is installed.
62n/a true_system_site_packages = self.system_site_packages
63n/a self.system_site_packages = False
64n/a self.create_configuration(context)
65n/a self.setup_python(context)
66n/a if self.with_pip:
67n/a self._setup_pip(context)
68n/a if not self.upgrade:
69n/a self.setup_scripts(context)
70n/a self.post_setup(context)
71n/a if true_system_site_packages:
72n/a # We had set it to False before, now
73n/a # restore it and rewrite the configuration
74n/a self.system_site_packages = True
75n/a self.create_configuration(context)
76n/a
77n/a def clear_directory(self, path):
78n/a for fn in os.listdir(path):
79n/a fn = os.path.join(path, fn)
80n/a if os.path.islink(fn) or os.path.isfile(fn):
81n/a os.remove(fn)
82n/a elif os.path.isdir(fn):
83n/a shutil.rmtree(fn)
84n/a
85n/a def ensure_directories(self, env_dir):
86n/a """
87n/a Create the directories for the environment.
88n/a
89n/a Returns a context object which holds paths in the environment,
90n/a for use by subsequent logic.
91n/a """
92n/a
93n/a def create_if_needed(d):
94n/a if not os.path.exists(d):
95n/a os.makedirs(d)
96n/a elif os.path.islink(d) or os.path.isfile(d):
97n/a raise ValueError('Unable to create directory %r' % d)
98n/a
99n/a if os.path.exists(env_dir) and self.clear:
100n/a self.clear_directory(env_dir)
101n/a context = types.SimpleNamespace()
102n/a context.env_dir = env_dir
103n/a context.env_name = os.path.split(env_dir)[1]
104n/a prompt = self.prompt if self.prompt is not None else context.env_name
105n/a context.prompt = '(%s) ' % prompt
106n/a create_if_needed(env_dir)
107n/a env = os.environ
108n/a if sys.platform == 'darwin' and '__PYVENV_LAUNCHER__' in env:
109n/a executable = os.environ['__PYVENV_LAUNCHER__']
110n/a else:
111n/a executable = sys.executable
112n/a dirname, exename = os.path.split(os.path.abspath(executable))
113n/a context.executable = executable
114n/a context.python_dir = dirname
115n/a context.python_exe = exename
116n/a if sys.platform == 'win32':
117n/a binname = 'Scripts'
118n/a incpath = 'Include'
119n/a libpath = os.path.join(env_dir, 'Lib', 'site-packages')
120n/a else:
121n/a binname = 'bin'
122n/a incpath = 'include'
123n/a libpath = os.path.join(env_dir, 'lib',
124n/a 'python%d.%d' % sys.version_info[:2],
125n/a 'site-packages')
126n/a context.inc_path = path = os.path.join(env_dir, incpath)
127n/a create_if_needed(path)
128n/a create_if_needed(libpath)
129n/a # Issue 21197: create lib64 as a symlink to lib on 64-bit non-OS X POSIX
130n/a if ((sys.maxsize > 2**32) and (os.name == 'posix') and
131n/a (sys.platform != 'darwin')):
132n/a link_path = os.path.join(env_dir, 'lib64')
133n/a if not os.path.exists(link_path): # Issue #21643
134n/a os.symlink('lib', link_path)
135n/a context.bin_path = binpath = os.path.join(env_dir, binname)
136n/a context.bin_name = binname
137n/a context.env_exe = os.path.join(binpath, exename)
138n/a create_if_needed(binpath)
139n/a return context
140n/a
141n/a def create_configuration(self, context):
142n/a """
143n/a Create a configuration file indicating where the environment's Python
144n/a was copied from, and whether the system site-packages should be made
145n/a available in the environment.
146n/a
147n/a :param context: The information for the environment creation request
148n/a being processed.
149n/a """
150n/a context.cfg_path = path = os.path.join(context.env_dir, 'pyvenv.cfg')
151n/a with open(path, 'w', encoding='utf-8') as f:
152n/a f.write('home = %s\n' % context.python_dir)
153n/a if self.system_site_packages:
154n/a incl = 'true'
155n/a else:
156n/a incl = 'false'
157n/a f.write('include-system-site-packages = %s\n' % incl)
158n/a f.write('version = %d.%d.%d\n' % sys.version_info[:3])
159n/a
160n/a if os.name == 'nt':
161n/a def include_binary(self, f):
162n/a if f.endswith(('.pyd', '.dll')):
163n/a result = True
164n/a else:
165n/a result = f.startswith('python') and f.endswith('.exe')
166n/a return result
167n/a
168n/a def symlink_or_copy(self, src, dst, relative_symlinks_ok=False):
169n/a """
170n/a Try symlinking a file, and if that fails, fall back to copying.
171n/a """
172n/a force_copy = not self.symlinks
173n/a if not force_copy:
174n/a try:
175n/a if not os.path.islink(dst): # can't link to itself!
176n/a if relative_symlinks_ok:
177n/a assert os.path.dirname(src) == os.path.dirname(dst)
178n/a os.symlink(os.path.basename(src), dst)
179n/a else:
180n/a os.symlink(src, dst)
181n/a except Exception: # may need to use a more specific exception
182n/a logger.warning('Unable to symlink %r to %r', src, dst)
183n/a force_copy = True
184n/a if force_copy:
185n/a shutil.copyfile(src, dst)
186n/a
187n/a def setup_python(self, context):
188n/a """
189n/a Set up a Python executable in the environment.
190n/a
191n/a :param context: The information for the environment creation request
192n/a being processed.
193n/a """
194n/a binpath = context.bin_path
195n/a path = context.env_exe
196n/a copier = self.symlink_or_copy
197n/a copier(context.executable, path)
198n/a dirname = context.python_dir
199n/a if os.name != 'nt':
200n/a if not os.path.islink(path):
201n/a os.chmod(path, 0o755)
202n/a for suffix in ('python', 'python3'):
203n/a path = os.path.join(binpath, suffix)
204n/a if not os.path.exists(path):
205n/a # Issue 18807: make copies if
206n/a # symlinks are not wanted
207n/a copier(context.env_exe, path, relative_symlinks_ok=True)
208n/a if not os.path.islink(path):
209n/a os.chmod(path, 0o755)
210n/a else:
211n/a subdir = 'DLLs'
212n/a include = self.include_binary
213n/a files = [f for f in os.listdir(dirname) if include(f)]
214n/a for f in files:
215n/a src = os.path.join(dirname, f)
216n/a dst = os.path.join(binpath, f)
217n/a if dst != context.env_exe: # already done, above
218n/a copier(src, dst)
219n/a dirname = os.path.join(dirname, subdir)
220n/a if os.path.isdir(dirname):
221n/a files = [f for f in os.listdir(dirname) if include(f)]
222n/a for f in files:
223n/a src = os.path.join(dirname, f)
224n/a dst = os.path.join(binpath, f)
225n/a copier(src, dst)
226n/a # copy init.tcl over
227n/a for root, dirs, files in os.walk(context.python_dir):
228n/a if 'init.tcl' in files:
229n/a tcldir = os.path.basename(root)
230n/a tcldir = os.path.join(context.env_dir, 'Lib', tcldir)
231n/a if not os.path.exists(tcldir):
232n/a os.makedirs(tcldir)
233n/a src = os.path.join(root, 'init.tcl')
234n/a dst = os.path.join(tcldir, 'init.tcl')
235n/a shutil.copyfile(src, dst)
236n/a break
237n/a
238n/a def _setup_pip(self, context):
239n/a """Installs or upgrades pip in a virtual environment"""
240n/a # We run ensurepip in isolated mode to avoid side effects from
241n/a # environment vars, the current directory and anything else
242n/a # intended for the global Python environment
243n/a cmd = [context.env_exe, '-Im', 'ensurepip', '--upgrade',
244n/a '--default-pip']
245n/a subprocess.check_output(cmd, stderr=subprocess.STDOUT)
246n/a
247n/a def setup_scripts(self, context):
248n/a """
249n/a Set up scripts into the created environment from a directory.
250n/a
251n/a This method installs the default scripts into the environment
252n/a being created. You can prevent the default installation by overriding
253n/a this method if you really need to, or if you need to specify
254n/a a different location for the scripts to install. By default, the
255n/a 'scripts' directory in the venv package is used as the source of
256n/a scripts to install.
257n/a """
258n/a path = os.path.abspath(os.path.dirname(__file__))
259n/a path = os.path.join(path, 'scripts')
260n/a self.install_scripts(context, path)
261n/a
262n/a def post_setup(self, context):
263n/a """
264n/a Hook for post-setup modification of the venv. Subclasses may install
265n/a additional packages or scripts here, add activation shell scripts, etc.
266n/a
267n/a :param context: The information for the environment creation request
268n/a being processed.
269n/a """
270n/a pass
271n/a
272n/a def replace_variables(self, text, context):
273n/a """
274n/a Replace variable placeholders in script text with context-specific
275n/a variables.
276n/a
277n/a Return the text passed in , but with variables replaced.
278n/a
279n/a :param text: The text in which to replace placeholder variables.
280n/a :param context: The information for the environment creation request
281n/a being processed.
282n/a """
283n/a text = text.replace('__VENV_DIR__', context.env_dir)
284n/a text = text.replace('__VENV_NAME__', context.env_name)
285n/a text = text.replace('__VENV_PROMPT__', context.prompt)
286n/a text = text.replace('__VENV_BIN_NAME__', context.bin_name)
287n/a text = text.replace('__VENV_PYTHON__', context.env_exe)
288n/a return text
289n/a
290n/a def install_scripts(self, context, path):
291n/a """
292n/a Install scripts into the created environment from a directory.
293n/a
294n/a :param context: The information for the environment creation request
295n/a being processed.
296n/a :param path: Absolute pathname of a directory containing script.
297n/a Scripts in the 'common' subdirectory of this directory,
298n/a and those in the directory named for the platform
299n/a being run on, are installed in the created environment.
300n/a Placeholder variables are replaced with environment-
301n/a specific values.
302n/a """
303n/a binpath = context.bin_path
304n/a plen = len(path)
305n/a for root, dirs, files in os.walk(path):
306n/a if root == path: # at top-level, remove irrelevant dirs
307n/a for d in dirs[:]:
308n/a if d not in ('common', os.name):
309n/a dirs.remove(d)
310n/a continue # ignore files in top level
311n/a for f in files:
312n/a srcfile = os.path.join(root, f)
313n/a suffix = root[plen:].split(os.sep)[2:]
314n/a if not suffix:
315n/a dstdir = binpath
316n/a else:
317n/a dstdir = os.path.join(binpath, *suffix)
318n/a if not os.path.exists(dstdir):
319n/a os.makedirs(dstdir)
320n/a dstfile = os.path.join(dstdir, f)
321n/a with open(srcfile, 'rb') as f:
322n/a data = f.read()
323n/a if not srcfile.endswith('.exe'):
324n/a try:
325n/a data = data.decode('utf-8')
326n/a data = self.replace_variables(data, context)
327n/a data = data.encode('utf-8')
328n/a except UnicodeError as e:
329n/a data = None
330n/a logger.warning('unable to copy script %r, '
331n/a 'may be binary: %s', srcfile, e)
332n/a if data is not None:
333n/a with open(dstfile, 'wb') as f:
334n/a f.write(data)
335n/a shutil.copymode(srcfile, dstfile)
336n/a
337n/a
338n/adef create(env_dir, system_site_packages=False, clear=False,
339n/a symlinks=False, with_pip=False, prompt=None):
340n/a """Create a virtual environment in a directory."""
341n/a builder = EnvBuilder(system_site_packages=system_site_packages,
342n/a clear=clear, symlinks=symlinks, with_pip=with_pip,
343n/a prompt=prompt)
344n/a builder.create(env_dir)
345n/a
346n/adef main(args=None):
347n/a compatible = True
348n/a if sys.version_info < (3, 3):
349n/a compatible = False
350n/a elif not hasattr(sys, 'base_prefix'):
351n/a compatible = False
352n/a if not compatible:
353n/a raise ValueError('This script is only for use with Python >= 3.3')
354n/a else:
355n/a import argparse
356n/a
357n/a parser = argparse.ArgumentParser(prog=__name__,
358n/a description='Creates virtual Python '
359n/a 'environments in one or '
360n/a 'more target '
361n/a 'directories.',
362n/a epilog='Once an environment has been '
363n/a 'created, you may wish to '
364n/a 'activate it, e.g. by '
365n/a 'sourcing an activate script '
366n/a 'in its bin directory.')
367n/a parser.add_argument('dirs', metavar='ENV_DIR', nargs='+',
368n/a help='A directory to create the environment in.')
369n/a parser.add_argument('--system-site-packages', default=False,
370n/a action='store_true', dest='system_site',
371n/a help='Give the virtual environment access to the '
372n/a 'system site-packages dir.')
373n/a if os.name == 'nt':
374n/a use_symlinks = False
375n/a else:
376n/a use_symlinks = True
377n/a group = parser.add_mutually_exclusive_group()
378n/a group.add_argument('--symlinks', default=use_symlinks,
379n/a action='store_true', dest='symlinks',
380n/a help='Try to use symlinks rather than copies, '
381n/a 'when symlinks are not the default for '
382n/a 'the platform.')
383n/a group.add_argument('--copies', default=not use_symlinks,
384n/a action='store_false', dest='symlinks',
385n/a help='Try to use copies rather than symlinks, '
386n/a 'even when symlinks are the default for '
387n/a 'the platform.')
388n/a parser.add_argument('--clear', default=False, action='store_true',
389n/a dest='clear', help='Delete the contents of the '
390n/a 'environment directory if it '
391n/a 'already exists, before '
392n/a 'environment creation.')
393n/a parser.add_argument('--upgrade', default=False, action='store_true',
394n/a dest='upgrade', help='Upgrade the environment '
395n/a 'directory to use this version '
396n/a 'of Python, assuming Python '
397n/a 'has been upgraded in-place.')
398n/a parser.add_argument('--without-pip', dest='with_pip',
399n/a default=True, action='store_false',
400n/a help='Skips installing or upgrading pip in the '
401n/a 'virtual environment (pip is bootstrapped '
402n/a 'by default)')
403n/a parser.add_argument('--prompt',
404n/a help='Provides an alternative prompt prefix for '
405n/a 'this environment.')
406n/a options = parser.parse_args(args)
407n/a if options.upgrade and options.clear:
408n/a raise ValueError('you cannot supply --upgrade and --clear together.')
409n/a builder = EnvBuilder(system_site_packages=options.system_site,
410n/a clear=options.clear,
411n/a symlinks=options.symlinks,
412n/a upgrade=options.upgrade,
413n/a with_pip=options.with_pip,
414n/a prompt=options.prompt)
415n/a for d in options.dirs:
416n/a builder.create(d)
417n/a
418n/aif __name__ == '__main__':
419n/a rc = 1
420n/a try:
421n/a main()
422n/a rc = 0
423n/a except Exception as e:
424n/a print('Error: %s' % e, file=sys.stderr)
425n/a sys.exit(rc)