| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263 |
- """Hatchling metadata hook: derive a PEP 440 version from netbox/release.yaml.
- NetBox stores the release version and any pre-release designation (for example
- "beta1") separately in netbox/release.yaml. This hook combines them into a
- canonical PEP 440 version so wheels publish correctly, including pre-releases.
- """
- import re
- from pathlib import Path
- from packaging.version import Version
- try:
- from hatchling.metadata.plugin.interface import MetadataHookInterface
- except ModuleNotFoundError:
- # hatchling is only installed inside the isolated build environment. Fall back
- # so this module (and compute_version) remains importable for unit testing.
- MetadataHookInterface = object
- def compute_version(version, designation):
- """Return a canonical PEP 440 version from a version and optional designation."""
- raw = f"{version}{designation}" if designation else version
- return str(Version(raw))
- def _read_release_field(text, field):
- match = re.search(rf'^{field}:\s*"?([^"\n]+?)"?\s*$', text, re.MULTILINE)
- return match.group(1).strip() if match else None
- def read_requirements(text):
- """Parse a pinned requirements.txt body into PEP 508 dependency specifiers.
- Assumes NetBox's flat "package==version" format (one top-level pin per line).
- Blank lines, comments, and pip option lines (starting with "-") are skipped.
- """
- dependencies = []
- for raw_line in text.splitlines():
- line = raw_line.split('#', 1)[0].strip()
- if not line or line.startswith('-'):
- continue
- dependencies.append(line)
- return dependencies
- class NetBoxMetadataHook(MetadataHookInterface):
- def update(self, metadata):
- root = Path(self.root)
- # Version: derived from release.yaml (version + optional designation).
- release_path = root / 'netbox' / 'release.yaml'
- text = release_path.read_text()
- version = _read_release_field(text, 'version')
- if not version:
- raise ValueError(f"Unable to read 'version' from {release_path}")
- designation = _read_release_field(text, 'designation')
- metadata['version'] = compute_version(version, designation)
- # Dependencies: requirements.txt is the single pinned source of truth, so the
- # published wheel's Requires-Dist matches the tested pins, not loose ranges.
- requirements_path = root / 'requirements.txt'
- metadata['dependencies'] = read_requirements(requirements_path.read_text())
|