From 7f394469277e1438270a4d22427c20a68de624d9 Mon Sep 17 00:00:00 2001 From: Anthony Volk Date: Mon, 4 May 2026 21:44:38 +0200 Subject: [PATCH] Add bundle runtime metadata --- .github/workflows/pr.yaml | 19 +++++++++++ .../add-bundle-runtime-metadata.changed.md | 1 + policyengine_us/build_metadata.py | 16 +++++---- policyengine_us/tests/test_build_metadata.py | 34 +++++++++++++++++-- pyproject.toml | 2 +- uv.lock | 10 +++--- 6 files changed, 68 insertions(+), 14 deletions(-) create mode 100644 changelog.d/add-bundle-runtime-metadata.changed.md diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index e6310073067..b8c72513091 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -31,6 +31,25 @@ jobs: echo "Types: added, changed, fixed, removed, breaking" exit 1 fi + BundleMetadataContract: + name: Validate bundle metadata contract + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.14" + - name: Install uv + uses: astral-sh/setup-uv@v5 + - name: Install package + run: uv pip install --system . + - name: Install bundle validation tooling + # Pin the test-only bundle contract dependency until policyengine-bundles + # has published releases suitable for ordinary dependency specifiers. + run: uv pip install --system "policyengine-bundles @ git+https://github.com/PolicyEngine/policyengine-bundles@8ae9f56fefcf89f69b8a7e3bc49928509c6207be" + - name: Validate runtime metadata contract + run: python -m pytest policyengine_us/tests/test_build_metadata.py Python-Compat: name: Install + smoke-import (py${{ matrix.python-version }}) runs-on: ubuntu-latest diff --git a/changelog.d/add-bundle-runtime-metadata.changed.md b/changelog.d/add-bundle-runtime-metadata.changed.md new file mode 100644 index 00000000000..e692fd11d99 --- /dev/null +++ b/changelog.d/add-bundle-runtime-metadata.changed.md @@ -0,0 +1 @@ +Added runtime metadata with installed policyengine-core identity for bundle validation. diff --git a/policyengine_us/build_metadata.py b/policyengine_us/build_metadata.py index 6e944cb6577..3e8b458204e 100644 --- a/policyengine_us/build_metadata.py +++ b/policyengine_us/build_metadata.py @@ -6,6 +6,8 @@ from pathlib import Path import subprocess +from policyengine_core import get_runtime_metadata as get_core_runtime_metadata + PACKAGE_NAME = "policyengine-us" PACKAGE_ROOT = Path(__file__).resolve().parent DATA_BUILD_SURFACE = ( @@ -35,11 +37,8 @@ def _iter_surface_files() -> list[Path]: return files -def _get_package_version() -> str | None: - try: - return metadata.version(PACKAGE_NAME) - except metadata.PackageNotFoundError: - return None +def _get_package_version() -> str: + return metadata.version(PACKAGE_NAME) def _get_git_sha() -> str | None: @@ -69,10 +68,15 @@ def get_data_build_fingerprint() -> str: return f"sha256:{digest.hexdigest()}" -def get_data_build_metadata() -> dict[str, str | None]: +def get_runtime_metadata() -> dict[str, object]: return { "name": PACKAGE_NAME, "version": _get_package_version(), "git_sha": _get_git_sha(), "data_build_fingerprint": get_data_build_fingerprint(), + "core": get_core_runtime_metadata(), } + + +def get_data_build_metadata() -> dict[str, object]: + return get_runtime_metadata() diff --git a/policyengine_us/tests/test_build_metadata.py b/policyengine_us/tests/test_build_metadata.py index fd95f0bbba8..1b78a248f71 100644 --- a/policyengine_us/tests/test_build_metadata.py +++ b/policyengine_us/tests/test_build_metadata.py @@ -1,8 +1,11 @@ from unittest.mock import patch +import pytest + from policyengine_us.build_metadata import ( get_data_build_fingerprint, get_data_build_metadata, + get_runtime_metadata, ) @@ -16,7 +19,7 @@ def test_data_build_fingerprint_is_stable_within_process(): assert first == second -def test_get_data_build_metadata_includes_version_git_sha_and_fingerprint(): +def test_get_runtime_metadata_includes_version_git_sha_fingerprint_and_core(): get_data_build_fingerprint.cache_clear() with ( @@ -32,12 +35,39 @@ def test_get_data_build_metadata_includes_version_git_sha_and_fingerprint(): "policyengine_us.build_metadata.get_data_build_fingerprint", return_value="sha256:fingerprint", ), + patch( + "policyengine_us.build_metadata.get_core_runtime_metadata", + return_value={ + "name": "policyengine-core", + "version": "3.26.0", + "git_sha": "coredeadbeef", + }, + ), ): - metadata = get_data_build_metadata() + metadata = get_runtime_metadata() assert metadata == { "name": "policyengine-us", "version": "1.602.0", "git_sha": "deadbeef", "data_build_fingerprint": "sha256:fingerprint", + "core": { + "name": "policyengine-core", + "version": "3.26.0", + "git_sha": "coredeadbeef", + }, } + + +def test_get_data_build_metadata_uses_runtime_metadata(): + with patch( + "policyengine_us.build_metadata.get_runtime_metadata", + return_value={"name": "policyengine-us"}, + ): + assert get_data_build_metadata() == {"name": "policyengine-us"} + + +def test_runtime_metadata_uses_bundle_contract_when_available(): + policyengine_bundles = pytest.importorskip("policyengine_bundles") + + policyengine_bundles.load_component_metadata(get_runtime_metadata()) diff --git a/pyproject.toml b/pyproject.toml index 80214a5682d..c40d793a682 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,7 @@ classifiers = [ dependencies = [ "microdf-python>=1.0.0", "pandas>=2.0", - "policyengine-core>=3.25.1", + "policyengine-core>=3.26.0", "spm-calculator>=0.2.0", "tables>=3.9", "tqdm>=4.67.1", diff --git a/uv.lock b/uv.lock index ab3334c406c..a2961fcc5df 100644 --- a/uv.lock +++ b/uv.lock @@ -2941,7 +2941,7 @@ wheels = [ [[package]] name = "policyengine-core" -version = "3.25.2" +version = "3.26.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "dpath" }, @@ -2967,14 +2967,14 @@ dependencies = [ { name = "standard-imghdr" }, { name = "wheel" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d1/ed/117c487e09bc2d5dae1d39baef2d317158433f66004f38f19f6ed82110eb/policyengine_core-3.25.2.tar.gz", hash = "sha256:de80b64b969e3c6b5a3046e29e5b9f2ce56c82f874064f65556fa60c8c423f17", size = 466431, upload-time = "2026-04-21T17:37:40.654Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/69/adb6407c97de5260a938344f9eafa9979bf8f97aec8c628538d906ecdec2/policyengine_core-3.26.0.tar.gz", hash = "sha256:a571026ef418653ec18f087463cf37e9be730e90ad4376cb10997f0ddf9f8eda", size = 468190, upload-time = "2026-05-04T19:26:27.707Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/22/b2297927a2091a22267c112e8d5d5d2b374d0f1ce67f257816fee1388170/policyengine_core-3.25.2-py3-none-any.whl", hash = "sha256:c884961937940e16730fb473d486728ed8c66250dc65df15257ad611e6655b09", size = 231186, upload-time = "2026-04-21T17:37:39.148Z" }, + { url = "https://files.pythonhosted.org/packages/bc/f3/0e98b30d4eb7b309c3f1f1d8c2354595f78319ce2442eda069f02a47f4d1/policyengine_core-3.26.0-py3-none-any.whl", hash = "sha256:d63a4622233b61c4c5fc64d4f65030d65b2564ac63ac87b17d545d63cdf17194", size = 232135, upload-time = "2026-05-04T19:26:25.693Z" }, ] [[package]] name = "policyengine-us" -version = "1.665.0" +version = "1.680.0" source = { editable = "." } dependencies = [ { name = "microdf-python" }, @@ -3009,7 +3009,7 @@ requires-dist = [ { name = "jupyter-book", marker = "extra == 'dev'", specifier = ">=1.0.4.post1" }, { name = "microdf-python", specifier = ">=1.0.0" }, { name = "pandas", specifier = ">=2.0" }, - { name = "policyengine-core", specifier = ">=3.25.1" }, + { name = "policyengine-core", specifier = ">=3.26.0" }, { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.9.0" }, { name = "setuptools", marker = "extra == 'dev'", specifier = ">=80.9.0" }, { name = "spm-calculator", specifier = ">=0.2.0" },