diff --git a/projects/policyengine-api-simulation/src/modal/app.py b/projects/policyengine-api-simulation/src/modal/app.py index b57666745..e2729c13a 100644 --- a/projects/policyengine-api-simulation/src/modal/app.py +++ b/projects/policyengine-api-simulation/src/modal/app.py @@ -144,6 +144,10 @@ def build_base_simulation_image() -> modal.Image: "fastapi>=0.115.0", "tables>=3.10.2", "logfire>=3.0.0", + # logfire imports importlib_metadata unconditionally but does + # not declare it as a dependency on Python 3.13, so install it + # explicitly or workers crash on ``import logfire``. + "importlib-metadata>=8", "policyengine-observability[fastapi]>=1.3.0,<2", ) .run_commands( diff --git a/projects/policyengine-api-simulation/src/modal/gateway/app.py b/projects/policyengine-api-simulation/src/modal/gateway/app.py index e2a48825d..64144453e 100644 --- a/projects/policyengine-api-simulation/src/modal/gateway/app.py +++ b/projects/policyengine-api-simulation/src/modal/gateway/app.py @@ -29,6 +29,10 @@ # the auth module at runtime here. "cryptography>=41.0.0", "logfire>=3.0.0", + # logfire imports importlib_metadata unconditionally but does not + # declare it as a dependency on Python 3.13, so install it + # explicitly or the container crashes at startup. + "importlib-metadata>=8", "policyengine-observability[fastapi]>=1.3.0,<2", ) .add_local_python_source( diff --git a/projects/policyengine-api-simulation/tests/test_modal_bundle_image.py b/projects/policyengine-api-simulation/tests/test_modal_bundle_image.py index a41227f68..1bb8c7596 100644 --- a/projects/policyengine-api-simulation/tests/test_modal_bundle_image.py +++ b/projects/policyengine-api-simulation/tests/test_modal_bundle_image.py @@ -95,6 +95,10 @@ def test_modal_image_uses_policyengine_bundle_install(monkeypatch): packages = pip_install_calls[0][1] assert "policyengine-observability[fastapi]>=1.3.0,<2" in packages assert "logfire>=3.0.0" in packages + # logfire needs importlib_metadata at import time on Python 3.13 but + # does not declare it; without the explicit install every worker + # crashes on ``import logfire``. + assert "importlib-metadata>=8" in packages runtime_secret_sets = { name: kwargs["secrets"] for name, kwargs in app.app.function_calls @@ -164,6 +168,9 @@ def test_gateway_image_installs_dual_observability(monkeypatch): packages = pip_install_calls[0][1] assert "policyengine-observability[fastapi]>=1.3.0,<2" in packages assert "logfire>=3.0.0" in packages + # Same importlib_metadata gap as the simulation image: without this the + # gateway ASGI factory dies in configure_logfire and every request 303s. + assert "importlib-metadata>=8" in packages function_kwargs = {name: kwargs for name, kwargs in app.app.function_calls} assert function_kwargs["web_app"]["secrets"] == [