diff --git a/changelog.d/645.fixed.md b/changelog.d/645.fixed.md new file mode 100644 index 00000000..2b4678be --- /dev/null +++ b/changelog.d/645.fixed.md @@ -0,0 +1 @@ +The UK reform contract's household route now runs when country imports are skipped, so the candidate script's `POLICYENGINE_SKIP_COUNTRY_IMPORTS` no longer turns every `wrapper.household` row into an error and rejects every UK candidate. diff --git a/src/policyengine/tax_benefit_models/uk/budget_reform_contract.py b/src/policyengine/tax_benefit_models/uk/budget_reform_contract.py index 8f14c956..b2dc837e 100644 --- a/src/policyengine/tax_benefit_models/uk/budget_reform_contract.py +++ b/src/policyengine/tax_benefit_models/uk/budget_reform_contract.py @@ -1257,10 +1257,14 @@ class _WrapperHouseholdRoute: route = "wrapper.household" def _call(self, case: Case, year: int, reform, variables: Sequence[str]): - import policyengine as pe + # Import the country module itself, not ``policyengine.uk``: callers such + # as scripts/check_uk_candidate.py set POLICYENGINE_SKIP_COUNTRY_IMPORTS, + # which leaves ``policyengine.uk`` as None. It is the same + # ``calculate_household`` that ``policyengine.uk`` exposes. + from policyengine.tax_benefit_models import uk as pe_uk household = case.household - return pe.uk.calculate_household( + return pe_uk.calculate_household( people=[dict(person) for person in household.people], benunit=dict(household.benunit), household=dict(household.household), diff --git a/tests/test_uk_budget_reform_contract.py b/tests/test_uk_budget_reform_contract.py index a0a6b80d..18895f3c 100644 --- a/tests/test_uk_budget_reform_contract.py +++ b/tests/test_uk_budget_reform_contract.py @@ -9,6 +9,10 @@ import datetime import json +import os +import subprocess +import sys +import textwrap import pytest @@ -2004,3 +2008,45 @@ def array(tag, variable, year): order = status[(population.POPULATION_QUERY_ORDER, "unreformed")] assert order["status"] == "fail" assert order["detail"]["income_tax:2026_matches"] is False + + +def test_the_household_route_runs_when_country_imports_are_skipped(): + # scripts/check_uk_candidate.py sets POLICYENGINE_SKIP_COUNTRY_IMPORTS=1 + # before the qualification gates run this contract, and that leaves + # ``policyengine.uk`` as None. Reaching the wrapper through it made every + # wrapper.household row an error, so every UK candidate was rejected. + code = textwrap.dedent( + """ + import json + import os + + os.environ["POLICYENGINE_SKIP_COUNTRY_IMPORTS"] = "1" + import policyengine + + assert policyengine.uk is None + from policyengine.tax_benefit_models.uk.budget_reform_contract import ( + CASES, + evaluate_case, + ) + + case = next(case for case in CASES if case.id == "april_rate_change") + rows = evaluate_case(case, "wrapper.household") + print(json.dumps([[row["status"], row.get("detail")] for row in rows])) + """ + ) + env = { + key: value + for key, value in os.environ.items() + if key != "POLICYENGINE_SKIP_COUNTRY_IMPORTS" + } + result = subprocess.run( + [sys.executable, "-c", code], + capture_output=True, + text=True, + env=env, + timeout=900, + ) + assert result.returncode == 0, result.stderr + rows = json.loads(result.stdout.strip().splitlines()[-1]) + assert rows + assert [row for row in rows if row[0] == "error"] == [], rows