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.
Files changed (237) hide show
  1. openfisca_core/__init__.py +0 -0
  2. openfisca_core/commons/__init__.py +18 -0
  3. openfisca_core/commons/dummy.py +25 -0
  4. openfisca_core/commons/formulas.py +121 -0
  5. openfisca_core/commons/misc.py +109 -0
  6. openfisca_core/commons/py.typed +0 -0
  7. openfisca_core/commons/rates.py +114 -0
  8. openfisca_core/commons/tests/__init__.py +0 -0
  9. openfisca_core/commons/tests/test_dummy.py +9 -0
  10. openfisca_core/commons/tests/test_formulas.py +75 -0
  11. openfisca_core/commons/tests/test_rates.py +26 -0
  12. openfisca_core/data_storage/__init__.py +7 -0
  13. openfisca_core/data_storage/in_memory_storage.py +198 -0
  14. openfisca_core/data_storage/on_disk_storage.py +307 -0
  15. openfisca_core/data_storage/types.py +14 -0
  16. openfisca_core/entities/__init__.py +23 -0
  17. openfisca_core/entities/_core_entity.py +217 -0
  18. openfisca_core/entities/_description.py +55 -0
  19. openfisca_core/entities/entity.py +60 -0
  20. openfisca_core/entities/group_entity.py +123 -0
  21. openfisca_core/entities/helpers.py +165 -0
  22. openfisca_core/entities/py.typed +0 -0
  23. openfisca_core/entities/role.py +92 -0
  24. openfisca_core/entities/tests/__init__.py +0 -0
  25. openfisca_core/entities/tests/test_entity.py +10 -0
  26. openfisca_core/entities/tests/test_group_entity.py +70 -0
  27. openfisca_core/entities/tests/test_role.py +11 -0
  28. openfisca_core/entities/types.py +44 -0
  29. openfisca_core/errors/__init__.py +64 -0
  30. openfisca_core/errors/cycle_error.py +2 -0
  31. openfisca_core/errors/empty_argument_error.py +39 -0
  32. openfisca_core/errors/nan_creation_error.py +2 -0
  33. openfisca_core/errors/parameter_not_found_error.py +16 -0
  34. openfisca_core/errors/parameter_parsing_error.py +18 -0
  35. openfisca_core/errors/period_mismatch_error.py +9 -0
  36. openfisca_core/errors/situation_parsing_error.py +26 -0
  37. openfisca_core/errors/spiral_error.py +2 -0
  38. openfisca_core/errors/variable_name_config_error.py +2 -0
  39. openfisca_core/errors/variable_not_found_error.py +29 -0
  40. openfisca_core/experimental/__init__.py +9 -0
  41. openfisca_core/experimental/_errors.py +5 -0
  42. openfisca_core/experimental/_memory_config.py +41 -0
  43. openfisca_core/formula_helpers.py +9 -0
  44. openfisca_core/holders/__init__.py +33 -0
  45. openfisca_core/holders/helpers.py +93 -0
  46. openfisca_core/holders/holder.py +328 -0
  47. openfisca_core/holders/tests/__init__.py +0 -0
  48. openfisca_core/holders/tests/test_helpers.py +134 -0
  49. openfisca_core/holders/types.py +3 -0
  50. openfisca_core/indexed_enums/__init__.py +18 -0
  51. openfisca_core/indexed_enums/_enum_type.py +79 -0
  52. openfisca_core/indexed_enums/_errors.py +35 -0
  53. openfisca_core/indexed_enums/_guards.py +209 -0
  54. openfisca_core/indexed_enums/_utils.py +180 -0
  55. openfisca_core/indexed_enums/config.py +6 -0
  56. openfisca_core/indexed_enums/enum.py +218 -0
  57. openfisca_core/indexed_enums/enum_array.py +316 -0
  58. openfisca_core/indexed_enums/py.typed +0 -0
  59. openfisca_core/indexed_enums/tests/__init__.py +0 -0
  60. openfisca_core/indexed_enums/tests/test_enum.py +135 -0
  61. openfisca_core/indexed_enums/tests/test_enum_array.py +30 -0
  62. openfisca_core/indexed_enums/types.py +49 -0
  63. openfisca_core/memory_config.py +9 -0
  64. openfisca_core/model_api.py +71 -0
  65. openfisca_core/parameters/__init__.py +71 -0
  66. openfisca_core/parameters/at_instant_like.py +17 -0
  67. openfisca_core/parameters/config.py +55 -0
  68. openfisca_core/parameters/helpers.py +106 -0
  69. openfisca_core/parameters/parameter.py +217 -0
  70. openfisca_core/parameters/parameter_at_instant.py +75 -0
  71. openfisca_core/parameters/parameter_node.py +176 -0
  72. openfisca_core/parameters/parameter_node_at_instant.py +57 -0
  73. openfisca_core/parameters/parameter_scale.py +121 -0
  74. openfisca_core/parameters/parameter_scale_bracket.py +7 -0
  75. openfisca_core/parameters/values_history.py +5 -0
  76. openfisca_core/parameters/vectorial_parameter_node_at_instant.py +179 -0
  77. openfisca_core/periods/__init__.py +77 -0
  78. openfisca_core/periods/_errors.py +28 -0
  79. openfisca_core/periods/_parsers.py +122 -0
  80. openfisca_core/periods/config.py +20 -0
  81. openfisca_core/periods/date_unit.py +110 -0
  82. openfisca_core/periods/helpers.py +314 -0
  83. openfisca_core/periods/instant_.py +226 -0
  84. openfisca_core/periods/period_.py +919 -0
  85. openfisca_core/periods/py.typed +0 -0
  86. openfisca_core/periods/tests/__init__.py +0 -0
  87. openfisca_core/periods/tests/helpers/__init__.py +0 -0
  88. openfisca_core/periods/tests/helpers/test_helpers.py +65 -0
  89. openfisca_core/periods/tests/helpers/test_instant.py +73 -0
  90. openfisca_core/periods/tests/helpers/test_period.py +134 -0
  91. openfisca_core/periods/tests/test_instant.py +32 -0
  92. openfisca_core/periods/tests/test_parsers.py +129 -0
  93. openfisca_core/periods/tests/test_period.py +283 -0
  94. openfisca_core/populations/__init__.py +64 -0
  95. openfisca_core/populations/_core_population.py +454 -0
  96. openfisca_core/populations/_errors.py +65 -0
  97. openfisca_core/populations/group_population.py +326 -0
  98. openfisca_core/populations/population.py +142 -0
  99. openfisca_core/populations/tests/__init__.py +0 -0
  100. openfisca_core/populations/tests/test_members_position.py +116 -0
  101. openfisca_core/populations/types.py +110 -0
  102. openfisca_core/projectors/__init__.py +39 -0
  103. openfisca_core/projectors/entity_to_person_projector.py +12 -0
  104. openfisca_core/projectors/first_person_to_entity_projector.py +13 -0
  105. openfisca_core/projectors/helpers.py +139 -0
  106. openfisca_core/projectors/projector.py +38 -0
  107. openfisca_core/projectors/typing.py +27 -0
  108. openfisca_core/projectors/unique_role_to_entity_projector.py +14 -0
  109. openfisca_core/rates.py +9 -0
  110. openfisca_core/reforms/__init__.py +24 -0
  111. openfisca_core/reforms/in_yaml_test_reform.py +28 -0
  112. openfisca_core/reforms/reform.py +86 -0
  113. openfisca_core/scripts/__init__.py +106 -0
  114. openfisca_core/scripts/__pycache__/__init__.cpython-310-pytest-8.3.3.pyc +0 -0
  115. openfisca_core/scripts/__pycache__/__init__.cpython-310-pytest-8.4.2.pyc +0 -0
  116. openfisca_core/scripts/__pycache__/__init__.cpython-310.pyc +0 -0
  117. openfisca_core/scripts/__pycache__/__init__.cpython-311-pytest-8.3.3.pyc +0 -0
  118. openfisca_core/scripts/__pycache__/__init__.cpython-311-pytest-8.4.2.pyc +0 -0
  119. openfisca_core/scripts/__pycache__/__init__.cpython-311.pyc +0 -0
  120. openfisca_core/scripts/__pycache__/__init__.cpython-312-pytest-8.4.2.pyc +0 -0
  121. openfisca_core/scripts/__pycache__/__init__.cpython-312.pyc +0 -0
  122. openfisca_core/scripts/__pycache__/__init__.cpython-313-pytest-8.3.3.pyc +0 -0
  123. openfisca_core/scripts/__pycache__/__init__.cpython-313-pytest-8.4.2.pyc +0 -0
  124. openfisca_core/scripts/__pycache__/__init__.cpython-313.pyc +0 -0
  125. openfisca_core/scripts/__pycache__/find_placeholders.cpython-311.pyc +0 -0
  126. openfisca_core/scripts/__pycache__/find_placeholders.cpython-313.pyc +0 -0
  127. openfisca_core/scripts/__pycache__/measure_numpy_condition_notations.cpython-311.pyc +0 -0
  128. openfisca_core/scripts/__pycache__/measure_numpy_condition_notations.cpython-313.pyc +0 -0
  129. openfisca_core/scripts/__pycache__/measure_performances.cpython-311.pyc +0 -0
  130. openfisca_core/scripts/__pycache__/measure_performances.cpython-313.pyc +0 -0
  131. openfisca_core/scripts/__pycache__/measure_performances_fancy_indexing.cpython-311.pyc +0 -0
  132. openfisca_core/scripts/__pycache__/measure_performances_fancy_indexing.cpython-313.pyc +0 -0
  133. openfisca_core/scripts/__pycache__/openfisca_command.cpython-310.pyc +0 -0
  134. openfisca_core/scripts/__pycache__/openfisca_command.cpython-311.pyc +0 -0
  135. openfisca_core/scripts/__pycache__/openfisca_command.cpython-312.pyc +0 -0
  136. openfisca_core/scripts/__pycache__/openfisca_command.cpython-313.pyc +0 -0
  137. openfisca_core/scripts/__pycache__/remove_fuzzy.cpython-311.pyc +0 -0
  138. openfisca_core/scripts/__pycache__/remove_fuzzy.cpython-313.pyc +0 -0
  139. openfisca_core/scripts/__pycache__/run_test.cpython-310.pyc +0 -0
  140. openfisca_core/scripts/__pycache__/run_test.cpython-311.pyc +0 -0
  141. openfisca_core/scripts/__pycache__/run_test.cpython-312.pyc +0 -0
  142. openfisca_core/scripts/__pycache__/run_test.cpython-313.pyc +0 -0
  143. openfisca_core/scripts/__pycache__/simulation_generator.cpython-311.pyc +0 -0
  144. openfisca_core/scripts/__pycache__/simulation_generator.cpython-313.pyc +0 -0
  145. openfisca_core/scripts/assets/__init__.py +0 -0
  146. openfisca_core/scripts/assets/__pycache__/__init__.cpython-311.pyc +0 -0
  147. openfisca_core/scripts/assets/__pycache__/__init__.cpython-313.pyc +0 -0
  148. openfisca_core/scripts/assets/index.html +140 -0
  149. openfisca_core/scripts/find_placeholders.py +51 -0
  150. openfisca_core/scripts/measure_numpy_condition_notations.py +137 -0
  151. openfisca_core/scripts/measure_performances.py +291 -0
  152. openfisca_core/scripts/measure_performances_fancy_indexing.py +89 -0
  153. openfisca_core/scripts/migrations/__init__.py +0 -0
  154. openfisca_core/scripts/migrations/__pycache__/__init__.cpython-311.pyc +0 -0
  155. openfisca_core/scripts/migrations/__pycache__/__init__.cpython-313.pyc +0 -0
  156. openfisca_core/scripts/migrations/__pycache__/v24_to_25.cpython-311.pyc +0 -0
  157. openfisca_core/scripts/migrations/__pycache__/v24_to_25.cpython-313.pyc +0 -0
  158. openfisca_core/scripts/migrations/v16_2_to_v17/__init__.py +0 -0
  159. openfisca_core/scripts/migrations/v16_2_to_v17/__pycache__/__init__.cpython-311.pyc +0 -0
  160. openfisca_core/scripts/migrations/v16_2_to_v17/__pycache__/__init__.cpython-313.pyc +0 -0
  161. openfisca_core/scripts/migrations/v16_2_to_v17/__pycache__/xml_to_yaml_country_template.cpython-311.pyc +0 -0
  162. openfisca_core/scripts/migrations/v16_2_to_v17/__pycache__/xml_to_yaml_country_template.cpython-313.pyc +0 -0
  163. openfisca_core/scripts/migrations/v16_2_to_v17/__pycache__/xml_to_yaml_extension_template.cpython-311.pyc +0 -0
  164. openfisca_core/scripts/migrations/v16_2_to_v17/__pycache__/xml_to_yaml_extension_template.cpython-313.pyc +0 -0
  165. openfisca_core/scripts/migrations/v16_2_to_v17/legislation.xsd +102 -0
  166. openfisca_core/scripts/migrations/v16_2_to_v17/xml_to_yaml_country_template.py +30 -0
  167. openfisca_core/scripts/migrations/v16_2_to_v17/xml_to_yaml_extension_template.py +26 -0
  168. openfisca_core/scripts/migrations/v24_to_25.py +156 -0
  169. openfisca_core/scripts/openfisca_command.py +192 -0
  170. openfisca_core/scripts/remove_fuzzy.py +213 -0
  171. openfisca_core/scripts/run_test.py +51 -0
  172. openfisca_core/scripts/simulation_generator.py +90 -0
  173. openfisca_core/simulation_builder.py +16 -0
  174. openfisca_core/simulations/__init__.py +45 -0
  175. openfisca_core/simulations/_build_default_simulation.py +158 -0
  176. openfisca_core/simulations/_build_from_variables.py +230 -0
  177. openfisca_core/simulations/_type_guards.py +298 -0
  178. openfisca_core/simulations/helpers.py +111 -0
  179. openfisca_core/simulations/simulation.py +618 -0
  180. openfisca_core/simulations/simulation_builder.py +855 -0
  181. openfisca_core/simulations/typing.py +203 -0
  182. openfisca_core/taxbenefitsystems/__init__.py +26 -0
  183. openfisca_core/taxbenefitsystems/tax_benefit_system.py +584 -0
  184. openfisca_core/taxscales/__init__.py +35 -0
  185. openfisca_core/taxscales/abstract_rate_tax_scale.py +41 -0
  186. openfisca_core/taxscales/abstract_tax_scale.py +53 -0
  187. openfisca_core/taxscales/amount_tax_scale_like.py +55 -0
  188. openfisca_core/taxscales/helpers.py +44 -0
  189. openfisca_core/taxscales/linear_average_rate_tax_scale.py +76 -0
  190. openfisca_core/taxscales/marginal_amount_tax_scale.py +34 -0
  191. openfisca_core/taxscales/marginal_rate_tax_scale.py +286 -0
  192. openfisca_core/taxscales/rate_tax_scale_like.py +214 -0
  193. openfisca_core/taxscales/single_amount_tax_scale.py +32 -0
  194. openfisca_core/taxscales/tax_scale_like.py +65 -0
  195. openfisca_core/tools/__init__.py +94 -0
  196. openfisca_core/tools/parallel_plugin.py +55 -0
  197. openfisca_core/tools/simulation_dumper.py +137 -0
  198. openfisca_core/tools/test_runner.py +802 -0
  199. openfisca_core/tracers/__init__.py +40 -0
  200. openfisca_core/tracers/computation_log.py +196 -0
  201. openfisca_core/tracers/flat_trace.py +87 -0
  202. openfisca_core/tracers/full_tracer.py +172 -0
  203. openfisca_core/tracers/performance_log.py +133 -0
  204. openfisca_core/tracers/simple_tracer.py +64 -0
  205. openfisca_core/tracers/trace_node.py +121 -0
  206. openfisca_core/tracers/tracing_parameter_node_at_instant.py +81 -0
  207. openfisca_core/types.py +548 -0
  208. openfisca_core/variables/__init__.py +26 -0
  209. openfisca_core/variables/config.py +53 -0
  210. openfisca_core/variables/helpers.py +67 -0
  211. openfisca_core/variables/tests/__init__.py +0 -0
  212. openfisca_core/variables/tests/test_definition_period.py +43 -0
  213. openfisca_core/variables/variable.py +474 -0
  214. openfisca_core/warnings/__init__.py +25 -0
  215. openfisca_core/warnings/libyaml_warning.py +2 -0
  216. openfisca_core/warnings/tempfile_warning.py +2 -0
  217. openfisca_core_test-46.0.0.data/data/share/openfisca/openfisca-core/CHANGELOG.md +4209 -0
  218. openfisca_core_test-46.0.0.data/data/share/openfisca/openfisca-core/README.md +152 -0
  219. openfisca_core_test-46.0.0.dist-info/METADATA +194 -0
  220. openfisca_core_test-46.0.0.dist-info/RECORD +237 -0
  221. openfisca_core_test-46.0.0.dist-info/WHEEL +5 -0
  222. openfisca_core_test-46.0.0.dist-info/entry_points.txt +3 -0
  223. openfisca_core_test-46.0.0.dist-info/licenses/LICENSE +661 -0
  224. openfisca_core_test-46.0.0.dist-info/top_level.txt +2 -0
  225. openfisca_web_api/__init__.py +0 -0
  226. openfisca_web_api/app.py +226 -0
  227. openfisca_web_api/errors.py +11 -0
  228. openfisca_web_api/handlers.py +90 -0
  229. openfisca_web_api/loader/__init__.py +22 -0
  230. openfisca_web_api/loader/entities.py +28 -0
  231. openfisca_web_api/loader/parameters.py +122 -0
  232. openfisca_web_api/loader/spec.py +168 -0
  233. openfisca_web_api/loader/tax_benefit_system.py +27 -0
  234. openfisca_web_api/loader/variables.py +121 -0
  235. openfisca_web_api/openAPI.yml +434 -0
  236. openfisca_web_api/scripts/__init__.py +0 -0
  237. openfisca_web_api/scripts/serve.py +99 -0
@@ -0,0 +1,230 @@
1
+ """This module contains the _BuildFromVariables class."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Self
6
+
7
+ from openfisca_core import errors
8
+
9
+ from ._build_default_simulation import _BuildDefaultSimulation
10
+ from ._type_guards import is_variable_dated
11
+ from .simulation import Simulation
12
+ from .typing import Entity, Population, TaxBenefitSystem, Variables
13
+
14
+
15
+ class _BuildFromVariables:
16
+ """Build a simulation from variables.
17
+
18
+ Args:
19
+ tax_benefit_system(TaxBenefitSystem): The tax-benefit system.
20
+ params(Variables): The simulation parameters.
21
+
22
+ Examples:
23
+ >>> from openfisca_core import entities, periods, taxbenefitsystems, variables
24
+
25
+ >>> role = {"key": "stray", "plural": "stray", "label": "", "doc": ""}
26
+ >>> single_entity = entities.Entity("dog", "dogs", "", "")
27
+ >>> group_entity = entities.GroupEntity("pack", "packs", "", "", [role])
28
+
29
+ >>> class salary(variables.Variable):
30
+ ... definition_period = periods.DateUnit.MONTH
31
+ ... entity = single_entity
32
+ ... value_type = int
33
+
34
+ >>> class taxes(variables.Variable):
35
+ ... definition_period = periods.DateUnit.MONTH
36
+ ... entity = group_entity
37
+ ... value_type = int
38
+
39
+ >>> test_entities = [single_entity, group_entity]
40
+ >>> tax_benefit_system = taxbenefitsystems.TaxBenefitSystem(test_entities)
41
+ >>> tax_benefit_system.load_variable(salary)
42
+ <...salary object at ...>
43
+ >>> tax_benefit_system.load_variable(taxes)
44
+ <...taxes object at ...>
45
+ >>> period = "2023-12"
46
+ >>> variables = {"salary": {period: 10000}, "taxes": 5000}
47
+ >>> builder = (
48
+ ... _BuildFromVariables(tax_benefit_system, variables, period)
49
+ ... .add_dated_values()
50
+ ... .add_undated_values()
51
+ ... )
52
+
53
+ >>> dogs = builder.populations["dog"].get_holder("salary")
54
+ >>> dogs.get_array(period)
55
+ array([10000], dtype=int32)
56
+
57
+ >>> pack = builder.populations["pack"].get_holder("taxes")
58
+ >>> pack.get_array(period)
59
+ array([5000], dtype=int32)
60
+
61
+ """
62
+
63
+ #: The number of Population.
64
+ count: int
65
+
66
+ #: The Simulation's default period.
67
+ default_period: str | None
68
+
69
+ #: The built populations.
70
+ populations: dict[str, Population[Entity]]
71
+
72
+ #: The built simulation.
73
+ simulation: Simulation
74
+
75
+ #: The simulation parameters.
76
+ variables: Variables
77
+
78
+ def __init__(
79
+ self,
80
+ tax_benefit_system: TaxBenefitSystem,
81
+ params: Variables,
82
+ default_period: str | None = None,
83
+ ) -> None:
84
+ self.count = _person_count(params)
85
+
86
+ default_builder = (
87
+ _BuildDefaultSimulation(tax_benefit_system, self.count)
88
+ .add_count()
89
+ .add_ids()
90
+ .add_members_entity_id()
91
+ )
92
+
93
+ self.variables = params
94
+ self.simulation = default_builder.simulation
95
+ self.populations = default_builder.populations
96
+ self.default_period = default_period
97
+
98
+ def add_dated_values(self) -> Self:
99
+ """Add the dated input values to the Simulation.
100
+
101
+ Returns:
102
+ _BuildFromVariables: The builder.
103
+
104
+ Examples:
105
+ >>> from openfisca_core import entities, periods, taxbenefitsystems, variables
106
+
107
+ >>> role = {"key": "stray", "plural": "stray", "label": "", "doc": ""}
108
+ >>> single_entity = entities.Entity("dog", "dogs", "", "")
109
+ >>> group_entity = entities.GroupEntity("pack", "packs", "", "", [role])
110
+
111
+
112
+ >>> class salary(variables.Variable):
113
+ ... definition_period = periods.DateUnit.MONTH
114
+ ... entity = single_entity
115
+ ... value_type = int
116
+
117
+ >>> class taxes(variables.Variable):
118
+ ... definition_period = periods.DateUnit.MONTH
119
+ ... entity = group_entity
120
+ ... value_type = int
121
+
122
+ >>> test_entities = [single_entity, group_entity]
123
+ >>> tax_benefit_system = taxbenefitsystems.TaxBenefitSystem(test_entities)
124
+ >>> tax_benefit_system.load_variable(salary)
125
+ <...salary object at ...>
126
+ >>> tax_benefit_system.load_variable(taxes)
127
+ <...taxes object at ...>
128
+ >>> period = "2023-12"
129
+ >>> variables = {"salary": {period: 10000}, "taxes": 5000}
130
+ >>> builder = _BuildFromVariables(tax_benefit_system, variables)
131
+ >>> builder.add_dated_values()
132
+ <..._BuildFromVariables object at ...>
133
+
134
+ >>> dogs = builder.populations["dog"].get_holder("salary")
135
+ >>> dogs.get_array(period)
136
+ array([10000], dtype=int32)
137
+
138
+ >>> pack = builder.populations["pack"].get_holder("taxes")
139
+ >>> pack.get_array(period)
140
+
141
+ """
142
+ for variable, value in self.variables.items():
143
+ if is_variable_dated(dated_variable := value):
144
+ for period, dated_value in dated_variable.items():
145
+ self.simulation.set_input(variable, period, dated_value)
146
+
147
+ return self
148
+
149
+ def add_undated_values(self) -> Self:
150
+ """Add the undated input values to the Simulation.
151
+
152
+ Returns:
153
+ _BuildFromVariables: The builder.
154
+
155
+ Raises:
156
+ SituationParsingError: If there is not a default period set.
157
+
158
+ Examples:
159
+ >>> from openfisca_core import entities, periods, taxbenefitsystems, variables
160
+
161
+ >>> role = {"key": "stray", "plural": "stray", "label": "", "doc": ""}
162
+ >>> single_entity = entities.Entity("dog", "dogs", "", "")
163
+ >>> group_entity = entities.GroupEntity("pack", "packs", "", "", [role])
164
+
165
+ >>> class salary(variables.Variable):
166
+ ... definition_period = periods.DateUnit.MONTH
167
+ ... entity = single_entity
168
+ ... value_type = int
169
+
170
+ >>> class taxes(variables.Variable):
171
+ ... definition_period = periods.DateUnit.MONTH
172
+ ... entity = group_entity
173
+ ... value_type = int
174
+
175
+ >>> test_entities = [single_entity, group_entity]
176
+ >>> tax_benefit_system = taxbenefitsystems.TaxBenefitSystem(test_entities)
177
+ >>> tax_benefit_system.load_variable(salary)
178
+ <...salary object at ...>
179
+ >>> tax_benefit_system.load_variable(taxes)
180
+ <...taxes object at ...>
181
+ >>> period = "2023-12"
182
+ >>> variables = {"salary": {period: 10000}, "taxes": 5000}
183
+ >>> builder = _BuildFromVariables(tax_benefit_system, variables)
184
+ >>> builder.add_undated_values()
185
+ Traceback (most recent call last):
186
+ openfisca_core.errors.situation_parsing_error.SituationParsingError
187
+ >>> builder.default_period = period
188
+ >>> builder.add_undated_values()
189
+ <..._BuildFromVariables object at ...>
190
+
191
+ >>> dogs = builder.populations["dog"].get_holder("salary")
192
+ >>> dogs.get_array(period)
193
+
194
+ >>> pack = builder.populations["pack"].get_holder("taxes")
195
+ >>> pack.get_array(period)
196
+ array([5000], dtype=int32)
197
+
198
+ """
199
+ for variable, value in self.variables.items():
200
+ if not is_variable_dated(undated_value := value):
201
+ if (period := self.default_period) is None:
202
+ message = (
203
+ "Can't deal with type: expected object. Input "
204
+ "variables should be set for specific periods. For "
205
+ "instance: "
206
+ " {'salary': {'2017-01': 2000, '2017-02': 2500}}"
207
+ " {'birth_date': {'ETERNITY': '1980-01-01'}}"
208
+ )
209
+
210
+ raise errors.SituationParsingError([variable], message)
211
+
212
+ self.simulation.set_input(variable, period, undated_value)
213
+
214
+ return self
215
+
216
+
217
+ def _person_count(params: Variables) -> int:
218
+ try:
219
+ first_value = next(iter(params.values()))
220
+
221
+ if isinstance(first_value, dict):
222
+ first_value = next(iter(first_value.values()))
223
+
224
+ if isinstance(first_value, str):
225
+ return 1
226
+
227
+ return len(first_value)
228
+
229
+ except Exception:
230
+ return 1
@@ -0,0 +1,298 @@
1
+ """Type guards to help type narrowing simulation parameters."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Iterable
6
+ from typing import TypeGuard
7
+
8
+ from .typing import (
9
+ Axes,
10
+ DatedVariable,
11
+ FullySpecifiedEntities,
12
+ ImplicitGroupEntities,
13
+ Params,
14
+ UndatedVariable,
15
+ Variables,
16
+ )
17
+
18
+
19
+ def are_entities_fully_specified(
20
+ params: Params,
21
+ items: Iterable[str],
22
+ ) -> TypeGuard[FullySpecifiedEntities]:
23
+ """Check if the params contain fully specified entities.
24
+
25
+ Args:
26
+ params(Params): Simulation parameters.
27
+ items(Iterable[str]): List of entities in plural form.
28
+
29
+ Returns:
30
+ bool: True if the params contain fully specified entities.
31
+
32
+ Examples:
33
+ >>> entities = {"persons", "households"}
34
+
35
+ >>> params = {
36
+ ... "axes": [
37
+ ... [
38
+ ... {
39
+ ... "count": 2,
40
+ ... "max": 3000,
41
+ ... "min": 0,
42
+ ... "name": "rent",
43
+ ... "period": "2018-11",
44
+ ... }
45
+ ... ]
46
+ ... ],
47
+ ... "households": {
48
+ ... "housea": {"parents": ["Alicia", "Javier"]},
49
+ ... "houseb": {"parents": ["Tom"]},
50
+ ... },
51
+ ... "persons": {"Alicia": {"salary": {"2018-11": 0}}, "Javier": {}, "Tom": {}},
52
+ ... }
53
+
54
+ >>> are_entities_fully_specified(params, entities)
55
+ True
56
+
57
+ >>> params = {"persons": {"Javier": {"salary": {"2018-11": [2000, 3000]}}}}
58
+
59
+ >>> are_entities_fully_specified(params, entities)
60
+ True
61
+
62
+ >>> params = {"persons": {"Javier": {"salary": {"2018-11": 2000}}}}
63
+
64
+ >>> are_entities_fully_specified(params, entities)
65
+ True
66
+
67
+ >>> params = {"household": {"parents": ["Javier"]}}
68
+
69
+ >>> are_entities_fully_specified(params, entities)
70
+ False
71
+
72
+ >>> params = {"salary": {"2016-10": 12000}}
73
+
74
+ >>> are_entities_fully_specified(params, entities)
75
+ False
76
+
77
+ >>> params = {"salary": 12000}
78
+
79
+ >>> are_entities_fully_specified(params, entities)
80
+ False
81
+
82
+ >>> params = {}
83
+
84
+ >>> are_entities_fully_specified(params, entities)
85
+ False
86
+
87
+ """
88
+ if not params:
89
+ return False
90
+
91
+ return all(key in items for key in params if key != "axes")
92
+
93
+
94
+ def are_entities_short_form(
95
+ params: Params,
96
+ items: Iterable[str],
97
+ ) -> TypeGuard[ImplicitGroupEntities]:
98
+ """Check if the params contain short form entities.
99
+
100
+ Args:
101
+ params(Params): Simulation parameters.
102
+ items(Iterable[str]): List of entities in singular form.
103
+
104
+ Returns:
105
+ bool: True if the params contain short form entities.
106
+
107
+ Examples:
108
+ >>> entities = {"person", "household"}
109
+
110
+ >>> params = {
111
+ ... "persons": {"Javier": {"salary": {"2018-11": 2000}}},
112
+ ... "households": {"household": {"parents": ["Javier"]}},
113
+ ... "axes": [[{"count": 1, "max": 1, "min": 1, "name": "household"}]],
114
+ ... }
115
+
116
+ >>> are_entities_short_form(params, entities)
117
+ False
118
+
119
+ >>> params = {"persons": {"Javier": {"salary": {"2018-11": 2000}}}}
120
+
121
+ >>> are_entities_short_form(params, entities)
122
+ False
123
+
124
+ >>> params = {
125
+ ... "persons": {"Javier": {"salary": {"2018-11": 2000}}},
126
+ ... "household": {"parents": ["Javier"]},
127
+ ... "axes": [[{"count": 1, "max": 1, "min": 1, "name": "household"}]],
128
+ ... }
129
+
130
+ >>> are_entities_short_form(params, entities)
131
+ True
132
+
133
+ >>> params = {
134
+ ... "household": {"parents": ["Javier"]},
135
+ ... "axes": [[{"count": 1, "max": 1, "min": 1, "name": "household"}]],
136
+ ... }
137
+
138
+ >>> are_entities_short_form(params, entities)
139
+ True
140
+
141
+ >>> params = {"household": {"parents": ["Javier"]}}
142
+
143
+ >>> are_entities_short_form(params, entities)
144
+ True
145
+
146
+ >>> params = {"household": {"parents": "Javier"}}
147
+
148
+ >>> are_entities_short_form(params, entities)
149
+ True
150
+
151
+ >>> params = {"salary": {"2016-10": 12000}}
152
+
153
+ >>> are_entities_short_form(params, entities)
154
+ False
155
+
156
+ >>> params = {"salary": 12000}
157
+
158
+ >>> are_entities_short_form(params, entities)
159
+ False
160
+
161
+ >>> params = {}
162
+
163
+ >>> are_entities_short_form(params, entities)
164
+ False
165
+
166
+ """
167
+ return bool(set(params).intersection(items))
168
+
169
+
170
+ def are_entities_specified(
171
+ params: Params,
172
+ items: Iterable[str],
173
+ ) -> TypeGuard[Variables]:
174
+ """Check if the params contains entities at all.
175
+
176
+ Args:
177
+ params(Params): Simulation parameters.
178
+ items(Iterable[str]): List of variables.
179
+
180
+ Returns:
181
+ bool: True if the params does not contain variables at the root level.
182
+
183
+ Examples:
184
+ >>> variables = {"salary"}
185
+
186
+ >>> params = {
187
+ ... "persons": {"Javier": {"salary": {"2018-11": 2000}}},
188
+ ... "households": {"household": {"parents": ["Javier"]}},
189
+ ... "axes": [[{"count": 1, "max": 1, "min": 1, "name": "household"}]],
190
+ ... }
191
+
192
+ >>> are_entities_specified(params, variables)
193
+ True
194
+
195
+ >>> params = {"persons": {"Javier": {"salary": {"2018-11": [2000, 3000]}}}}
196
+
197
+ >>> are_entities_specified(params, variables)
198
+ True
199
+
200
+ >>> params = {"persons": {"Javier": {"salary": {"2018-11": 2000}}}}
201
+
202
+ >>> are_entities_specified(params, variables)
203
+ True
204
+
205
+ >>> params = {"household": {"parents": ["Javier"]}}
206
+
207
+ >>> are_entities_specified(params, variables)
208
+ True
209
+
210
+ >>> params = {"salary": {"2016-10": [12000, 13000]}}
211
+
212
+ >>> are_entities_specified(params, variables)
213
+ False
214
+
215
+ >>> params = {"salary": {"2016-10": 12000}}
216
+
217
+ >>> are_entities_specified(params, variables)
218
+ False
219
+
220
+ >>> params = {"salary": [12000, 13000]}
221
+
222
+ >>> are_entities_specified(params, variables)
223
+ False
224
+
225
+ >>> params = {"salary": 12000}
226
+
227
+ >>> are_entities_specified(params, variables)
228
+ False
229
+
230
+ >>> params = {}
231
+
232
+ >>> are_entities_specified(params, variables)
233
+ False
234
+
235
+ """
236
+ if not params:
237
+ return False
238
+
239
+ return not any(key in items for key in params)
240
+
241
+
242
+ def has_axes(params: Params) -> TypeGuard[Axes]:
243
+ """Check if the params contains axes.
244
+
245
+ Args:
246
+ params(Params): Simulation parameters.
247
+
248
+ Returns:
249
+ bool: True if the params contain axes.
250
+
251
+ Examples:
252
+ >>> params = {
253
+ ... "persons": {"Javier": {"salary": {"2018-11": 2000}}},
254
+ ... "households": {"household": {"parents": ["Javier"]}},
255
+ ... "axes": [[{"count": 1, "max": 1, "min": 1, "name": "household"}]],
256
+ ... }
257
+
258
+ >>> has_axes(params)
259
+ True
260
+
261
+ >>> params = {"persons": {"Javier": {"salary": {"2018-11": [2000, 3000]}}}}
262
+
263
+ >>> has_axes(params)
264
+ False
265
+
266
+ """
267
+ return params.get("axes", None) is not None
268
+
269
+
270
+ def is_variable_dated(
271
+ variable: DatedVariable | UndatedVariable,
272
+ ) -> TypeGuard[DatedVariable]:
273
+ """Check if the variable is dated.
274
+
275
+ Args:
276
+ variable(DatedVariable | UndatedVariable): A variable.
277
+
278
+ Returns:
279
+ bool: True if the variable is dated.
280
+
281
+ Examples:
282
+ >>> variable = {"2018-11": [2000, 3000]}
283
+
284
+ >>> is_variable_dated(variable)
285
+ True
286
+
287
+ >>> variable = {"2018-11": 2000}
288
+
289
+ >>> is_variable_dated(variable)
290
+ True
291
+
292
+ >>> variable = 2000
293
+
294
+ >>> is_variable_dated(variable)
295
+ False
296
+
297
+ """
298
+ return isinstance(variable, dict)
@@ -0,0 +1,111 @@
1
+ from collections.abc import Iterable
2
+
3
+ from openfisca_core import errors
4
+
5
+ from .typing import ParamsWithoutAxes
6
+
7
+
8
+ def calculate_output_add(simulation, variable_name: str, period):
9
+ return simulation.calculate_add(variable_name, period)
10
+
11
+
12
+ def calculate_output_divide(simulation, variable_name: str, period):
13
+ return simulation.calculate_divide(variable_name, period)
14
+
15
+
16
+ def check_type(input, input_type, path=None) -> None:
17
+ json_type_map = {
18
+ dict: "Object",
19
+ list: "Array",
20
+ str: "String",
21
+ }
22
+
23
+ if path is None:
24
+ path = []
25
+
26
+ if not isinstance(input, input_type):
27
+ raise errors.SituationParsingError(
28
+ path,
29
+ f"Invalid type: must be of type '{json_type_map[input_type]}'.",
30
+ )
31
+
32
+
33
+ def check_unexpected_entities(
34
+ params: ParamsWithoutAxes,
35
+ entities: Iterable[str],
36
+ ) -> None:
37
+ """Check if the input contains entities that are not in the system.
38
+
39
+ Args:
40
+ params(ParamsWithoutAxes): Simulation parameters.
41
+ entities(Iterable[str]): List of entities in plural form.
42
+
43
+ Raises:
44
+ SituationParsingError: If there are entities that are not in the system.
45
+
46
+ Examples:
47
+ >>> entities = {"persons", "households"}
48
+
49
+ >>> params = {
50
+ ... "persons": {"Javier": {"salary": {"2018-11": 2000}}},
51
+ ... "households": {"household": {"parents": ["Javier"]}},
52
+ ... }
53
+
54
+ >>> check_unexpected_entities(params, entities)
55
+
56
+ >>> params = {"dogs": {"Bart": {"damages": {"2018-11": 2000}}}}
57
+
58
+ >>> check_unexpected_entities(params, entities)
59
+ Traceback (most recent call last):
60
+ openfisca_core.errors.situation_parsing_error.SituationParsingError
61
+
62
+ """
63
+ if has_unexpected_entities(params, entities):
64
+ unexpected_entities = [entity for entity in params if entity not in entities]
65
+
66
+ message = (
67
+ "Some entities in the situation are not defined in the loaded tax "
68
+ "and benefit system. "
69
+ f"These entities are not found: {', '.join(unexpected_entities)}. "
70
+ f"The defined entities are: {', '.join(entities)}."
71
+ )
72
+
73
+ raise errors.SituationParsingError([unexpected_entities[0]], message)
74
+
75
+
76
+ def has_unexpected_entities(params: ParamsWithoutAxes, entities: Iterable[str]) -> bool:
77
+ """Check if the input contains entities that are not in the system.
78
+
79
+ Args:
80
+ params(ParamsWithoutAxes): Simulation parameters.
81
+ entities(Iterable[str]): List of entities in plural form.
82
+
83
+ Returns:
84
+ bool: True if the input contains entities that are not in the system.
85
+
86
+ Examples:
87
+ >>> entities = {"persons", "households"}
88
+
89
+ >>> params = {
90
+ ... "persons": {"Javier": {"salary": {"2018-11": 2000}}},
91
+ ... "households": {"household": {"parents": ["Javier"]}},
92
+ ... }
93
+
94
+ >>> has_unexpected_entities(params, entities)
95
+ False
96
+
97
+ >>> params = {"dogs": {"Bart": {"damages": {"2018-11": 2000}}}}
98
+
99
+ >>> has_unexpected_entities(params, entities)
100
+ True
101
+
102
+ """
103
+ return any(entity for entity in params if entity not in entities)
104
+
105
+
106
+ def transform_to_strict_syntax(data):
107
+ if isinstance(data, (str, int)):
108
+ data = [data]
109
+ if isinstance(data, list):
110
+ return [str(item) if isinstance(item, int) else item for item in data]
111
+ return data