1import email
2import zipfile
3
4import pytest
5
6from pdm.pep517 import api
7from tests.testutils import build_fixture_project, get_tarball_names, get_wheel_names
8
9
10def test_build_single_module(tmp_path):
11    with build_fixture_project("demo-module"):
12        wheel_name = api.build_wheel(tmp_path.as_posix())
13        sdist_name = api.build_sdist(tmp_path.as_posix())
14        assert api.get_requires_for_build_sdist() == []
15        assert api.get_requires_for_build_wheel() == []
16        assert sdist_name == "demo-module-0.1.0.tar.gz"
17        assert wheel_name == "demo_module-0.1.0-py3-none-any.whl"
18        tar_names = get_tarball_names(tmp_path / sdist_name)
19        for name in [
20            "foo_module.py",
21            "bar_module.py",
22            "LICENSE",
23            "pyproject.toml",
24            "PKG-INFO",
25            "README.md",
26        ]:
27            assert f"demo-module-0.1.0/{name}" in tar_names
28
29        zip_names = get_wheel_names(tmp_path / wheel_name)
30        for name in ["foo_module.py", "bar_module.py"]:
31            assert name in zip_names
32
33        for name in ("pyproject.toml", "LICENSE"):
34            assert name not in zip_names
35
36
37def test_build_package(tmp_path):
38    with build_fixture_project("demo-package"):
39        wheel_name = api.build_wheel(tmp_path.as_posix())
40        sdist_name = api.build_sdist(tmp_path.as_posix())
41        assert sdist_name == "demo-package-0.1.0.tar.gz"
42        assert wheel_name == "demo_package-0.1.0-py2.py3-none-any.whl"
43
44        tar_names = get_tarball_names(tmp_path / sdist_name)
45        assert "demo-package-0.1.0/my_package/__init__.py" in tar_names
46        assert "demo-package-0.1.0/my_package/data.json" in tar_names
47        assert "demo-package-0.1.0/single_module.py" not in tar_names
48        assert "demo-package-0.1.0/data_out.json" in tar_names
49
50        zip_names = get_wheel_names(tmp_path / wheel_name)
51        assert "my_package/__init__.py" in zip_names
52        assert "my_package/data.json" in zip_names
53        assert "single_module.py" not in zip_names
54        assert "data_out.json" not in zip_names
55
56
57def test_build_src_package(tmp_path):
58    with build_fixture_project("demo-src-package"):
59        wheel_name = api.build_wheel(tmp_path.as_posix())
60        sdist_name = api.build_sdist(tmp_path.as_posix())
61        assert sdist_name == "demo-package-0.1.0.tar.gz"
62        assert wheel_name == "demo_package-0.1.0-py3-none-any.whl"
63
64        tar_names = get_tarball_names(tmp_path / sdist_name)
65        zip_names = get_wheel_names(tmp_path / wheel_name)
66        assert "demo-package-0.1.0/src/my_package/__init__.py" in tar_names
67        assert "demo-package-0.1.0/src/my_package/data.json" in tar_names
68
69        assert "my_package/__init__.py" in zip_names
70        assert "my_package/data.json" in zip_names
71
72
73def test_build_package_include(tmp_path):
74    with build_fixture_project("demo-package-include"):
75        wheel_name = api.build_wheel(tmp_path.as_posix())
76        sdist_name = api.build_sdist(tmp_path.as_posix())
77        assert sdist_name == "demo-package-0.1.0.tar.gz"
78        assert wheel_name == "demo_package-0.1.0-py3-none-any.whl"
79
80        tar_names = get_tarball_names(tmp_path / sdist_name)
81        zip_names = get_wheel_names(tmp_path / wheel_name)
82
83        assert "demo-package-0.1.0/my_package/__init__.py" in tar_names
84        assert "demo-package-0.1.0/my_package/data.json" not in tar_names
85        assert "demo-package-0.1.0/requirements.txt" in tar_names
86        assert "demo-package-0.1.0/data_out.json" in tar_names
87
88        assert "my_package/__init__.py" in zip_names
89        assert "my_package/data.json" not in zip_names
90        assert "requirements.txt" in zip_names
91        assert "data_out.json" in zip_names
92
93
94def test_namespace_package_by_include(tmp_path):
95    with build_fixture_project("demo-pep420-package"):
96        wheel_name = api.build_wheel(tmp_path.as_posix())
97        sdist_name = api.build_sdist(tmp_path.as_posix())
98        assert sdist_name == "demo-package-0.1.0.tar.gz"
99        assert wheel_name == "demo_package-0.1.0-py3-none-any.whl"
100
101        tar_names = get_tarball_names(tmp_path / sdist_name)
102        zip_names = get_wheel_names(tmp_path / wheel_name)
103        assert "demo-package-0.1.0/foo/my_package/__init__.py" in tar_names
104        assert "demo-package-0.1.0/foo/my_package/data.json" in tar_names
105
106        assert "foo/my_package/__init__.py" in zip_names
107        assert "foo/my_package/data.json" in zip_names
108
109
110def test_build_explicit_package_dir(tmp_path):
111    with build_fixture_project("demo-explicit-package-dir"):
112        wheel_name = api.build_wheel(tmp_path.as_posix())
113        sdist_name = api.build_sdist(tmp_path.as_posix())
114        assert sdist_name == "demo-package-0.1.0.tar.gz"
115        assert wheel_name == "demo_package-0.1.0-py3-none-any.whl"
116
117        tar_names = get_tarball_names(tmp_path / sdist_name)
118        zip_names = get_wheel_names(tmp_path / wheel_name)
119        assert "demo-package-0.1.0/foo/my_package/__init__.py" in tar_names
120        assert "demo-package-0.1.0/foo/my_package/data.json" in tar_names
121
122        assert "my_package/__init__.py" in zip_names
123        assert "my_package/data.json" in zip_names
124
125
126def test_prepare_metadata(tmp_path):
127    with build_fixture_project("demo-package"):
128        dist_info = api.prepare_metadata_for_build_wheel(tmp_path.as_posix())
129        assert dist_info == "demo_package-0.1.0.dist-info"
130        for filename in ("WHEEL", "METADATA"):
131            assert (tmp_path / dist_info / filename).is_file()
132
133
134@pytest.mark.xfail
135def test_build_legacypackage(tmp_path):
136    with build_fixture_project("demo-legacy"):
137        wheel_name = api.build_wheel(tmp_path.as_posix())
138        sdist_name = api.build_sdist(tmp_path.as_posix())
139        assert sdist_name == "demo-legacy-0.1.0.tar.gz"
140        assert wheel_name == "demo_legacy-0.1.0-py3-none-any.whl"
141
142        tar_names = get_tarball_names(tmp_path / sdist_name)
143        assert "demo-legacy-0.1.0/my_package/__init__.py" in tar_names
144        assert "demo-legacy-0.1.0/my_package/data.json" in tar_names
145        assert "demo-legacy-0.1.0/single_module.py" not in tar_names
146        assert "demo-legacy-0.1.0/data_out.json" not in tar_names
147
148        zip_names = get_wheel_names(tmp_path / wheel_name)
149        assert "my_package/__init__.py" in zip_names
150        assert "my_package/data.json" in zip_names
151        assert "single_module.py" not in zip_names
152        assert "data_out.json" not in zip_names
153
154
155def test_build_package_with_modules_in_src(tmp_path):
156    with build_fixture_project("demo-src-pymodule"):
157        wheel_name = api.build_wheel(tmp_path.as_posix())
158        sdist_name = api.build_sdist(tmp_path.as_posix())
159
160        tar_names = get_tarball_names(tmp_path / sdist_name)
161        assert "demo-module-0.1.0/src/foo_module.py" in tar_names
162
163        zip_names = get_wheel_names(tmp_path / wheel_name)
164        assert "foo_module.py" in zip_names
165
166
167def test_build_with_cextension(tmp_path):
168    with build_fixture_project("demo-cextension"):
169        wheel_name = api.build_wheel(tmp_path.as_posix())
170        sdist_name = api.build_sdist(tmp_path.as_posix())
171        assert api.get_requires_for_build_sdist() == []
172        assert api.get_requires_for_build_wheel() == ["setuptools>=40.8.0"]
173
174        zip_names = get_wheel_names(tmp_path / wheel_name)
175        assert "my_package/__init__.py" in zip_names
176        assert (
177            "my_package/hellomodule.c" not in zip_names
178        ), "Not collect c files while building wheel"
179
180        tar_names = get_tarball_names(tmp_path / sdist_name)
181        assert "demo-package-0.1.0/my_package/__init__.py" in tar_names
182        assert (
183            "demo-package-0.1.0/my_package/hellomodule.c" in tar_names
184        ), "Collect c files while building sdist"
185        assert not any(
186            path.startswith("build") for path in tar_names
187        ), 'Not collect c files in temporary directory "./build"'
188
189
190def test_build_with_cextension_in_src(tmp_path):
191    with build_fixture_project("demo-cextension-in-src"):
192        wheel_name = api.build_wheel(tmp_path.as_posix())
193        sdist_name = api.build_sdist(tmp_path.as_posix())
194
195        zip_names = get_wheel_names(tmp_path / wheel_name)
196        assert "my_package/__init__.py" in zip_names
197        assert (
198            "my_package/hellomodule.c" not in zip_names
199        ), "Not collect c files while building wheel"
200
201        tar_names = get_tarball_names(tmp_path / sdist_name)
202        assert "demo-package-0.1.0/src/my_package/__init__.py" in tar_names
203        assert (
204            "demo-package-0.1.0/src/my_package/hellomodule.c" in tar_names
205        ), "Collect c files while building sdist"
206        assert not any(
207            path.startswith("build") for path in tar_names
208        ), 'Not collect c files in temporary directory "./build"'
209
210
211def test_build_editable(tmp_path):
212    with build_fixture_project("demo-package") as project:
213        wheel_name = api.build_editable(tmp_path.as_posix())
214        assert api.get_requires_for_build_editable() == []
215        with zipfile.ZipFile(tmp_path / wheel_name) as zf:
216            namelist = zf.namelist()
217            assert "demo_package.pth" in namelist
218            assert "_demo_package.py" in namelist
219
220            metadata = email.message_from_bytes(
221                zf.read("demo_package-0.1.0.dist-info/METADATA")
222            )
223            assert "editables" in metadata.get_all("Requires-Dist", [])
224
225            pth_content = zf.read("demo_package.pth").decode("utf-8").strip()
226            assert pth_content == "import _demo_package"
227
228            proxy_module = zf.read("_demo_package.py").decode("utf-8").strip()
229            assert proxy_module == (
230                "from editables.redirector import RedirectingFinder as F\n"
231                "F.install()\n"
232                "F.map_module('my_package', {0!r})".format(
233                    str((project / "my_package" / "__init__.py").resolve())
234                )
235            )
236
237
238def test_build_editable_src(tmp_path):
239    with build_fixture_project("demo-src-package-include") as project:
240        wheel_name = api.build_editable(tmp_path.as_posix())
241
242        with zipfile.ZipFile(tmp_path / wheel_name) as zf:
243            namelist = zf.namelist()
244            assert "demo_package.pth" in namelist
245            assert "_demo_package.py" in namelist
246            assert "my_package/data.json" not in namelist
247            assert "data_out.json" in namelist
248
249            pth_content = zf.read("demo_package.pth").decode("utf-8").strip()
250            assert pth_content == "import _demo_package"
251
252            proxy_module = zf.read("_demo_package.py").decode("utf-8").strip()
253            assert proxy_module == (
254                "from editables.redirector import RedirectingFinder as F\n"
255                "F.install()\n"
256                "F.map_module('my_package', {0!r})".format(
257                    str((project / "sub" / "my_package" / "__init__.py").resolve())
258                )
259            )
260
261
262def test_build_editable_pep420(tmp_path):
263    with build_fixture_project("demo-pep420-package") as project:
264        wheel_name = api.build_editable(tmp_path.as_posix())
265
266        with zipfile.ZipFile(tmp_path / wheel_name) as zf:
267            namelist = zf.namelist()
268            assert "demo_package.pth" in namelist
269            assert "_demo_package.py" not in namelist
270
271            metadata = email.message_from_bytes(
272                zf.read("demo_package-0.1.0.dist-info/METADATA")
273            )
274            assert "editables" not in metadata.get_all("Requires-Dist", [])
275
276            pth_content = zf.read("demo_package.pth").decode("utf-8").strip()
277            assert pth_content == str(project.resolve())
278