OLD | NEW |
(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 |
OLD | NEW |