• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# -*- coding: utf-8 -*-
2import contextlib
3import os
4import string
5import subprocess
6import sys
7import tarfile
8import zipfile
9
10# These tests must be run explicitly
11# They require CMake 3.15+ (--install)
12
13DIR = os.path.abspath(os.path.dirname(__file__))
14MAIN_DIR = os.path.dirname(os.path.dirname(DIR))
15
16
17main_headers = {
18    "include/pybind11/attr.h",
19    "include/pybind11/buffer_info.h",
20    "include/pybind11/cast.h",
21    "include/pybind11/chrono.h",
22    "include/pybind11/common.h",
23    "include/pybind11/complex.h",
24    "include/pybind11/eigen.h",
25    "include/pybind11/embed.h",
26    "include/pybind11/eval.h",
27    "include/pybind11/functional.h",
28    "include/pybind11/iostream.h",
29    "include/pybind11/numpy.h",
30    "include/pybind11/operators.h",
31    "include/pybind11/options.h",
32    "include/pybind11/pybind11.h",
33    "include/pybind11/pytypes.h",
34    "include/pybind11/stl.h",
35    "include/pybind11/stl_bind.h",
36}
37
38detail_headers = {
39    "include/pybind11/detail/class.h",
40    "include/pybind11/detail/common.h",
41    "include/pybind11/detail/descr.h",
42    "include/pybind11/detail/init.h",
43    "include/pybind11/detail/internals.h",
44    "include/pybind11/detail/typeid.h",
45}
46
47cmake_files = {
48    "share/cmake/pybind11/FindPythonLibsNew.cmake",
49    "share/cmake/pybind11/pybind11Common.cmake",
50    "share/cmake/pybind11/pybind11Config.cmake",
51    "share/cmake/pybind11/pybind11ConfigVersion.cmake",
52    "share/cmake/pybind11/pybind11NewTools.cmake",
53    "share/cmake/pybind11/pybind11Targets.cmake",
54    "share/cmake/pybind11/pybind11Tools.cmake",
55}
56
57py_files = {
58    "__init__.py",
59    "__main__.py",
60    "_version.py",
61    "_version.pyi",
62    "commands.py",
63    "py.typed",
64    "setup_helpers.py",
65    "setup_helpers.pyi",
66}
67
68headers = main_headers | detail_headers
69src_files = headers | cmake_files
70all_files = src_files | py_files
71
72
73sdist_files = {
74    "pybind11",
75    "pybind11/include",
76    "pybind11/include/pybind11",
77    "pybind11/include/pybind11/detail",
78    "pybind11/share",
79    "pybind11/share/cmake",
80    "pybind11/share/cmake/pybind11",
81    "pyproject.toml",
82    "setup.cfg",
83    "setup.py",
84    "LICENSE",
85    "MANIFEST.in",
86    "README.rst",
87    "PKG-INFO",
88}
89
90local_sdist_files = {
91    ".egg-info",
92    ".egg-info/PKG-INFO",
93    ".egg-info/SOURCES.txt",
94    ".egg-info/dependency_links.txt",
95    ".egg-info/not-zip-safe",
96    ".egg-info/top_level.txt",
97}
98
99
100def test_build_sdist(monkeypatch, tmpdir):
101
102    monkeypatch.chdir(MAIN_DIR)
103
104    out = subprocess.check_output(
105        [
106            sys.executable,
107            "setup.py",
108            "sdist",
109            "--formats=tar",
110            "--dist-dir",
111            str(tmpdir),
112        ]
113    )
114    if hasattr(out, "decode"):
115        out = out.decode()
116
117    (sdist,) = tmpdir.visit("*.tar")
118
119    with tarfile.open(str(sdist)) as tar:
120        start = tar.getnames()[0] + "/"
121        version = start[9:-1]
122        simpler = set(n.split("/", 1)[-1] for n in tar.getnames()[1:])
123
124        with contextlib.closing(
125            tar.extractfile(tar.getmember(start + "setup.py"))
126        ) as f:
127            setup_py = f.read()
128
129        with contextlib.closing(
130            tar.extractfile(tar.getmember(start + "pyproject.toml"))
131        ) as f:
132            pyproject_toml = f.read()
133
134    files = set("pybind11/{}".format(n) for n in all_files)
135    files |= sdist_files
136    files |= set("pybind11{}".format(n) for n in local_sdist_files)
137    files.add("pybind11.egg-info/entry_points.txt")
138    files.add("pybind11.egg-info/requires.txt")
139    assert simpler == files
140
141    with open(os.path.join(MAIN_DIR, "tools", "setup_main.py.in"), "rb") as f:
142        contents = (
143            string.Template(f.read().decode())
144            .substitute(version=version, extra_cmd="")
145            .encode()
146        )
147        assert setup_py == contents
148
149    with open(os.path.join(MAIN_DIR, "tools", "pyproject.toml"), "rb") as f:
150        contents = f.read()
151        assert pyproject_toml == contents
152
153
154def test_build_global_dist(monkeypatch, tmpdir):
155
156    monkeypatch.chdir(MAIN_DIR)
157    monkeypatch.setenv("PYBIND11_GLOBAL_SDIST", "1")
158
159    out = subprocess.check_output(
160        [
161            sys.executable,
162            "setup.py",
163            "sdist",
164            "--formats=tar",
165            "--dist-dir",
166            str(tmpdir),
167        ]
168    )
169    if hasattr(out, "decode"):
170        out = out.decode()
171
172    (sdist,) = tmpdir.visit("*.tar")
173
174    with tarfile.open(str(sdist)) as tar:
175        start = tar.getnames()[0] + "/"
176        version = start[16:-1]
177        simpler = set(n.split("/", 1)[-1] for n in tar.getnames()[1:])
178
179        with contextlib.closing(
180            tar.extractfile(tar.getmember(start + "setup.py"))
181        ) as f:
182            setup_py = f.read()
183
184        with contextlib.closing(
185            tar.extractfile(tar.getmember(start + "pyproject.toml"))
186        ) as f:
187            pyproject_toml = f.read()
188
189    files = set("pybind11/{}".format(n) for n in all_files)
190    files |= sdist_files
191    files |= set("pybind11_global{}".format(n) for n in local_sdist_files)
192    assert simpler == files
193
194    with open(os.path.join(MAIN_DIR, "tools", "setup_global.py.in"), "rb") as f:
195        contents = (
196            string.Template(f.read().decode())
197            .substitute(version=version, extra_cmd="")
198            .encode()
199        )
200        assert setup_py == contents
201
202    with open(os.path.join(MAIN_DIR, "tools", "pyproject.toml"), "rb") as f:
203        contents = f.read()
204        assert pyproject_toml == contents
205
206
207def tests_build_wheel(monkeypatch, tmpdir):
208    monkeypatch.chdir(MAIN_DIR)
209
210    subprocess.check_output(
211        [sys.executable, "-m", "pip", "wheel", ".", "-w", str(tmpdir)]
212    )
213
214    (wheel,) = tmpdir.visit("*.whl")
215
216    files = set("pybind11/{}".format(n) for n in all_files)
217    files |= {
218        "dist-info/LICENSE",
219        "dist-info/METADATA",
220        "dist-info/RECORD",
221        "dist-info/WHEEL",
222        "dist-info/entry_points.txt",
223        "dist-info/top_level.txt",
224    }
225
226    with zipfile.ZipFile(str(wheel)) as z:
227        names = z.namelist()
228
229    trimmed = set(n for n in names if "dist-info" not in n)
230    trimmed |= set(
231        "dist-info/{}".format(n.split("/", 1)[-1]) for n in names if "dist-info" in n
232    )
233    assert files == trimmed
234
235
236def tests_build_global_wheel(monkeypatch, tmpdir):
237    monkeypatch.chdir(MAIN_DIR)
238    monkeypatch.setenv("PYBIND11_GLOBAL_SDIST", "1")
239
240    subprocess.check_output(
241        [sys.executable, "-m", "pip", "wheel", ".", "-w", str(tmpdir)]
242    )
243
244    (wheel,) = tmpdir.visit("*.whl")
245
246    files = set("data/data/{}".format(n) for n in src_files)
247    files |= set("data/headers/{}".format(n[8:]) for n in headers)
248    files |= {
249        "dist-info/LICENSE",
250        "dist-info/METADATA",
251        "dist-info/WHEEL",
252        "dist-info/top_level.txt",
253        "dist-info/RECORD",
254    }
255
256    with zipfile.ZipFile(str(wheel)) as z:
257        names = z.namelist()
258
259    beginning = names[0].split("/", 1)[0].rsplit(".", 1)[0]
260    trimmed = set(n[len(beginning) + 1 :] for n in names)
261
262    assert files == trimmed
263