Skip to content

Commit 2c72f93

Browse files
authored
Merge pull request #1659 from PolicyEngine/add-bundle-runtime-metadata
Add bundle runtime metadata
2 parents 6d91142 + 0d54d19 commit 2c72f93

6 files changed

Lines changed: 93 additions & 42 deletions

File tree

.github/workflows/pr_code_changes.yaml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,25 @@ jobs:
4242
uses: astral-sh/setup-uv@v8.1.0
4343
- name: Check formatting
4444
run: uvx ruff format --check .
45+
BundleMetadataContract:
46+
name: Validate bundle metadata contract
47+
runs-on: ubuntu-latest
48+
steps:
49+
- uses: actions/checkout@v6
50+
- name: Set up Python
51+
uses: actions/setup-python@v6
52+
with:
53+
python-version: "3.14"
54+
- name: Install uv
55+
uses: astral-sh/setup-uv@v8.1.0
56+
- name: Install package
57+
run: uv pip install --system .
58+
- name: Install bundle validation tooling
59+
# Pin the test-only bundle contract dependency until policyengine-bundles
60+
# has published releases suitable for ordinary dependency specifiers.
61+
run: uv pip install --system "policyengine-bundles @ git+https://github.com/PolicyEngine/policyengine-bundles@8ae9f56fefcf89f69b8a7e3bc49928509c6207be"
62+
- name: Validate runtime metadata contract
63+
run: python -m pytest policyengine_uk/tests/test_build_metadata.py
4564
Test:
4665
runs-on: macos-latest
4766
permissions:
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added runtime metadata with installed policyengine-core identity for bundle validation.

policyengine_uk/build_metadata.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
from pathlib import Path
77
import subprocess
88

9+
from policyengine_core import get_runtime_metadata as get_core_runtime_metadata
10+
911
PACKAGE_NAME = "policyengine-uk"
1012
PACKAGE_ROOT = Path(__file__).resolve().parent
1113
DATA_BUILD_SURFACE = (
@@ -39,11 +41,8 @@ def _iter_surface_files() -> list[Path]:
3941
return files
4042

4143

42-
def _get_package_version() -> str | None:
43-
try:
44-
return metadata.version(PACKAGE_NAME)
45-
except metadata.PackageNotFoundError:
46-
return None
44+
def _get_package_version() -> str:
45+
return metadata.version(PACKAGE_NAME)
4746

4847

4948
def _get_git_sha() -> str | None:
@@ -73,10 +72,15 @@ def get_data_build_fingerprint() -> str:
7372
return f"sha256:{digest.hexdigest()}"
7473

7574

76-
def get_data_build_metadata() -> dict[str, str | None]:
75+
def get_runtime_metadata() -> dict[str, object]:
7776
return {
7877
"name": PACKAGE_NAME,
7978
"version": _get_package_version(),
8079
"git_sha": _get_git_sha(),
8180
"data_build_fingerprint": get_data_build_fingerprint(),
81+
"core": get_core_runtime_metadata(),
8282
}
83+
84+
85+
def get_data_build_metadata() -> dict[str, object]:
86+
return get_runtime_metadata()
Lines changed: 58 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,22 @@
1-
from contextlib import ExitStack
1+
import importlib.util
2+
from pathlib import Path
3+
import sys
24
from unittest.mock import patch
35

4-
from policyengine_uk.build_metadata import (
5-
get_data_build_fingerprint,
6-
get_data_build_metadata,
6+
import pytest
7+
8+
BUILD_METADATA_PATH = Path(__file__).resolve().parents[1] / "build_metadata.py"
9+
SPEC = importlib.util.spec_from_file_location(
10+
"policyengine_uk_build_metadata_under_test",
11+
BUILD_METADATA_PATH,
712
)
13+
build_metadata = importlib.util.module_from_spec(SPEC)
14+
sys.modules[SPEC.name] = build_metadata
15+
SPEC.loader.exec_module(build_metadata)
16+
17+
get_data_build_fingerprint = build_metadata.get_data_build_fingerprint
18+
get_data_build_metadata = build_metadata.get_data_build_metadata
19+
get_runtime_metadata = build_metadata.get_runtime_metadata
820

921

1022
def test_data_build_fingerprint_is_stable_within_process():
@@ -17,33 +29,48 @@ def test_data_build_fingerprint_is_stable_within_process():
1729
assert first == second
1830

1931

20-
def test_get_data_build_metadata_includes_version_git_sha_and_fingerprint():
32+
def test_get_runtime_metadata_includes_required_bundle_fields(monkeypatch):
2133
get_data_build_fingerprint.cache_clear()
2234

23-
with ExitStack() as stack:
24-
stack.enter_context(
25-
patch(
26-
"policyengine_uk.build_metadata._get_package_version",
27-
return_value="2.74.0",
28-
)
29-
)
30-
stack.enter_context(
31-
patch(
32-
"policyengine_uk.build_metadata._get_git_sha",
33-
return_value="deadbeef",
34-
)
35-
)
36-
stack.enter_context(
37-
patch(
38-
"policyengine_uk.build_metadata.get_data_build_fingerprint",
39-
return_value="sha256:fingerprint",
40-
)
41-
)
42-
metadata = get_data_build_metadata()
43-
44-
assert metadata == {
45-
"name": "policyengine-uk",
46-
"version": "2.74.0",
47-
"git_sha": "deadbeef",
48-
"data_build_fingerprint": "sha256:fingerprint",
35+
monkeypatch.setattr(build_metadata, "_get_package_version", lambda: "2.74.0")
36+
monkeypatch.setattr(build_metadata, "_get_git_sha", lambda: "deadbeef")
37+
monkeypatch.setattr(
38+
build_metadata,
39+
"get_data_build_fingerprint",
40+
lambda: "sha256:fingerprint",
41+
)
42+
monkeypatch.setattr(
43+
build_metadata,
44+
"get_core_runtime_metadata",
45+
lambda: {
46+
"name": "policyengine-core",
47+
"version": "3.26.0",
48+
"git_sha": "coredeadbeef",
49+
},
50+
)
51+
52+
metadata = get_runtime_metadata()
53+
54+
assert metadata["name"] == "policyengine-uk"
55+
assert metadata["version"] == "2.74.0"
56+
assert metadata["git_sha"] == "deadbeef"
57+
assert metadata["data_build_fingerprint"] == "sha256:fingerprint"
58+
assert metadata["core"] == {
59+
"name": "policyengine-core",
60+
"version": "3.26.0",
61+
"git_sha": "coredeadbeef",
4962
}
63+
64+
65+
def test_get_data_build_metadata_uses_runtime_metadata():
66+
with patch(
67+
f"{SPEC.name}.get_runtime_metadata",
68+
return_value={"name": "policyengine-uk"},
69+
):
70+
assert get_data_build_metadata() == {"name": "policyengine-uk"}
71+
72+
73+
def test_runtime_metadata_uses_bundle_contract_when_available():
74+
policyengine_bundles = pytest.importorskip("policyengine_bundles")
75+
76+
policyengine_bundles.load_component_metadata(get_runtime_metadata())

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ classifiers = [
2626
]
2727
requires-python = ">=3.9"
2828
dependencies = [
29-
"policyengine-core>=3.25.0",
29+
"policyengine-core>=3.26.0",
3030
"microdf-python>=1.2.1",
3131
"pydantic>=2.11.7",
3232
"tables>=3.9.2,<3.10.2; python_version < '3.10'",

uv.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)