scripts.vendor_members_hook
Reference › scripts › vendor_members_hook
scripts/vendor_members_hook.py is a Hatchling build hook that vendors the three compiled workspace members, codemod_fast, cxpt and ctok, into the wheel (RUL-052, option (b): vendor compiled artifacts). Without it the base wheel depended on three workspace members published to no registry and could not be installed outside the repository. VendoredMembersHook.initialize runs only inside Hatchling's build environment, so the repository's own tests never import it. For each member listed in _MEMBERS it compiles the C extension in place with setup.py build_ext --inplace, using the build interpreter (the free-threaded 3.14t, so the GIL setting of the module holds), then force-includes every file of the member's package, including compiled objects, py.typed and stubs, as a top-level package in the wheel. It sets the build data so the wheel is platform and ABI tagged (cp314t, Linux) and not pure Python, which is the accepted cost of the ruling. A missing member source or package raises RuntimeError instead of producing a hollow wheel. See tools_import_closure and verify_package for related packaging checks.
At a glance
| Source | scripts/vendor_members_hook.py |
| Lines of code | 75 |
| Classes | 1 |
| Functions | 0 |
| Imports (internal) | none |
| Imported by | none |
| Tests that import it | none |
Class diagram
Dependency diagram
No internal imports and no importers.
API
Classes
VendoredMembersHook
Bases: BuildHookInterface · Line 34
| Method | Signature | Summary |
|---|---|---|
initialize |
initialize(version: str, build_data: dict) -> None |
Source
"""Hatchling build hook — vendor the three compiled workspace members into the
wheel (RUL-052 §5.1 option (b): "vendor compiled artifacts into the wheel").
The base wheel declared runtime deps on three workspace members published to no
registry (``codemod-fast``, ``cxpt``, ``ctok``) and could never install
externally. This hook compiles each member's C extension in-place against the
project's free-threaded interpreter (so ``PyUnstable_Module_SetGIL`` sticks) and
force-includes the compiled packages as TOP-LEVEL packages in the wheel, so the
members ship INSIDE the wheel and the runtime deps can drop. Shipping binary
extensions makes the wheel platform/ABI-tagged (cp314t/linux), never py3-none-any
— the accepted consequence of the ruling.
The hook runs only inside hatchling's build environment (hatchling is a
build-system requirement, not a dev dependency), so it is never imported by the
repo's own test suite.
"""
from __future__ import annotations
import subprocess
import sys
from pathlib import Path
from hatchling.builders.hooks.plugin.interface import BuildHookInterface
#: (member source dir relative to the repo root, its top-level import package).
_MEMBERS = (
("scripts/_codemod_fast", "codemod_fast"),
("scripts/_cxpt", "cxpt"),
("scripts/_ctok", "ctok"),
)
class VendoredMembersHook(BuildHookInterface):
PLUGIN_NAME = "custom"
def initialize(self, version: str, build_data: dict) -> None:
root = Path(self.root)
force_include = build_data.setdefault("force_include", {})
# The vendored .so extensions make the wheel platform/ABI-tagged
# (cp314t/linux), never py3-none-any — the accepted consequence of the
# ruling. hatchling only infers the platform tag (and drops purelib)
# when told; force_include alone leaves both at the pure-Python default.
build_data["pure_python"] = False
build_data["infer_tag"] = True
for rel_dir, pkg in _MEMBERS:
member_dir = root / rel_dir
if not (member_dir / "setup.py").is_file():
# A source checkout without the member (e.g. a partial clone)
# cannot vendor it — fail loudly rather than ship a hollow wheel.
raise RuntimeError(f"vendored member missing: {member_dir}/setup.py")
# Compile the extension in-place against THIS interpreter. The build
# env's python is the project's free-threaded 3.14t, so the ABI and
# the PyUnstable_Module_SetGIL call resolve correctly (the members'
# setup.py derive a content-addressed version from the .c/.h).
subprocess.run(
[sys.executable, "setup.py", "build_ext", "--inplace"],
cwd=str(member_dir),
check=True,
capture_output=True,
text=True,
)
# Force-include every file of the package (pure-Python modules, the
# compiled .so, PEP 561 py.typed + .pyi stubs) at the TOP-LEVEL path.
pkg_dir = member_dir / pkg
if not pkg_dir.is_dir():
raise RuntimeError(f"vendored member package missing: {pkg_dir}")
for path in sorted(pkg_dir.rglob("*")):
if path.is_file():
force_include[str(path)] = str(path.relative_to(member_dir))
super().initialize(version, build_data)