Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(289)

Side by Side Diff: recipe_engine/third_party/setuptools/tests/test_easy_install.py

Issue 1344583003: Recipe package system. (Closed) Base URL: git@github.com:luci/recipes-py.git@master
Patch Set: Recompiled proto Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 """Easy install Tests
2 """
3 import sys
4 import os
5 import shutil
6 import tempfile
7 import unittest
8 import site
9 import contextlib
10 import textwrap
11 import tarfile
12 import logging
13 import distutils.core
14
15 from setuptools.compat import StringIO, BytesIO, urlparse
16 from setuptools.sandbox import run_setup, SandboxViolation
17 from setuptools.command.easy_install import (
18 easy_install, fix_jython_executable, get_script_args, nt_quote_arg)
19 from setuptools.command.easy_install import PthDistributions
20 from setuptools.command import easy_install as easy_install_pkg
21 from setuptools.dist import Distribution
22 from pkg_resources import working_set, VersionConflict
23 from pkg_resources import Distribution as PRDistribution
24 import setuptools.tests.server
25 import pkg_resources
26 from .py26compat import skipIf
27
28 class FakeDist(object):
29 def get_entry_map(self, group):
30 if group != 'console_scripts':
31 return {}
32 return {'name': 'ep'}
33
34 def as_requirement(self):
35 return 'spec'
36
37 WANTED = """\
38 #!%s
39 # EASY-INSTALL-ENTRY-SCRIPT: 'spec','console_scripts','name'
40 __requires__ = 'spec'
41 import sys
42 from pkg_resources import load_entry_point
43
44 if __name__ == '__main__':
45 sys.exit(
46 load_entry_point('spec', 'console_scripts', 'name')()
47 )
48 """ % nt_quote_arg(fix_jython_executable(sys.executable, ""))
49
50 SETUP_PY = """\
51 from setuptools import setup
52
53 setup(name='foo')
54 """
55
56 class TestEasyInstallTest(unittest.TestCase):
57
58 def test_install_site_py(self):
59 dist = Distribution()
60 cmd = easy_install(dist)
61 cmd.sitepy_installed = False
62 cmd.install_dir = tempfile.mkdtemp()
63 try:
64 cmd.install_site_py()
65 sitepy = os.path.join(cmd.install_dir, 'site.py')
66 self.assertTrue(os.path.exists(sitepy))
67 finally:
68 shutil.rmtree(cmd.install_dir)
69
70 def test_get_script_args(self):
71 dist = FakeDist()
72
73 old_platform = sys.platform
74 try:
75 name, script = [i for i in next(get_script_args(dist))][0:2]
76 finally:
77 sys.platform = old_platform
78
79 self.assertEqual(script, WANTED)
80
81 def test_no_find_links(self):
82 # new option '--no-find-links', that blocks find-links added at
83 # the project level
84 dist = Distribution()
85 cmd = easy_install(dist)
86 cmd.check_pth_processing = lambda: True
87 cmd.no_find_links = True
88 cmd.find_links = ['link1', 'link2']
89 cmd.install_dir = os.path.join(tempfile.mkdtemp(), 'ok')
90 cmd.args = ['ok']
91 cmd.ensure_finalized()
92 self.assertEqual(cmd.package_index.scanned_urls, {})
93
94 # let's try without it (default behavior)
95 cmd = easy_install(dist)
96 cmd.check_pth_processing = lambda: True
97 cmd.find_links = ['link1', 'link2']
98 cmd.install_dir = os.path.join(tempfile.mkdtemp(), 'ok')
99 cmd.args = ['ok']
100 cmd.ensure_finalized()
101 keys = sorted(cmd.package_index.scanned_urls.keys())
102 self.assertEqual(keys, ['link1', 'link2'])
103
104
105 class TestPTHFileWriter(unittest.TestCase):
106 def test_add_from_cwd_site_sets_dirty(self):
107 '''a pth file manager should set dirty
108 if a distribution is in site but also the cwd
109 '''
110 pth = PthDistributions('does-not_exist', [os.getcwd()])
111 self.assertTrue(not pth.dirty)
112 pth.add(PRDistribution(os.getcwd()))
113 self.assertTrue(pth.dirty)
114
115 def test_add_from_site_is_ignored(self):
116 if os.name != 'nt':
117 location = '/test/location/does-not-have-to-exist'
118 else:
119 location = 'c:\\does_not_exist'
120 pth = PthDistributions('does-not_exist', [location, ])
121 self.assertTrue(not pth.dirty)
122 pth.add(PRDistribution(location))
123 self.assertTrue(not pth.dirty)
124
125
126 class TestUserInstallTest(unittest.TestCase):
127
128 def setUp(self):
129 self.dir = tempfile.mkdtemp()
130 setup = os.path.join(self.dir, 'setup.py')
131 f = open(setup, 'w')
132 f.write(SETUP_PY)
133 f.close()
134 self.old_cwd = os.getcwd()
135 os.chdir(self.dir)
136
137 self.old_enable_site = site.ENABLE_USER_SITE
138 self.old_file = easy_install_pkg.__file__
139 self.old_base = site.USER_BASE
140 site.USER_BASE = tempfile.mkdtemp()
141 self.old_site = site.USER_SITE
142 site.USER_SITE = tempfile.mkdtemp()
143 easy_install_pkg.__file__ = site.USER_SITE
144
145 def tearDown(self):
146 os.chdir(self.old_cwd)
147 shutil.rmtree(self.dir)
148
149 shutil.rmtree(site.USER_BASE)
150 shutil.rmtree(site.USER_SITE)
151 site.USER_BASE = self.old_base
152 site.USER_SITE = self.old_site
153 site.ENABLE_USER_SITE = self.old_enable_site
154 easy_install_pkg.__file__ = self.old_file
155
156 def test_user_install_implied(self):
157 site.ENABLE_USER_SITE = True # disabled sometimes
158 #XXX: replace with something meaningfull
159 dist = Distribution()
160 dist.script_name = 'setup.py'
161 cmd = easy_install(dist)
162 cmd.args = ['py']
163 cmd.ensure_finalized()
164 self.assertTrue(cmd.user, 'user should be implied')
165
166 def test_multiproc_atexit(self):
167 try:
168 __import__('multiprocessing')
169 except ImportError:
170 # skip the test if multiprocessing is not available
171 return
172
173 log = logging.getLogger('test_easy_install')
174 logging.basicConfig(level=logging.INFO, stream=sys.stderr)
175 log.info('this should not break')
176
177 def test_user_install_not_implied_without_usersite_enabled(self):
178 site.ENABLE_USER_SITE = False # usually enabled
179 #XXX: replace with something meaningfull
180 dist = Distribution()
181 dist.script_name = 'setup.py'
182 cmd = easy_install(dist)
183 cmd.args = ['py']
184 cmd.initialize_options()
185 self.assertFalse(cmd.user, 'NOT user should be implied')
186
187 def test_local_index(self):
188 # make sure the local index is used
189 # when easy_install looks for installed
190 # packages
191 new_location = tempfile.mkdtemp()
192 target = tempfile.mkdtemp()
193 egg_file = os.path.join(new_location, 'foo-1.0.egg-info')
194 f = open(egg_file, 'w')
195 try:
196 f.write('Name: foo\n')
197 finally:
198 f.close()
199
200 sys.path.append(target)
201 old_ppath = os.environ.get('PYTHONPATH')
202 os.environ['PYTHONPATH'] = os.path.pathsep.join(sys.path)
203 try:
204 dist = Distribution()
205 dist.script_name = 'setup.py'
206 cmd = easy_install(dist)
207 cmd.install_dir = target
208 cmd.args = ['foo']
209 cmd.ensure_finalized()
210 cmd.local_index.scan([new_location])
211 res = cmd.easy_install('foo')
212 actual = os.path.normcase(os.path.realpath(res.location))
213 expected = os.path.normcase(os.path.realpath(new_location))
214 self.assertEqual(actual, expected)
215 finally:
216 sys.path.remove(target)
217 for basedir in [new_location, target, ]:
218 if not os.path.exists(basedir) or not os.path.isdir(basedir):
219 continue
220 try:
221 shutil.rmtree(basedir)
222 except:
223 pass
224 if old_ppath is not None:
225 os.environ['PYTHONPATH'] = old_ppath
226 else:
227 del os.environ['PYTHONPATH']
228
229 def test_setup_requires(self):
230 """Regression test for Distribute issue #318
231
232 Ensure that a package with setup_requires can be installed when
233 setuptools is installed in the user site-packages without causing a
234 SandboxViolation.
235 """
236
237 test_pkg = create_setup_requires_package(self.dir)
238 test_setup_py = os.path.join(test_pkg, 'setup.py')
239
240 try:
241 with quiet_context():
242 with reset_setup_stop_context():
243 run_setup(test_setup_py, ['install'])
244 except SandboxViolation:
245 self.fail('Installation caused SandboxViolation')
246 except IndexError:
247 # Test fails in some cases due to bugs in Python
248 # See https://bitbucket.org/pypa/setuptools/issue/201
249 pass
250
251
252 class TestSetupRequires(unittest.TestCase):
253
254 def test_setup_requires_honors_fetch_params(self):
255 """
256 When easy_install installs a source distribution which specifies
257 setup_requires, it should honor the fetch parameters (such as
258 allow-hosts, index-url, and find-links).
259 """
260 # set up a server which will simulate an alternate package index.
261 p_index = setuptools.tests.server.MockServer()
262 p_index.start()
263 netloc = 1
264 p_index_loc = urlparse(p_index.url)[netloc]
265 if p_index_loc.endswith(':0'):
266 # Some platforms (Jython) don't find a port to which to bind,
267 # so skip this test for them.
268 return
269 with quiet_context():
270 # create an sdist that has a build-time dependency.
271 with TestSetupRequires.create_sdist() as dist_file:
272 with tempdir_context() as temp_install_dir:
273 with environment_context(PYTHONPATH=temp_install_dir):
274 ei_params = ['--index-url', p_index.url,
275 '--allow-hosts', p_index_loc,
276 '--exclude-scripts', '--install-dir', temp_install_d ir,
277 dist_file]
278 with reset_setup_stop_context():
279 with argv_context(['easy_install']):
280 # attempt to install the dist. It should fail be cause
281 # it doesn't exist.
282 self.assertRaises(SystemExit,
283 easy_install_pkg.main, ei_params)
284 # there should have been two or three requests to the server
285 # (three happens on Python 3.3a)
286 self.assertTrue(2 <= len(p_index.requests) <= 3)
287 self.assertEqual(p_index.requests[0].path, '/does-not-exist/')
288
289 @staticmethod
290 @contextlib.contextmanager
291 def create_sdist():
292 """
293 Return an sdist with a setup_requires dependency (of something that
294 doesn't exist)
295 """
296 with tempdir_context() as dir:
297 dist_path = os.path.join(dir, 'setuptools-test-fetcher-1.0.tar.gz')
298 make_trivial_sdist(
299 dist_path,
300 textwrap.dedent("""
301 import setuptools
302 setuptools.setup(
303 name="setuptools-test-fetcher",
304 version="1.0",
305 setup_requires = ['does-not-exist'],
306 )
307 """).lstrip())
308 yield dist_path
309
310 def test_setup_requires_overrides_version_conflict(self):
311 """
312 Regression test for issue #323.
313
314 Ensures that a distribution's setup_requires requirements can still be
315 installed and used locally even if a conflicting version of that
316 requirement is already on the path.
317 """
318
319 pr_state = pkg_resources.__getstate__()
320 fake_dist = PRDistribution('does-not-matter', project_name='foobar',
321 version='0.0')
322 working_set.add(fake_dist)
323
324 try:
325 with tempdir_context() as temp_dir:
326 test_pkg = create_setup_requires_package(temp_dir)
327 test_setup_py = os.path.join(test_pkg, 'setup.py')
328 with quiet_context() as (stdout, stderr):
329 with reset_setup_stop_context():
330 try:
331 # Don't even need to install the package, just
332 # running the setup.py at all is sufficient
333 run_setup(test_setup_py, ['--name'])
334 except VersionConflict:
335 self.fail('Installing setup.py requirements '
336 'caused a VersionConflict')
337
338 lines = stdout.readlines()
339 self.assertTrue(len(lines) > 0)
340 self.assertTrue(lines[-1].strip(), 'test_pkg')
341 finally:
342 pkg_resources.__setstate__(pr_state)
343
344
345 def create_setup_requires_package(path):
346 """Creates a source tree under path for a trivial test package that has a
347 single requirement in setup_requires--a tarball for that requirement is
348 also created and added to the dependency_links argument.
349 """
350
351 test_setup_attrs = {
352 'name': 'test_pkg', 'version': '0.0',
353 'setup_requires': ['foobar==0.1'],
354 'dependency_links': [os.path.abspath(path)]
355 }
356
357 test_pkg = os.path.join(path, 'test_pkg')
358 test_setup_py = os.path.join(test_pkg, 'setup.py')
359 os.mkdir(test_pkg)
360
361 f = open(test_setup_py, 'w')
362 f.write(textwrap.dedent("""\
363 import setuptools
364 setuptools.setup(**%r)
365 """ % test_setup_attrs))
366 f.close()
367
368 foobar_path = os.path.join(path, 'foobar-0.1.tar.gz')
369 make_trivial_sdist(
370 foobar_path,
371 textwrap.dedent("""\
372 import setuptools
373 setuptools.setup(
374 name='foobar',
375 version='0.1'
376 )
377 """))
378
379 return test_pkg
380
381
382 def make_trivial_sdist(dist_path, setup_py):
383 """Create a simple sdist tarball at dist_path, containing just a
384 setup.py, the contents of which are provided by the setup_py string.
385 """
386
387 setup_py_file = tarfile.TarInfo(name='setup.py')
388 try:
389 # Python 3 (StringIO gets converted to io module)
390 MemFile = BytesIO
391 except AttributeError:
392 MemFile = StringIO
393 setup_py_bytes = MemFile(setup_py.encode('utf-8'))
394 setup_py_file.size = len(setup_py_bytes.getvalue())
395 dist = tarfile.open(dist_path, 'w:gz')
396 try:
397 dist.addfile(setup_py_file, fileobj=setup_py_bytes)
398 finally:
399 dist.close()
400
401
402 @contextlib.contextmanager
403 def tempdir_context(cd=lambda dir:None):
404 temp_dir = tempfile.mkdtemp()
405 orig_dir = os.getcwd()
406 try:
407 cd(temp_dir)
408 yield temp_dir
409 finally:
410 cd(orig_dir)
411 shutil.rmtree(temp_dir)
412
413 @contextlib.contextmanager
414 def environment_context(**updates):
415 old_env = os.environ.copy()
416 os.environ.update(updates)
417 try:
418 yield
419 finally:
420 for key in updates:
421 del os.environ[key]
422 os.environ.update(old_env)
423
424 @contextlib.contextmanager
425 def argv_context(repl):
426 old_argv = sys.argv[:]
427 sys.argv[:] = repl
428 yield
429 sys.argv[:] = old_argv
430
431 @contextlib.contextmanager
432 def reset_setup_stop_context():
433 """
434 When the setuptools tests are run using setup.py test, and then
435 one wants to invoke another setup() command (such as easy_install)
436 within those tests, it's necessary to reset the global variable
437 in distutils.core so that the setup() command will run naturally.
438 """
439 setup_stop_after = distutils.core._setup_stop_after
440 distutils.core._setup_stop_after = None
441 yield
442 distutils.core._setup_stop_after = setup_stop_after
443
444
445 @contextlib.contextmanager
446 def quiet_context():
447 """
448 Redirect stdout/stderr to StringIO objects to prevent console output from
449 distutils commands.
450 """
451
452 old_stdout = sys.stdout
453 old_stderr = sys.stderr
454 new_stdout = sys.stdout = StringIO()
455 new_stderr = sys.stderr = StringIO()
456 try:
457 yield new_stdout, new_stderr
458 finally:
459 new_stdout.seek(0)
460 new_stderr.seek(0)
461 sys.stdout = old_stdout
462 sys.stderr = old_stderr
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698