Reforms#

The policyengine_core.reforms module contains the definition for Reform, a class which can be used to edit a TaxBenefitSystem instance.

Reform#

class policyengine_core.reforms.Reform(baseline: TaxBenefitSystem)#

A modified TaxBenefitSystem

All reforms must subclass Reform and implement a method apply().

In this method, the reform can add or replace variables and call modify_parameters to modify the parameters of the legislation.

Example:

>>> from policyengine_core import reforms
>>> from policyengine_core.parameters import load_parameter_file
>>>
>>> def modify_my_parameters(parameters):
>>>     # Add new parameters
>>>     new_parameters = load_parameter_file(name='reform_name', file_path='path_to_yaml_file.yaml')
>>>     parameters.add_child('reform_name', new_parameters)
>>>
>>>     # Update a value
>>>     parameters.taxes.some_tax.some_param.update(period=some_period, value=1000.0)
>>>
>>>    return parameters
>>>
>>> class MyReform(reforms.Reform):
>>>    def apply(self):
>>>        self.add_variable(some_variable)
>>>        self.update_variable(some_other_variable)
>>>        self.modify_parameters(modifier_function = modify_my_parameters)
static from_api(api_id: str, country_id: str = None) → Reform#

Create a reform from a dictionary of parameters.

Parameters:

parameters – A dictionary of parameter -> { period -> value } pairs.

Returns:

A reform.

static from_dict(parameter_values: dict, country_id: str = None, name: str = None) → Reform#

Create a reform from a dictionary of parameters.

Parameters:

parameter_values – A mapping of path -> {period_key: value} (or the path -> scalar shorthand, applied across year:2000:100).

Period-key formats, interpreted per parameter (see the module-level _*_update_kwargs helpers):

  • Bare ISO instant ("2026" / "2026-01" / "2026-01-01"): the value applies from that instant onward, flattening any later scheduled breakpoints (e.g. uprating). The instant is literal: {"2026-06-01": v} applies from 1 June 2026, so an annual value read at 1 January 2026 is still the baseline — use "2026-01-01" for a full-year-onward change. To keep later breakpoints, bound the change with a range instead.

  • Range "start.stop" (e.g. "2026-01-01.2027-12-31"): the value applies over the bounded interval [start, stop]; the prior value is restored afterwards.

  • Compound bounded period ("year:2026:5" / "month:2026-01:3"): the value applies over that bounded period only.

  • "ETERNITY": the value applies for all time.

Entries are applied in ascending start-instant order, so a later-starting key never clobbers an earlier one. Entries sharing a start instant are applied in their given order (the last wins for any overlap). Non-string keys are coerced to str.

Invalid input raises during construction before any parameter is modified: a malformed period key or bad value yields no partial reform, and errors from Parameter.update propagate rather than being silently swallowed.

Returns:

A reform.

modify_parameters(modifier_function: Callable[[ParameterNode], ParameterNode]) → None#

Make modifications on the parameters of the legislation.

Call this function in apply() if the reform asks for legislation parameter modifications.

Parameters:

modifier_function – A function that takes a ParameterNode and should return an object of the same type.