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
|
import os
import sys
import subprocess
import platform
import pathlib
from textwrap import dedent
import jaraco.envs
import jaraco.path
import pip_run.launch
import pytest
from . import namespaces
EXAMPLE = {
'pyproject.toml': dedent("""\
[build-system]
requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "mypkg"
version = "3.14159"
license = {text = "MIT"}
description = "This is a Python package"
dynamic = ["readme"]
classifiers = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers"
]
urls = {Homepage = "http://github.com"}
dependencies = ['importlib-metadata; python_version<"3.8"']
[tool.setuptools]
package-dir = {"" = "src"}
packages = {find = {where = ["src"]}}
license-files = ["LICENSE*"]
[tool.setuptools.dynamic]
readme = {file = "README.rst"}
[tool.distutils.egg_info]
tag-build = ".post0"
"""),
"MANIFEST.in": dedent("""\
global-include *.py *.txt
global-exclude *.py[cod]
""").strip(),
"README.rst": "This is a ``README``",
"LICENSE.txt": "---- placeholder MIT license ----",
"src": {
"mypkg": {
"__init__.py": dedent("""\
import sys
if sys.version_info[:2] >= (3, 8):
from importlib.metadata import PackageNotFoundError, version
else:
from importlib_metadata import PackageNotFoundError, version
try:
__version__ = version(__name__)
except PackageNotFoundError:
__version__ = "unknown"
"""),
"__main__.py": dedent("""\
from importlib.resources import read_text
from . import __version__, __name__ as parent
from .mod import x
data = read_text(parent, "data.txt")
print(__version__, data, x)
"""),
"mod.py": "x = ''",
"data.txt": "Hello World",
}
}
}
SETUP_SCRIPT_STUB = "__import__('setuptools').setup()"
@pytest.mark.parametrize(
"files",
[
{**EXAMPLE, "setup.py": SETUP_SCRIPT_STUB},
EXAMPLE, # No setup.py script
]
)
def test_editable_with_pyproject(tmp_path, venv, files):
project = tmp_path / "mypkg"
project.mkdir()
jaraco.path.build(files, prefix=project)
cmd = [venv.exe(), "-m", "pip", "install",
"--no-build-isolation", # required to force current version of setuptools
"-e", str(project)]
print(str(subprocess.check_output(cmd), "utf-8"))
cmd = [venv.exe(), "-m", "mypkg"]
assert subprocess.check_output(cmd).strip() == b"3.14159.post0 Hello World"
(project / "src/mypkg/data.txt").write_text("foobar")
(project / "src/mypkg/mod.py").write_text("x = 42")
assert subprocess.check_output(cmd).strip() == b"3.14159.post0 foobar 42"
class TestLegacyNamespaces:
"""Ported from test_develop"""
def test_namespace_package_importable(self, venv, tmp_path):
"""
Installing two packages sharing the same namespace, one installed
naturally using pip or `--single-version-externally-managed`
and the other installed in editable mode should leave the namespace
intact and both packages reachable by import.
"""
pkg_A = namespaces.build_namespace_package(tmp_path, 'myns.pkgA')
pkg_B = namespaces.build_namespace_package(tmp_path, 'myns.pkgB')
# use pip to install to the target directory
venv.run(["python", "-m", "pip", "install", str(pkg_A)])
venv.run(["python", "-m", "pip", "install", "-e", str(pkg_B)])
venv.run(["python", "-c", "import myns.pkgA; import myns.pkgB"])
# additionally ensure that pkg_resources import works
venv.run(["python", "-c", "import pkg_resources"])
# Moved here from test_develop:
@pytest.mark.xfail(
platform.python_implementation() == 'PyPy',
reason="Workaround fails on PyPy (why?)",
)
def test_editable_with_prefix(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)
|