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,328 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import warnings
5
+ from collections.abc import Sequence
6
+ from typing import Any
7
+
8
+ import numpy
9
+ import psutil
10
+
11
+ from openfisca_core import (
12
+ commons,
13
+ errors,
14
+ periods,
15
+ types,
16
+ )
17
+ from openfisca_core import (
18
+ data_storage as storage,
19
+ )
20
+ from openfisca_core import (
21
+ indexed_enums as enums,
22
+ )
23
+
24
+ from . import types as t
25
+
26
+
27
+ class Holder:
28
+ """A holder keeps tracks of a variable values after they have been calculated, or set as an input."""
29
+
30
+ def __init__(self, variable, population) -> None:
31
+ self.population = population
32
+ self.variable = variable
33
+ self.simulation = population.simulation
34
+ self._eternal = self.variable.definition_period == periods.DateUnit.ETERNITY
35
+ self._memory_storage = storage.InMemoryStorage(is_eternal=self._eternal)
36
+
37
+ # By default, do not activate on-disk storage, or variable dropping
38
+ self._disk_storage = None
39
+ self._on_disk_storable = False
40
+ self._do_not_store = False
41
+ if self.simulation and self.simulation.memory_config:
42
+ if (
43
+ self.variable.name
44
+ not in self.simulation.memory_config.priority_variables
45
+ ):
46
+ self._disk_storage = self.create_disk_storage()
47
+ self._on_disk_storable = True
48
+ if self.variable.name in self.simulation.memory_config.variables_to_drop:
49
+ self._do_not_store = True
50
+
51
+ def clone(self, population: t.CorePopulation) -> t.Holder:
52
+ """Copy the holder just enough to be able to run a new simulation without modifying the original simulation."""
53
+ new = commons.empty_clone(self)
54
+ new_dict = new.__dict__
55
+
56
+ for key, value in self.__dict__.items():
57
+ if key not in ("population", "formula", "simulation"):
58
+ new_dict[key] = value
59
+
60
+ new_dict["population"] = population
61
+ new_dict["simulation"] = population.simulation
62
+
63
+ return new
64
+
65
+ def create_disk_storage(self, directory=None, preserve=False):
66
+ if directory is None:
67
+ directory = self.simulation.data_storage_dir
68
+ storage_dir = os.path.join(directory, self.variable.name)
69
+ if not os.path.isdir(storage_dir):
70
+ os.mkdir(storage_dir)
71
+ return storage.OnDiskStorage(
72
+ storage_dir,
73
+ self._eternal,
74
+ preserve_storage_dir=preserve,
75
+ )
76
+
77
+ def delete_arrays(self, period=None) -> None:
78
+ """If ``period`` is ``None``, remove all known values of the variable.
79
+
80
+ If ``period`` is not ``None``, only remove all values for any period included in period (e.g. if period is "2017", values for "2017-01", "2017-07", etc. would be removed)
81
+ """
82
+ self._memory_storage.delete(period)
83
+ if self._disk_storage:
84
+ self._disk_storage.delete(period)
85
+
86
+ def get_array(self, period):
87
+ """Get the value of the variable for the given period.
88
+
89
+ If the value is not known, return ``None``.
90
+ """
91
+ if self.variable.is_neutralized:
92
+ return self.default_array()
93
+ value = self._memory_storage.get(period)
94
+ if value is not None:
95
+ return value
96
+ if self._disk_storage:
97
+ return self._disk_storage.get(period)
98
+ return None
99
+
100
+ def get_memory_usage(self) -> t.MemoryUsage:
101
+ """Get data about the virtual memory usage of the Holder.
102
+
103
+ Returns:
104
+ Memory usage data.
105
+
106
+ Examples:
107
+ >>> from pprint import pprint
108
+
109
+ >>> from openfisca_core import (
110
+ ... entities,
111
+ ... populations,
112
+ ... simulations,
113
+ ... taxbenefitsystems,
114
+ ... variables,
115
+ ... )
116
+
117
+ >>> entity = entities.Entity("", "", "", "")
118
+
119
+ >>> class MyVariable(variables.Variable):
120
+ ... definition_period = periods.DateUnit.YEAR
121
+ ... entity = entity
122
+ ... value_type = int
123
+
124
+ >>> population = populations.Population(entity)
125
+ >>> variable = MyVariable()
126
+ >>> holder = Holder(variable, population)
127
+
128
+ >>> tbs = taxbenefitsystems.TaxBenefitSystem([entity])
129
+ >>> entities = {entity.key: population}
130
+ >>> simulation = simulations.Simulation(tbs, entities)
131
+ >>> holder.simulation = simulation
132
+
133
+ >>> pprint(holder.get_memory_usage(), indent=3)
134
+ { 'cell_size': nan,
135
+ 'dtype': <class 'numpy.int32'>,
136
+ 'nb_arrays': 0,
137
+ 'nb_cells_by_array': 0,
138
+ 'total_nb_bytes': 0...
139
+
140
+ """
141
+ usage = t.MemoryUsage(
142
+ nb_cells_by_array=self.population.count,
143
+ dtype=self.variable.dtype,
144
+ )
145
+
146
+ usage.update(self._memory_storage.get_memory_usage())
147
+
148
+ if self.simulation.trace:
149
+ nb_requests = self.simulation.tracer.get_nb_requests(self.variable.name)
150
+ usage.update(
151
+ {
152
+ "nb_requests": nb_requests,
153
+ "nb_requests_by_array": (
154
+ nb_requests / float(usage["nb_arrays"])
155
+ if usage["nb_arrays"] > 0
156
+ else numpy.nan
157
+ ),
158
+ },
159
+ )
160
+
161
+ return usage
162
+
163
+ def get_known_periods(self):
164
+ """Get the list of periods the variable value is known for."""
165
+ return list(self._memory_storage.get_known_periods()) + list(
166
+ self._disk_storage.get_known_periods() if self._disk_storage else [],
167
+ )
168
+
169
+ def set_input(
170
+ self,
171
+ period: types.Period,
172
+ array: numpy.ndarray | Sequence[Any],
173
+ ) -> numpy.ndarray | None:
174
+ """Set a Variable's array of values of a given Period.
175
+
176
+ Args:
177
+ period: The period at which the value is set.
178
+ array: The input value for the variable.
179
+
180
+ Returns:
181
+ The set input array.
182
+
183
+ Note:
184
+ If a ``set_input`` property has been set for the variable, this
185
+ method may accept inputs for periods not matching the
186
+ ``definition_period`` of the Variable. To read
187
+ more about this, check the `documentation`_.
188
+
189
+ Examples:
190
+ >>> from openfisca_core import entities, populations, variables
191
+
192
+ >>> entity = entities.Entity("", "", "", "")
193
+
194
+ >>> class MyVariable(variables.Variable):
195
+ ... definition_period = periods.DateUnit.YEAR
196
+ ... entity = entity
197
+ ... value_type = float
198
+
199
+ >>> variable = MyVariable()
200
+
201
+ >>> population = populations.Population(entity)
202
+ >>> population.count = 2
203
+
204
+ >>> holder = Holder(variable, population)
205
+ >>> holder.set_input("2018", numpy.array([12.5, 14]))
206
+ >>> holder.get_array("2018")
207
+ array([12.5, 14. ], dtype=float32)
208
+
209
+ >>> holder.set_input("2018", [12.5, 14])
210
+ >>> holder.get_array("2018")
211
+ array([12.5, 14. ], dtype=float32)
212
+
213
+ .. _documentation:
214
+ https://openfisca.org/doc/coding-the-legislation/35_periods.html#set-input-automatically-process-variable-inputs-defined-for-periods-not-matching-the-definition-period
215
+
216
+ """
217
+ period = periods.period(period)
218
+
219
+ if period.unit == periods.DateUnit.ETERNITY and not self._eternal:
220
+ error_message = os.linesep.join(
221
+ [
222
+ "Unable to set a value for variable {1} for {0}.",
223
+ "{1} is only defined for {2}s. Please adapt your input.",
224
+ ],
225
+ ).format(
226
+ periods.DateUnit.ETERNITY.upper(),
227
+ self.variable.name,
228
+ self.variable.definition_period,
229
+ )
230
+ raise errors.PeriodMismatchError(
231
+ self.variable.name,
232
+ period,
233
+ self.variable.definition_period,
234
+ error_message,
235
+ )
236
+ if self.variable.is_neutralized:
237
+ warning_message = f"You cannot set a value for the variable {self.variable.name}, as it has been neutralized. The value you provided ({array}) will be ignored."
238
+ return warnings.warn(warning_message, Warning, stacklevel=2)
239
+ if self.variable.value_type in (float, int) and isinstance(array, str):
240
+ array = commons.eval_expression(array)
241
+ if self.variable.set_input:
242
+ return self.variable.set_input(self, period, array)
243
+ return self._set(period, array)
244
+
245
+ def _to_array(self, value):
246
+ if not isinstance(value, numpy.ndarray):
247
+ value = numpy.asarray(value)
248
+ if value.ndim == 0:
249
+ # 0-dim arrays are casted to scalar when they interact with float. We don't want that.
250
+ value = value.reshape(1)
251
+ if len(value) != self.population.count:
252
+ msg = f'Unable to set value "{value}" for variable "{self.variable.name}", as its length is {len(value)} while there are {self.population.count} {self.population.entity.plural} in the simulation.'
253
+ raise ValueError(
254
+ msg,
255
+ )
256
+ if self.variable.value_type == enums.Enum:
257
+ value = self.variable.possible_values.encode(value)
258
+ if value.dtype != self.variable.dtype:
259
+ try:
260
+ value = value.astype(self.variable.dtype)
261
+ except ValueError:
262
+ msg = f'Unable to set value "{value}" for variable "{self.variable.name}", as the variable dtype "{self.variable.dtype}" does not match the value dtype "{value.dtype}".'
263
+ raise ValueError(
264
+ msg,
265
+ )
266
+ return value
267
+
268
+ def _set(self, period, value) -> None:
269
+ value = self._to_array(value)
270
+ if not self._eternal:
271
+ if period is None:
272
+ msg = (
273
+ f"A period must be specified to set values, except for variables with "
274
+ f"{periods.DateUnit.ETERNITY.upper()} as as period_definition."
275
+ )
276
+ raise ValueError(
277
+ msg,
278
+ )
279
+ if self.variable.definition_period != period.unit or period.size > 1:
280
+ name = self.variable.name
281
+ period_size_adj = (
282
+ f"{period.unit}"
283
+ if (period.size == 1)
284
+ else f"{period.size}-{period.unit}s"
285
+ )
286
+ error_message = os.linesep.join(
287
+ [
288
+ f'Unable to set a value for variable "{name}" for {period_size_adj}-long period "{period}".',
289
+ f'"{name}" can only be set for one {self.variable.definition_period} at a time. Please adapt your input.',
290
+ f'If you are the maintainer of "{name}", you can consider adding it a set_input attribute to enable automatic period casting.',
291
+ ],
292
+ )
293
+
294
+ raise errors.PeriodMismatchError(
295
+ self.variable.name,
296
+ period,
297
+ self.variable.definition_period,
298
+ error_message,
299
+ )
300
+
301
+ should_store_on_disk = (
302
+ self._on_disk_storable
303
+ and self._memory_storage.get(period) is None
304
+ and psutil.virtual_memory().percent # If there is already a value in memory, replace it and don't put a new value in the disk storage
305
+ >= self.simulation.memory_config.max_memory_occupation_pc
306
+ )
307
+
308
+ if should_store_on_disk:
309
+ self._disk_storage.put(value, period)
310
+ else:
311
+ self._memory_storage.put(value, period)
312
+
313
+ def put_in_cache(self, value, period) -> None:
314
+ if self._do_not_store:
315
+ return
316
+
317
+ if (
318
+ self.simulation.opt_out_cache
319
+ and self.simulation.tax_benefit_system.cache_blacklist
320
+ and self.variable.name in self.simulation.tax_benefit_system.cache_blacklist
321
+ ):
322
+ return
323
+
324
+ self._set(period, value)
325
+
326
+ def default_array(self):
327
+ """Return a new array of the appropriate length for the entity, filled with the variable default values."""
328
+ return self.variable.default_array(self.population.count)
File without changes
@@ -0,0 +1,134 @@
1
+ import pytest
2
+
3
+ from openfisca_core import holders, tools
4
+ from openfisca_core.entities import Entity
5
+ from openfisca_core.holders import Holder
6
+ from openfisca_core.periods import DateUnit, Instant, Period
7
+ from openfisca_core.populations import Population
8
+ from openfisca_core.variables import Variable
9
+
10
+
11
+ @pytest.fixture
12
+ def people():
13
+ return Entity(
14
+ key="person",
15
+ plural="people",
16
+ label="An individual member of a larger group.",
17
+ doc="People have the particularity of not being someone else.",
18
+ )
19
+
20
+
21
+ @pytest.fixture
22
+ def Income(people):
23
+ return type(
24
+ "Income",
25
+ (Variable,),
26
+ {"value_type": float, "entity": people},
27
+ )
28
+
29
+
30
+ @pytest.fixture
31
+ def population(people):
32
+ population = Population(people)
33
+ population.count = 1
34
+ return population
35
+
36
+
37
+ @pytest.mark.parametrize(
38
+ ("dispatch_unit", "definition_unit", "values", "expected"),
39
+ [
40
+ (DateUnit.YEAR, DateUnit.YEAR, [1.0], [3.0]),
41
+ (DateUnit.YEAR, DateUnit.MONTH, [1.0], [36.0]),
42
+ (DateUnit.YEAR, DateUnit.DAY, [1.0], [1096.0]),
43
+ (DateUnit.YEAR, DateUnit.WEEK, [1.0], [157.0]),
44
+ (DateUnit.YEAR, DateUnit.WEEKDAY, [1.0], [1096.0]),
45
+ (DateUnit.MONTH, DateUnit.YEAR, [1.0], [1.0]),
46
+ (DateUnit.MONTH, DateUnit.MONTH, [1.0], [3.0]),
47
+ (DateUnit.MONTH, DateUnit.DAY, [1.0], [90.0]),
48
+ (DateUnit.MONTH, DateUnit.WEEK, [1.0], [13.0]),
49
+ (DateUnit.MONTH, DateUnit.WEEKDAY, [1.0], [90.0]),
50
+ (DateUnit.DAY, DateUnit.YEAR, [1.0], [1.0]),
51
+ (DateUnit.DAY, DateUnit.MONTH, [1.0], [1.0]),
52
+ (DateUnit.DAY, DateUnit.DAY, [1.0], [3.0]),
53
+ (DateUnit.DAY, DateUnit.WEEK, [1.0], [1.0]),
54
+ (DateUnit.DAY, DateUnit.WEEKDAY, [1.0], [3.0]),
55
+ (DateUnit.WEEK, DateUnit.YEAR, [1.0], [1.0]),
56
+ (DateUnit.WEEK, DateUnit.MONTH, [1.0], [1.0]),
57
+ (DateUnit.WEEK, DateUnit.DAY, [1.0], [21.0]),
58
+ (DateUnit.WEEK, DateUnit.WEEK, [1.0], [3.0]),
59
+ (DateUnit.WEEK, DateUnit.WEEKDAY, [1.0], [21.0]),
60
+ (DateUnit.WEEKDAY, DateUnit.YEAR, [1.0], [1.0]),
61
+ (DateUnit.WEEKDAY, DateUnit.MONTH, [1.0], [1.0]),
62
+ (DateUnit.WEEKDAY, DateUnit.DAY, [1.0], [3.0]),
63
+ (DateUnit.WEEKDAY, DateUnit.WEEK, [1.0], [1.0]),
64
+ (DateUnit.WEEKDAY, DateUnit.WEEKDAY, [1.0], [3.0]),
65
+ ],
66
+ )
67
+ def test_set_input_dispatch_by_period(
68
+ Income,
69
+ population,
70
+ dispatch_unit,
71
+ definition_unit,
72
+ values,
73
+ expected,
74
+ ) -> None:
75
+ Income.definition_period = definition_unit
76
+ income = Income()
77
+ holder = Holder(income, population)
78
+ instant = Instant((2022, 1, 1))
79
+ dispatch_period = Period((dispatch_unit, instant, 3))
80
+
81
+ holders.set_input_dispatch_by_period(holder, dispatch_period, values)
82
+ total = sum(map(holder.get_array, holder.get_known_periods()))
83
+
84
+ tools.assert_near(total, expected, absolute_error_margin=0.001)
85
+
86
+
87
+ @pytest.mark.parametrize(
88
+ ("divide_unit", "definition_unit", "values", "expected"),
89
+ [
90
+ (DateUnit.YEAR, DateUnit.YEAR, [3.0], [1.0]),
91
+ (DateUnit.YEAR, DateUnit.MONTH, [36.0], [1.0]),
92
+ (DateUnit.YEAR, DateUnit.DAY, [1095.0], [1.0]),
93
+ (DateUnit.YEAR, DateUnit.WEEK, [157.0], [1.0]),
94
+ (DateUnit.YEAR, DateUnit.WEEKDAY, [1095.0], [1.0]),
95
+ (DateUnit.MONTH, DateUnit.YEAR, [1.0], [1.0]),
96
+ (DateUnit.MONTH, DateUnit.MONTH, [3.0], [1.0]),
97
+ (DateUnit.MONTH, DateUnit.DAY, [90.0], [1.0]),
98
+ (DateUnit.MONTH, DateUnit.WEEK, [13.0], [1.0]),
99
+ (DateUnit.MONTH, DateUnit.WEEKDAY, [90.0], [1.0]),
100
+ (DateUnit.DAY, DateUnit.YEAR, [1.0], [1.0]),
101
+ (DateUnit.DAY, DateUnit.MONTH, [1.0], [1.0]),
102
+ (DateUnit.DAY, DateUnit.DAY, [3.0], [1.0]),
103
+ (DateUnit.DAY, DateUnit.WEEK, [1.0], [1.0]),
104
+ (DateUnit.DAY, DateUnit.WEEKDAY, [3.0], [1.0]),
105
+ (DateUnit.WEEK, DateUnit.YEAR, [1.0], [1.0]),
106
+ (DateUnit.WEEK, DateUnit.MONTH, [1.0], [1.0]),
107
+ (DateUnit.WEEK, DateUnit.DAY, [21.0], [1.0]),
108
+ (DateUnit.WEEK, DateUnit.WEEK, [3.0], [1.0]),
109
+ (DateUnit.WEEK, DateUnit.WEEKDAY, [21.0], [1.0]),
110
+ (DateUnit.WEEKDAY, DateUnit.YEAR, [1.0], [1.0]),
111
+ (DateUnit.WEEKDAY, DateUnit.MONTH, [1.0], [1.0]),
112
+ (DateUnit.WEEKDAY, DateUnit.DAY, [3.0], [1.0]),
113
+ (DateUnit.WEEKDAY, DateUnit.WEEK, [1.0], [1.0]),
114
+ (DateUnit.WEEKDAY, DateUnit.WEEKDAY, [3.0], [1.0]),
115
+ ],
116
+ )
117
+ def test_set_input_divide_by_period(
118
+ Income,
119
+ population,
120
+ divide_unit,
121
+ definition_unit,
122
+ values,
123
+ expected,
124
+ ) -> None:
125
+ Income.definition_period = definition_unit
126
+ income = Income()
127
+ holder = Holder(income, population)
128
+ instant = Instant((2022, 1, 1))
129
+ divide_period = Period((divide_unit, instant, 3))
130
+
131
+ holders.set_input_divide_by_period(holder, divide_period, values)
132
+ last = holder.get_array(holder.get_known_periods()[-1])
133
+
134
+ tools.assert_near(last, expected, absolute_error_margin=0.001)
@@ -0,0 +1,3 @@
1
+ from openfisca_core.types import CorePopulation, Holder, MemoryUsage
2
+
3
+ __all__ = ["CorePopulation", "Holder", "MemoryUsage"]
@@ -0,0 +1,18 @@
1
+ """Enumerations for variables with a limited set of possible values."""
2
+
3
+ from . import types
4
+ from ._enum_type import EnumType
5
+ from ._errors import EnumEncodingError, EnumMemberNotFoundError
6
+ from .config import ENUM_ARRAY_DTYPE
7
+ from .enum import Enum
8
+ from .enum_array import EnumArray
9
+
10
+ __all__ = [
11
+ "ENUM_ARRAY_DTYPE",
12
+ "Enum",
13
+ "EnumArray",
14
+ "EnumEncodingError",
15
+ "EnumMemberNotFoundError",
16
+ "EnumType",
17
+ "types",
18
+ ]
@@ -0,0 +1,79 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import final
4
+
5
+ import numpy
6
+
7
+ from . import types as t
8
+
9
+
10
+ @final
11
+ class EnumType(t.EnumType):
12
+ """Meta class for creating an indexed :class:`.Enum`.
13
+
14
+ Examples:
15
+ >>> from openfisca_core import indexed_enums as enum
16
+
17
+ >>> class Enum(enum.Enum, metaclass=enum.EnumType):
18
+ ... pass
19
+
20
+ >>> Enum.items
21
+ Traceback (most recent call last):
22
+ AttributeError: ...
23
+
24
+ >>> class Housing(Enum):
25
+ ... OWNER = "Owner"
26
+ ... TENANT = "Tenant"
27
+
28
+ >>> Housing.indices
29
+ array([0, 1], dtype=int16)
30
+
31
+ >>> Housing.names
32
+ array(['OWNER', 'TENANT'], dtype='<U6')
33
+
34
+ >>> Housing.enums
35
+ array([Housing.OWNER, Housing.TENANT], dtype=object)
36
+
37
+ """
38
+
39
+ def __new__(
40
+ metacls,
41
+ name: str,
42
+ bases: tuple[type, ...],
43
+ classdict: t.EnumDict,
44
+ **kwds: object,
45
+ ) -> t.EnumType:
46
+ """Create a new indexed enum class."""
47
+ # Create the enum class.
48
+ cls = super().__new__(metacls, name, bases, classdict, **kwds)
49
+
50
+ # If the enum class has no members, return it as is.
51
+ if not cls.__members__:
52
+ return cls
53
+
54
+ # Add the indices attribute to the enum class.
55
+ cls.indices = numpy.arange(len(cls), dtype=t.EnumDType)
56
+
57
+ # Add the names attribute to the enum class.
58
+ cls.names = numpy.array(cls._member_names_, dtype=t.StrDType)
59
+
60
+ # Add the enums attribute to the enum class.
61
+ cls.enums = numpy.array(cls, dtype=t.ObjDType)
62
+
63
+ # Return the modified enum class.
64
+ return cls
65
+
66
+ def __dir__(cls) -> list[str]:
67
+ return sorted({"indices", "names", "enums", *super().__dir__()})
68
+
69
+ def __hash__(cls) -> int:
70
+ return object.__hash__(cls.__name__)
71
+
72
+ def __eq__(cls, other: object) -> bool:
73
+ return hash(cls) == hash(other)
74
+
75
+ def __ne__(cls, other: object) -> bool:
76
+ return hash(cls) != hash(other)
77
+
78
+
79
+ __all__ = ["EnumType"]
@@ -0,0 +1,35 @@
1
+ from __future__ import annotations
2
+
3
+ from . import types as t
4
+
5
+
6
+ class EnumEncodingError(TypeError):
7
+ """Raised when an enum is encoded with an unsupported type."""
8
+
9
+ def __init__(
10
+ self, enum_class: type[t.Enum], value: t.VarArray | t.ArrayLike[object]
11
+ ) -> None:
12
+ msg = (
13
+ f"Failed to encode \"{value}\" of type '{value[0].__class__.__name__}', "
14
+ "as it is not supported. Please, try again with an array of "
15
+ f"'{int.__name__}', '{str.__name__}', or '{enum_class.__name__}'."
16
+ )
17
+ super().__init__(msg)
18
+
19
+
20
+ class EnumMemberNotFoundError(IndexError):
21
+ """Raised when a member is not found in an enum."""
22
+
23
+ def __init__(self, enum_class: type[t.Enum]) -> None:
24
+ index = [str(enum.index) for enum in enum_class]
25
+ names = [enum.name for enum in enum_class]
26
+ msg = (
27
+ f"Some members were not found in enum '{enum_class.__name__}'. "
28
+ f"Possible values are: {', '.join(names[:-1])}, and {names[-1]!s}; "
29
+ f"or their corresponding indices: {', '.join(index[:-1])}, and "
30
+ f"{index[-1]}."
31
+ )
32
+ super().__init__(msg)
33
+
34
+
35
+ __all__ = ["EnumEncodingError", "EnumMemberNotFoundError"]