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,92 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Iterable
|
|
4
|
+
|
|
5
|
+
from . import types as t
|
|
6
|
+
from ._description import _Description
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Role:
|
|
10
|
+
"""The role of an ``Entity`` within a ``GroupEntity``.
|
|
11
|
+
|
|
12
|
+
Each ``Entity`` related to a ``GroupEntity`` has a ``Role``. For example,
|
|
13
|
+
if you have a family, its roles could include a parent, a child, and so on.
|
|
14
|
+
Or if you have a tax household, its roles could include the taxpayer, a
|
|
15
|
+
spouse, several dependents, and the like.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
description: A description of the Role.
|
|
19
|
+
entity: The Entity to which the Role belongs.
|
|
20
|
+
|
|
21
|
+
Examples:
|
|
22
|
+
>>> from openfisca_core import entities
|
|
23
|
+
|
|
24
|
+
>>> entity = entities.GroupEntity("key", "plural", "label", "doc", [])
|
|
25
|
+
>>> role = entities.Role({"key": "parent"}, entity)
|
|
26
|
+
|
|
27
|
+
>>> repr(entities.Role)
|
|
28
|
+
"<class 'openfisca_core.entities.role.Role'>"
|
|
29
|
+
|
|
30
|
+
>>> repr(role)
|
|
31
|
+
'Role(parent)'
|
|
32
|
+
|
|
33
|
+
>>> str(role)
|
|
34
|
+
'Role(parent)'
|
|
35
|
+
|
|
36
|
+
>>> {role}
|
|
37
|
+
{Role(parent)}
|
|
38
|
+
|
|
39
|
+
>>> role.key
|
|
40
|
+
'parent'
|
|
41
|
+
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
#: The ``GroupEntity`` the Role belongs to.
|
|
45
|
+
entity: t.GroupEntity
|
|
46
|
+
|
|
47
|
+
#: A description of the ``Role``.
|
|
48
|
+
description: _Description
|
|
49
|
+
|
|
50
|
+
#: Max number of members.
|
|
51
|
+
max: None | int = None
|
|
52
|
+
|
|
53
|
+
#: A list of subroles.
|
|
54
|
+
subroles: None | Iterable[Role] = None
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def key(self) -> t.RoleKey:
|
|
58
|
+
"""A key to identify the ``Role``."""
|
|
59
|
+
return t.RoleKey(self.description.key)
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def plural(self) -> None | t.RolePlural:
|
|
63
|
+
"""The ``key`` pluralised."""
|
|
64
|
+
if (plural := self.description.plural) is None:
|
|
65
|
+
return None
|
|
66
|
+
return t.RolePlural(plural)
|
|
67
|
+
|
|
68
|
+
@property
|
|
69
|
+
def label(self) -> None | str:
|
|
70
|
+
"""A summary description."""
|
|
71
|
+
return self.description.label
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
def doc(self) -> None | str:
|
|
75
|
+
"""A full description, non-indented."""
|
|
76
|
+
return self.description.doc
|
|
77
|
+
|
|
78
|
+
def __init__(self, description: t.RoleParams, entity: t.GroupEntity) -> None:
|
|
79
|
+
self.description = _Description(
|
|
80
|
+
key=description["key"],
|
|
81
|
+
plural=description.get("plural"),
|
|
82
|
+
label=description.get("label"),
|
|
83
|
+
doc=description.get("doc"),
|
|
84
|
+
)
|
|
85
|
+
self.entity = entity
|
|
86
|
+
self.max = description.get("max")
|
|
87
|
+
|
|
88
|
+
def __repr__(self) -> str:
|
|
89
|
+
return f"Role({self.key})"
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
__all__ = ["Role"]
|
|
File without changes
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from openfisca_core import entities
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def test_init_when_doc_indented() -> None:
|
|
5
|
+
"""De-indent the ``doc`` attribute if it is passed at initialisation."""
|
|
6
|
+
key = "\tkey"
|
|
7
|
+
doc = "\tdoc"
|
|
8
|
+
entity = entities.Entity(key, "label", "plural", doc)
|
|
9
|
+
assert entity.key == key
|
|
10
|
+
assert entity.doc == doc.lstrip()
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
from collections.abc import Mapping
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
from openfisca_core import entities
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@pytest.fixture
|
|
10
|
+
def parent() -> str:
|
|
11
|
+
return "parent"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@pytest.fixture
|
|
15
|
+
def uncle() -> str:
|
|
16
|
+
return "uncle"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@pytest.fixture
|
|
20
|
+
def first_parent() -> str:
|
|
21
|
+
return "first_parent"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@pytest.fixture
|
|
25
|
+
def second_parent() -> str:
|
|
26
|
+
return "second_parent"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@pytest.fixture
|
|
30
|
+
def third_parent() -> str:
|
|
31
|
+
return "third_parent"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@pytest.fixture
|
|
35
|
+
def role(parent: str, first_parent: str, third_parent: str) -> Mapping[str, Any]:
|
|
36
|
+
return {"key": parent, "subroles": {first_parent, third_parent}}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@pytest.fixture
|
|
40
|
+
def group_entity(role: Mapping[str, Any]) -> entities.GroupEntity:
|
|
41
|
+
return entities.GroupEntity("key", "label", "plural", "doc", (role,))
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_init_when_doc_indented() -> None:
|
|
45
|
+
"""De-indent the ``doc`` attribute if it is passed at initialisation."""
|
|
46
|
+
key = "\tkey"
|
|
47
|
+
doc = "\tdoc"
|
|
48
|
+
group_entity = entities.GroupEntity(key, "label", "plural", doc, ())
|
|
49
|
+
assert group_entity.key == key
|
|
50
|
+
assert group_entity.doc == doc.lstrip()
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_group_entity_with_roles(
|
|
54
|
+
group_entity: entities.GroupEntity,
|
|
55
|
+
parent: str,
|
|
56
|
+
uncle: str,
|
|
57
|
+
) -> None:
|
|
58
|
+
"""Assign a Role for each role-like passed as argument."""
|
|
59
|
+
assert hasattr(group_entity, parent.upper())
|
|
60
|
+
assert not hasattr(group_entity, uncle.upper())
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def test_group_entity_with_subroles(
|
|
64
|
+
group_entity: entities.GroupEntity,
|
|
65
|
+
first_parent: str,
|
|
66
|
+
second_parent: str,
|
|
67
|
+
) -> None:
|
|
68
|
+
"""Assign a Role for each subrole-like passed as argument."""
|
|
69
|
+
assert hasattr(group_entity, first_parent.upper())
|
|
70
|
+
assert not hasattr(group_entity, second_parent.upper())
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from openfisca_core import entities
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def test_init_when_doc_indented() -> None:
|
|
5
|
+
"""De-indent the ``doc`` attribute if it is passed at initialisation."""
|
|
6
|
+
key = "\tkey"
|
|
7
|
+
doc = "\tdoc"
|
|
8
|
+
entity = entities.GroupEntity("key", "plural", "label", "doc", [])
|
|
9
|
+
role = entities.Role({"key": key, "doc": doc}, entity)
|
|
10
|
+
assert role.key == key
|
|
11
|
+
assert role.doc == doc.lstrip()
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from typing import Required
|
|
2
|
+
|
|
3
|
+
from typing_extensions import TypedDict
|
|
4
|
+
|
|
5
|
+
from openfisca_core.types import (
|
|
6
|
+
CoreEntity,
|
|
7
|
+
EntityKey,
|
|
8
|
+
EntityPlural,
|
|
9
|
+
GroupEntity,
|
|
10
|
+
Role,
|
|
11
|
+
RoleKey,
|
|
12
|
+
RolePlural,
|
|
13
|
+
SingleEntity,
|
|
14
|
+
TaxBenefitSystem,
|
|
15
|
+
Variable,
|
|
16
|
+
VariableName,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
# Entities
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class RoleParams(TypedDict, total=False):
|
|
23
|
+
key: Required[str]
|
|
24
|
+
plural: str
|
|
25
|
+
label: str
|
|
26
|
+
doc: str
|
|
27
|
+
max: int
|
|
28
|
+
subroles: list[str]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
__all__ = [
|
|
32
|
+
"CoreEntity",
|
|
33
|
+
"EntityKey",
|
|
34
|
+
"EntityPlural",
|
|
35
|
+
"GroupEntity",
|
|
36
|
+
"Role",
|
|
37
|
+
"RoleKey",
|
|
38
|
+
"RoleParams",
|
|
39
|
+
"RolePlural",
|
|
40
|
+
"SingleEntity",
|
|
41
|
+
"TaxBenefitSystem",
|
|
42
|
+
"Variable",
|
|
43
|
+
"VariableName",
|
|
44
|
+
]
|
|
@@ -0,0 +1,64 @@
|
|
|
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 .cycle_error import CycleError
|
|
25
|
+
from .empty_argument_error import EmptyArgumentError
|
|
26
|
+
from .nan_creation_error import NaNCreationError
|
|
27
|
+
from .parameter_not_found_error import (
|
|
28
|
+
ParameterNotFoundError,
|
|
29
|
+
)
|
|
30
|
+
from .parameter_not_found_error import (
|
|
31
|
+
ParameterNotFoundError as ParameterNotFound,
|
|
32
|
+
)
|
|
33
|
+
from .parameter_parsing_error import ParameterParsingError
|
|
34
|
+
from .period_mismatch_error import PeriodMismatchError
|
|
35
|
+
from .situation_parsing_error import SituationParsingError
|
|
36
|
+
from .spiral_error import SpiralError
|
|
37
|
+
from .variable_name_config_error import (
|
|
38
|
+
VariableNameConflictError,
|
|
39
|
+
)
|
|
40
|
+
from .variable_name_config_error import (
|
|
41
|
+
VariableNameConflictError as VariableNameConflict,
|
|
42
|
+
)
|
|
43
|
+
from .variable_not_found_error import (
|
|
44
|
+
VariableNotFoundError,
|
|
45
|
+
)
|
|
46
|
+
from .variable_not_found_error import (
|
|
47
|
+
VariableNotFoundError as VariableNotFound,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
__all__ = [
|
|
51
|
+
"CycleError",
|
|
52
|
+
"EmptyArgumentError",
|
|
53
|
+
"NaNCreationError",
|
|
54
|
+
"ParameterNotFound", # Deprecated alias for "ParameterNotFoundError
|
|
55
|
+
"ParameterNotFoundError",
|
|
56
|
+
"ParameterParsingError",
|
|
57
|
+
"PeriodMismatchError",
|
|
58
|
+
"SituationParsingError",
|
|
59
|
+
"SpiralError",
|
|
60
|
+
"VariableNameConflict", # Deprecated alias for "VariableNameConflictError"
|
|
61
|
+
"VariableNameConflictError",
|
|
62
|
+
"VariableNotFound", # Deprecated alias for "VariableNotFoundError"
|
|
63
|
+
"VariableNotFoundError",
|
|
64
|
+
]
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import traceback
|
|
3
|
+
|
|
4
|
+
import numpy
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class EmptyArgumentError(IndexError):
|
|
8
|
+
"""Exception raised when a method is called with an empty argument."""
|
|
9
|
+
|
|
10
|
+
message: str
|
|
11
|
+
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
class_name: str,
|
|
15
|
+
method_name: str,
|
|
16
|
+
arg_name: str,
|
|
17
|
+
arg_value: list | numpy.ndarray,
|
|
18
|
+
) -> None:
|
|
19
|
+
message = [
|
|
20
|
+
f"'{class_name}.{method_name}' can't be run with an empty '{arg_name}':\n",
|
|
21
|
+
f">>> {arg_name}",
|
|
22
|
+
f"{arg_value}\n",
|
|
23
|
+
"Here are some hints to help you get this working:\n",
|
|
24
|
+
f"- Check that '{class_name}' isn't empty (see '{class_name}.add_bracket')",
|
|
25
|
+
(
|
|
26
|
+
f"- Check that '{arg_name}' is being properly assigned "
|
|
27
|
+
f"('{arg_name}' should be a non empty '{type(arg_value).__name__}')\n"
|
|
28
|
+
),
|
|
29
|
+
"For further support, please do not hesitate to:\n",
|
|
30
|
+
"- Take a look at the official documentation https://openfisca.org/doc",
|
|
31
|
+
"- Open an issue on https://github.com/openfisca/openfisca-core/issues/new",
|
|
32
|
+
"- Mention us via https://twitter.com/openfisca",
|
|
33
|
+
"- Drop us a line to contact@openfisca.org\n",
|
|
34
|
+
"😃",
|
|
35
|
+
]
|
|
36
|
+
stacktrace = os.linesep.join(traceback.format_stack())
|
|
37
|
+
self.message = os.linesep.join([f" {line}" for line in message])
|
|
38
|
+
self.message = os.linesep.join([stacktrace, self.message])
|
|
39
|
+
super().__init__(self.message)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
class ParameterNotFoundError(AttributeError):
|
|
2
|
+
"""Exception raised when a parameter is not found in the parameters."""
|
|
3
|
+
|
|
4
|
+
def __init__(self, name, instant_str, variable_name=None) -> None:
|
|
5
|
+
""":param name: Name of the parameter
|
|
6
|
+
:param instant_str: Instant where the parameter does not exist, in the format `YYYY-MM-DD`.
|
|
7
|
+
:param variable_name: If the parameter was queried during the computation of a variable, name of that variable.
|
|
8
|
+
"""
|
|
9
|
+
self.name = name
|
|
10
|
+
self.instant_str = instant_str
|
|
11
|
+
self.variable_name = variable_name
|
|
12
|
+
message = f"The parameter '{name}'"
|
|
13
|
+
if variable_name is not None:
|
|
14
|
+
message += f" requested by variable '{variable_name}'"
|
|
15
|
+
message += f" was not found in the {instant_str} tax and benefit system."
|
|
16
|
+
super().__init__(message)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ParameterParsingError(Exception):
|
|
5
|
+
"""Exception raised when a parameter cannot be parsed."""
|
|
6
|
+
|
|
7
|
+
def __init__(self, message, file=None, traceback=None) -> None:
|
|
8
|
+
""":param message: Error message
|
|
9
|
+
:param file: Parameter file which caused the error (optional)
|
|
10
|
+
:param traceback: Traceback (optional)
|
|
11
|
+
"""
|
|
12
|
+
if file is not None:
|
|
13
|
+
message = os.linesep.join(
|
|
14
|
+
[f"Error parsing parameter file '{file}':", message],
|
|
15
|
+
)
|
|
16
|
+
if traceback is not None:
|
|
17
|
+
message = os.linesep.join([traceback, message])
|
|
18
|
+
super().__init__(message)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
class PeriodMismatchError(ValueError):
|
|
2
|
+
"""Exception raised when one tries to set a variable value for a period that doesn't match its definition period."""
|
|
3
|
+
|
|
4
|
+
def __init__(self, variable_name: str, period, definition_period, message) -> None:
|
|
5
|
+
self.variable_name = variable_name
|
|
6
|
+
self.period = period
|
|
7
|
+
self.definition_period = definition_period
|
|
8
|
+
self.message = message
|
|
9
|
+
ValueError.__init__(self, self.message)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from collections.abc import Iterable
|
|
5
|
+
|
|
6
|
+
import dpath
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SituationParsingError(Exception):
|
|
10
|
+
"""Exception raised when the situation provided as an input for a simulation cannot be parsed."""
|
|
11
|
+
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
path: Iterable[str],
|
|
15
|
+
message: str,
|
|
16
|
+
code: int | None = None,
|
|
17
|
+
) -> None:
|
|
18
|
+
self.error = {}
|
|
19
|
+
dpath_path = "/".join([str(item) for item in path])
|
|
20
|
+
message = str(message).strip(os.linesep).replace(os.linesep, " ")
|
|
21
|
+
dpath.new(self.error, dpath_path, message)
|
|
22
|
+
self.code = code
|
|
23
|
+
Exception.__init__(self, str(self.error))
|
|
24
|
+
|
|
25
|
+
def __str__(self) -> str:
|
|
26
|
+
return str(self.error)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class VariableNotFoundError(Exception):
|
|
5
|
+
"""Exception raised when a variable has been queried but is not defined in the TaxBenefitSystem."""
|
|
6
|
+
|
|
7
|
+
def __init__(self, variable_name: str, tax_benefit_system) -> None:
|
|
8
|
+
""":param variable_name: Name of the variable that was queried.
|
|
9
|
+
:param tax_benefit_system: Tax benefits system that does not contain `variable_name`
|
|
10
|
+
"""
|
|
11
|
+
country_package_metadata = tax_benefit_system.get_package_metadata()
|
|
12
|
+
country_package_name = country_package_metadata["name"]
|
|
13
|
+
country_package_version = country_package_metadata["version"]
|
|
14
|
+
if country_package_version:
|
|
15
|
+
country_package_id = f"{country_package_name}@{country_package_version}"
|
|
16
|
+
else:
|
|
17
|
+
country_package_id = country_package_name
|
|
18
|
+
message = os.linesep.join(
|
|
19
|
+
[
|
|
20
|
+
f"You tried to calculate or to set a value for variable '{variable_name}', but it was not found in the loaded tax and benefit system ({country_package_id}).",
|
|
21
|
+
f"Are you sure you spelled '{variable_name}' correctly?",
|
|
22
|
+
"If this code used to work and suddenly does not, this is most probably linked to an update of the tax and benefit system.",
|
|
23
|
+
"Look at its changelog to learn about renames and removals and update your code. If it is an official package,",
|
|
24
|
+
f"it is probably available on <https://github.com/openfisca/{country_package_name}/blob/main/CHANGELOG.md>.",
|
|
25
|
+
],
|
|
26
|
+
)
|
|
27
|
+
self.message = message
|
|
28
|
+
self.variable_name = variable_name
|
|
29
|
+
Exception.__init__(self, self.message)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import warnings
|
|
4
|
+
from collections.abc import Iterable
|
|
5
|
+
|
|
6
|
+
from ._errors import MemoryConfigWarning
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class MemoryConfig:
|
|
10
|
+
"""Experimental memory configuration."""
|
|
11
|
+
|
|
12
|
+
#: Maximum memory occupation allowed.
|
|
13
|
+
max_memory_occupation: float
|
|
14
|
+
|
|
15
|
+
#: Priority variables.
|
|
16
|
+
priority_variables: frozenset[str]
|
|
17
|
+
|
|
18
|
+
#: Variables to drop.
|
|
19
|
+
variables_to_drop: frozenset[str]
|
|
20
|
+
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
max_memory_occupation: str | float,
|
|
24
|
+
priority_variables: Iterable[str] = frozenset(),
|
|
25
|
+
variables_to_drop: Iterable[str] = frozenset(),
|
|
26
|
+
) -> None:
|
|
27
|
+
message = (
|
|
28
|
+
"Memory configuration is a feature that is still currently under ",
|
|
29
|
+
"experimentation. You are very welcome to use it and send us ",
|
|
30
|
+
"precious feedback, but keep in mind that the way it is used might ",
|
|
31
|
+
"change without any major version bump.",
|
|
32
|
+
)
|
|
33
|
+
warnings.warn(message, MemoryConfigWarning, stacklevel=2)
|
|
34
|
+
|
|
35
|
+
self.max_memory_occupation = float(max_memory_occupation)
|
|
36
|
+
if self.max_memory_occupation > 1:
|
|
37
|
+
msg = "max_memory_occupation must be <= 1"
|
|
38
|
+
raise ValueError(msg)
|
|
39
|
+
self.max_memory_occupation_pc = self.max_memory_occupation * 100
|
|
40
|
+
self.priority_variables = frozenset(priority_variables)
|
|
41
|
+
self.variables_to_drop = frozenset(variables_to_drop)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# The formula_helpers module has been deprecated since X.X.X,
|
|
2
|
+
# and will be removed in the future.
|
|
3
|
+
#
|
|
4
|
+
# The helpers have been moved to the commons module.
|
|
5
|
+
#
|
|
6
|
+
# The following are transitional imports to ensure non-breaking changes.
|
|
7
|
+
# Could be deprecated in the next major release.
|
|
8
|
+
|
|
9
|
+
from openfisca_core.commons import apply_thresholds, concat, switch # noqa: F401
|
|
@@ -0,0 +1,33 @@
|
|
|
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 . import types
|
|
25
|
+
from .helpers import set_input_dispatch_by_period, set_input_divide_by_period
|
|
26
|
+
from .holder import Holder
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
"Holder",
|
|
30
|
+
"set_input_dispatch_by_period",
|
|
31
|
+
"set_input_divide_by_period",
|
|
32
|
+
"types",
|
|
33
|
+
]
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
import numpy
|
|
4
|
+
|
|
5
|
+
from openfisca_core import periods
|
|
6
|
+
|
|
7
|
+
log = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def set_input_dispatch_by_period(holder, period, array) -> None:
|
|
11
|
+
"""This function can be declared as a ``set_input`` attribute of a variable.
|
|
12
|
+
|
|
13
|
+
In this case, the variable will accept inputs on larger periods that its definition period, and the value for the larger period will be applied to all its subperiods.
|
|
14
|
+
|
|
15
|
+
To read more about ``set_input`` attributes, check 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>`_.
|
|
16
|
+
"""
|
|
17
|
+
array = holder._to_array(array)
|
|
18
|
+
|
|
19
|
+
period_size = period.size
|
|
20
|
+
period_unit = period.unit
|
|
21
|
+
|
|
22
|
+
if holder.variable.definition_period not in (
|
|
23
|
+
periods.DateUnit.isoformat + periods.DateUnit.isocalendar
|
|
24
|
+
):
|
|
25
|
+
msg = "set_input_dispatch_by_period can't be used for eternal variables."
|
|
26
|
+
raise ValueError(
|
|
27
|
+
msg,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
cached_period_unit = holder.variable.definition_period
|
|
31
|
+
after_instant = period.start.offset(period_size, period_unit)
|
|
32
|
+
|
|
33
|
+
# Cache the input data, skipping the existing cached months
|
|
34
|
+
sub_period = periods.Period((cached_period_unit, period.start, 1))
|
|
35
|
+
while sub_period.start < after_instant:
|
|
36
|
+
existing_array = holder.get_array(sub_period)
|
|
37
|
+
if existing_array is None:
|
|
38
|
+
holder._set(sub_period, array)
|
|
39
|
+
else:
|
|
40
|
+
# The array of the current sub-period is reused for the next ones.
|
|
41
|
+
# TODO: refactor or document this behavior
|
|
42
|
+
array = existing_array
|
|
43
|
+
sub_period = sub_period.offset(1)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def set_input_divide_by_period(holder, period, array) -> None:
|
|
47
|
+
"""This function can be declared as a ``set_input`` attribute of a variable.
|
|
48
|
+
|
|
49
|
+
In this case, the variable will accept inputs on larger periods that its definition period, and the value for the larger period will be divided between its subperiods.
|
|
50
|
+
|
|
51
|
+
To read more about ``set_input`` attributes, check 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>`_.
|
|
52
|
+
"""
|
|
53
|
+
if not isinstance(array, numpy.ndarray):
|
|
54
|
+
array = numpy.array(array)
|
|
55
|
+
period_size = period.size
|
|
56
|
+
period_unit = period.unit
|
|
57
|
+
|
|
58
|
+
if holder.variable.definition_period not in (
|
|
59
|
+
periods.DateUnit.isoformat + periods.DateUnit.isocalendar
|
|
60
|
+
):
|
|
61
|
+
msg = "set_input_divide_by_period can't be used for eternal variables."
|
|
62
|
+
raise ValueError(
|
|
63
|
+
msg,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
cached_period_unit = holder.variable.definition_period
|
|
67
|
+
after_instant = period.start.offset(period_size, period_unit)
|
|
68
|
+
|
|
69
|
+
# Count the number of elementary periods to change, and the difference with what is already known.
|
|
70
|
+
remaining_array = array.copy()
|
|
71
|
+
sub_period = periods.Period((cached_period_unit, period.start, 1))
|
|
72
|
+
sub_periods_count = 0
|
|
73
|
+
while sub_period.start < after_instant:
|
|
74
|
+
existing_array = holder.get_array(sub_period)
|
|
75
|
+
if existing_array is not None:
|
|
76
|
+
remaining_array -= existing_array
|
|
77
|
+
else:
|
|
78
|
+
sub_periods_count += 1
|
|
79
|
+
sub_period = sub_period.offset(1)
|
|
80
|
+
|
|
81
|
+
# Cache the input data
|
|
82
|
+
if sub_periods_count > 0:
|
|
83
|
+
divided_array = remaining_array / sub_periods_count
|
|
84
|
+
sub_period = periods.Period((cached_period_unit, period.start, 1))
|
|
85
|
+
while sub_period.start < after_instant:
|
|
86
|
+
if holder.get_array(sub_period) is None:
|
|
87
|
+
holder._set(sub_period, divided_array)
|
|
88
|
+
sub_period = sub_period.offset(1)
|
|
89
|
+
elif not (remaining_array == 0).all():
|
|
90
|
+
msg = f"Inconsistent input: variable {holder.variable.name} has already been set for all months contained in period {period}, and value {array} provided for {period} doesn't match the total ({array - remaining_array}). This error may also be thrown if you try to call set_input twice for the same variable and period."
|
|
91
|
+
raise ValueError(
|
|
92
|
+
msg,
|
|
93
|
+
)
|