blob: f53eb37dc7147bda0865f96cbd71dac04e4f0a29 [file] [log] [blame]
Vinay Sajip42211422012-05-26 20:36:12 +01001"""
2Test harness for the venv module.
Vinay Sajip7ded1f02012-05-26 03:45:29 +01003
Vinay Sajip42211422012-05-26 20:36:12 +01004Copyright (C) 2011-2012 Vinay Sajip.
Vinay Sajip28952442012-06-25 00:47:46 +01005Licensed to the PSF under a contributor agreement.
Vinay Sajip7ded1f02012-05-26 03:45:29 +01006"""
7
8import os
9import os.path
10import shutil
Vinay Sajip3874e542012-07-03 16:56:40 +010011import subprocess
Vinay Sajip7ded1f02012-05-26 03:45:29 +010012import sys
13import tempfile
14from test.support import (captured_stdout, captured_stderr, run_unittest,
Nick Coghland76cdc12013-11-23 11:37:28 +100015 can_symlink, EnvironmentVarGuard)
Vinay Sajip7ded1f02012-05-26 03:45:29 +010016import unittest
17import venv
Nick Coghlan878d2582013-11-24 12:45:25 +100018try:
19 import ssl
20except ImportError:
21 ssl = None
Vinay Sajip7ded1f02012-05-26 03:45:29 +010022
Nick Coghlan8fbdb092013-11-23 00:30:34 +100023skipInVenv = unittest.skipIf(sys.prefix != sys.base_prefix,
24 'Test not appropriate in a venv')
25
26
Vinay Sajip7ded1f02012-05-26 03:45:29 +010027class BaseTest(unittest.TestCase):
28 """Base class for venv tests."""
29
30 def setUp(self):
Ned Deily045bd532012-07-13 15:48:04 -070031 self.env_dir = os.path.realpath(tempfile.mkdtemp())
Vinay Sajip7ded1f02012-05-26 03:45:29 +010032 if os.name == 'nt':
33 self.bindir = 'Scripts'
Vinay Sajip7ded1f02012-05-26 03:45:29 +010034 self.lib = ('Lib',)
35 self.include = 'Include'
Vinay Sajip7ded1f02012-05-26 03:45:29 +010036 else:
37 self.bindir = 'bin'
Vinay Sajip7ded1f02012-05-26 03:45:29 +010038 self.lib = ('lib', 'python%s' % sys.version[:3])
39 self.include = 'include'
Vinay Sajip28952442012-06-25 00:47:46 +010040 if sys.platform == 'darwin' and '__PYVENV_LAUNCHER__' in os.environ:
41 executable = os.environ['__PYVENV_LAUNCHER__']
Vinay Sajip382a7c02012-05-28 16:34:47 +010042 else:
43 executable = sys.executable
44 self.exe = os.path.split(executable)[-1]
Vinay Sajip7ded1f02012-05-26 03:45:29 +010045
46 def tearDown(self):
47 shutil.rmtree(self.env_dir)
48
49 def run_with_capture(self, func, *args, **kwargs):
50 with captured_stdout() as output:
51 with captured_stderr() as error:
52 func(*args, **kwargs)
53 return output.getvalue(), error.getvalue()
54
55 def get_env_file(self, *args):
56 return os.path.join(self.env_dir, *args)
57
58 def get_text_file_contents(self, *args):
59 with open(self.get_env_file(*args), 'r') as f:
60 result = f.read()
61 return result
62
63class BasicTest(BaseTest):
64 """Test venv module functionality."""
65
Vinay Sajipb3b49cd2012-05-27 18:39:22 +010066 def isdir(self, *args):
67 fn = self.get_env_file(*args)
68 self.assertTrue(os.path.isdir(fn))
69
Vinay Sajip7ded1f02012-05-26 03:45:29 +010070 def test_defaults(self):
71 """
72 Test the create function with default arguments.
73 """
Vinay Sajip7ded1f02012-05-26 03:45:29 +010074 shutil.rmtree(self.env_dir)
75 self.run_with_capture(venv.create, self.env_dir)
Vinay Sajipb3b49cd2012-05-27 18:39:22 +010076 self.isdir(self.bindir)
77 self.isdir(self.include)
78 self.isdir(*self.lib)
Vinay Sajip7ded1f02012-05-26 03:45:29 +010079 data = self.get_text_file_contents('pyvenv.cfg')
Vinay Sajip28952442012-06-25 00:47:46 +010080 if sys.platform == 'darwin' and ('__PYVENV_LAUNCHER__'
Vinay Sajip7ded1f02012-05-26 03:45:29 +010081 in os.environ):
Vinay Sajip28952442012-06-25 00:47:46 +010082 executable = os.environ['__PYVENV_LAUNCHER__']
Vinay Sajip7ded1f02012-05-26 03:45:29 +010083 else:
84 executable = sys.executable
85 path = os.path.dirname(executable)
86 self.assertIn('home = %s' % path, data)
Vinay Sajip7ded1f02012-05-26 03:45:29 +010087 fn = self.get_env_file(self.bindir, self.exe)
Vinay Sajip7e203492012-05-27 17:30:09 +010088 if not os.path.exists(fn): # diagnostics for Windows buildbot failures
Vinay Sajipb3b49cd2012-05-27 18:39:22 +010089 bd = self.get_env_file(self.bindir)
90 print('Contents of %r:' % bd)
91 print(' %r' % os.listdir(bd))
Vinay Sajip7e203492012-05-27 17:30:09 +010092 self.assertTrue(os.path.exists(fn), 'File %r should exist.' % fn)
Vinay Sajip7ded1f02012-05-26 03:45:29 +010093
Nick Coghlan8fbdb092013-11-23 00:30:34 +100094 @skipInVenv
Vinay Sajip3874e542012-07-03 16:56:40 +010095 def test_prefixes(self):
96 """
97 Test that the prefix values are as expected.
98 """
99 #check our prefixes
100 self.assertEqual(sys.base_prefix, sys.prefix)
101 self.assertEqual(sys.base_exec_prefix, sys.exec_prefix)
102
103 # check a venv's prefixes
104 shutil.rmtree(self.env_dir)
105 self.run_with_capture(venv.create, self.env_dir)
106 envpy = os.path.join(self.env_dir, self.bindir, self.exe)
107 cmd = [envpy, '-c', None]
108 for prefix, expected in (
109 ('prefix', self.env_dir),
110 ('prefix', self.env_dir),
111 ('base_prefix', sys.prefix),
112 ('base_exec_prefix', sys.exec_prefix)):
113 cmd[2] = 'import sys; print(sys.%s)' % prefix
114 p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
115 stderr=subprocess.PIPE)
116 out, err = p.communicate()
Antoine Pitrou9c92a692012-08-05 00:33:10 +0200117 self.assertEqual(out.strip(), expected.encode())
Vinay Sajip3874e542012-07-03 16:56:40 +0100118
Vinay Sajipbd40d3e2012-10-11 17:22:45 +0100119 if sys.platform == 'win32':
120 ENV_SUBDIRS = (
121 ('Scripts',),
122 ('Include',),
123 ('Lib',),
124 ('Lib', 'site-packages'),
125 )
126 else:
127 ENV_SUBDIRS = (
128 ('bin',),
129 ('include',),
130 ('lib',),
131 ('lib', 'python%d.%d' % sys.version_info[:2]),
132 ('lib', 'python%d.%d' % sys.version_info[:2], 'site-packages'),
133 )
134
135 def create_contents(self, paths, filename):
136 """
137 Create some files in the environment which are unrelated
138 to the virtual environment.
139 """
140 for subdirs in paths:
141 d = os.path.join(self.env_dir, *subdirs)
142 os.mkdir(d)
143 fn = os.path.join(d, filename)
144 with open(fn, 'wb') as f:
145 f.write(b'Still here?')
146
Vinay Sajip7ded1f02012-05-26 03:45:29 +0100147 def test_overwrite_existing(self):
148 """
Vinay Sajipbd40d3e2012-10-11 17:22:45 +0100149 Test creating environment in an existing directory.
Vinay Sajip7ded1f02012-05-26 03:45:29 +0100150 """
Vinay Sajipbd40d3e2012-10-11 17:22:45 +0100151 self.create_contents(self.ENV_SUBDIRS, 'foo')
152 venv.create(self.env_dir)
153 for subdirs in self.ENV_SUBDIRS:
154 fn = os.path.join(self.env_dir, *(subdirs + ('foo',)))
155 self.assertTrue(os.path.exists(fn))
156 with open(fn, 'rb') as f:
157 self.assertEqual(f.read(), b'Still here?')
158
Vinay Sajip7ded1f02012-05-26 03:45:29 +0100159 builder = venv.EnvBuilder(clear=True)
160 builder.create(self.env_dir)
Vinay Sajipbd40d3e2012-10-11 17:22:45 +0100161 for subdirs in self.ENV_SUBDIRS:
162 fn = os.path.join(self.env_dir, *(subdirs + ('foo',)))
163 self.assertFalse(os.path.exists(fn))
164
165 def clear_directory(self, path):
166 for fn in os.listdir(path):
167 fn = os.path.join(path, fn)
168 if os.path.islink(fn) or os.path.isfile(fn):
169 os.remove(fn)
170 elif os.path.isdir(fn):
171 shutil.rmtree(fn)
172
173 def test_unoverwritable_fails(self):
174 #create a file clashing with directories in the env dir
175 for paths in self.ENV_SUBDIRS[:3]:
176 fn = os.path.join(self.env_dir, *paths)
177 with open(fn, 'wb') as f:
178 f.write(b'')
179 self.assertRaises((ValueError, OSError), venv.create, self.env_dir)
180 self.clear_directory(self.env_dir)
Vinay Sajip7ded1f02012-05-26 03:45:29 +0100181
Vinay Sajipb3b49cd2012-05-27 18:39:22 +0100182 def test_upgrade(self):
183 """
184 Test upgrading an existing environment directory.
185 """
186 builder = venv.EnvBuilder(upgrade=True)
187 self.run_with_capture(builder.create, self.env_dir)
188 self.isdir(self.bindir)
189 self.isdir(self.include)
190 self.isdir(*self.lib)
191 fn = self.get_env_file(self.bindir, self.exe)
192 if not os.path.exists(fn): # diagnostics for Windows buildbot failures
193 bd = self.get_env_file(self.bindir)
194 print('Contents of %r:' % bd)
195 print(' %r' % os.listdir(bd))
196 self.assertTrue(os.path.exists(fn), 'File %r should exist.' % fn)
197
Vinay Sajip7ded1f02012-05-26 03:45:29 +0100198 def test_isolation(self):
199 """
200 Test isolation from system site-packages
201 """
202 for ssp, s in ((True, 'true'), (False, 'false')):
203 builder = venv.EnvBuilder(clear=True, system_site_packages=ssp)
204 builder.create(self.env_dir)
205 data = self.get_text_file_contents('pyvenv.cfg')
206 self.assertIn('include-system-site-packages = %s\n' % s, data)
207
208 @unittest.skipUnless(can_symlink(), 'Needs symlinks')
209 def test_symlinking(self):
210 """
211 Test symlinking works as expected
212 """
213 for usl in (False, True):
214 builder = venv.EnvBuilder(clear=True, symlinks=usl)
Vinay Sajip90db6612012-07-17 17:33:46 +0100215 builder.create(self.env_dir)
216 fn = self.get_env_file(self.bindir, self.exe)
217 # Don't test when False, because e.g. 'python' is always
218 # symlinked to 'python3.3' in the env, even when symlinking in
219 # general isn't wanted.
220 if usl:
221 self.assertTrue(os.path.islink(fn))
222
223 # If a venv is created from a source build and that venv is used to
224 # run the test, the pyvenv.cfg in the venv created in the test will
225 # point to the venv being used to run the test, and we lose the link
226 # to the source build - so Python can't initialise properly.
Nick Coghlan8fbdb092013-11-23 00:30:34 +1000227 @skipInVenv
Vinay Sajip90db6612012-07-17 17:33:46 +0100228 def test_executable(self):
229 """
230 Test that the sys.executable value is as expected.
231 """
232 shutil.rmtree(self.env_dir)
233 self.run_with_capture(venv.create, self.env_dir)
234 envpy = os.path.join(os.path.realpath(self.env_dir), self.bindir, self.exe)
235 cmd = [envpy, '-c', 'import sys; print(sys.executable)']
236 p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
237 stderr=subprocess.PIPE)
238 out, err = p.communicate()
Antoine Pitrou9c92a692012-08-05 00:33:10 +0200239 self.assertEqual(out.strip(), envpy.encode())
Vinay Sajip90db6612012-07-17 17:33:46 +0100240
241 @unittest.skipUnless(can_symlink(), 'Needs symlinks')
242 def test_executable_symlinks(self):
243 """
244 Test that the sys.executable value is as expected.
245 """
246 shutil.rmtree(self.env_dir)
247 builder = venv.EnvBuilder(clear=True, symlinks=True)
248 builder.create(self.env_dir)
249 envpy = os.path.join(os.path.realpath(self.env_dir), self.bindir, self.exe)
250 cmd = [envpy, '-c', 'import sys; print(sys.executable)']
251 p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
252 stderr=subprocess.PIPE)
253 out, err = p.communicate()
Antoine Pitrou9c92a692012-08-05 00:33:10 +0200254 self.assertEqual(out.strip(), envpy.encode())
Vinay Sajip7ded1f02012-05-26 03:45:29 +0100255
Nick Coghlan8fbdb092013-11-23 00:30:34 +1000256
257@skipInVenv
258class EnsurePipTest(BaseTest):
259 """Test venv module installation of pip."""
260
261 def test_no_pip_by_default(self):
262 shutil.rmtree(self.env_dir)
263 self.run_with_capture(venv.create, self.env_dir)
264 envpy = os.path.join(os.path.realpath(self.env_dir), self.bindir, self.exe)
265 try_import = 'try:\n import pip\nexcept ImportError:\n print("OK")'
266 cmd = [envpy, '-c', try_import]
267 p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
268 stderr=subprocess.PIPE)
269 out, err = p.communicate()
270 self.assertEqual(err, b"")
271 self.assertEqual(out.strip(), b"OK")
272
273 def test_explicit_no_pip(self):
274 shutil.rmtree(self.env_dir)
275 self.run_with_capture(venv.create, self.env_dir, with_pip=False)
276 envpy = os.path.join(os.path.realpath(self.env_dir), self.bindir, self.exe)
277 try_import = 'try:\n import pip\nexcept ImportError:\n print("OK")'
278 cmd = [envpy, '-c', try_import]
279 p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
280 stderr=subprocess.PIPE)
281 out, err = p.communicate()
282 self.assertEqual(err, b"")
283 self.assertEqual(out.strip(), b"OK")
284
Nick Coghlan878d2582013-11-24 12:45:25 +1000285 # Temporary skip for http://bugs.python.org/issue19744
286 @unittest.skipIf(ssl is None, 'pip needs SSL support')
Nick Coghlan8fbdb092013-11-23 00:30:34 +1000287 def test_with_pip(self):
288 shutil.rmtree(self.env_dir)
Nick Coghland76cdc12013-11-23 11:37:28 +1000289 with EnvironmentVarGuard() as envvars:
290 # pip's cross-version compatibility may trigger deprecation
291 # warnings in current versions of Python. Ensure related
292 # environment settings don't cause venv to fail.
293 envvars["PYTHONWARNINGS"] = "e"
Nick Coghlan091167c2013-11-24 14:58:31 +1000294 # pip doesn't ignore environment variables when running in
295 # isolated mode, and we don't have an active virtualenv here
296 # See http://bugs.python.org/issue19734 for details
297 del envvars["PIP_REQUIRE_VIRTUALENV"]
Nick Coghlan6fd12f22013-11-24 11:36:31 +1000298 try:
299 self.run_with_capture(venv.create, self.env_dir, with_pip=True)
300 except subprocess.CalledProcessError as exc:
301 # The output this produces can be a little hard to read, but
302 # least it has all the details
303 details = exc.output.decode(errors="replace")
304 msg = "{}\n\n**Subprocess Output**\n{}".format(exc, details)
305 self.fail(msg)
Nick Coghlan8fbdb092013-11-23 00:30:34 +1000306 envpy = os.path.join(os.path.realpath(self.env_dir), self.bindir, self.exe)
Nick Coghlan1d1d8342013-11-24 16:49:20 +1000307 cmd = [envpy, '-Im', 'pip', '--version']
Nick Coghlan8fbdb092013-11-23 00:30:34 +1000308 p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
Nick Coghlan1d1d8342013-11-24 16:49:20 +1000309 stderr=subprocess.PIPE)
Nick Coghlan8fbdb092013-11-23 00:30:34 +1000310 out, err = p.communicate()
Nick Coghlan6fd12f22013-11-24 11:36:31 +1000311 # We force everything to text, so unittest gives the detailed diff
312 # if we get unexpected results
313 err = err.decode("latin-1") # Force to text, prevent decoding errors
314 self.assertEqual(err, "")
315 out = out.decode("latin-1") # Force to text, prevent decoding errors
316 env_dir = os.fsencode(self.env_dir).decode("latin-1")
317 self.assertTrue(out.startswith("pip"))
318 self.assertIn(env_dir, out)
Nick Coghlan8fbdb092013-11-23 00:30:34 +1000319
320
Vinay Sajip7ded1f02012-05-26 03:45:29 +0100321def test_main():
Nick Coghlan8fbdb092013-11-23 00:30:34 +1000322 run_unittest(BasicTest, EnsurePipTest)
Vinay Sajip7ded1f02012-05-26 03:45:29 +0100323
324if __name__ == "__main__":
325 test_main()