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
|
"""develop tests
"""
import os
import sys
import subprocess
import platform
import pathlib
import textwrap
from setuptools.command import test
import pytest
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)
@staticmethod
def install_workaround(site_packages):
site_packages.mkdir(parents=True)
sc = site_packages / 'sitecustomize.py'
sc.write_text(
textwrap.dedent(
"""
import site
import pathlib
here = pathlib.Path(__file__).parent
site.addsitedir(str(here))
"""
).lstrip()
)
@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'
prefix.mkdir()
# 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)
)
# install the workaround
self.install_workaround(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, 7) and platform.system() == 'Windows':
exe = str(exe)
subprocess.check_call([exe], env=env)
|