• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"""develop tests
2"""
3
4import os
5import sys
6import subprocess
7import platform
8import pathlib
9
10from setuptools.command import test
11
12import pytest
13import pip_run.launch
14
15from setuptools.command.develop import develop
16from setuptools.dist import Distribution
17from . import contexts
18from . import namespaces
19
20SETUP_PY = """\
21from setuptools import setup
22
23setup(name='foo',
24    packages=['foo'],
25)
26"""
27
28INIT_PY = """print "foo"
29"""
30
31
32@pytest.fixture
33def temp_user(monkeypatch):
34    with contexts.tempdir() as user_base:
35        with contexts.tempdir() as user_site:
36            monkeypatch.setattr('site.USER_BASE', user_base)
37            monkeypatch.setattr('site.USER_SITE', user_site)
38            yield
39
40
41@pytest.fixture
42def test_env(tmpdir, temp_user):
43    target = tmpdir
44    foo = target.mkdir('foo')
45    setup = target / 'setup.py'
46    if setup.isfile():
47        raise ValueError(dir(target))
48    with setup.open('w') as f:
49        f.write(SETUP_PY)
50    init = foo / '__init__.py'
51    with init.open('w') as f:
52        f.write(INIT_PY)
53    with target.as_cwd():
54        yield target
55
56
57class TestDevelop:
58    in_virtualenv = hasattr(sys, 'real_prefix')
59    in_venv = hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix
60
61    def test_console_scripts(self, tmpdir):
62        """
63        Test that console scripts are installed and that they reference
64        only the project by name and not the current version.
65        """
66        pytest.skip(
67            "TODO: needs a fixture to cause 'develop' "
68            "to be invoked without mutating environment."
69        )
70        settings = dict(
71            name='foo',
72            packages=['foo'],
73            version='0.0',
74            entry_points={
75                'console_scripts': [
76                    'foocmd = foo:foo',
77                ],
78            },
79        )
80        dist = Distribution(settings)
81        dist.script_name = 'setup.py'
82        cmd = develop(dist)
83        cmd.ensure_finalized()
84        cmd.install_dir = tmpdir
85        cmd.run()
86        # assert '0.0' not in foocmd_text
87
88
89class TestResolver:
90    """
91    TODO: These tests were written with a minimal understanding
92    of what _resolve_setup_path is intending to do. Come up with
93    more meaningful cases that look like real-world scenarios.
94    """
95
96    def test_resolve_setup_path_cwd(self):
97        assert develop._resolve_setup_path('.', '.', '.') == '.'
98
99    def test_resolve_setup_path_one_dir(self):
100        assert develop._resolve_setup_path('pkgs', '.', 'pkgs') == '../'
101
102    def test_resolve_setup_path_one_dir_trailing_slash(self):
103        assert develop._resolve_setup_path('pkgs/', '.', 'pkgs') == '../'
104
105
106class TestNamespaces:
107    @staticmethod
108    def install_develop(src_dir, target):
109
110        develop_cmd = [
111            sys.executable,
112            'setup.py',
113            'develop',
114            '--install-dir',
115            str(target),
116        ]
117        with src_dir.as_cwd():
118            with test.test.paths_on_pythonpath([str(target)]):
119                subprocess.check_call(develop_cmd)
120
121    @pytest.mark.skipif(
122        bool(os.environ.get("APPVEYOR")),
123        reason="https://github.com/pypa/setuptools/issues/851",
124    )
125    @pytest.mark.skipif(
126        platform.python_implementation() == 'PyPy',
127        reason="https://github.com/pypa/setuptools/issues/1202",
128    )
129    def test_namespace_package_importable(self, tmpdir):
130        """
131        Installing two packages sharing the same namespace, one installed
132        naturally using pip or `--single-version-externally-managed`
133        and the other installed using `develop` should leave the namespace
134        in tact and both packages reachable by import.
135        """
136        pkg_A = namespaces.build_namespace_package(tmpdir, 'myns.pkgA')
137        pkg_B = namespaces.build_namespace_package(tmpdir, 'myns.pkgB')
138        target = tmpdir / 'packages'
139        # use pip to install to the target directory
140        install_cmd = [
141            sys.executable,
142            '-m',
143            'pip',
144            'install',
145            str(pkg_A),
146            '-t',
147            str(target),
148        ]
149        subprocess.check_call(install_cmd)
150        self.install_develop(pkg_B, target)
151        namespaces.make_site_dir(target)
152        try_import = [
153            sys.executable,
154            '-c',
155            'import myns.pkgA; import myns.pkgB',
156        ]
157        with test.test.paths_on_pythonpath([str(target)]):
158            subprocess.check_call(try_import)
159
160        # additionally ensure that pkg_resources import works
161        pkg_resources_imp = [
162            sys.executable,
163            '-c',
164            'import pkg_resources',
165        ]
166        with test.test.paths_on_pythonpath([str(target)]):
167            subprocess.check_call(pkg_resources_imp)
168
169    @pytest.mark.xfail(
170        platform.python_implementation() == 'PyPy',
171        reason="Workaround fails on PyPy (why?)",
172    )
173    def test_editable_prefix(self, tmp_path, sample_project):
174        """
175        Editable install to a prefix should be discoverable.
176        """
177        prefix = tmp_path / 'prefix'
178
179        # figure out where pip will likely install the package
180        site_packages = prefix / next(
181            pathlib.Path(path).relative_to(sys.prefix)
182            for path in sys.path
183            if 'site-packages' in path and path.startswith(sys.prefix)
184        )
185        site_packages.mkdir(parents=True)
186
187        # install workaround
188        pip_run.launch.inject_sitecustomize(str(site_packages))
189
190        env = dict(os.environ, PYTHONPATH=str(site_packages))
191        cmd = [
192            sys.executable,
193            '-m',
194            'pip',
195            'install',
196            '--editable',
197            str(sample_project),
198            '--prefix',
199            str(prefix),
200            '--no-build-isolation',
201        ]
202        subprocess.check_call(cmd, env=env)
203
204        # now run 'sample' with the prefix on the PYTHONPATH
205        bin = 'Scripts' if platform.system() == 'Windows' else 'bin'
206        exe = prefix / bin / 'sample'
207        if sys.version_info < (3, 8) and platform.system() == 'Windows':
208            exe = str(exe)
209        subprocess.check_call([exe], env=env)
210