Skip to content

Commit 2a9212f

Browse files
authored
Merge pull request #959 from PolicyEngine/observability-3-0-4
Move to policyengine-observability 3.0.4 and remove the scopes module
2 parents 58445d1 + 3d9109d commit 2a9212f

31 files changed

Lines changed: 189 additions & 363 deletions

File tree

‎changelog_entry.yaml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
- Ran Stage 12 US national simulations as 20 region-group calls each, and built an independent, immutable Stage 12 cache of three annual US datasets and 60 current-law baselines before each deployment, publishing it only after a recorded determinism check and recording its release in the v2 version manifest under a new key, while the existing key keeps a copy that earlier readers accept
1717
- Made the budget refuse a household count outside its country's plausible range (above 200 million in the US or 60 million in the UK, negative or not finite), raising ImplausibleEntityCountError instead of publishing a count like #767's
1818
changed:
19+
- Required policyengine-observability 3.0.4 (>=3.0.4,<4) in the observability library, the entrypoint, the gateway and the executor, and locked it in the six locks that include it, where it is the only package that moves; 3.0.4 annotates its span and operation scopes' __exit__ and __aexit__ as returning Literal[False], so the services now open spans and operations with runtime.span and runtime.operation directly, and the policyengine_simulation_observability.scopes module, which only retyped those scopes for Pyright, is removed with its tests; a new observability library test fails if an older release is installed, Pyright still reports no errors in any package, and 3.0.3's fix, which keeps the recorded exception on the request.completed record and server span of a request that ends with an error outcome but passes no exception to end_request, is included
1920
- Enforced the file-length rule sim.quality.python-file-length, now that every tracked Python file is within its limit: its empty baseline and its downgrade section are gone, and its tier is metric rather than mechanical, because it measures a number against a threshold, with the at-the-limit and one-over fixtures as its below and above proof
2021
- Moved the entrypoint's backend calls, response telemetry and comparison dispatch out of app.py (925 lines, now 583) into entry_runtime.py, which app.py re-exports and which still logs under the policyengine_simulation_entry.app logger; the routes stay in app.py, so FastAPI and ruff read their annotations as before; the 2,251-line deployment asset test file is split into seven test files and a support module named test_deployment_assets_support.py, the rules registry names each moved test by its new file, and its guard-test discovery now covers tests/test_deployment_assets*.py
2122
- Moved the worker's credential setup, request year and policy parsing, and region resolution out of simulation_runtime.py (1,111 lines, now 651) into gcp_credentials.py, request_policy.py and region_resolution.py; simulation_runtime.py keeps dataset selection and loading, simulation building, local-area breakdowns and the run itself, which tests replace by name there, and re-exports the moved names, so every caller still imports them from simulation_runtime; the shared helpers _parse_year, _normalise_policy, _compile_interval_policy, _runtime_policy (now wrapper_policy), _resolve_region, _resolve_dataset_reference and _require_local_area_breakdown are now public, and the credential messages still log under the simulation_runtime logger; the 3,048-line output builder test file is split into seven test files and a module of shared fakes, all named test_simulation_output_*.py so the weighted-frame rule still checks them

‎docs/engineering/skills/observability.md‎

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ add `_observability_context` to it.
5252

5353
At the receiving function, validate the separate argument with
5454
`normalize_observability_context` and pass the result to
55-
`scopes.operation(runtime, ..., remote_context=context)`. The operation scope must
55+
`runtime.operation(..., remote_context=context)`. The operation scope must
5656
cover the worker call. Child dispatches made inside that scope call
5757
`runtime.capture_context()` again and pass the resulting context through their
5858
own keyword-only argument.
@@ -79,11 +79,10 @@ All span names for supported run configurations are defined in
7979
appropriate `StagePlan` and call `plan.name(Stage.VALUE)`. Do not add span name
8080
string literals in services or workers.
8181

82-
Open a span with `scopes.span(runtime, plan.name(Stage.VALUE))`, and an
83-
operation with `scopes.operation(runtime, ...)`, from
84-
`policyengine_simulation_observability.scopes` rather than `runtime.span(...)`
85-
or `runtime.operation(...)`. Each returns the runtime's own scope, typed so
86-
that Pyright knows an exception raised in the block propagates; see
82+
Open a span with `runtime.span(plan.name(Stage.VALUE))`, and an operation with
83+
`runtime.operation(...)`. policyengine-observability 3.0.4 types both scopes
84+
correctly: their `__exit__` and `__aexit__` return `Literal[False]`, so Pyright
85+
knows an exception raised in the block propagates; see
8786
`docs/engineering/skills/type-checking.md`.
8887

8988
When adding a run configuration or runtime stage:

‎docs/engineering/skills/type-checking.md‎

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -173,12 +173,14 @@ section is still in the registry fails the guard on every pull request.
173173
same line: `# pyright: ignore[reportAttributeAccessIssue] # <reason>`.
174174
- If an error shows a real bug, first write a test that demonstrates it, then
175175
fix it.
176-
- Open observability spans and operations with `scopes.span(runtime, ...)` and
177-
`scopes.operation(runtime, ...)` from
178-
`policyengine_simulation_observability.scopes`, not `runtime.span(...)` or
179-
`runtime.operation(...)`. policyengine-observability types its scope
180-
managers as possibly suppressing exceptions, so after `with runtime.span(...)`
181-
Pyright reports each variable the block assigned as possibly unbound.
176+
- Open observability spans and operations with `runtime.span(...)` and
177+
`runtime.operation(...)`. policyengine-observability 3.0.4 types them
178+
correctly: their `__exit__` and `__aexit__` return `Literal[False]`, so
179+
Pyright knows they never suppress an exception and doesn't report a variable
180+
the block assigns as possibly unbound after it. Every package that depends
181+
on policyengine-observability requires 3.0.4 or later, and
182+
`libs/policyengine-simulation-observability/tests/test_scope_exit_typing.py`
183+
fails if an older release is installed.
182184
- Before reading a simulation's output frames, narrow
183185
`<simulation>.output_dataset.data` with the `EntityFrames` protocol in
184186
`policyengine_simulation_executor.output_frames`, and keep reading through

‎libs/policyengine-simulation-contract/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.

‎libs/policyengine-simulation-observability/pyproject.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ requires-python = ">=3.13"
1010
dependencies = [
1111
"fastapi>=0.115.0",
1212
"pydantic>=2.0",
13-
"policyengine-observability[fastapi,google,otlp-grpc]>=3.0.2,<4",
13+
"policyengine-observability[fastapi,google,otlp-grpc]>=3.0.4,<4",
1414
]
1515

1616
[project.optional-dependencies]

‎libs/policyengine-simulation-observability/src/policyengine_simulation_observability/scopes.py‎

Lines changed: 0 additions & 54 deletions
This file was deleted.
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
"""The installed policyengine-observability types its scopes as never suppressing.
2+
3+
The services open spans and operations with ``runtime.span(...)`` and
4+
``runtime.operation(...)``. A type checker reads an ``__exit__`` or
5+
``__aexit__`` annotated as returning ``bool`` as "this context manager may
6+
suppress the exception raised in its block", so Pyright would then report every
7+
variable such a block assigns as possibly unbound after it, and every function
8+
that returns from inside the block as possibly not returning.
9+
policyengine-observability 3.0.4 annotates both methods as returning
10+
``Literal[False]``. These tests fail if an older release is installed.
11+
"""
12+
13+
from typing import Literal, get_type_hints
14+
15+
import pytest
16+
from policyengine_simulation_observability.observability import build_runtime
17+
18+
19+
@pytest.fixture
20+
def runtime(monkeypatch):
21+
monkeypatch.setenv("OTEL_SDK_DISABLED", "true")
22+
real_runtime = build_runtime(
23+
service_name="policyengine-simulation-scope-typing-test",
24+
service_role="test",
25+
platform="local",
26+
environment="test",
27+
)
28+
yield real_runtime
29+
real_runtime.shutdown()
30+
31+
32+
@pytest.mark.parametrize("exit_method_name", ["__exit__", "__aexit__"])
33+
@pytest.mark.parametrize(
34+
"open_scope",
35+
[
36+
pytest.param(lambda runtime: runtime.span("scope_typing"), id="span"),
37+
pytest.param(lambda runtime: runtime.operation("scope_typing"), id="operation"),
38+
],
39+
)
40+
def test_scope_exit_is_typed_as_never_suppressing_an_exception(
41+
runtime, open_scope, exit_method_name
42+
):
43+
scope_manager = open_scope(runtime)
44+
exit_method = getattr(type(scope_manager), exit_method_name)
45+
46+
assert get_type_hints(exit_method)["return"] == Literal[False]

‎libs/policyengine-simulation-observability/tests/test_scopes.py‎

Lines changed: 0 additions & 103 deletions
This file was deleted.

‎libs/policyengine-simulation-observability/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.

‎libs/policyengine-stage12-persistence/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)