blob: 613d9c3a6a39b4deb722f492c2e590eda2a78b43 [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
18
Nick Coghlan8fbdb092013-11-23 00:30:34 +100019skipInVenv = unittest.skipIf(sys.prefix != sys.base_prefix,
20 'Test not appropriate in a venv')
21
22
Vinay Sajip7ded1f02012-05-26 03:45:29 +010023class BaseTest(unittest.TestCase):
24 """Base class for venv tests."""
25
26 def setUp(self):
Ned Deily045bd532012-07-13 15:48:04 -070027 self.env_dir = os.path.realpath(tempfile.mkdtemp())
Vinay Sajip7ded1f02012-05-26 03:45:29 +010028 if os.name == 'nt':
29 self.bindir = 'Scripts'
Vinay Sajip7ded1f02012-05-26 03:45:29 +010030 self.lib = ('Lib',)
31 self.include = 'Include'
Vinay Sajip7ded1f02012-05-26 03:45:29 +010032 else:
33 self.bindir = 'bin'
Vinay Sajip7ded1f02012-05-26 03:45:29 +010034 self.lib = ('lib', 'python%s' % sys.version[:3])
35 self.include = 'include'
Vinay Sajip28952442012-06-25 00:47:46 +010036 if sys.platform == 'darwin' and '__PYVENV_LAUNCHER__' in os.environ:
37 executable = os.environ['__PYVENV_LAUNCHER__']
Vinay Sajip382a7c02012-05-28 16:34:47 +010038 else:
39 executable = sys.executable
40 self.exe = os.path.split(executable)[-1]
Vinay Sajip7ded1f02012-05-26 03:45:29 +010041
42 def tearDown(self):
43 shutil.rmtree(self.env_dir)
44
45 def run_with_capture(self, func, *args, **kwargs):
46 with captured_stdout() as output:
47 with captured_stderr() as error:
48 func(*args, **kwargs)
49 return output.getvalue(), error.getvalue()
50
51 def get_env_file(self, *args):
52 return os.path.join(self.env_dir, *args)
53
54 def get_text_file_contents(self, *args):
55 with open(self.get_env_file(*args), 'r') as f:
56 result = f.read()
57 return result
58
59class BasicTest(BaseTest):
60 """Test venv module functionality."""
61
Vinay Sajipb3b49cd2012-05-27 18:39:22 +010062 def isdir(self, *args):
63 fn = self.get_env_file(*args)
64 self.assertTrue(os.path.isdir(fn))
65
Vinay Sajip7ded1f02012-05-26 03:45:29 +010066 def test_defaults(self):
67 """
68 Test the create function with default arguments.
69 """
Vinay Sajip7ded1f02012-05-26 03:45:29 +010070 shutil.rmtree(self.env_dir)
71 self.run_with_capture(venv.create, self.env_dir)
Vinay Sajipb3b49cd2012-05-27 18:39:22 +010072 self.isdir(self.bindir)
73 self.isdir(self.include)
74 self.isdir(*self.lib)
Vinay Sajip7ded1f02012-05-26 03:45:29 +010075 data = self.get_text_file_contents('pyvenv.cfg')
Vinay Sajip28952442012-06-25 00:47:46 +010076 if sys.platform == 'darwin' and ('__PYVENV_LAUNCHER__'
Vinay Sajip7ded1f02012-05-26 03:45:29 +010077 in os.environ):
Vinay Sajip28952442012-06-25 00:47:46 +010078 executable = os.environ['__PYVENV_LAUNCHER__']
Vinay Sajip7ded1f02012-05-26 03:45:29 +010079 else:
80 executable = sys.executable
81 path = os.path.dirname(executable)
82 self.assertIn('home = %s' % path, data)
Vinay Sajip7ded1f02012-05-26 03:45:29 +010083 fn = self.get_env_file(self.bindir, self.exe)
Vinay Sajip7e203492012-05-27 17:30:09 +010084 if not os.path.exists(fn): # diagnostics for Windows buildbot failures
Vinay Sajipb3b49cd2012-05-27 18:39:22 +010085 bd = self.get_env_file(self.bindir)
86 print('Contents of %r:' % bd)
87 print(' %r' % os.listdir(bd))
Vinay Sajip7e203492012-05-27 17:30:09 +010088 self.assertTrue(os.path.exists(fn), 'File %r should exist.' % fn)
Vinay Sajip7ded1f02012-05-26 03:45:29 +010089
Nick Coghlan8fbdb092013-11-23 00:30:34 +100090 @skipInVenv
Vinay Sajip3874e542012-07-03 16:56:40 +010091 def test_prefixes(self):
92 """
93 Test that the prefix values are as expected.
94 """
95 #check our prefixes
96 self.assertEqual(sys.base_prefix, sys.prefix)
97 self.assertEqual(sys.base_exec_prefix, sys.exec_prefix)
98
99 # check a venv's prefixes
100 shutil.rmtree(self.env_dir)
101 self.run_with_capture(venv.create, self.env_dir)
102 envpy = os.path.join(self.env_dir, self.bindir, self.exe)
103 cmd = [envpy, '-c', None]
104 for prefix, expected in (
105 ('prefix', self.env_dir),
106 ('prefix', self.env_dir),
107 ('base_prefix', sys.prefix),
108 ('base_exec_prefix', sys.exec_prefix)):
109 cmd[2] = 'import sys; print(sys.%s)' % prefix
110 p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
111 stderr=subprocess.PIPE)
112 out, err = p.communicate()
Antoine Pitrou9c92a692012-08-05 00:33:10 +0200113 self.assertEqual(out.strip(), expected.encode())
Vinay Sajip3874e542012-07-03 16:56:40 +0100114
Vinay Sajipbd40d3e2012-10-11 17:22:45 +0100115 if sys.platform == 'win32':
116 ENV_SUBDIRS = (
117 ('Scripts',),
118 ('Include',),
119 ('Lib',),
120 ('Lib', 'site-packages'),
121 )
122 else:
123 ENV_SUBDIRS = (
124 ('bin',),
125 ('include',),
126 ('lib',),
127 ('lib', 'python%d.%d' % sys.version_info[:2]),
128 ('lib', 'python%d.%d' % sys.version_info[:2], 'site-packages'),
129 )
130
131 def create_contents(self, paths, filename):
132 """
133 Create some files in the environment which are unrelated
134 to the virtual environment.
135 """
136 for subdirs in paths:
137 d = os.path.join(self.env_dir, *subdirs)
138 os.mkdir(d)
139 fn = os.path.join(d, filename)
140 with open(fn, 'wb') as f:
141 f.write(b'Still here?')
142
Vinay Sajip7ded1f02012-05-26 03:45:29 +0100143 def test_overwrite_existing(self):
144 """
Vinay Sajipbd40d3e2012-10-11 17:22:45 +0100145 Test creating environment in an existing directory.
Vinay Sajip7ded1f02012-05-26 03:45:29 +0100146 """
Vinay Sajipbd40d3e2012-10-11 17:22:45 +0100147 self.create_contents(self.ENV_SUBDIRS, 'foo')
148 venv.create(self.env_dir)
149 for subdirs in self.ENV_SUBDIRS:
150 fn = os.path.join(self.env_dir, *(subdirs + ('foo',)))
151 self.assertTrue(os.path.exists(fn))
152 with open(fn, 'rb') as f:
153 self.assertEqual(f.read(), b'Still here?')
154
Vinay Sajip7ded1f02012-05-26 03:45:29 +0100155 builder = venv.EnvBuilder(clear=True)
156 builder.create(self.env_dir)
Vinay Sajipbd40d3e2012-10-11 17:22:45 +0100157 for subdirs in self.ENV_SUBDIRS:
158 fn = os.path.join(self.env_dir, *(subdirs + ('foo',)))
159 self.assertFalse(os.path.exists(fn))
160
161 def clear_directory(self, path):
162 for fn in os.listdir(path):
163 fn = os.path.join(path, fn)
164 if os.path.islink(fn) or os.path.isfile(fn):
165 os.remove(fn)
166 elif os.path.isdir(fn):
167 shutil.rmtree(fn)
168
169 def test_unoverwritable_fails(self):
170 #create a file clashing with directories in the env dir
171 for paths in self.ENV_SUBDIRS[:3]:
172 fn = os.path.join(self.env_dir, *paths)
173 with open(fn, 'wb') as f:
174 f.write(b'')
175 self.assertRaises((ValueError, OSError), venv.create, self.env_dir)
176 self.clear_directory(self.env_dir)
Vinay Sajip7ded1f02012-05-26 03:45:29 +0100177
Vinay Sajipb3b49cd2012-05-27 18:39:22 +0100178 def test_upgrade(self):
179 """
180 Test upgrading an existing environment directory.
181 """
182 builder = venv.EnvBuilder(upgrade=True)
183 self.run_with_capture(builder.create, self.env_dir)
184 self.isdir(self.bindir)
185 self.isdir(self.include)
186 self.isdir(*self.lib)
187 fn = self.get_env_file(self.bindir, self.exe)
188 if not os.path.exists(fn): # diagnostics for Windows buildbot failures
189 bd = self.get_env_file(self.bindir)
190 print('Contents of %r:' % bd)
191 print(' %r' % os.listdir(bd))
192 self.assertTrue(os.path.exists(fn), 'File %r should exist.' % fn)
193
Vinay Sajip7ded1f02012-05-26 03:45:29 +0100194 def test_isolation(self):
195 """
196 Test isolation from system site-packages
197 """
198 for ssp, s in ((True, 'true'), (False, 'false')):
199 builder = venv.EnvBuilder(clear=True, system_site_packages=ssp)
200 builder.create(self.env_dir)
201 data = self.get_text_file_contents('pyvenv.cfg')
202 self.assertIn('include-system-site-packages = %s\n' % s, data)
203
204 @unittest.skipUnless(can_symlink(), 'Needs symlinks')
205 def test_symlinking(self):
206 """
207 Test symlinking works as expected
208 """
209 for usl in (False, True):
210 builder = venv.EnvBuilder(clear=True, symlinks=usl)
Vinay Sajip90db6612012-07-17 17:33:46 +0100211 builder.create(self.env_dir)
212 fn = self.get_env_file(self.bindir, self.exe)
213 # Don't test when False, because e.g. 'python' is always
214 # symlinked to 'python3.3' in the env, even when symlinking in
215 # general isn't wanted.
216 if usl:
217 self.assertTrue(os.path.islink(fn))
218
219 # If a venv is created from a source build and that venv is used to
220 # run the test, the pyvenv.cfg in the venv created in the test will
221 # point to the venv being used to run the test, and we lose the link
222 # to the source build - so Python can't initialise properly.
Nick Coghlan8fbdb092013-11-23 00:30:34 +1000223 @skipInVenv
Vinay Sajip90db6612012-07-17 17:33:46 +0100224 def test_executable(self):
225 """
226 Test that the sys.executable value is as expected.
227 """
228 shutil.rmtree(self.env_dir)
229 self.run_with_capture(venv.create, self.env_dir)
230 envpy = os.path.join(os.path.realpath(self.env_dir), self.bindir, self.exe)
231 cmd = [envpy, '-c', 'import sys; print(sys.executable)']
232 p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
233 stderr=subprocess.PIPE)
234 out, err = p.communicate()
Antoine Pitrou9c92a692012-08-05 00:33:10 +0200235 self.assertEqual(out.strip(), envpy.encode())
Vinay Sajip90db6612012-07-17 17:33:46 +0100236
237 @unittest.skipUnless(can_symlink(), 'Needs symlinks')
238 def test_executable_symlinks(self):
239 """
240 Test that the sys.executable value is as expected.
241 """
242 shutil.rmtree(self.env_dir)
243 builder = venv.EnvBuilder(clear=True, symlinks=True)
244 builder.create(self.env_dir)
245 envpy = os.path.join(os.path.realpath(self.env_dir), self.bindir, self.exe)
246 cmd = [envpy, '-c', 'import sys; print(sys.executable)']
247 p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
248 stderr=subprocess.PIPE)
249 out, err = p.communicate()
Antoine Pitrou9c92a692012-08-05 00:33:10 +0200250 self.assertEqual(out.strip(), envpy.encode())
Vinay Sajip7ded1f02012-05-26 03:45:29 +0100251
Nick Coghlan8fbdb092013-11-23 00:30:34 +1000252
253@skipInVenv
254class EnsurePipTest(BaseTest):
255 """Test venv module installation of pip."""
256
257 def test_no_pip_by_default(self):
258 shutil.rmtree(self.env_dir)
259 self.run_with_capture(venv.create, self.env_dir)
260 envpy = os.path.join(os.path.realpath(self.env_dir), self.bindir, self.exe)
261 try_import = 'try:\n import pip\nexcept ImportError:\n print("OK")'
262 cmd = [envpy, '-c', try_import]
263 p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
264 stderr=subprocess.PIPE)
265 out, err = p.communicate()
266 self.assertEqual(err, b"")
267 self.assertEqual(out.strip(), b"OK")
268
269 def test_explicit_no_pip(self):
270 shutil.rmtree(self.env_dir)
271 self.run_with_capture(venv.create, self.env_dir, with_pip=False)
272 envpy = os.path.join(os.path.realpath(self.env_dir), self.bindir, self.exe)
273 try_import = 'try:\n import pip\nexcept ImportError:\n print("OK")'
274 cmd = [envpy, '-c', try_import]
275 p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
276 stderr=subprocess.PIPE)
277 out, err = p.communicate()
278 self.assertEqual(err, b"")
279 self.assertEqual(out.strip(), b"OK")
280
281 def test_with_pip(self):
282 shutil.rmtree(self.env_dir)
Nick Coghland76cdc12013-11-23 11:37:28 +1000283 with EnvironmentVarGuard() as envvars:
284 # pip's cross-version compatibility may trigger deprecation
285 # warnings in current versions of Python. Ensure related
286 # environment settings don't cause venv to fail.
287 envvars["PYTHONWARNINGS"] = "e"
Nick Coghlan6fd12f22013-11-24 11:36:31 +1000288 try:
289 self.run_with_capture(venv.create, self.env_dir, with_pip=True)
290 except subprocess.CalledProcessError as exc:
291 # The output this produces can be a little hard to read, but
292 # least it has all the details
293 details = exc.output.decode(errors="replace")
294 msg = "{}\n\n**Subprocess Output**\n{}".format(exc, details)
295 self.fail(msg)
Nick Coghlan8fbdb092013-11-23 00:30:34 +1000296 envpy = os.path.join(os.path.realpath(self.env_dir), self.bindir, self.exe)
297 cmd = [envpy, '-m', 'pip', '--version']
298 p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
299 stderr=subprocess.PIPE)
300 out, err = p.communicate()
Nick Coghlan6fd12f22013-11-24 11:36:31 +1000301 # We force everything to text, so unittest gives the detailed diff
302 # if we get unexpected results
303 err = err.decode("latin-1") # Force to text, prevent decoding errors
304 self.assertEqual(err, "")
305 out = out.decode("latin-1") # Force to text, prevent decoding errors
306 env_dir = os.fsencode(self.env_dir).decode("latin-1")
307 self.assertTrue(out.startswith("pip"))
308 self.assertIn(env_dir, out)
Nick Coghlan8fbdb092013-11-23 00:30:34 +1000309
310
Vinay Sajip7ded1f02012-05-26 03:45:29 +0100311def test_main():
Nick Coghlan8fbdb092013-11-23 00:30:34 +1000312 run_unittest(BasicTest, EnsurePipTest)
Vinay Sajip7ded1f02012-05-26 03:45:29 +0100313
314if __name__ == "__main__":
315 test_main()