aboutsummaryrefslogtreecommitdiff
path: root/setuptools/tests/test_develop.py
blob: c52072ac1e93f85d1eead839a9bb3f5427a85f93 (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
"""develop tests
"""

import os
import sys
import subprocess
import platform
import pathlib

from setuptools.command import test

import pytest
import pip_run.launch

from setuptools.command.develop import develop
from setuptools.dist import Distribution
from . import contexts
from . import namespaces

SETUP_PY = """\
from setuptools import setup

setup(name='foo',
    packages=['foo'],
)
"""

INIT_PY = """print "foo"
"""


@pytest.fixture
def temp_user(monkeypatch):
    with contexts.tempdir() as user_base:
        with contexts.tempdir() as user_site:
            monkeypatch.setattr('site.USER_BASE', user_base)
            monkeypatch.setattr('site.USER_SITE', user_site)
            yield


@pytest.fixture
def test_env(tmpdir, temp_user):
    target = tmpdir
    foo = target.mkdir('foo')
    setup = target / 'setup.py'
    if setup.isfile():
        raise ValueError(dir(target))
    with setup.open('w') as f:
        f.write(SETUP_PY)
    init = foo / '__init__.py'
    with init.open('w') as f:
        f.write(INIT_PY)
    with target.as_cwd():
        yield target


class TestDevelop:
    in_virtualenv = hasattr(sys, 'real_prefix')
    in_venv = hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix

    def test_console_scripts(self, tmpdir):
        """
        Test that console scripts are installed and that they reference
        only the project by name and not the current version.
        """
        pytest.skip(
            "TODO: needs a fixture to cause 'develop' "
            "to be invoked without mutating environment."
        )
        settings = dict(
            name='foo',
            packages=['foo'],
            version='0.0',
            entry_points={
                'console_scripts': [
                    'foocmd = foo:foo',
                ],
            },
        )
        dist = Distribution(settings)
        dist.script_name = 'setup.py'
        cmd = develop(dist)
        cmd.ensure_finalized()
        cmd.install_dir = tmpdir
        cmd.run()
        # assert '0.0' not in foocmd_text


class TestResolver:
    """
    TODO: These tests were written with a minimal understanding
    of what _resolve_setup_path is intending to do. Come up with
    more meaningful cases that look like real-world scenarios.
    """

    def test_resolve_setup_path_cwd(self):
        assert develop._resolve_setup_path('.', '.', '.') == '.'

    def test_resolve_setup_path_one_dir(self):
        assert develop._resolve_setup_path('pkgs', '.', 'pkgs') == '../'

    def test_resolve_setup_path_one_dir_trailing_slash(self):
        assert develop._resolve_setup_path('pkgs/', '.', 'pkgs') == '../'


class TestNamespaces:
    @staticmethod
    def install_develop(src_dir, target):

        develop_cmd = [
            sys.executable,
            'setup.py',
            'develop',
            '--install-dir',
            str(target),
        ]
        with src_dir.as_cwd():
            with test.test.paths_on_pythonpath([str(target)]):
                subprocess.check_call(develop_cmd)

    @pytest.mark.skipif(
        bool(os.environ.get("APPVEYOR")),
        reason="https://github.com/pypa/setuptools/issues/851",
    )
    @pytest.mark.skipif(
        platform.python_implementation() == 'PyPy',
        reason="https://github.com/pypa/setuptools/issues/1202",
    )
    def test_namespace_package_importable(self, tmpdir):
        """
        Installing two packages sharing the same namespace, one installed
        naturally using pip or `--single-version-externally-managed`
        and the other installed using `develop` should leave the namespace
        in tact and both packages reachable by import.
        """
        pkg_A = namespaces.build_namespace_package(tmpdir, 'myns.pkgA')
        pkg_B = namespaces.build_namespace_package(tmpdir, 'myns.pkgB')
        target = tmpdir / 'packages'
        # use pip to install to the target directory
        install_cmd = [
            sys.executable,
            '-m',
            'pip',
            'install',
            str(pkg_A),
            '-t',
            str(target),
        ]
        subprocess.check_call(install_cmd)
        self.install_develop(pkg_B, target)
        namespaces.make_site_dir(target)
        try_import = [
            sys.executable,
            '-c',
            'import myns.pkgA; import myns.pkgB',
        ]
        with test.test.paths_on_pythonpath([str(target)]):
            subprocess.check_call(try_import)

        # additionally ensure that pkg_resources import works
        pkg_resources_imp = [
            sys.executable,
            '-c',
            'import pkg_resources',
        ]
        with test.test.paths_on_pythonpath([str(target)]):
            subprocess.check_call(pkg_resources_imp)

    @pytest.mark.xfail(
        platform.python_implementation() == 'PyPy',
        reason="Workaround fails on PyPy (why?)",
    )
    def test_editable_prefix(self, tmp_path, sample_project):
        """
        Editable install to a prefix should be discoverable.
        """
        prefix = tmp_path / 'prefix'

        # figure out where pip will likely install the package
        site_packages = prefix / next(
            pathlib.Path(path).relative_to(sys.prefix)
            for path in sys.path
            if 'site-packages' in path and path.startswith(sys.prefix)
        )
        site_packages.mkdir(parents=True)

        # install workaround
        pip_run.launch.inject_sitecustomize(str(site_packages))

        env = dict(os.environ, PYTHONPATH=str(site_packages))
        cmd = [
            sys.executable,
            '-m',
            'pip',
            'install',
            '--editable',
            str(sample_project),
            '--prefix',
            str(prefix),
            '--no-build-isolation',
        ]
        subprocess.check_call(cmd, env=env)

        # now run 'sample' with the prefix on the PYTHONPATH
        bin = 'Scripts' if platform.system() == 'Windows' else 'bin'
        exe = prefix / bin / 'sample'
        if sys.version_info < (3, 8) and platform.system() == 'Windows':
            exe = str(exe)
        subprocess.check_call([exe], env=env)