ยปCore Development>Code coverage>Lib/test/test_venv.py

Python code coverage for Lib/test/test_venv.py

#countcontent
1n/a"""
2n/aTest harness for the venv module.
3n/a
4n/aCopyright (C) 2011-2012 Vinay Sajip.
5n/aLicensed to the PSF under a contributor agreement.
6n/a"""
7n/a
8n/aimport ensurepip
9n/aimport os
10n/aimport os.path
11n/aimport re
12n/aimport struct
13n/aimport subprocess
14n/aimport sys
15n/aimport tempfile
16n/afrom test.support import (captured_stdout, captured_stderr,
17n/a can_symlink, EnvironmentVarGuard, rmtree)
18n/aimport unittest
19n/aimport venv
20n/a
21n/a
22n/atry:
23n/a import threading
24n/aexcept ImportError:
25n/a threading = None
26n/a
27n/atry:
28n/a import ctypes
29n/aexcept ImportError:
30n/a ctypes = None
31n/a
32n/askipInVenv = unittest.skipIf(sys.prefix != sys.base_prefix,
33n/a 'Test not appropriate in a venv')
34n/a
35n/aclass BaseTest(unittest.TestCase):
36n/a """Base class for venv tests."""
37n/a maxDiff = 80 * 50
38n/a
39n/a def setUp(self):
40n/a self.env_dir = os.path.realpath(tempfile.mkdtemp())
41n/a if os.name == 'nt':
42n/a self.bindir = 'Scripts'
43n/a self.lib = ('Lib',)
44n/a self.include = 'Include'
45n/a else:
46n/a self.bindir = 'bin'
47n/a self.lib = ('lib', 'python%d.%d' % sys.version_info[:2])
48n/a self.include = 'include'
49n/a if sys.platform == 'darwin' and '__PYVENV_LAUNCHER__' in os.environ:
50n/a executable = os.environ['__PYVENV_LAUNCHER__']
51n/a else:
52n/a executable = sys.executable
53n/a self.exe = os.path.split(executable)[-1]
54n/a
55n/a def tearDown(self):
56n/a rmtree(self.env_dir)
57n/a
58n/a def run_with_capture(self, func, *args, **kwargs):
59n/a with captured_stdout() as output:
60n/a with captured_stderr() as error:
61n/a func(*args, **kwargs)
62n/a return output.getvalue(), error.getvalue()
63n/a
64n/a def get_env_file(self, *args):
65n/a return os.path.join(self.env_dir, *args)
66n/a
67n/a def get_text_file_contents(self, *args):
68n/a with open(self.get_env_file(*args), 'r') as f:
69n/a result = f.read()
70n/a return result
71n/a
72n/aclass BasicTest(BaseTest):
73n/a """Test venv module functionality."""
74n/a
75n/a def isdir(self, *args):
76n/a fn = self.get_env_file(*args)
77n/a self.assertTrue(os.path.isdir(fn))
78n/a
79n/a def test_defaults(self):
80n/a """
81n/a Test the create function with default arguments.
82n/a """
83n/a rmtree(self.env_dir)
84n/a self.run_with_capture(venv.create, self.env_dir)
85n/a self.isdir(self.bindir)
86n/a self.isdir(self.include)
87n/a self.isdir(*self.lib)
88n/a # Issue 21197
89n/a p = self.get_env_file('lib64')
90n/a conditions = ((struct.calcsize('P') == 8) and (os.name == 'posix') and
91n/a (sys.platform != 'darwin'))
92n/a if conditions:
93n/a self.assertTrue(os.path.islink(p))
94n/a else:
95n/a self.assertFalse(os.path.exists(p))
96n/a data = self.get_text_file_contents('pyvenv.cfg')
97n/a if sys.platform == 'darwin' and ('__PYVENV_LAUNCHER__'
98n/a in os.environ):
99n/a executable = os.environ['__PYVENV_LAUNCHER__']
100n/a else:
101n/a executable = sys.executable
102n/a path = os.path.dirname(executable)
103n/a self.assertIn('home = %s' % path, data)
104n/a fn = self.get_env_file(self.bindir, self.exe)
105n/a if not os.path.exists(fn): # diagnostics for Windows buildbot failures
106n/a bd = self.get_env_file(self.bindir)
107n/a print('Contents of %r:' % bd)
108n/a print(' %r' % os.listdir(bd))
109n/a self.assertTrue(os.path.exists(fn), 'File %r should exist.' % fn)
110n/a
111n/a def test_prompt(self):
112n/a env_name = os.path.split(self.env_dir)[1]
113n/a
114n/a builder = venv.EnvBuilder()
115n/a context = builder.ensure_directories(self.env_dir)
116n/a self.assertEqual(context.prompt, '(%s) ' % env_name)
117n/a
118n/a builder = venv.EnvBuilder(prompt='My prompt')
119n/a context = builder.ensure_directories(self.env_dir)
120n/a self.assertEqual(context.prompt, '(My prompt) ')
121n/a
122n/a @skipInVenv
123n/a def test_prefixes(self):
124n/a """
125n/a Test that the prefix values are as expected.
126n/a """
127n/a #check our prefixes
128n/a self.assertEqual(sys.base_prefix, sys.prefix)
129n/a self.assertEqual(sys.base_exec_prefix, sys.exec_prefix)
130n/a
131n/a # check a venv's prefixes
132n/a rmtree(self.env_dir)
133n/a self.run_with_capture(venv.create, self.env_dir)
134n/a envpy = os.path.join(self.env_dir, self.bindir, self.exe)
135n/a cmd = [envpy, '-c', None]
136n/a for prefix, expected in (
137n/a ('prefix', self.env_dir),
138n/a ('prefix', self.env_dir),
139n/a ('base_prefix', sys.prefix),
140n/a ('base_exec_prefix', sys.exec_prefix)):
141n/a cmd[2] = 'import sys; print(sys.%s)' % prefix
142n/a p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
143n/a stderr=subprocess.PIPE)
144n/a out, err = p.communicate()
145n/a self.assertEqual(out.strip(), expected.encode())
146n/a
147n/a if sys.platform == 'win32':
148n/a ENV_SUBDIRS = (
149n/a ('Scripts',),
150n/a ('Include',),
151n/a ('Lib',),
152n/a ('Lib', 'site-packages'),
153n/a )
154n/a else:
155n/a ENV_SUBDIRS = (
156n/a ('bin',),
157n/a ('include',),
158n/a ('lib',),
159n/a ('lib', 'python%d.%d' % sys.version_info[:2]),
160n/a ('lib', 'python%d.%d' % sys.version_info[:2], 'site-packages'),
161n/a )
162n/a
163n/a def create_contents(self, paths, filename):
164n/a """
165n/a Create some files in the environment which are unrelated
166n/a to the virtual environment.
167n/a """
168n/a for subdirs in paths:
169n/a d = os.path.join(self.env_dir, *subdirs)
170n/a os.mkdir(d)
171n/a fn = os.path.join(d, filename)
172n/a with open(fn, 'wb') as f:
173n/a f.write(b'Still here?')
174n/a
175n/a def test_overwrite_existing(self):
176n/a """
177n/a Test creating environment in an existing directory.
178n/a """
179n/a self.create_contents(self.ENV_SUBDIRS, 'foo')
180n/a venv.create(self.env_dir)
181n/a for subdirs in self.ENV_SUBDIRS:
182n/a fn = os.path.join(self.env_dir, *(subdirs + ('foo',)))
183n/a self.assertTrue(os.path.exists(fn))
184n/a with open(fn, 'rb') as f:
185n/a self.assertEqual(f.read(), b'Still here?')
186n/a
187n/a builder = venv.EnvBuilder(clear=True)
188n/a builder.create(self.env_dir)
189n/a for subdirs in self.ENV_SUBDIRS:
190n/a fn = os.path.join(self.env_dir, *(subdirs + ('foo',)))
191n/a self.assertFalse(os.path.exists(fn))
192n/a
193n/a def clear_directory(self, path):
194n/a for fn in os.listdir(path):
195n/a fn = os.path.join(path, fn)
196n/a if os.path.islink(fn) or os.path.isfile(fn):
197n/a os.remove(fn)
198n/a elif os.path.isdir(fn):
199n/a rmtree(fn)
200n/a
201n/a def test_unoverwritable_fails(self):
202n/a #create a file clashing with directories in the env dir
203n/a for paths in self.ENV_SUBDIRS[:3]:
204n/a fn = os.path.join(self.env_dir, *paths)
205n/a with open(fn, 'wb') as f:
206n/a f.write(b'')
207n/a self.assertRaises((ValueError, OSError), venv.create, self.env_dir)
208n/a self.clear_directory(self.env_dir)
209n/a
210n/a def test_upgrade(self):
211n/a """
212n/a Test upgrading an existing environment directory.
213n/a """
214n/a # See Issue #21643: the loop needs to run twice to ensure
215n/a # that everything works on the upgrade (the first run just creates
216n/a # the venv).
217n/a for upgrade in (False, True):
218n/a builder = venv.EnvBuilder(upgrade=upgrade)
219n/a self.run_with_capture(builder.create, self.env_dir)
220n/a self.isdir(self.bindir)
221n/a self.isdir(self.include)
222n/a self.isdir(*self.lib)
223n/a fn = self.get_env_file(self.bindir, self.exe)
224n/a if not os.path.exists(fn):
225n/a # diagnostics for Windows buildbot failures
226n/a bd = self.get_env_file(self.bindir)
227n/a print('Contents of %r:' % bd)
228n/a print(' %r' % os.listdir(bd))
229n/a self.assertTrue(os.path.exists(fn), 'File %r should exist.' % fn)
230n/a
231n/a def test_isolation(self):
232n/a """
233n/a Test isolation from system site-packages
234n/a """
235n/a for ssp, s in ((True, 'true'), (False, 'false')):
236n/a builder = venv.EnvBuilder(clear=True, system_site_packages=ssp)
237n/a builder.create(self.env_dir)
238n/a data = self.get_text_file_contents('pyvenv.cfg')
239n/a self.assertIn('include-system-site-packages = %s\n' % s, data)
240n/a
241n/a @unittest.skipUnless(can_symlink(), 'Needs symlinks')
242n/a def test_symlinking(self):
243n/a """
244n/a Test symlinking works as expected
245n/a """
246n/a for usl in (False, True):
247n/a builder = venv.EnvBuilder(clear=True, symlinks=usl)
248n/a builder.create(self.env_dir)
249n/a fn = self.get_env_file(self.bindir, self.exe)
250n/a # Don't test when False, because e.g. 'python' is always
251n/a # symlinked to 'python3.3' in the env, even when symlinking in
252n/a # general isn't wanted.
253n/a if usl:
254n/a self.assertTrue(os.path.islink(fn))
255n/a
256n/a # If a venv is created from a source build and that venv is used to
257n/a # run the test, the pyvenv.cfg in the venv created in the test will
258n/a # point to the venv being used to run the test, and we lose the link
259n/a # to the source build - so Python can't initialise properly.
260n/a @skipInVenv
261n/a def test_executable(self):
262n/a """
263n/a Test that the sys.executable value is as expected.
264n/a """
265n/a rmtree(self.env_dir)
266n/a self.run_with_capture(venv.create, self.env_dir)
267n/a envpy = os.path.join(os.path.realpath(self.env_dir), self.bindir, self.exe)
268n/a cmd = [envpy, '-c', 'import sys; print(sys.executable)']
269n/a p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
270n/a stderr=subprocess.PIPE)
271n/a out, err = p.communicate()
272n/a self.assertEqual(out.strip(), envpy.encode())
273n/a
274n/a @unittest.skipUnless(can_symlink(), 'Needs symlinks')
275n/a def test_executable_symlinks(self):
276n/a """
277n/a Test that the sys.executable value is as expected.
278n/a """
279n/a rmtree(self.env_dir)
280n/a builder = venv.EnvBuilder(clear=True, symlinks=True)
281n/a builder.create(self.env_dir)
282n/a envpy = os.path.join(os.path.realpath(self.env_dir), self.bindir, self.exe)
283n/a cmd = [envpy, '-c', 'import sys; print(sys.executable)']
284n/a p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
285n/a stderr=subprocess.PIPE)
286n/a out, err = p.communicate()
287n/a self.assertEqual(out.strip(), envpy.encode())
288n/a
289n/a
290n/a@skipInVenv
291n/aclass EnsurePipTest(BaseTest):
292n/a """Test venv module installation of pip."""
293n/a def assert_pip_not_installed(self):
294n/a envpy = os.path.join(os.path.realpath(self.env_dir),
295n/a self.bindir, self.exe)
296n/a try_import = 'try:\n import pip\nexcept ImportError:\n print("OK")'
297n/a cmd = [envpy, '-c', try_import]
298n/a p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
299n/a stderr=subprocess.PIPE)
300n/a out, err = p.communicate()
301n/a # We force everything to text, so unittest gives the detailed diff
302n/a # if we get unexpected results
303n/a err = err.decode("latin-1") # Force to text, prevent decoding errors
304n/a self.assertEqual(err, "")
305n/a out = out.decode("latin-1") # Force to text, prevent decoding errors
306n/a self.assertEqual(out.strip(), "OK")
307n/a
308n/a
309n/a def test_no_pip_by_default(self):
310n/a rmtree(self.env_dir)
311n/a self.run_with_capture(venv.create, self.env_dir)
312n/a self.assert_pip_not_installed()
313n/a
314n/a def test_explicit_no_pip(self):
315n/a rmtree(self.env_dir)
316n/a self.run_with_capture(venv.create, self.env_dir, with_pip=False)
317n/a self.assert_pip_not_installed()
318n/a
319n/a def test_devnull(self):
320n/a # Fix for issue #20053 uses os.devnull to force a config file to
321n/a # appear empty. However http://bugs.python.org/issue20541 means
322n/a # that doesn't currently work properly on Windows. Once that is
323n/a # fixed, the "win_location" part of test_with_pip should be restored
324n/a with open(os.devnull, "rb") as f:
325n/a self.assertEqual(f.read(), b"")
326n/a
327n/a # Issue #20541: os.path.exists('nul') is False on Windows
328n/a if os.devnull.lower() == 'nul':
329n/a self.assertFalse(os.path.exists(os.devnull))
330n/a else:
331n/a self.assertTrue(os.path.exists(os.devnull))
332n/a
333n/a def do_test_with_pip(self, system_site_packages):
334n/a rmtree(self.env_dir)
335n/a with EnvironmentVarGuard() as envvars:
336n/a # pip's cross-version compatibility may trigger deprecation
337n/a # warnings in current versions of Python. Ensure related
338n/a # environment settings don't cause venv to fail.
339n/a envvars["PYTHONWARNINGS"] = "e"
340n/a # ensurepip is different enough from a normal pip invocation
341n/a # that we want to ensure it ignores the normal pip environment
342n/a # variable settings. We set PIP_NO_INSTALL here specifically
343n/a # to check that ensurepip (and hence venv) ignores it.
344n/a # See http://bugs.python.org/issue19734
345n/a envvars["PIP_NO_INSTALL"] = "1"
346n/a # Also check that we ignore the pip configuration file
347n/a # See http://bugs.python.org/issue20053
348n/a with tempfile.TemporaryDirectory() as home_dir:
349n/a envvars["HOME"] = home_dir
350n/a bad_config = "[global]\nno-install=1"
351n/a # Write to both config file names on all platforms to reduce
352n/a # cross-platform variation in test code behaviour
353n/a win_location = ("pip", "pip.ini")
354n/a posix_location = (".pip", "pip.conf")
355n/a # Skips win_location due to http://bugs.python.org/issue20541
356n/a for dirname, fname in (posix_location,):
357n/a dirpath = os.path.join(home_dir, dirname)
358n/a os.mkdir(dirpath)
359n/a fpath = os.path.join(dirpath, fname)
360n/a with open(fpath, 'w') as f:
361n/a f.write(bad_config)
362n/a
363n/a # Actually run the create command with all that unhelpful
364n/a # config in place to ensure we ignore it
365n/a try:
366n/a self.run_with_capture(venv.create, self.env_dir,
367n/a system_site_packages=system_site_packages,
368n/a with_pip=True)
369n/a except subprocess.CalledProcessError as exc:
370n/a # The output this produces can be a little hard to read,
371n/a # but at least it has all the details
372n/a details = exc.output.decode(errors="replace")
373n/a msg = "{}\n\n**Subprocess Output**\n{}"
374n/a self.fail(msg.format(exc, details))
375n/a # Ensure pip is available in the virtual environment
376n/a envpy = os.path.join(os.path.realpath(self.env_dir), self.bindir, self.exe)
377n/a cmd = [envpy, '-Im', 'pip', '--version']
378n/a p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
379n/a stderr=subprocess.PIPE)
380n/a out, err = p.communicate()
381n/a # We force everything to text, so unittest gives the detailed diff
382n/a # if we get unexpected results
383n/a err = err.decode("latin-1") # Force to text, prevent decoding errors
384n/a self.assertEqual(err, "")
385n/a out = out.decode("latin-1") # Force to text, prevent decoding errors
386n/a expected_version = "pip {}".format(ensurepip.version())
387n/a self.assertEqual(out[:len(expected_version)], expected_version)
388n/a env_dir = os.fsencode(self.env_dir).decode("latin-1")
389n/a self.assertIn(env_dir, out)
390n/a
391n/a # http://bugs.python.org/issue19728
392n/a # Check the private uninstall command provided for the Windows
393n/a # installers works (at least in a virtual environment)
394n/a cmd = [envpy, '-Im', 'ensurepip._uninstall']
395n/a with EnvironmentVarGuard() as envvars:
396n/a p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
397n/a stderr=subprocess.PIPE)
398n/a out, err = p.communicate()
399n/a # We force everything to text, so unittest gives the detailed diff
400n/a # if we get unexpected results
401n/a err = err.decode("latin-1") # Force to text, prevent decoding errors
402n/a # Ignore the warning:
403n/a # "The directory '$HOME/.cache/pip/http' or its parent directory
404n/a # is not owned by the current user and the cache has been disabled.
405n/a # Please check the permissions and owner of that directory. If
406n/a # executing pip with sudo, you may want sudo's -H flag."
407n/a # where $HOME is replaced by the HOME environment variable.
408n/a err = re.sub("^The directory .* or its parent directory is not owned "
409n/a "by the current user .*$", "", err, flags=re.MULTILINE)
410n/a self.assertEqual(err.rstrip(), "")
411n/a # Being fairly specific regarding the expected behaviour for the
412n/a # initial bundling phase in Python 3.4. If the output changes in
413n/a # future pip versions, this test can likely be relaxed further.
414n/a out = out.decode("latin-1") # Force to text, prevent decoding errors
415n/a self.assertIn("Successfully uninstalled pip", out)
416n/a self.assertIn("Successfully uninstalled setuptools", out)
417n/a # Check pip is now gone from the virtual environment. This only
418n/a # applies in the system_site_packages=False case, because in the
419n/a # other case, pip may still be available in the system site-packages
420n/a if not system_site_packages:
421n/a self.assert_pip_not_installed()
422n/a
423n/a @unittest.skipUnless(threading, 'some dependencies of pip import threading'
424n/a ' module unconditionally')
425n/a # Issue #26610: pip/pep425tags.py requires ctypes
426n/a @unittest.skipUnless(ctypes, 'pip requires ctypes')
427n/a def test_with_pip(self):
428n/a self.do_test_with_pip(False)
429n/a self.do_test_with_pip(True)
430n/a
431n/aif __name__ == "__main__":
432n/a unittest.main()