wrap/pybind11/setup.py
Go to the documentation of this file.
1 #!/usr/bin/env python3
2 
3 # Setup script for PyPI; use CMakeFile.txt to build extension modules
4 
5 import contextlib
6 import os
7 import re
8 import shutil
9 import string
10 import subprocess
11 import sys
12 from pathlib import Path
13 from tempfile import TemporaryDirectory
14 from typing import Dict, Iterator, List, Union
15 
16 import setuptools.command.sdist
17 
18 DIR = Path(__file__).parent.absolute()
19 VERSION_REGEX = re.compile(
20  r"^\s*#\s*define\s+PYBIND11_VERSION_([A-Z]+)\s+(.*)$", re.MULTILINE
21 )
22 VERSION_FILE = Path("pybind11/_version.py")
23 COMMON_FILE = Path("include/pybind11/detail/common.h")
24 
25 
26 def build_expected_version_hex(matches: Dict[str, str]) -> str:
27  patch_level_serial = matches["PATCH"]
28  serial = None
29  major = int(matches["MAJOR"])
30  minor = int(matches["MINOR"])
31  flds = patch_level_serial.split(".")
32  if flds:
33  patch = int(flds[0])
34  if len(flds) == 1:
35  level = "0"
36  serial = 0
37  elif len(flds) == 2:
38  level_serial = flds[1]
39  for level in ("a", "b", "c", "dev"):
40  if level_serial.startswith(level):
41  serial = int(level_serial[len(level) :])
42  break
43  if serial is None:
44  msg = f'Invalid PYBIND11_VERSION_PATCH: "{patch_level_serial}"'
45  raise RuntimeError(msg)
46  version_hex_str = f"{major:02x}{minor:02x}{patch:02x}{level[:1]}{serial:x}"
47  return f"0x{version_hex_str.upper()}"
48 
49 
50 # PYBIND11_GLOBAL_SDIST will build a different sdist, with the python-headers
51 # files, and the sys.prefix files (CMake and headers).
52 
53 global_sdist = os.environ.get("PYBIND11_GLOBAL_SDIST", False)
54 
55 setup_py = Path(
56  "tools/setup_global.py.in" if global_sdist else "tools/setup_main.py.in"
57 )
58 extra_cmd = 'cmdclass["sdist"] = SDist\n'
59 
60 to_src = (
61  (Path("pyproject.toml"), Path("tools/pyproject.toml")),
62  (Path("setup.py"), setup_py),
63 )
64 
65 
66 # Read the listed version
67 loc: Dict[str, str] = {}
68 code = compile(VERSION_FILE.read_text(encoding="utf-8"), "pybind11/_version.py", "exec")
69 exec(code, loc)
70 version = loc["__version__"]
71 
72 # Verify that the version matches the one in C++
73 matches = dict(VERSION_REGEX.findall(COMMON_FILE.read_text(encoding="utf8")))
74 cpp_version = "{MAJOR}.{MINOR}.{PATCH}".format(**matches)
75 if version != cpp_version:
76  msg = f"Python version {version} does not match C++ version {cpp_version}!"
77  raise RuntimeError(msg)
78 
79 version_hex = matches.get("HEX", "MISSING")
80 exp_version_hex = build_expected_version_hex(matches)
81 if version_hex != exp_version_hex:
82  msg = f"PYBIND11_VERSION_HEX {version_hex} does not match expected value {exp_version_hex}!"
83  raise RuntimeError(msg)
84 
85 
86 # TODO: use literals & overload (typing extensions or Python 3.8)
87 def get_and_replace(
88  filename: Path, binary: bool = False, **opts: str
89 ) -> Union[bytes, str]:
90  if binary:
91  contents = filename.read_bytes()
92  return string.Template(contents.decode()).substitute(opts).encode()
93 
94  return string.Template(filename.read_text()).substitute(opts)
95 
96 
97 # Use our input files instead when making the SDist (and anything that depends
98 # on it, like a wheel)
99 class SDist(setuptools.command.sdist.sdist): # type: ignore[misc]
100  def make_release_tree(self, base_dir: str, files: List[str]) -> None:
101  super().make_release_tree(base_dir, files)
102 
103  for to, src in to_src:
104  txt = get_and_replace(src, binary=True, version=version, extra_cmd="")
105 
106  dest = Path(base_dir) / to
107 
108  # This is normally linked, so unlink before writing!
109  dest.unlink()
110  dest.write_bytes(txt) # type: ignore[arg-type]
111 
112 
113 # Remove the CMake install directory when done
114 @contextlib.contextmanager
115 def remove_output(*sources: str) -> Iterator[None]:
116  try:
117  yield
118  finally:
119  for src in sources:
120  shutil.rmtree(src)
121 
122 
123 with remove_output("pybind11/include", "pybind11/share"):
124  # Generate the files if they are not present.
125  with TemporaryDirectory() as tmpdir:
126  cmd = ["cmake", "-S", ".", "-B", tmpdir] + [
127  "-DCMAKE_INSTALL_PREFIX=pybind11",
128  "-DBUILD_TESTING=OFF",
129  "-DPYBIND11_NOPYTHON=ON",
130  ]
131  if "CMAKE_ARGS" in os.environ:
132  fcommand = [
133  c
134  for c in os.environ["CMAKE_ARGS"].split()
135  if "DCMAKE_INSTALL_PREFIX" not in c
136  ]
137  cmd += fcommand
138  subprocess.run(cmd, check=True, cwd=DIR, stdout=sys.stdout, stderr=sys.stderr)
139  subprocess.run(
140  ["cmake", "--install", tmpdir],
141  check=True,
142  cwd=DIR,
143  stdout=sys.stdout,
144  stderr=sys.stderr,
145  )
146 
147  txt = get_and_replace(setup_py, version=version, extra_cmd=extra_cmd)
148  code = compile(txt, setup_py, "exec")
149  exec(code, {"SDist": SDist})
def make_release_tree
def get_and_replace
void exec(const str &expr, object global=globals(), object local=object())
Definition: eval.h:88
void split(const G &g, const PredecessorMap< KEY > &tree, G &Ab1, G &Ab2)
Definition: graph-inl.h:245
std::string format(const std::string &str, const std::vector< std::string > &find, const std::vector< std::string > &replace)
def build_expected_version_hex
Definition: pytypes.h:1924
int encode(Index i, Index j)
size_t len(handle h)
Get the length of a Python object.
Definition: pytypes.h:2244


gtsam
Author(s):
autogenerated on Tue Jul 4 2023 02:35:42