OpenFisca-Core-test 46.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- openfisca_core/__init__.py +0 -0
- openfisca_core/commons/__init__.py +18 -0
- openfisca_core/commons/dummy.py +25 -0
- openfisca_core/commons/formulas.py +121 -0
- openfisca_core/commons/misc.py +109 -0
- openfisca_core/commons/py.typed +0 -0
- openfisca_core/commons/rates.py +114 -0
- openfisca_core/commons/tests/__init__.py +0 -0
- openfisca_core/commons/tests/test_dummy.py +9 -0
- openfisca_core/commons/tests/test_formulas.py +75 -0
- openfisca_core/commons/tests/test_rates.py +26 -0
- openfisca_core/data_storage/__init__.py +7 -0
- openfisca_core/data_storage/in_memory_storage.py +198 -0
- openfisca_core/data_storage/on_disk_storage.py +307 -0
- openfisca_core/data_storage/types.py +14 -0
- openfisca_core/entities/__init__.py +23 -0
- openfisca_core/entities/_core_entity.py +217 -0
- openfisca_core/entities/_description.py +55 -0
- openfisca_core/entities/entity.py +60 -0
- openfisca_core/entities/group_entity.py +123 -0
- openfisca_core/entities/helpers.py +165 -0
- openfisca_core/entities/py.typed +0 -0
- openfisca_core/entities/role.py +92 -0
- openfisca_core/entities/tests/__init__.py +0 -0
- openfisca_core/entities/tests/test_entity.py +10 -0
- openfisca_core/entities/tests/test_group_entity.py +70 -0
- openfisca_core/entities/tests/test_role.py +11 -0
- openfisca_core/entities/types.py +44 -0
- openfisca_core/errors/__init__.py +64 -0
- openfisca_core/errors/cycle_error.py +2 -0
- openfisca_core/errors/empty_argument_error.py +39 -0
- openfisca_core/errors/nan_creation_error.py +2 -0
- openfisca_core/errors/parameter_not_found_error.py +16 -0
- openfisca_core/errors/parameter_parsing_error.py +18 -0
- openfisca_core/errors/period_mismatch_error.py +9 -0
- openfisca_core/errors/situation_parsing_error.py +26 -0
- openfisca_core/errors/spiral_error.py +2 -0
- openfisca_core/errors/variable_name_config_error.py +2 -0
- openfisca_core/errors/variable_not_found_error.py +29 -0
- openfisca_core/experimental/__init__.py +9 -0
- openfisca_core/experimental/_errors.py +5 -0
- openfisca_core/experimental/_memory_config.py +41 -0
- openfisca_core/formula_helpers.py +9 -0
- openfisca_core/holders/__init__.py +33 -0
- openfisca_core/holders/helpers.py +93 -0
- openfisca_core/holders/holder.py +328 -0
- openfisca_core/holders/tests/__init__.py +0 -0
- openfisca_core/holders/tests/test_helpers.py +134 -0
- openfisca_core/holders/types.py +3 -0
- openfisca_core/indexed_enums/__init__.py +18 -0
- openfisca_core/indexed_enums/_enum_type.py +79 -0
- openfisca_core/indexed_enums/_errors.py +35 -0
- openfisca_core/indexed_enums/_guards.py +209 -0
- openfisca_core/indexed_enums/_utils.py +180 -0
- openfisca_core/indexed_enums/config.py +6 -0
- openfisca_core/indexed_enums/enum.py +218 -0
- openfisca_core/indexed_enums/enum_array.py +316 -0
- openfisca_core/indexed_enums/py.typed +0 -0
- openfisca_core/indexed_enums/tests/__init__.py +0 -0
- openfisca_core/indexed_enums/tests/test_enum.py +135 -0
- openfisca_core/indexed_enums/tests/test_enum_array.py +30 -0
- openfisca_core/indexed_enums/types.py +49 -0
- openfisca_core/memory_config.py +9 -0
- openfisca_core/model_api.py +71 -0
- openfisca_core/parameters/__init__.py +71 -0
- openfisca_core/parameters/at_instant_like.py +17 -0
- openfisca_core/parameters/config.py +55 -0
- openfisca_core/parameters/helpers.py +106 -0
- openfisca_core/parameters/parameter.py +217 -0
- openfisca_core/parameters/parameter_at_instant.py +75 -0
- openfisca_core/parameters/parameter_node.py +176 -0
- openfisca_core/parameters/parameter_node_at_instant.py +57 -0
- openfisca_core/parameters/parameter_scale.py +121 -0
- openfisca_core/parameters/parameter_scale_bracket.py +7 -0
- openfisca_core/parameters/values_history.py +5 -0
- openfisca_core/parameters/vectorial_parameter_node_at_instant.py +179 -0
- openfisca_core/periods/__init__.py +77 -0
- openfisca_core/periods/_errors.py +28 -0
- openfisca_core/periods/_parsers.py +122 -0
- openfisca_core/periods/config.py +20 -0
- openfisca_core/periods/date_unit.py +110 -0
- openfisca_core/periods/helpers.py +314 -0
- openfisca_core/periods/instant_.py +226 -0
- openfisca_core/periods/period_.py +919 -0
- openfisca_core/periods/py.typed +0 -0
- openfisca_core/periods/tests/__init__.py +0 -0
- openfisca_core/periods/tests/helpers/__init__.py +0 -0
- openfisca_core/periods/tests/helpers/test_helpers.py +65 -0
- openfisca_core/periods/tests/helpers/test_instant.py +73 -0
- openfisca_core/periods/tests/helpers/test_period.py +134 -0
- openfisca_core/periods/tests/test_instant.py +32 -0
- openfisca_core/periods/tests/test_parsers.py +129 -0
- openfisca_core/periods/tests/test_period.py +283 -0
- openfisca_core/populations/__init__.py +64 -0
- openfisca_core/populations/_core_population.py +454 -0
- openfisca_core/populations/_errors.py +65 -0
- openfisca_core/populations/group_population.py +326 -0
- openfisca_core/populations/population.py +142 -0
- openfisca_core/populations/tests/__init__.py +0 -0
- openfisca_core/populations/tests/test_members_position.py +116 -0
- openfisca_core/populations/types.py +110 -0
- openfisca_core/projectors/__init__.py +39 -0
- openfisca_core/projectors/entity_to_person_projector.py +12 -0
- openfisca_core/projectors/first_person_to_entity_projector.py +13 -0
- openfisca_core/projectors/helpers.py +139 -0
- openfisca_core/projectors/projector.py +38 -0
- openfisca_core/projectors/typing.py +27 -0
- openfisca_core/projectors/unique_role_to_entity_projector.py +14 -0
- openfisca_core/rates.py +9 -0
- openfisca_core/reforms/__init__.py +24 -0
- openfisca_core/reforms/in_yaml_test_reform.py +28 -0
- openfisca_core/reforms/reform.py +86 -0
- openfisca_core/scripts/__init__.py +106 -0
- openfisca_core/scripts/__pycache__/__init__.cpython-310-pytest-8.3.3.pyc +0 -0
- openfisca_core/scripts/__pycache__/__init__.cpython-310-pytest-8.4.2.pyc +0 -0
- openfisca_core/scripts/__pycache__/__init__.cpython-310.pyc +0 -0
- openfisca_core/scripts/__pycache__/__init__.cpython-311-pytest-8.3.3.pyc +0 -0
- openfisca_core/scripts/__pycache__/__init__.cpython-311-pytest-8.4.2.pyc +0 -0
- openfisca_core/scripts/__pycache__/__init__.cpython-311.pyc +0 -0
- openfisca_core/scripts/__pycache__/__init__.cpython-312-pytest-8.4.2.pyc +0 -0
- openfisca_core/scripts/__pycache__/__init__.cpython-312.pyc +0 -0
- openfisca_core/scripts/__pycache__/__init__.cpython-313-pytest-8.3.3.pyc +0 -0
- openfisca_core/scripts/__pycache__/__init__.cpython-313-pytest-8.4.2.pyc +0 -0
- openfisca_core/scripts/__pycache__/__init__.cpython-313.pyc +0 -0
- openfisca_core/scripts/__pycache__/find_placeholders.cpython-311.pyc +0 -0
- openfisca_core/scripts/__pycache__/find_placeholders.cpython-313.pyc +0 -0
- openfisca_core/scripts/__pycache__/measure_numpy_condition_notations.cpython-311.pyc +0 -0
- openfisca_core/scripts/__pycache__/measure_numpy_condition_notations.cpython-313.pyc +0 -0
- openfisca_core/scripts/__pycache__/measure_performances.cpython-311.pyc +0 -0
- openfisca_core/scripts/__pycache__/measure_performances.cpython-313.pyc +0 -0
- openfisca_core/scripts/__pycache__/measure_performances_fancy_indexing.cpython-311.pyc +0 -0
- openfisca_core/scripts/__pycache__/measure_performances_fancy_indexing.cpython-313.pyc +0 -0
- openfisca_core/scripts/__pycache__/openfisca_command.cpython-310.pyc +0 -0
- openfisca_core/scripts/__pycache__/openfisca_command.cpython-311.pyc +0 -0
- openfisca_core/scripts/__pycache__/openfisca_command.cpython-312.pyc +0 -0
- openfisca_core/scripts/__pycache__/openfisca_command.cpython-313.pyc +0 -0
- openfisca_core/scripts/__pycache__/remove_fuzzy.cpython-311.pyc +0 -0
- openfisca_core/scripts/__pycache__/remove_fuzzy.cpython-313.pyc +0 -0
- openfisca_core/scripts/__pycache__/run_test.cpython-310.pyc +0 -0
- openfisca_core/scripts/__pycache__/run_test.cpython-311.pyc +0 -0
- openfisca_core/scripts/__pycache__/run_test.cpython-312.pyc +0 -0
- openfisca_core/scripts/__pycache__/run_test.cpython-313.pyc +0 -0
- openfisca_core/scripts/__pycache__/simulation_generator.cpython-311.pyc +0 -0
- openfisca_core/scripts/__pycache__/simulation_generator.cpython-313.pyc +0 -0
- openfisca_core/scripts/assets/__init__.py +0 -0
- openfisca_core/scripts/assets/__pycache__/__init__.cpython-311.pyc +0 -0
- openfisca_core/scripts/assets/__pycache__/__init__.cpython-313.pyc +0 -0
- openfisca_core/scripts/assets/index.html +140 -0
- openfisca_core/scripts/find_placeholders.py +51 -0
- openfisca_core/scripts/measure_numpy_condition_notations.py +137 -0
- openfisca_core/scripts/measure_performances.py +291 -0
- openfisca_core/scripts/measure_performances_fancy_indexing.py +89 -0
- openfisca_core/scripts/migrations/__init__.py +0 -0
- openfisca_core/scripts/migrations/__pycache__/__init__.cpython-311.pyc +0 -0
- openfisca_core/scripts/migrations/__pycache__/__init__.cpython-313.pyc +0 -0
- openfisca_core/scripts/migrations/__pycache__/v24_to_25.cpython-311.pyc +0 -0
- openfisca_core/scripts/migrations/__pycache__/v24_to_25.cpython-313.pyc +0 -0
- openfisca_core/scripts/migrations/v16_2_to_v17/__init__.py +0 -0
- openfisca_core/scripts/migrations/v16_2_to_v17/__pycache__/__init__.cpython-311.pyc +0 -0
- openfisca_core/scripts/migrations/v16_2_to_v17/__pycache__/__init__.cpython-313.pyc +0 -0
- openfisca_core/scripts/migrations/v16_2_to_v17/__pycache__/xml_to_yaml_country_template.cpython-311.pyc +0 -0
- openfisca_core/scripts/migrations/v16_2_to_v17/__pycache__/xml_to_yaml_country_template.cpython-313.pyc +0 -0
- openfisca_core/scripts/migrations/v16_2_to_v17/__pycache__/xml_to_yaml_extension_template.cpython-311.pyc +0 -0
- openfisca_core/scripts/migrations/v16_2_to_v17/__pycache__/xml_to_yaml_extension_template.cpython-313.pyc +0 -0
- openfisca_core/scripts/migrations/v16_2_to_v17/legislation.xsd +102 -0
- openfisca_core/scripts/migrations/v16_2_to_v17/xml_to_yaml_country_template.py +30 -0
- openfisca_core/scripts/migrations/v16_2_to_v17/xml_to_yaml_extension_template.py +26 -0
- openfisca_core/scripts/migrations/v24_to_25.py +156 -0
- openfisca_core/scripts/openfisca_command.py +192 -0
- openfisca_core/scripts/remove_fuzzy.py +213 -0
- openfisca_core/scripts/run_test.py +51 -0
- openfisca_core/scripts/simulation_generator.py +90 -0
- openfisca_core/simulation_builder.py +16 -0
- openfisca_core/simulations/__init__.py +45 -0
- openfisca_core/simulations/_build_default_simulation.py +158 -0
- openfisca_core/simulations/_build_from_variables.py +230 -0
- openfisca_core/simulations/_type_guards.py +298 -0
- openfisca_core/simulations/helpers.py +111 -0
- openfisca_core/simulations/simulation.py +618 -0
- openfisca_core/simulations/simulation_builder.py +855 -0
- openfisca_core/simulations/typing.py +203 -0
- openfisca_core/taxbenefitsystems/__init__.py +26 -0
- openfisca_core/taxbenefitsystems/tax_benefit_system.py +584 -0
- openfisca_core/taxscales/__init__.py +35 -0
- openfisca_core/taxscales/abstract_rate_tax_scale.py +41 -0
- openfisca_core/taxscales/abstract_tax_scale.py +53 -0
- openfisca_core/taxscales/amount_tax_scale_like.py +55 -0
- openfisca_core/taxscales/helpers.py +44 -0
- openfisca_core/taxscales/linear_average_rate_tax_scale.py +76 -0
- openfisca_core/taxscales/marginal_amount_tax_scale.py +34 -0
- openfisca_core/taxscales/marginal_rate_tax_scale.py +286 -0
- openfisca_core/taxscales/rate_tax_scale_like.py +214 -0
- openfisca_core/taxscales/single_amount_tax_scale.py +32 -0
- openfisca_core/taxscales/tax_scale_like.py +65 -0
- openfisca_core/tools/__init__.py +94 -0
- openfisca_core/tools/parallel_plugin.py +55 -0
- openfisca_core/tools/simulation_dumper.py +137 -0
- openfisca_core/tools/test_runner.py +802 -0
- openfisca_core/tracers/__init__.py +40 -0
- openfisca_core/tracers/computation_log.py +196 -0
- openfisca_core/tracers/flat_trace.py +87 -0
- openfisca_core/tracers/full_tracer.py +172 -0
- openfisca_core/tracers/performance_log.py +133 -0
- openfisca_core/tracers/simple_tracer.py +64 -0
- openfisca_core/tracers/trace_node.py +121 -0
- openfisca_core/tracers/tracing_parameter_node_at_instant.py +81 -0
- openfisca_core/types.py +548 -0
- openfisca_core/variables/__init__.py +26 -0
- openfisca_core/variables/config.py +53 -0
- openfisca_core/variables/helpers.py +67 -0
- openfisca_core/variables/tests/__init__.py +0 -0
- openfisca_core/variables/tests/test_definition_period.py +43 -0
- openfisca_core/variables/variable.py +474 -0
- openfisca_core/warnings/__init__.py +25 -0
- openfisca_core/warnings/libyaml_warning.py +2 -0
- openfisca_core/warnings/tempfile_warning.py +2 -0
- openfisca_core_test-46.0.0.data/data/share/openfisca/openfisca-core/CHANGELOG.md +4209 -0
- openfisca_core_test-46.0.0.data/data/share/openfisca/openfisca-core/README.md +152 -0
- openfisca_core_test-46.0.0.dist-info/METADATA +194 -0
- openfisca_core_test-46.0.0.dist-info/RECORD +237 -0
- openfisca_core_test-46.0.0.dist-info/WHEEL +5 -0
- openfisca_core_test-46.0.0.dist-info/entry_points.txt +3 -0
- openfisca_core_test-46.0.0.dist-info/licenses/LICENSE +661 -0
- openfisca_core_test-46.0.0.dist-info/top_level.txt +2 -0
- openfisca_web_api/__init__.py +0 -0
- openfisca_web_api/app.py +226 -0
- openfisca_web_api/errors.py +11 -0
- openfisca_web_api/handlers.py +90 -0
- openfisca_web_api/loader/__init__.py +22 -0
- openfisca_web_api/loader/entities.py +28 -0
- openfisca_web_api/loader/parameters.py +122 -0
- openfisca_web_api/loader/spec.py +168 -0
- openfisca_web_api/loader/tax_benefit_system.py +27 -0
- openfisca_web_api/loader/variables.py +121 -0
- openfisca_web_api/openAPI.yml +434 -0
- openfisca_web_api/scripts/__init__.py +0 -0
- openfisca_web_api/scripts/serve.py +99 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from openfisca_core import periods
|
|
4
|
+
from openfisca_core.variables import Variable
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@pytest.fixture
|
|
8
|
+
def variable(persons):
|
|
9
|
+
class TestVariable(Variable):
|
|
10
|
+
value_type = float
|
|
11
|
+
entity = persons
|
|
12
|
+
|
|
13
|
+
return TestVariable
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_weekday_variable(variable) -> None:
|
|
17
|
+
variable.definition_period = periods.WEEKDAY
|
|
18
|
+
assert variable()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_week_variable(variable) -> None:
|
|
22
|
+
variable.definition_period = periods.WEEK
|
|
23
|
+
assert variable()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_day_variable(variable) -> None:
|
|
27
|
+
variable.definition_period = periods.DAY
|
|
28
|
+
assert variable()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def test_month_variable(variable) -> None:
|
|
32
|
+
variable.definition_period = periods.MONTH
|
|
33
|
+
assert variable()
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_year_variable(variable) -> None:
|
|
37
|
+
variable.definition_period = periods.YEAR
|
|
38
|
+
assert variable()
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def test_eternity_variable(variable) -> None:
|
|
42
|
+
variable.definition_period = periods.ETERNITY
|
|
43
|
+
assert variable()
|
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import datetime
|
|
4
|
+
import re
|
|
5
|
+
import textwrap
|
|
6
|
+
from typing import NoReturn
|
|
7
|
+
|
|
8
|
+
import numpy
|
|
9
|
+
import sortedcontainers
|
|
10
|
+
|
|
11
|
+
from openfisca_core import commons, periods
|
|
12
|
+
from openfisca_core import types as t
|
|
13
|
+
from openfisca_core.entities import Entity, GroupEntity
|
|
14
|
+
from openfisca_core.indexed_enums import Enum, EnumArray
|
|
15
|
+
from openfisca_core.periods import DateUnit, Period
|
|
16
|
+
|
|
17
|
+
from . import config, helpers
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Variable:
|
|
21
|
+
"""A `variable <https://openfisca.org/doc/key-concepts/variables.html>`_ of the legislation.
|
|
22
|
+
|
|
23
|
+
Main attributes:
|
|
24
|
+
|
|
25
|
+
.. attribute:: name
|
|
26
|
+
|
|
27
|
+
Name of the variable
|
|
28
|
+
|
|
29
|
+
.. attribute:: value_type
|
|
30
|
+
|
|
31
|
+
The value type of the variable. Possible value types in OpenFisca are ``int`` ``float`` ``bool`` ``str`` ``date`` and ``Enum``.
|
|
32
|
+
|
|
33
|
+
.. attribute:: entity
|
|
34
|
+
|
|
35
|
+
`Entity <https://openfisca.org/doc/key-concepts/person,_entities,_role.html>`_ the variable is defined for. For instance : ``Person``, ``Household``.
|
|
36
|
+
|
|
37
|
+
.. attribute:: definition_period
|
|
38
|
+
|
|
39
|
+
`Period <https://openfisca.org/doc/coding-the-legislation/35_periods.html>`_ the variable is defined for. Possible value: ``DateUnit.DAY``, ``DateUnit.MONTH``, ``DateUnit.YEAR``, ``DateUnit.ETERNITY``.
|
|
40
|
+
|
|
41
|
+
.. attribute:: formulas
|
|
42
|
+
|
|
43
|
+
Formulas used to calculate the variable
|
|
44
|
+
|
|
45
|
+
.. attribute:: label
|
|
46
|
+
|
|
47
|
+
Description of the variable
|
|
48
|
+
|
|
49
|
+
.. attribute:: reference
|
|
50
|
+
|
|
51
|
+
Legislative reference describing the variable.
|
|
52
|
+
|
|
53
|
+
.. attribute:: default_value
|
|
54
|
+
|
|
55
|
+
`Default value <https://openfisca.org/doc/key-concepts/variables.html#default-values>`_ of the variable.
|
|
56
|
+
|
|
57
|
+
Secondary attributes:
|
|
58
|
+
|
|
59
|
+
.. attribute:: baseline_variable
|
|
60
|
+
|
|
61
|
+
If the variable has been introduced in a `reform <https://openfisca.org/doc/key-concepts/reforms.html>`_ to replace another variable, baseline_variable is the replaced variable.
|
|
62
|
+
|
|
63
|
+
.. attribute:: dtype
|
|
64
|
+
|
|
65
|
+
Numpy `dtype <https://docs.scipy.org/doc/numpy-1.13.0/reference/generated/numpy.dtype.html>`_ used under the hood for the variable.
|
|
66
|
+
|
|
67
|
+
.. attribute:: end
|
|
68
|
+
|
|
69
|
+
`Date <https://openfisca.org/doc/coding-the-legislation/40_legislation_evolutions.html#variable-end>`_ when the variable disappears from the legislation.
|
|
70
|
+
|
|
71
|
+
.. attribute:: is_neutralized
|
|
72
|
+
|
|
73
|
+
True if the variable is neutralized. Neutralized variables never use their formula, and only return their default values when calculated.
|
|
74
|
+
|
|
75
|
+
.. attribute:: json_type
|
|
76
|
+
|
|
77
|
+
JSON type corresponding to the variable.
|
|
78
|
+
|
|
79
|
+
.. attribute:: max_length
|
|
80
|
+
|
|
81
|
+
If the value type of the variable is ``str``, max length of the string allowed. ``None`` if there is no limit.
|
|
82
|
+
|
|
83
|
+
.. attribute:: possible_values
|
|
84
|
+
|
|
85
|
+
If the value type of the variable is ``Enum``, contains the values the variable can take.
|
|
86
|
+
|
|
87
|
+
.. attribute:: set_input
|
|
88
|
+
|
|
89
|
+
Function used to automatically process variable inputs defined for periods not matching the definition_period of the variable. See more on the `documentation <https://openfisca.org/doc/coding-the-legislation/35_periods.html#set-input-automatically-process-variable-inputs-defined-for-periods-not-matching-the-definition-period>`_. Possible values are ``set_input_dispatch_by_period``, ``set_input_divide_by_period``, or nothing.
|
|
90
|
+
|
|
91
|
+
.. attribute:: unit
|
|
92
|
+
|
|
93
|
+
Free text field describing the unit of the variable. Only used as metadata.
|
|
94
|
+
|
|
95
|
+
.. attribute:: documentation
|
|
96
|
+
|
|
97
|
+
Free multilines text field describing the variable context and usage.
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
__name__: str
|
|
101
|
+
|
|
102
|
+
def __init__(self, baseline_variable=None) -> None:
|
|
103
|
+
self.name = self.__class__.__name__
|
|
104
|
+
attr = {
|
|
105
|
+
name: value
|
|
106
|
+
for name, value in self.__class__.__dict__.items()
|
|
107
|
+
if not name.startswith("__")
|
|
108
|
+
}
|
|
109
|
+
self.baseline_variable = baseline_variable
|
|
110
|
+
self.value_type = self.set(
|
|
111
|
+
attr,
|
|
112
|
+
"value_type",
|
|
113
|
+
required=True,
|
|
114
|
+
allowed_values=config.VALUE_TYPES.keys(),
|
|
115
|
+
)
|
|
116
|
+
self.dtype = config.VALUE_TYPES[self.value_type]["dtype"]
|
|
117
|
+
self.json_type = config.VALUE_TYPES[self.value_type]["json_type"]
|
|
118
|
+
if self.value_type == Enum:
|
|
119
|
+
self.possible_values = self.set(
|
|
120
|
+
attr,
|
|
121
|
+
"possible_values",
|
|
122
|
+
required=True,
|
|
123
|
+
setter=self.set_possible_values,
|
|
124
|
+
)
|
|
125
|
+
if self.value_type == str:
|
|
126
|
+
self.max_length = self.set(attr, "max_length", allowed_type=int)
|
|
127
|
+
if self.max_length:
|
|
128
|
+
self.dtype = f"|S{self.max_length}"
|
|
129
|
+
if self.value_type == Enum:
|
|
130
|
+
self.default_value = self.set(
|
|
131
|
+
attr,
|
|
132
|
+
"default_value",
|
|
133
|
+
allowed_type=self.possible_values,
|
|
134
|
+
required=True,
|
|
135
|
+
)
|
|
136
|
+
else:
|
|
137
|
+
self.default_value = self.set(
|
|
138
|
+
attr,
|
|
139
|
+
"default_value",
|
|
140
|
+
allowed_type=self.value_type,
|
|
141
|
+
default=config.VALUE_TYPES[self.value_type].get("default"),
|
|
142
|
+
)
|
|
143
|
+
self.entity = self.set(attr, "entity", required=True, setter=self.set_entity)
|
|
144
|
+
self.definition_period = self.set(
|
|
145
|
+
attr,
|
|
146
|
+
"definition_period",
|
|
147
|
+
required=True,
|
|
148
|
+
allowed_values=DateUnit,
|
|
149
|
+
)
|
|
150
|
+
self.label = self.set(attr, "label", allowed_type=str, setter=self.set_label)
|
|
151
|
+
self.end = self.set(attr, "end", allowed_type=str, setter=self.set_end)
|
|
152
|
+
self.reference = self.set(attr, "reference", setter=self.set_reference)
|
|
153
|
+
self.cerfa_field = self.set(attr, "cerfa_field", allowed_type=(str, dict))
|
|
154
|
+
self.unit = self.set(attr, "unit", allowed_type=str)
|
|
155
|
+
self.documentation = self.set(
|
|
156
|
+
attr,
|
|
157
|
+
"documentation",
|
|
158
|
+
allowed_type=str,
|
|
159
|
+
setter=self.set_documentation,
|
|
160
|
+
)
|
|
161
|
+
self.set_input = self.set_set_input(attr.pop("set_input", None))
|
|
162
|
+
self.calculate_output = self.set_calculate_output(
|
|
163
|
+
attr.pop("calculate_output", None),
|
|
164
|
+
)
|
|
165
|
+
self.is_period_size_independent = self.set(
|
|
166
|
+
attr,
|
|
167
|
+
"is_period_size_independent",
|
|
168
|
+
allowed_type=bool,
|
|
169
|
+
default=config.VALUE_TYPES[self.value_type]["is_period_size_independent"],
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
self.introspection_data = self.set(
|
|
173
|
+
attr,
|
|
174
|
+
"introspection_data",
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
formulas_attr, unexpected_attrs = helpers._partition(
|
|
178
|
+
attr,
|
|
179
|
+
lambda name, value: name.startswith(config.FORMULA_NAME_PREFIX),
|
|
180
|
+
)
|
|
181
|
+
self.formulas = self.set_formulas(formulas_attr)
|
|
182
|
+
|
|
183
|
+
if unexpected_attrs:
|
|
184
|
+
msg = 'Unexpected attributes in definition of variable "{}": {!r}'.format(
|
|
185
|
+
self.name,
|
|
186
|
+
", ".join(sorted(unexpected_attrs.keys())),
|
|
187
|
+
)
|
|
188
|
+
raise ValueError(
|
|
189
|
+
msg,
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
self.is_neutralized = False
|
|
193
|
+
|
|
194
|
+
# ----- Setters used to build the variable ----- #
|
|
195
|
+
|
|
196
|
+
def set(
|
|
197
|
+
self,
|
|
198
|
+
attributes,
|
|
199
|
+
attribute_name,
|
|
200
|
+
required=False,
|
|
201
|
+
allowed_values=None,
|
|
202
|
+
allowed_type=None,
|
|
203
|
+
setter=None,
|
|
204
|
+
default=None,
|
|
205
|
+
):
|
|
206
|
+
value = attributes.pop(attribute_name, None)
|
|
207
|
+
if value is None and self.baseline_variable:
|
|
208
|
+
return getattr(self.baseline_variable, attribute_name)
|
|
209
|
+
if required and value is None:
|
|
210
|
+
msg = f"Missing attribute '{attribute_name}' in definition of variable '{self.name}'."
|
|
211
|
+
raise ValueError(
|
|
212
|
+
msg,
|
|
213
|
+
)
|
|
214
|
+
if allowed_values is not None and value not in allowed_values:
|
|
215
|
+
msg = f"Invalid value '{value}' for attribute '{attribute_name}' in variable '{self.name}'. Allowed values are '{allowed_values}'."
|
|
216
|
+
raise ValueError(
|
|
217
|
+
msg,
|
|
218
|
+
)
|
|
219
|
+
if (
|
|
220
|
+
allowed_type is not None
|
|
221
|
+
and value is not None
|
|
222
|
+
and not isinstance(value, allowed_type)
|
|
223
|
+
):
|
|
224
|
+
if allowed_type == float and isinstance(value, int):
|
|
225
|
+
value = float(value)
|
|
226
|
+
else:
|
|
227
|
+
msg = f"Invalid value '{value}' for attribute '{attribute_name}' in variable '{self.name}'. Must be of type '{allowed_type}'."
|
|
228
|
+
raise ValueError(
|
|
229
|
+
msg,
|
|
230
|
+
)
|
|
231
|
+
if setter is not None:
|
|
232
|
+
value = setter(value)
|
|
233
|
+
if value is None and default is not None:
|
|
234
|
+
return default
|
|
235
|
+
return value
|
|
236
|
+
|
|
237
|
+
def set_entity(self, entity):
|
|
238
|
+
if not isinstance(entity, (Entity, GroupEntity)):
|
|
239
|
+
msg = (
|
|
240
|
+
f"Invalid value '{entity}' for attribute 'entity' in variable "
|
|
241
|
+
f"'{self.name}'. Must be an instance of Entity or GroupEntity."
|
|
242
|
+
)
|
|
243
|
+
raise ValueError(
|
|
244
|
+
msg,
|
|
245
|
+
)
|
|
246
|
+
return entity
|
|
247
|
+
|
|
248
|
+
def set_possible_values(self, possible_values):
|
|
249
|
+
if not issubclass(possible_values, Enum):
|
|
250
|
+
msg = f"Invalid value '{possible_values}' for attribute 'possible_values' in variable '{self.name}'. Must be a subclass of {Enum}."
|
|
251
|
+
raise ValueError(
|
|
252
|
+
msg,
|
|
253
|
+
)
|
|
254
|
+
return possible_values
|
|
255
|
+
|
|
256
|
+
def set_label(self, label):
|
|
257
|
+
if label:
|
|
258
|
+
return label
|
|
259
|
+
return None
|
|
260
|
+
|
|
261
|
+
def set_end(self, end):
|
|
262
|
+
if end:
|
|
263
|
+
try:
|
|
264
|
+
return datetime.datetime.strptime(end, "%Y-%m-%d").date()
|
|
265
|
+
except ValueError:
|
|
266
|
+
msg = f"Incorrect 'end' attribute format in '{self.name}'. 'YYYY-MM-DD' expected where YYYY, MM and DD are year, month and day. Found: {end}"
|
|
267
|
+
raise ValueError(
|
|
268
|
+
msg,
|
|
269
|
+
)
|
|
270
|
+
return None
|
|
271
|
+
|
|
272
|
+
def set_reference(self, reference):
|
|
273
|
+
if reference:
|
|
274
|
+
if isinstance(reference, str):
|
|
275
|
+
reference = [reference]
|
|
276
|
+
elif isinstance(reference, list):
|
|
277
|
+
pass
|
|
278
|
+
elif isinstance(reference, tuple):
|
|
279
|
+
reference = list(reference)
|
|
280
|
+
else:
|
|
281
|
+
msg = f"The reference of the variable {self.name} is a {type(reference)} instead of a String or a List of Strings."
|
|
282
|
+
raise TypeError(
|
|
283
|
+
msg,
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
for element in reference:
|
|
287
|
+
if not isinstance(element, str):
|
|
288
|
+
msg = f"The reference of the variable {self.name} is a {type(reference)} instead of a String or a List of Strings."
|
|
289
|
+
raise TypeError(
|
|
290
|
+
msg,
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
return reference
|
|
294
|
+
|
|
295
|
+
def set_documentation(self, documentation):
|
|
296
|
+
if documentation:
|
|
297
|
+
return textwrap.dedent(documentation)
|
|
298
|
+
return None
|
|
299
|
+
|
|
300
|
+
def set_set_input(self, set_input):
|
|
301
|
+
if not set_input and self.baseline_variable:
|
|
302
|
+
return self.baseline_variable.set_input
|
|
303
|
+
return set_input
|
|
304
|
+
|
|
305
|
+
def set_calculate_output(self, calculate_output):
|
|
306
|
+
if not calculate_output and self.baseline_variable:
|
|
307
|
+
return self.baseline_variable.calculate_output
|
|
308
|
+
return calculate_output
|
|
309
|
+
|
|
310
|
+
def set_formulas(self, formulas_attr):
|
|
311
|
+
formulas = sortedcontainers.sorteddict.SortedDict()
|
|
312
|
+
for formula_name, formula in formulas_attr.items():
|
|
313
|
+
starting_date = self.parse_formula_name(formula_name)
|
|
314
|
+
|
|
315
|
+
if self.end is not None and starting_date > self.end:
|
|
316
|
+
msg = f'You declared that "{self.name}" ends on "{self.end}", but you wrote a formula to calculate it from "{starting_date}" ({formula_name}). The "end" attribute of a variable must be posterior to the start dates of all its formulas.'
|
|
317
|
+
raise ValueError(
|
|
318
|
+
msg,
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
formulas[str(starting_date)] = formula
|
|
322
|
+
|
|
323
|
+
# If the variable is reforming a baseline variable, keep the formulas from the latter when they are not overridden by new formulas.
|
|
324
|
+
if self.baseline_variable is not None:
|
|
325
|
+
first_reform_formula_date = formulas.peekitem(0)[0] if formulas else None
|
|
326
|
+
formulas.update(
|
|
327
|
+
{
|
|
328
|
+
baseline_start_date: baseline_formula
|
|
329
|
+
for baseline_start_date, baseline_formula in self.baseline_variable.formulas.items()
|
|
330
|
+
if first_reform_formula_date is None
|
|
331
|
+
or baseline_start_date < first_reform_formula_date
|
|
332
|
+
},
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
return formulas
|
|
336
|
+
|
|
337
|
+
def parse_formula_name(self, attribute_name):
|
|
338
|
+
"""Returns the starting date of a formula based on its name.
|
|
339
|
+
|
|
340
|
+
Valid dated name formats are : 'formula', 'formula_YYYY', 'formula_YYYY_MM' and 'formula_YYYY_MM_DD' where YYYY, MM and DD are a year, month and day.
|
|
341
|
+
|
|
342
|
+
By convention, the starting date of:
|
|
343
|
+
- `formula` is `0001-01-01` (minimal date in Python)
|
|
344
|
+
- `formula_YYYY` is `YYYY-01-01`
|
|
345
|
+
- `formula_YYYY_MM` is `YYYY-MM-01`
|
|
346
|
+
"""
|
|
347
|
+
|
|
348
|
+
def raise_error() -> NoReturn:
|
|
349
|
+
msg = f'Unrecognized formula name in variable "{self.name}". Expecting "formula_YYYY" or "formula_YYYY_MM" or "formula_YYYY_MM_DD where YYYY, MM and DD are year, month and day. Found: "{attribute_name}".'
|
|
350
|
+
raise ValueError(
|
|
351
|
+
msg,
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
if attribute_name == config.FORMULA_NAME_PREFIX:
|
|
355
|
+
return datetime.date.min
|
|
356
|
+
|
|
357
|
+
FORMULA_REGEX = r"formula_(\d{4})(?:_(\d{2}))?(?:_(\d{2}))?$" # YYYY or YYYY_MM or YYYY_MM_DD
|
|
358
|
+
|
|
359
|
+
match = re.match(FORMULA_REGEX, attribute_name)
|
|
360
|
+
if not match:
|
|
361
|
+
raise_error()
|
|
362
|
+
date_str = "-".join(
|
|
363
|
+
[match.group(1), match.group(2) or "01", match.group(3) or "01"],
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
try:
|
|
367
|
+
return datetime.datetime.strptime(date_str, "%Y-%m-%d").date()
|
|
368
|
+
except ValueError: # formula_2005_99_99 for instance
|
|
369
|
+
raise_error()
|
|
370
|
+
|
|
371
|
+
# ----- Methods ----- #
|
|
372
|
+
|
|
373
|
+
def is_input_variable(self):
|
|
374
|
+
"""Returns True if the variable is an input variable."""
|
|
375
|
+
return len(self.formulas) == 0
|
|
376
|
+
|
|
377
|
+
@classmethod
|
|
378
|
+
def get_introspection_data(cls):
|
|
379
|
+
try:
|
|
380
|
+
return cls.introspection_data
|
|
381
|
+
except AttributeError:
|
|
382
|
+
return "", None, 0
|
|
383
|
+
|
|
384
|
+
def get_formula(
|
|
385
|
+
self,
|
|
386
|
+
period: None | t.Instant | t.Period | str | int = None,
|
|
387
|
+
) -> None | t.Formula:
|
|
388
|
+
"""Returns the formula to compute the variable at the given period.
|
|
389
|
+
|
|
390
|
+
If no period is given and the variable has several formulas, the method
|
|
391
|
+
returns the oldest formula.
|
|
392
|
+
|
|
393
|
+
Args:
|
|
394
|
+
period: The period to get the formula.
|
|
395
|
+
|
|
396
|
+
Returns:
|
|
397
|
+
Formula used to compute the variable.
|
|
398
|
+
|
|
399
|
+
"""
|
|
400
|
+
instant: None | t.Instant
|
|
401
|
+
|
|
402
|
+
if not self.formulas:
|
|
403
|
+
return None
|
|
404
|
+
|
|
405
|
+
if period is None:
|
|
406
|
+
return self.formulas.peekitem(
|
|
407
|
+
index=0,
|
|
408
|
+
)[
|
|
409
|
+
1
|
|
410
|
+
] # peekitem gets the 1st key-value tuple (the oldest start_date and formula). Return the formula.
|
|
411
|
+
|
|
412
|
+
if isinstance(period, Period):
|
|
413
|
+
instant = period.start
|
|
414
|
+
else:
|
|
415
|
+
try:
|
|
416
|
+
instant = periods.period(period).start
|
|
417
|
+
except ValueError:
|
|
418
|
+
instant = periods.instant(period)
|
|
419
|
+
|
|
420
|
+
if instant is None:
|
|
421
|
+
return None
|
|
422
|
+
|
|
423
|
+
if self.end and instant.date > self.end:
|
|
424
|
+
return None
|
|
425
|
+
|
|
426
|
+
instant_str = str(instant)
|
|
427
|
+
|
|
428
|
+
for start_date in reversed(self.formulas):
|
|
429
|
+
if start_date <= instant_str:
|
|
430
|
+
return self.formulas[start_date]
|
|
431
|
+
|
|
432
|
+
return None
|
|
433
|
+
|
|
434
|
+
def clone(self):
|
|
435
|
+
return self.__class__()
|
|
436
|
+
|
|
437
|
+
def check_set_value(self, value):
|
|
438
|
+
if self.value_type == Enum and isinstance(value, str):
|
|
439
|
+
try:
|
|
440
|
+
value = self.possible_values[value].index
|
|
441
|
+
except KeyError:
|
|
442
|
+
possible_values = [item.name for item in self.possible_values]
|
|
443
|
+
msg = "'{}' is not a known value for '{}'. Possible values are ['{}'].".format(
|
|
444
|
+
value,
|
|
445
|
+
self.name,
|
|
446
|
+
"', '".join(possible_values),
|
|
447
|
+
)
|
|
448
|
+
raise ValueError(
|
|
449
|
+
msg,
|
|
450
|
+
)
|
|
451
|
+
if self.value_type in (float, int) and isinstance(value, str):
|
|
452
|
+
value = commons.eval_expression(value)
|
|
453
|
+
|
|
454
|
+
try:
|
|
455
|
+
value = numpy.array([value], dtype=self.dtype)[0]
|
|
456
|
+
except (TypeError, ValueError):
|
|
457
|
+
if self.value_type == datetime.date:
|
|
458
|
+
error_message = f"Can't deal with date: '{value}'."
|
|
459
|
+
else:
|
|
460
|
+
error_message = f"Can't deal with value: expected type {self.json_type}, received '{value}'."
|
|
461
|
+
raise ValueError(error_message)
|
|
462
|
+
except OverflowError:
|
|
463
|
+
error_message = f"Can't deal with value: '{value}', it's too large for type '{self.json_type}'."
|
|
464
|
+
raise ValueError(error_message)
|
|
465
|
+
|
|
466
|
+
return value
|
|
467
|
+
|
|
468
|
+
def default_array(self, array_size):
|
|
469
|
+
array = numpy.empty(array_size, dtype=self.dtype)
|
|
470
|
+
if self.value_type == Enum:
|
|
471
|
+
array.fill(self.default_value.index)
|
|
472
|
+
return EnumArray(array, self.possible_values)
|
|
473
|
+
array.fill(self.default_value)
|
|
474
|
+
return array
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Transitional imports to ensure non-breaking changes.
|
|
2
|
+
# Could be deprecated in the next major release.
|
|
3
|
+
#
|
|
4
|
+
# How imports are being used today:
|
|
5
|
+
#
|
|
6
|
+
# >>> from openfisca_core.module import symbol
|
|
7
|
+
#
|
|
8
|
+
# The previous example provokes cyclic dependency problems
|
|
9
|
+
# that prevent us from modularizing the different components
|
|
10
|
+
# of the library so to make them easier to test and to maintain.
|
|
11
|
+
#
|
|
12
|
+
# How could them be used after the next major release:
|
|
13
|
+
#
|
|
14
|
+
# >>> from openfisca_core import module
|
|
15
|
+
# >>> module.symbol()
|
|
16
|
+
#
|
|
17
|
+
# And for classes:
|
|
18
|
+
#
|
|
19
|
+
# >>> from openfisca_core.module import Symbol
|
|
20
|
+
# >>> Symbol()
|
|
21
|
+
#
|
|
22
|
+
# See: https://www.python.org/dev/peps/pep-0008/#imports
|
|
23
|
+
|
|
24
|
+
from .libyaml_warning import LibYAMLWarning # noqa: F401
|
|
25
|
+
from .tempfile_warning import TempfileWarning # noqa: F401
|