Skip to content

Commit c89a088

Browse files
MaxGhenisclaude
andcommitted
Upgrade breakdown enum mismatch from WARNING to ERROR
Parameters with keys not in the breakdown variable's possible values now raise ValueError instead of silently logging a warning. This prevents data loss when parameter YAML files use keys that don't match the breakdown enum (e.g., using snap_utility_region keys with a state_code breakdown). Partial coverage (enum values missing from YAML) remains allowed. Closes #444 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent ff93ef4 commit c89a088

3 files changed

Lines changed: 115 additions & 10 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Upgrade breakdown enum mismatch from WARNING to ValueError for early detection of parameter key errors.

policyengine_core/parameters/operations/homogenize_parameters.py

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -96,17 +96,24 @@ def homogenize_parameter_node(
9696
{"0000-01-01": default_value, "2040-01-01": default_value},
9797
),
9898
)
99+
possible_values_str = {str(v) for v in possible_values}
100+
extra_children = []
101+
for child in node.children:
102+
child_key = child.split(".")[-1]
103+
if (
104+
child_key not in possible_values_str
105+
and str(child_key) not in possible_values_str
106+
):
107+
extra_children.append(child_key)
108+
if extra_children:
109+
raise ValueError(
110+
f"Parameter {node.name} has children {extra_children} "
111+
f"that are not in the possible values of the breakdown "
112+
f"variable '{first_breakdown}'. Check that the breakdown "
113+
f"metadata references the correct variable and that all "
114+
f"parameter keys are valid enum values."
115+
)
99116
for child in node.children:
100-
if child.split(".")[-1] not in possible_values:
101-
try:
102-
int(child)
103-
is_int = True
104-
except:
105-
is_int = False
106-
if not is_int or str(child) not in node.children:
107-
logging.warning(
108-
f"Parameter {node.name} has a child {child} that is not in the possible values of {first_breakdown}, ignoring."
109-
)
110117
if further_breakdown:
111118
node.children[child] = homogenize_parameter_node(
112119
node.children[child], breakdown[1:], variables, default_value

tests/core/parameters/operations/test_nesting.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import pytest
2+
3+
14
def test_parameter_homogenization():
25
import numpy as np
36

@@ -90,3 +93,97 @@ class family_size(Variable):
9093
]
9194
== [1, 0, 0]
9295
).all()
96+
97+
98+
def test_breakdown_mismatch_raises_error():
99+
"""Extra parameter keys not in the breakdown enum should raise ValueError."""
100+
from policyengine_core.entities import Entity
101+
from policyengine_core.model_api import ETERNITY, Enum, Variable
102+
from policyengine_core.parameters import (
103+
ParameterNode,
104+
homogenize_parameter_structures,
105+
)
106+
from policyengine_core.taxbenefitsystems import TaxBenefitSystem
107+
108+
Person = Entity("person", "people", "Person", "A person")
109+
110+
class Color(Enum):
111+
RED = "Red"
112+
BLUE = "Blue"
113+
114+
class color(Variable):
115+
value_type = Enum
116+
entity = Person
117+
definition_period = ETERNITY
118+
possible_values = Color
119+
default_value = Color.RED
120+
label = "color"
121+
122+
# "GREEN" is not in the Color enum — should raise ValueError
123+
root = ParameterNode(
124+
data={
125+
"value_by_color": {
126+
"RED": {"2021-01-01": 1},
127+
"GREEN": {"2021-01-01": 2},
128+
"metadata": {
129+
"breakdown": ["color"],
130+
},
131+
}
132+
}
133+
)
134+
135+
system = TaxBenefitSystem([Person])
136+
system.add_variables(color)
137+
system.parameters = root
138+
139+
with pytest.raises(ValueError, match="GREEN"):
140+
homogenize_parameter_structures(
141+
system.parameters, system.variables, default_value=0
142+
)
143+
144+
145+
def test_breakdown_partial_coverage_is_ok():
146+
"""Missing enum values in parameter YAML should NOT raise an error."""
147+
from policyengine_core.entities import Entity
148+
from policyengine_core.model_api import ETERNITY, Enum, Variable
149+
from policyengine_core.parameters import (
150+
ParameterNode,
151+
homogenize_parameter_structures,
152+
)
153+
from policyengine_core.taxbenefitsystems import TaxBenefitSystem
154+
155+
Person = Entity("person", "people", "Person", "A person")
156+
157+
class Color(Enum):
158+
RED = "Red"
159+
BLUE = "Blue"
160+
GREEN = "Green"
161+
162+
class color(Variable):
163+
value_type = Enum
164+
entity = Person
165+
definition_period = ETERNITY
166+
possible_values = Color
167+
default_value = Color.RED
168+
label = "color"
169+
170+
# Only RED is present — BLUE and GREEN are missing but that's fine
171+
root = ParameterNode(
172+
data={
173+
"value_by_color": {
174+
"RED": {"2021-01-01": 1},
175+
"metadata": {
176+
"breakdown": ["color"],
177+
},
178+
}
179+
}
180+
)
181+
182+
system = TaxBenefitSystem([Person])
183+
system.add_variables(color)
184+
system.parameters = root
185+
186+
# Should not raise — partial coverage is allowed
187+
homogenize_parameter_structures(
188+
system.parameters, system.variables, default_value=0
189+
)

0 commit comments

Comments
 (0)