aboutsummaryrefslogtreecommitdiffstats
path: root/jython-tosca-parser/src/main/resources/Lib/site-packages/setuptools/tests/test_easy_install.py
blob: e71bbfc9e8892e7f88a0ba01b4f0bad1084a709f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
# -*- coding: utf-8 -*-

"""Easy install Tests
"""
from __future__ import absolute_import

import sys
import os
import shutil
import tempfile
import site
import contextlib
import tarfile
import logging
import itertools
import distutils.errors

import pytest
try:
    from unittest import mock
except ImportError:
    import mock

from setuptools import sandbox
from setuptools import compat
from setuptools.compat import StringIO, BytesIO, urlparse
from setuptools.sandbox import run_setup
import setuptools.command.easy_install as ei
from setuptools.command.easy_install import PthDistributions
from setuptools.command import easy_install as easy_install_pkg
from setuptools.dist import Distribution
from pkg_resources import working_set
from pkg_resources import Distribution as PRDistribution
import setuptools.tests.server
import pkg_resources

from .py26compat import tarfile_open
from . import contexts
from .textwrap import DALS


class FakeDist(object):
    def get_entry_map(self, group):
        if group != 'console_scripts':
            return {}
        return {'name': 'ep'}

    def as_requirement(self):
        return 'spec'

SETUP_PY = DALS("""
    from setuptools import setup

    setup(name='foo')
    """)

class TestEasyInstallTest:

    def test_install_site_py(self):
        dist = Distribution()
        cmd = ei.easy_install(dist)
        cmd.sitepy_installed = False
        cmd.install_dir = tempfile.mkdtemp()
        try:
            cmd.install_site_py()
            sitepy = os.path.join(cmd.install_dir, 'site.py')
            assert os.path.exists(sitepy)
        finally:
            shutil.rmtree(cmd.install_dir)

    def test_get_script_args(self):
        header = ei.CommandSpec.best().from_environment().as_header()
        expected = header + DALS("""
            # EASY-INSTALL-ENTRY-SCRIPT: 'spec','console_scripts','name'
            __requires__ = 'spec'
            import sys
            from pkg_resources import load_entry_point

            if __name__ == '__main__':
                sys.exit(
                    load_entry_point('spec', 'console_scripts', 'name')()
                )
            """)
        dist = FakeDist()

        args = next(ei.ScriptWriter.get_args(dist))
        name, script = itertools.islice(args, 2)

        assert script == expected

    def test_no_find_links(self):
        # new option '--no-find-links', that blocks find-links added at
        # the project level
        dist = Distribution()
        cmd = ei.easy_install(dist)
        cmd.check_pth_processing = lambda: True
        cmd.no_find_links = True
        cmd.find_links = ['link1', 'link2']
        cmd.install_dir = os.path.join(tempfile.mkdtemp(), 'ok')
        cmd.args = ['ok']
        cmd.ensure_finalized()
        assert cmd.package_index.scanned_urls == {}

        # let's try without it (default behavior)
        cmd = ei.easy_install(dist)
        cmd.check_pth_processing = lambda: True
        cmd.find_links = ['link1', 'link2']
        cmd.install_dir = os.path.join(tempfile.mkdtemp(), 'ok')
        cmd.args = ['ok']
        cmd.ensure_finalized()
        keys = sorted(cmd.package_index.scanned_urls.keys())
        assert keys == ['link1', 'link2']

    def test_write_exception(self):
        """
        Test that `cant_write_to_target` is rendered as a DistutilsError.
        """
        dist = Distribution()
        cmd = ei.easy_install(dist)
        cmd.install_dir = os.getcwd()
        with pytest.raises(distutils.errors.DistutilsError):
            cmd.cant_write_to_target()


class TestPTHFileWriter:
    def test_add_from_cwd_site_sets_dirty(self):
        '''a pth file manager should set dirty
        if a distribution is in site but also the cwd
        '''
        pth = PthDistributions('does-not_exist', [os.getcwd()])
        assert not pth.dirty
        pth.add(PRDistribution(os.getcwd()))
        assert pth.dirty

    def test_add_from_site_is_ignored(self):
        location = '/test/location/does-not-have-to-exist'
        # PthDistributions expects all locations to be normalized
        location = pkg_resources.normalize_path(location)
        pth = PthDistributions('does-not_exist', [location, ])
        assert not pth.dirty
        pth.add(PRDistribution(location))
        assert not pth.dirty


@pytest.yield_fixture
def setup_context(tmpdir):
    with (tmpdir/'setup.py').open('w') as f:
        f.write(SETUP_PY)
    with tmpdir.as_cwd():
        yield tmpdir


@pytest.mark.usefixtures("user_override")
@pytest.mark.usefixtures("setup_context")
class TestUserInstallTest:

    # prevent check that site-packages is writable. easy_install
    # shouldn't be writing to system site-packages during finalize
    # options, but while it does, bypass the behavior.
    prev_sp_write = mock.patch(
        'setuptools.command.easy_install.easy_install.check_site_dir',
        mock.Mock(),
    )

    # simulate setuptools installed in user site packages
    @mock.patch('setuptools.command.easy_install.__file__', site.USER_SITE)
    @mock.patch('site.ENABLE_USER_SITE', True)
    @prev_sp_write
    def test_user_install_not_implied_user_site_enabled(self):
        self.assert_not_user_site()

    @mock.patch('site.ENABLE_USER_SITE', False)
    @prev_sp_write
    def test_user_install_not_implied_user_site_disabled(self):
        self.assert_not_user_site()

    @staticmethod
    def assert_not_user_site():
        # create a finalized easy_install command
        dist = Distribution()
        dist.script_name = 'setup.py'
        cmd = ei.easy_install(dist)
        cmd.args = ['py']
        cmd.ensure_finalized()
        assert not cmd.user, 'user should not be implied'

    def test_multiproc_atexit(self):
        pytest.importorskip('multiprocessing')

        log = logging.getLogger('test_easy_install')
        logging.basicConfig(level=logging.INFO, stream=sys.stderr)
        log.info('this should not break')

    @pytest.fixture()
    def foo_package(self, tmpdir):
        egg_file = tmpdir / 'foo-1.0.egg-info'
        with egg_file.open('w') as f:
            f.write('Name: foo\n')
        return str(tmpdir)

    @pytest.yield_fixture()
    def install_target(self, tmpdir):
        target = str(tmpdir)
        with mock.patch('sys.path', sys.path + [target]):
            python_path = os.path.pathsep.join(sys.path)
            with mock.patch.dict(os.environ, PYTHONPATH=python_path):
                yield target

    def test_local_index(self, foo_package, install_target):
        """
        The local index must be used when easy_install locates installed
        packages.
        """
        dist = Distribution()
        dist.script_name = 'setup.py'
        cmd = ei.easy_install(dist)
        cmd.install_dir = install_target
        cmd.args = ['foo']
        cmd.ensure_finalized()
        cmd.local_index.scan([foo_package])
        res = cmd.easy_install('foo')
        actual = os.path.normcase(os.path.realpath(res.location))
        expected = os.path.normcase(os.path.realpath(foo_package))
        assert actual == expected

    @contextlib.contextmanager
    def user_install_setup_context(self, *args, **kwargs):
        """
        Wrap sandbox.setup_context to patch easy_install in that context to
        appear as user-installed.
        """
        with self.orig_context(*args, **kwargs):
            import setuptools.command.easy_install as ei
            ei.__file__ = site.USER_SITE
            yield

    def patched_setup_context(self):
        self.orig_context = sandbox.setup_context

        return mock.patch(
            'setuptools.sandbox.setup_context',
            self.user_install_setup_context,
        )


@pytest.yield_fixture
def distutils_package():
    distutils_setup_py = SETUP_PY.replace(
        'from setuptools import setup',
        'from distutils.core import setup',
    )
    with contexts.tempdir(cd=os.chdir):
        with open('setup.py', 'w') as f:
            f.write(distutils_setup_py)
        yield


class TestDistutilsPackage:
    def test_bdist_egg_available_on_distutils_pkg(self, distutils_package):
        run_setup('setup.py', ['bdist_egg'])


class TestSetupRequires:

    def test_setup_requires_honors_fetch_params(self):
        """
        When easy_install installs a source distribution which specifies
        setup_requires, it should honor the fetch parameters (such as
        allow-hosts, index-url, and find-links).
        """
        # set up a server which will simulate an alternate package index.
        p_index = setuptools.tests.server.MockServer()
        p_index.start()
        netloc = 1
        p_index_loc = urlparse(p_index.url)[netloc]
        if p_index_loc.endswith(':0'):
            # Some platforms (Jython) don't find a port to which to bind,
            #  so skip this test for them.
            return
        with contexts.quiet():
            # create an sdist that has a build-time dependency.
            with TestSetupRequires.create_sdist() as dist_file:
                with contexts.tempdir() as temp_install_dir:
                    with contexts.environment(PYTHONPATH=temp_install_dir):
                        ei_params = [
                            '--index-url', p_index.url,
                            '--allow-hosts', p_index_loc,
                            '--exclude-scripts',
                            '--install-dir', temp_install_dir,
                            dist_file,
                        ]
                        with sandbox.save_argv(['easy_install']):
                            # attempt to install the dist. It should fail because
                            #  it doesn't exist.
                            with pytest.raises(SystemExit):
                                easy_install_pkg.main(ei_params)
        # there should have been two or three requests to the server
        #  (three happens on Python 3.3a)
        assert 2 <= len(p_index.requests) <= 3
        assert p_index.requests[0].path == '/does-not-exist/'

    @staticmethod
    @contextlib.contextmanager
    def create_sdist():
        """
        Return an sdist with a setup_requires dependency (of something that
        doesn't exist)
        """
        with contexts.tempdir() as dir:
            dist_path = os.path.join(dir, 'setuptools-test-fetcher-1.0.tar.gz')
            script = DALS("""
                import setuptools
                setuptools.setup(
                    name="setuptools-test-fetcher",
                    version="1.0",
                    setup_requires = ['does-not-exist'],
                )
                """)
            make_trivial_sdist(dist_path, script)
            yield dist_path

    def test_setup_requires_overrides_version_conflict(self):
        """
        Regression test for issue #323.

        Ensures that a distribution's setup_requires requirements can still be
        installed and used locally even if a conflicting version of that
        requirement is already on the path.
        """

        pr_state = pkg_resources.__getstate__()
        fake_dist = PRDistribution('does-not-matter', project_name='foobar',
                                   version='0.0')
        working_set.add(fake_dist)

        try:
            with contexts.tempdir() as temp_dir:
                test_pkg = create_setup_requires_package(temp_dir)
                test_setup_py = os.path.join(test_pkg, 'setup.py')
                with contexts.quiet() as (stdout, stderr):
                    # Don't even need to install the package, just
                    # running the setup.py at all is sufficient
                    run_setup(test_setup_py, ['--name'])

                lines = stdout.readlines()
                assert len(lines) > 0
                assert lines[-1].strip(), 'test_pkg'
        finally:
            pkg_resources.__setstate__(pr_state)


def create_setup_requires_package(path):
    """Creates a source tree under path for a trivial test package that has a
    single requirement in setup_requires--a tarball for that requirement is
    also created and added to the dependency_links argument.
    """

    test_setup_attrs = {
        'name': 'test_pkg', 'version': '0.0',
        'setup_requires': ['foobar==0.1'],
        'dependency_links': [os.path.abspath(path)]
    }

    test_pkg = os.path.join(path, 'test_pkg')
    test_setup_py = os.path.join(test_pkg, 'setup.py')
    os.mkdir(test_pkg)

    with open(test_setup_py, 'w') as f:
        f.write(DALS("""
            import setuptools
            setuptools.setup(**%r)
        """ % test_setup_attrs))

    foobar_path = os.path.join(path, 'foobar-0.1.tar.gz')
    make_trivial_sdist(
        foobar_path,
        DALS("""
            import setuptools
            setuptools.setup(
                name='foobar',
                version='0.1'
            )
        """))

    return test_pkg


def make_trivial_sdist(dist_path, setup_py):
    """Create a simple sdist tarball at dist_path, containing just a
    setup.py, the contents of which are provided by the setup_py string.
    """

    setup_py_file = tarfile.TarInfo(name='setup.py')
    try:
        # Python 3 (StringIO gets converted to io module)
        MemFile = BytesIO
    except AttributeError:
        MemFile = StringIO
    setup_py_bytes = MemFile(setup_py.encode('utf-8'))
    setup_py_file.size = len(setup_py_bytes.getvalue())
    with tarfile_open(dist_path, 'w:gz') as dist:
        dist.addfile(setup_py_file, fileobj=setup_py_bytes)


class TestScriptHeader:
    non_ascii_exe = '/Users/José/bin/python'
    exe_with_spaces = r'C:\Program Files\Python33\python.exe'

    @pytest.mark.skipif(
        sys.platform.startswith('java') and ei.is_sh(sys.executable),
        reason="Test cannot run under java when executable is sh"
    )
    def test_get_script_header(self):
        expected = '#!%s\n' % ei.nt_quote_arg(os.path.normpath(sys.executable))
        actual = ei.ScriptWriter.get_script_header('#!/usr/local/bin/python')
        assert actual == expected

        expected = '#!%s -x\n' % ei.nt_quote_arg(os.path.normpath
            (sys.executable))
        actual = ei.ScriptWriter.get_script_header('#!/usr/bin/python -x')
        assert actual == expected

        actual = ei.ScriptWriter.get_script_header('#!/usr/bin/python',
            executable=self.non_ascii_exe)
        expected = '#!%s -x\n' % self.non_ascii_exe
        assert actual == expected

        actual = ei.ScriptWriter.get_script_header('#!/usr/bin/python',
            executable='"'+self.exe_with_spaces+'"')
        expected = '#!"%s"\n' % self.exe_with_spaces
        assert actual == expected

    @pytest.mark.xfail(
        compat.PY3 and os.environ.get("LC_CTYPE") in ("C", "POSIX"),
        reason="Test fails in this locale on Python 3"
    )
    @mock.patch.dict(sys.modules, java=mock.Mock(lang=mock.Mock(System=
        mock.Mock(getProperty=mock.Mock(return_value="")))))
    @mock.patch('sys.platform', 'java1.5.0_13')
    def test_get_script_header_jython_workaround(self, tmpdir):
        # Create a mock sys.executable that uses a shebang line
        header = DALS("""
            #!/usr/bin/python
            # -*- coding: utf-8 -*-
            """)
        exe = tmpdir / 'exe.py'
        with exe.open('w') as f:
            f.write(header)
        exe = str(exe)

        header = ei.ScriptWriter.get_script_header('#!/usr/local/bin/python',
            executable=exe)
        assert header == '#!/usr/bin/env %s\n' % exe

        expect_out = 'stdout' if sys.version_info < (2,7) else 'stderr'

        with contexts.quiet() as (stdout, stderr):
            # When options are included, generate a broken shebang line
            # with a warning emitted
            candidate = ei.ScriptWriter.get_script_header('#!/usr/bin/python -x',
                executable=exe)
            assert candidate == '#!%s -x\n' % exe
            output = locals()[expect_out]
            assert 'Unable to adapt shebang line' in output.getvalue()

        with contexts.quiet() as (stdout, stderr):
            candidate = ei.ScriptWriter.get_script_header('#!/usr/bin/python',
                executable=self.non_ascii_exe)
            assert candidate == '#!%s -x\n' % self.non_ascii_exe
            output = locals()[expect_out]
            assert 'Unable to adapt shebang line' in output.getvalue()


class TestCommandSpec:
    def test_custom_launch_command(self):
        """
        Show how a custom CommandSpec could be used to specify a #! executable
        which takes parameters.
        """
        cmd = ei.CommandSpec(['/usr/bin/env', 'python3'])
        assert cmd.as_header() == '#!/usr/bin/env python3\n'

    def test_from_param_for_CommandSpec_is_passthrough(self):
        """
        from_param should return an instance of a CommandSpec
        """
        cmd = ei.CommandSpec(['python'])
        cmd_new = ei.CommandSpec.from_param(cmd)
        assert cmd is cmd_new

    def test_from_environment_with_spaces_in_executable(self):
        with mock.patch('sys.executable', TestScriptHeader.exe_with_spaces):
            cmd = ei.CommandSpec.from_environment()
        assert len(cmd) == 1
        assert cmd.as_header().startswith('#!"')

    def test_from_simple_string_uses_shlex(self):
        """
        In order to support `executable = /usr/bin/env my-python`, make sure
        from_param invokes shlex on that input.
        """
        cmd = ei.CommandSpec.from_param('/usr/bin/env my-python')
        assert len(cmd) == 2
        assert '"' not in cmd.as_header()

    def test_sys_executable(self):
        """
        CommandSpec.from_string(sys.executable) should contain just that param.
        """
        writer = ei.ScriptWriter.best()
        cmd = writer.command_spec_class.from_string(sys.executable)
        assert len(cmd) == 1
        assert cmd[0] == sys.executable


class TestWindowsScriptWriter:
    def test_header(self):
        hdr = ei.WindowsScriptWriter.get_script_header('')
        assert hdr.startswith('#!')
        assert hdr.endswith('\n')
        hdr = hdr.lstrip('#!')
        hdr = hdr.rstrip('\n')
        # header should not start with an escaped quote
        assert not hdr.startswith('\\"')