File indexing completed on 2025-01-18 10:17:51
0001 import contextlib
0002 import os
0003 import string
0004 import subprocess
0005 import sys
0006 import tarfile
0007 import zipfile
0008
0009
0010
0011
0012 DIR = os.path.abspath(os.path.dirname(__file__))
0013 MAIN_DIR = os.path.dirname(os.path.dirname(DIR))
0014
0015 PKGCONFIG = """\
0016 prefix=${{pcfiledir}}/../../
0017 includedir=${{prefix}}/include
0018
0019 Name: pybind11
0020 Description: Seamless operability between C++11 and Python
0021 Version: {VERSION}
0022 Cflags: -I${{includedir}}
0023 """
0024
0025
0026 main_headers = {
0027 "include/pybind11/attr.h",
0028 "include/pybind11/buffer_info.h",
0029 "include/pybind11/cast.h",
0030 "include/pybind11/chrono.h",
0031 "include/pybind11/common.h",
0032 "include/pybind11/complex.h",
0033 "include/pybind11/eigen.h",
0034 "include/pybind11/embed.h",
0035 "include/pybind11/eval.h",
0036 "include/pybind11/functional.h",
0037 "include/pybind11/gil.h",
0038 "include/pybind11/iostream.h",
0039 "include/pybind11/numpy.h",
0040 "include/pybind11/operators.h",
0041 "include/pybind11/options.h",
0042 "include/pybind11/pybind11.h",
0043 "include/pybind11/pytypes.h",
0044 "include/pybind11/stl.h",
0045 "include/pybind11/stl_bind.h",
0046 }
0047
0048 detail_headers = {
0049 "include/pybind11/detail/class.h",
0050 "include/pybind11/detail/common.h",
0051 "include/pybind11/detail/descr.h",
0052 "include/pybind11/detail/init.h",
0053 "include/pybind11/detail/internals.h",
0054 "include/pybind11/detail/type_caster_base.h",
0055 "include/pybind11/detail/typeid.h",
0056 }
0057
0058 eigen_headers = {
0059 "include/pybind11/eigen/matrix.h",
0060 "include/pybind11/eigen/tensor.h",
0061 }
0062
0063 stl_headers = {
0064 "include/pybind11/stl/filesystem.h",
0065 }
0066
0067 cmake_files = {
0068 "share/cmake/pybind11/FindPythonLibsNew.cmake",
0069 "share/cmake/pybind11/pybind11Common.cmake",
0070 "share/cmake/pybind11/pybind11Config.cmake",
0071 "share/cmake/pybind11/pybind11ConfigVersion.cmake",
0072 "share/cmake/pybind11/pybind11NewTools.cmake",
0073 "share/cmake/pybind11/pybind11Targets.cmake",
0074 "share/cmake/pybind11/pybind11Tools.cmake",
0075 }
0076
0077 pkgconfig_files = {
0078 "share/pkgconfig/pybind11.pc",
0079 }
0080
0081 py_files = {
0082 "__init__.py",
0083 "__main__.py",
0084 "_version.py",
0085 "commands.py",
0086 "py.typed",
0087 "setup_helpers.py",
0088 }
0089
0090 headers = main_headers | detail_headers | eigen_headers | stl_headers
0091 src_files = headers | cmake_files | pkgconfig_files
0092 all_files = src_files | py_files
0093
0094
0095 sdist_files = {
0096 "pybind11",
0097 "pybind11/include",
0098 "pybind11/include/pybind11",
0099 "pybind11/include/pybind11/detail",
0100 "pybind11/include/pybind11/eigen",
0101 "pybind11/include/pybind11/stl",
0102 "pybind11/share",
0103 "pybind11/share/cmake",
0104 "pybind11/share/cmake/pybind11",
0105 "pybind11/share/pkgconfig",
0106 "pyproject.toml",
0107 "setup.cfg",
0108 "setup.py",
0109 "LICENSE",
0110 "MANIFEST.in",
0111 "README.rst",
0112 "PKG-INFO",
0113 }
0114
0115 local_sdist_files = {
0116 ".egg-info",
0117 ".egg-info/PKG-INFO",
0118 ".egg-info/SOURCES.txt",
0119 ".egg-info/dependency_links.txt",
0120 ".egg-info/not-zip-safe",
0121 ".egg-info/top_level.txt",
0122 }
0123
0124
0125 def read_tz_file(tar: tarfile.TarFile, name: str) -> bytes:
0126 start = tar.getnames()[0] + "/"
0127 inner_file = tar.extractfile(tar.getmember(f"{start}{name}"))
0128 assert inner_file
0129 with contextlib.closing(inner_file) as f:
0130 return f.read()
0131
0132
0133 def normalize_line_endings(value: bytes) -> bytes:
0134 return value.replace(os.linesep.encode("utf-8"), b"\n")
0135
0136
0137 def test_build_sdist(monkeypatch, tmpdir):
0138
0139 monkeypatch.chdir(MAIN_DIR)
0140
0141 subprocess.run(
0142 [sys.executable, "-m", "build", "--sdist", f"--outdir={tmpdir}"], check=True
0143 )
0144
0145 (sdist,) = tmpdir.visit("*.tar.gz")
0146
0147 with tarfile.open(str(sdist), "r:gz") as tar:
0148 start = tar.getnames()[0] + "/"
0149 version = start[9:-1]
0150 simpler = {n.split("/", 1)[-1] for n in tar.getnames()[1:]}
0151
0152 setup_py = read_tz_file(tar, "setup.py")
0153 pyproject_toml = read_tz_file(tar, "pyproject.toml")
0154 pkgconfig = read_tz_file(tar, "pybind11/share/pkgconfig/pybind11.pc")
0155 cmake_cfg = read_tz_file(
0156 tar, "pybind11/share/cmake/pybind11/pybind11Config.cmake"
0157 )
0158
0159 assert (
0160 'set(pybind11_INCLUDE_DIR "${PACKAGE_PREFIX_DIR}/include")'
0161 in cmake_cfg.decode("utf-8")
0162 )
0163
0164 files = {f"pybind11/{n}" for n in all_files}
0165 files |= sdist_files
0166 files |= {f"pybind11{n}" for n in local_sdist_files}
0167 files.add("pybind11.egg-info/entry_points.txt")
0168 files.add("pybind11.egg-info/requires.txt")
0169 assert simpler == files
0170
0171 with open(os.path.join(MAIN_DIR, "tools", "setup_main.py.in"), "rb") as f:
0172 contents = (
0173 string.Template(f.read().decode("utf-8"))
0174 .substitute(version=version, extra_cmd="")
0175 .encode("utf-8")
0176 )
0177 assert setup_py == contents
0178
0179 with open(os.path.join(MAIN_DIR, "tools", "pyproject.toml"), "rb") as f:
0180 contents = f.read()
0181 assert pyproject_toml == contents
0182
0183 simple_version = ".".join(version.split(".")[:3])
0184 pkgconfig_expected = PKGCONFIG.format(VERSION=simple_version).encode("utf-8")
0185 assert normalize_line_endings(pkgconfig) == pkgconfig_expected
0186
0187
0188 def test_build_global_dist(monkeypatch, tmpdir):
0189
0190 monkeypatch.chdir(MAIN_DIR)
0191 monkeypatch.setenv("PYBIND11_GLOBAL_SDIST", "1")
0192 subprocess.run(
0193 [sys.executable, "-m", "build", "--sdist", "--outdir", str(tmpdir)], check=True
0194 )
0195
0196 (sdist,) = tmpdir.visit("*.tar.gz")
0197
0198 with tarfile.open(str(sdist), "r:gz") as tar:
0199 start = tar.getnames()[0] + "/"
0200 version = start[16:-1]
0201 simpler = {n.split("/", 1)[-1] for n in tar.getnames()[1:]}
0202
0203 setup_py = read_tz_file(tar, "setup.py")
0204 pyproject_toml = read_tz_file(tar, "pyproject.toml")
0205 pkgconfig = read_tz_file(tar, "pybind11/share/pkgconfig/pybind11.pc")
0206 cmake_cfg = read_tz_file(
0207 tar, "pybind11/share/cmake/pybind11/pybind11Config.cmake"
0208 )
0209
0210 assert (
0211 'set(pybind11_INCLUDE_DIR "${PACKAGE_PREFIX_DIR}/include")'
0212 in cmake_cfg.decode("utf-8")
0213 )
0214
0215 files = {f"pybind11/{n}" for n in all_files}
0216 files |= sdist_files
0217 files |= {f"pybind11_global{n}" for n in local_sdist_files}
0218 assert simpler == files
0219
0220 with open(os.path.join(MAIN_DIR, "tools", "setup_global.py.in"), "rb") as f:
0221 contents = (
0222 string.Template(f.read().decode())
0223 .substitute(version=version, extra_cmd="")
0224 .encode("utf-8")
0225 )
0226 assert setup_py == contents
0227
0228 with open(os.path.join(MAIN_DIR, "tools", "pyproject.toml"), "rb") as f:
0229 contents = f.read()
0230 assert pyproject_toml == contents
0231
0232 simple_version = ".".join(version.split(".")[:3])
0233 pkgconfig_expected = PKGCONFIG.format(VERSION=simple_version).encode("utf-8")
0234 assert normalize_line_endings(pkgconfig) == pkgconfig_expected
0235
0236
0237 def tests_build_wheel(monkeypatch, tmpdir):
0238 monkeypatch.chdir(MAIN_DIR)
0239
0240 subprocess.run(
0241 [sys.executable, "-m", "pip", "wheel", ".", "-w", str(tmpdir)], check=True
0242 )
0243
0244 (wheel,) = tmpdir.visit("*.whl")
0245
0246 files = {f"pybind11/{n}" for n in all_files}
0247 files |= {
0248 "dist-info/LICENSE",
0249 "dist-info/METADATA",
0250 "dist-info/RECORD",
0251 "dist-info/WHEEL",
0252 "dist-info/entry_points.txt",
0253 "dist-info/top_level.txt",
0254 }
0255
0256 with zipfile.ZipFile(str(wheel)) as z:
0257 names = z.namelist()
0258
0259 trimmed = {n for n in names if "dist-info" not in n}
0260 trimmed |= {f"dist-info/{n.split('/', 1)[-1]}" for n in names if "dist-info" in n}
0261 assert files == trimmed
0262
0263
0264 def tests_build_global_wheel(monkeypatch, tmpdir):
0265 monkeypatch.chdir(MAIN_DIR)
0266 monkeypatch.setenv("PYBIND11_GLOBAL_SDIST", "1")
0267
0268 subprocess.run(
0269 [sys.executable, "-m", "pip", "wheel", ".", "-w", str(tmpdir)], check=True
0270 )
0271
0272 (wheel,) = tmpdir.visit("*.whl")
0273
0274 files = {f"data/data/{n}" for n in src_files}
0275 files |= {f"data/headers/{n[8:]}" for n in headers}
0276 files |= {
0277 "dist-info/LICENSE",
0278 "dist-info/METADATA",
0279 "dist-info/WHEEL",
0280 "dist-info/top_level.txt",
0281 "dist-info/RECORD",
0282 }
0283
0284 with zipfile.ZipFile(str(wheel)) as z:
0285 names = z.namelist()
0286
0287 beginning = names[0].split("/", 1)[0].rsplit(".", 1)[0]
0288 trimmed = {n[len(beginning) + 1 :] for n in names}
0289
0290 assert files == trimmed