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,198 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import KeysView, MutableMapping
4
+
5
+ import numpy
6
+
7
+ from openfisca_core import periods
8
+ from openfisca_core.periods import DateUnit
9
+
10
+ from . import types as t
11
+
12
+
13
+ class InMemoryStorage:
14
+ """Storing and retrieving calculated vectors in memory.
15
+
16
+ Args:
17
+ is_eternal: Whether the storage is eternal.
18
+
19
+ """
20
+
21
+ #: Whether the storage is eternal.
22
+ is_eternal: bool
23
+
24
+ #: A dictionary containing data that has been stored in memory.
25
+ _arrays: MutableMapping[t.Period, t.Array[t.DTypeGeneric]]
26
+
27
+ def __init__(self, is_eternal: bool = False) -> None:
28
+ self._arrays = {}
29
+ self.is_eternal = is_eternal
30
+
31
+ def get(self, period: None | t.Period = None) -> None | t.Array[t.DTypeGeneric]:
32
+ """Retrieve the data for the specified :obj:`.Period` from memory.
33
+
34
+ Args:
35
+ period: The :obj:`.Period` for which data should be retrieved.
36
+
37
+ Returns:
38
+ None: If no data is available.
39
+ EnumArray: The data for the specified :obj:`.Period`.
40
+ ndarray[generic]: The data for the specified :obj:`.Period`.
41
+
42
+ Examples:
43
+ >>> import numpy
44
+
45
+ >>> from openfisca_core import data_storage, periods
46
+
47
+ >>> storage = data_storage.InMemoryStorage()
48
+ >>> value = numpy.array([1, 2, 3])
49
+ >>> instant = periods.Instant((2017, 1, 1))
50
+ >>> period = periods.Period(("year", instant, 1))
51
+
52
+ >>> storage.put(value, period)
53
+
54
+ >>> storage.get(period)
55
+ array([1, 2, 3])
56
+
57
+ """
58
+ if self.is_eternal:
59
+ period = periods.period(DateUnit.ETERNITY)
60
+ period = periods.period(period)
61
+
62
+ values = self._arrays.get(period)
63
+ if values is None:
64
+ return None
65
+ return values
66
+
67
+ def put(self, value: t.Array[t.DTypeGeneric], period: None | t.Period) -> None:
68
+ """Store the specified data in memory for the specified :obj:`.Period`.
69
+
70
+ Args:
71
+ value: The data to store
72
+ period: The :obj:`.Period` for which the data should be stored.
73
+
74
+ Examples:
75
+ >>> import numpy
76
+
77
+ >>> from openfisca_core import data_storage, periods
78
+
79
+ >>> storage = data_storage.InMemoryStorage()
80
+ >>> value = numpy.array([1, "2", "salary"])
81
+ >>> instant = periods.Instant((2017, 1, 1))
82
+ >>> period = periods.Period(("year", instant, 1))
83
+
84
+ >>> storage.put(value, period)
85
+
86
+ >>> storage.get(period)
87
+ array(['1', '2', 'salary'], ...)
88
+
89
+ """
90
+ if self.is_eternal:
91
+ period = periods.period(DateUnit.ETERNITY)
92
+ period = periods.period(period)
93
+
94
+ self._arrays[period] = value
95
+
96
+ def delete(self, period: None | t.Period = None) -> None:
97
+ """Delete the data for the specified :obj:`.Period` from memory.
98
+
99
+ Args:
100
+ period: The :obj:`.Period` for which data should be deleted.
101
+
102
+ Note:
103
+ If ``period`` is specified, all data will be deleted.
104
+
105
+ Examples:
106
+ >>> import numpy
107
+
108
+ >>> from openfisca_core import data_storage, periods
109
+
110
+ >>> storage = data_storage.InMemoryStorage()
111
+ >>> value = numpy.array([1, 2, 3])
112
+ >>> instant = periods.Instant((2017, 1, 1))
113
+ >>> period = periods.Period(("year", instant, 1))
114
+
115
+ >>> storage.put(value, period)
116
+
117
+ >>> storage.get(period)
118
+ array([1, 2, 3])
119
+
120
+ >>> storage.delete(period)
121
+
122
+ >>> storage.get(period)
123
+
124
+ >>> storage.put(value, period)
125
+
126
+ >>> storage.delete()
127
+
128
+ >>> storage.get(period)
129
+
130
+ """
131
+ if period is None:
132
+ self._arrays = {}
133
+ return
134
+
135
+ if self.is_eternal:
136
+ period = periods.period(DateUnit.ETERNITY)
137
+ period = periods.period(period)
138
+
139
+ self._arrays = {
140
+ period_item: value
141
+ for period_item, value in self._arrays.items()
142
+ if not period.contains(period_item)
143
+ }
144
+
145
+ def get_known_periods(self) -> KeysView[t.Period]:
146
+ """List of storage's known periods.
147
+
148
+ Returns:
149
+ KeysView[Period]: A sequence containing the storage's known periods.
150
+
151
+ Examples:
152
+ >>> from openfisca_core import data_storage, periods
153
+
154
+ >>> storage = data_storage.InMemoryStorage()
155
+ >>> storage.get_known_periods()
156
+ dict_keys([])
157
+
158
+ >>> instant = periods.Instant((2017, 1, 1))
159
+ >>> period = periods.Period(("year", instant, 1))
160
+ >>> storage.put([], period)
161
+
162
+ >>> storage.get_known_periods()
163
+ dict_keys([Period(('year', Instant((2017, 1, 1)), 1))])
164
+
165
+ """
166
+ return self._arrays.keys()
167
+
168
+ def get_memory_usage(self) -> t.MemoryUsage:
169
+ """Memory usage of the storage.
170
+
171
+ Returns:
172
+ MemoryUsage: A dictionary representing the storage's memory usage.
173
+
174
+ Examples:
175
+ >>> from openfisca_core import data_storage
176
+
177
+ >>> storage = data_storage.InMemoryStorage()
178
+ >>> storage.get_memory_usage()
179
+ {'nb_arrays': 0, 'total_nb_bytes': 0, 'cell_size': nan}
180
+
181
+ """
182
+ if not self._arrays:
183
+ return {
184
+ "nb_arrays": 0,
185
+ "total_nb_bytes": 0,
186
+ "cell_size": numpy.nan,
187
+ }
188
+
189
+ nb_arrays = len(self._arrays)
190
+ array = next(iter(self._arrays.values()))
191
+ return {
192
+ "nb_arrays": nb_arrays,
193
+ "total_nb_bytes": array.nbytes * nb_arrays,
194
+ "cell_size": array.itemsize,
195
+ }
196
+
197
+
198
+ __all__ = ["InMemoryStorage"]
@@ -0,0 +1,307 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import shutil
5
+ from collections.abc import KeysView, MutableMapping
6
+
7
+ import numpy
8
+
9
+ from openfisca_core import periods
10
+ from openfisca_core.indexed_enums import EnumArray
11
+ from openfisca_core.periods import DateUnit
12
+
13
+ from . import types as t
14
+
15
+
16
+ class OnDiskStorage:
17
+ """Storing and retrieving calculated vectors on disk.
18
+
19
+ Args:
20
+ storage_dir: Path to store calculated vectors.
21
+ is_eternal: Whether the storage is eternal.
22
+ preserve_storage_dir: Whether to preserve the storage directory.
23
+
24
+ """
25
+
26
+ #: A dictionary containing data that has been stored on disk.
27
+ storage_dir: str
28
+
29
+ #: Whether the storage is eternal.
30
+ is_eternal: bool
31
+
32
+ #: Whether to preserve the storage directory.
33
+ preserve_storage_dir: bool
34
+
35
+ #: Mapping of file paths to possible :class:`.Enum` values.
36
+ _enums: MutableMapping[str, type[t.Enum]]
37
+
38
+ #: Mapping of periods to file paths.
39
+ _files: MutableMapping[t.Period, str]
40
+
41
+ def __init__(
42
+ self,
43
+ storage_dir: str,
44
+ is_eternal: bool = False,
45
+ preserve_storage_dir: bool = False,
46
+ enums: MutableMapping[str, type[t.Enum]] | None = None,
47
+ ) -> None:
48
+ self._files = {}
49
+ self._enums = {} if enums is None else enums
50
+ self.is_eternal = is_eternal
51
+ self.preserve_storage_dir = preserve_storage_dir
52
+ self.storage_dir = storage_dir
53
+
54
+ def _decode_file(self, file: str) -> t.Array[t.DTypeGeneric]:
55
+ """Decode a file by loading its contents as a :mod:`numpy` array.
56
+
57
+ Args:
58
+ file: Path to the file to be decoded.
59
+
60
+ Returns:
61
+ EnumArray: Representing the data in the file.
62
+ ndarray[generic]: Representing the data in the file.
63
+
64
+ Note:
65
+ If the file is associated with :class:`~indexed_enums.Enum` values, the
66
+ array is converted back to an :obj:`~indexed_enums.EnumArray` object.
67
+
68
+ Examples:
69
+ >>> import tempfile
70
+
71
+ >>> import numpy
72
+
73
+ >>> from openfisca_core import data_storage, indexed_enums, periods
74
+
75
+ >>> class Housing(indexed_enums.Enum):
76
+ ... OWNER = "Owner"
77
+ ... TENANT = "Tenant"
78
+ ... FREE_LODGER = "Free lodger"
79
+ ... HOMELESS = "Homeless"
80
+
81
+ >>> array = numpy.array([1])
82
+ >>> value = indexed_enums.EnumArray(array, Housing)
83
+ >>> instant = periods.Instant((2017, 1, 1))
84
+ >>> period = periods.Period(("year", instant, 1))
85
+
86
+ >>> with tempfile.TemporaryDirectory() as directory:
87
+ ... storage = data_storage.OnDiskStorage(directory)
88
+ ... storage.put(value, period)
89
+ ... storage._decode_file(storage._files[period])
90
+ EnumArray([Housing.TENANT])
91
+
92
+ """
93
+ enum = self._enums.get(self.storage_dir)
94
+
95
+ if enum is not None:
96
+ return EnumArray(numpy.load(file), enum)
97
+
98
+ array: t.Array[t.DTypeGeneric] = numpy.load(file)
99
+
100
+ return array
101
+
102
+ def get(self, period: None | t.Period = None) -> None | t.Array[t.DTypeGeneric]:
103
+ """Retrieve the data for the specified period from disk.
104
+
105
+ Args:
106
+ period: The period for which data should be retrieved.
107
+
108
+ Returns:
109
+ None: If no data is available.
110
+ EnumArray: Representing the data for the specified period.
111
+ ndarray[generic]: Representing the data for the specified period.
112
+
113
+ Examples:
114
+ >>> import tempfile
115
+
116
+ >>> import numpy
117
+
118
+ >>> from openfisca_core import data_storage, periods
119
+
120
+ >>> value = numpy.array([1, 2, 3])
121
+ >>> instant = periods.Instant((2017, 1, 1))
122
+ >>> period = periods.Period(("year", instant, 1))
123
+
124
+ >>> with tempfile.TemporaryDirectory() as directory:
125
+ ... storage = data_storage.OnDiskStorage(directory)
126
+ ... storage.put(value, period)
127
+ ... storage.get(period)
128
+ array([1, 2, 3])
129
+
130
+ """
131
+ if self.is_eternal:
132
+ period = periods.period(DateUnit.ETERNITY)
133
+ period = periods.period(period)
134
+
135
+ values = self._files.get(period)
136
+ if values is None:
137
+ return None
138
+ return self._decode_file(values)
139
+
140
+ def put(self, value: t.Array[t.DTypeGeneric], period: None | t.Period) -> None:
141
+ """Store the specified data on disk for the specified period.
142
+
143
+ Args:
144
+ value: The data to store
145
+ period: The period for which the data should be stored.
146
+
147
+ Examples:
148
+ >>> import tempfile
149
+
150
+ >>> import numpy
151
+
152
+ >>> from openfisca_core import data_storage, periods
153
+
154
+ >>> value = numpy.array([1, "2", "salary"])
155
+ >>> instant = periods.Instant((2017, 1, 1))
156
+ >>> period = periods.Period(("year", instant, 1))
157
+
158
+ >>> with tempfile.TemporaryDirectory() as directory:
159
+ ... storage = data_storage.OnDiskStorage(directory)
160
+ ... storage.put(value, period)
161
+ ... storage.get(period)
162
+ array(['1', '2', 'salary'], ...)
163
+
164
+ """
165
+ if self.is_eternal:
166
+ period = periods.period(DateUnit.ETERNITY)
167
+ period = periods.period(period)
168
+
169
+ filename = str(period)
170
+ path = os.path.join(self.storage_dir, filename) + ".npy"
171
+ if isinstance(value, EnumArray) and value.possible_values is not None:
172
+ self._enums[self.storage_dir] = value.possible_values
173
+ value = value.view(numpy.ndarray)
174
+ numpy.save(path, value)
175
+ self._files[period] = path
176
+
177
+ def delete(self, period: None | t.Period = None) -> None:
178
+ """Delete the data for the specified period from disk.
179
+
180
+ Args:
181
+ period: The period for which data should be deleted. If not
182
+ specified, all data will be deleted.
183
+
184
+ Examples:
185
+ >>> import tempfile
186
+
187
+ >>> import numpy
188
+
189
+ >>> from openfisca_core import data_storage, periods
190
+
191
+ >>> value = numpy.array([1, 2, 3])
192
+ >>> instant = periods.Instant((2017, 1, 1))
193
+ >>> period = periods.Period(("year", instant, 1))
194
+
195
+ >>> with tempfile.TemporaryDirectory() as directory:
196
+ ... storage = data_storage.OnDiskStorage(directory)
197
+ ... storage.put(value, period)
198
+ ... storage.get(period)
199
+ array([1, 2, 3])
200
+
201
+ >>> with tempfile.TemporaryDirectory() as directory:
202
+ ... storage = data_storage.OnDiskStorage(directory)
203
+ ... storage.put(value, period)
204
+ ... storage.delete(period)
205
+ ... storage.get(period)
206
+
207
+ >>> with tempfile.TemporaryDirectory() as directory:
208
+ ... storage = data_storage.OnDiskStorage(directory)
209
+ ... storage.put(value, period)
210
+ ... storage.delete()
211
+ ... storage.get(period)
212
+
213
+ """
214
+ if period is None:
215
+ self._files = {}
216
+ return
217
+
218
+ if self.is_eternal:
219
+ period = periods.period(DateUnit.ETERNITY)
220
+ period = periods.period(period)
221
+
222
+ self._files = {
223
+ period_item: value
224
+ for period_item, value in self._files.items()
225
+ if not period.contains(period_item)
226
+ }
227
+
228
+ def get_known_periods(self) -> KeysView[t.Period]:
229
+ """List of storage's known periods.
230
+
231
+ Returns:
232
+ KeysView[Period]: A sequence containing the storage's known periods.
233
+
234
+ Examples:
235
+ >>> import tempfile
236
+
237
+ >>> from openfisca_core import data_storage, periods
238
+
239
+ >>> instant = periods.Instant((2017, 1, 1))
240
+ >>> period = periods.Period(("year", instant, 1))
241
+
242
+ >>> with tempfile.TemporaryDirectory() as directory:
243
+ ... storage = data_storage.OnDiskStorage(directory)
244
+ ... storage.get_known_periods()
245
+ dict_keys([])
246
+
247
+ >>> with tempfile.TemporaryDirectory() as directory:
248
+ ... storage = data_storage.OnDiskStorage(directory)
249
+ ... storage.put([], period)
250
+ ... storage.get_known_periods()
251
+ dict_keys([Period(('year', Instant((2017, 1, 1)), 1))])
252
+
253
+ """
254
+ return self._files.keys()
255
+
256
+ def restore(self) -> None:
257
+ """Restore the storage from disk.
258
+
259
+ Examples:
260
+ >>> import tempfile
261
+
262
+ >>> import numpy
263
+
264
+ >>> from openfisca_core import data_storage, periods
265
+
266
+ >>> value = numpy.array([1, 2, 3])
267
+ >>> instant = periods.Instant((2017, 1, 1))
268
+ >>> period = periods.Period(("year", instant, 1))
269
+ >>> directory = tempfile.TemporaryDirectory()
270
+
271
+ >>> storage1 = data_storage.OnDiskStorage(directory.name)
272
+ >>> storage1.put(value, period)
273
+ >>> storage1._files
274
+ {Period(('year', Instant((2017, 1, 1)), 1)): '...2017.npy'}
275
+
276
+ >>> storage2 = data_storage.OnDiskStorage(directory.name)
277
+ >>> storage2._files
278
+ {}
279
+
280
+ >>> storage2.restore()
281
+ >>> storage2._files
282
+ {Period((<DateUnit.YEAR: 'year'>, Instant((2017, 1, 1...2017.npy'}
283
+
284
+ >>> directory.cleanup()
285
+
286
+ """
287
+ self._files = files = {}
288
+ # Restore self._files from content of storage_dir.
289
+ for filename in os.listdir(self.storage_dir):
290
+ if not filename.endswith(".npy"):
291
+ continue
292
+ path = os.path.join(self.storage_dir, filename)
293
+ filename_core = filename.rsplit(".", 1)[0]
294
+ period = periods.period(filename_core)
295
+ files[period] = path
296
+
297
+ def __del__(self) -> None:
298
+ if self.preserve_storage_dir:
299
+ return
300
+ shutil.rmtree(self.storage_dir) # Remove the holder temporary files
301
+ # If the simulation temporary directory is empty, remove it
302
+ parent_dir = os.path.abspath(os.path.join(self.storage_dir, os.pardir))
303
+ if not os.listdir(parent_dir):
304
+ shutil.rmtree(parent_dir)
305
+
306
+
307
+ __all__ = ["OnDiskStorage"]
@@ -0,0 +1,14 @@
1
+ from typing_extensions import TypedDict
2
+
3
+ from openfisca_core.types import Array, DTypeGeneric, Enum, Period
4
+
5
+
6
+ class MemoryUsage(TypedDict, total=True):
7
+ """Memory usage information."""
8
+
9
+ cell_size: float
10
+ nb_arrays: int
11
+ total_nb_bytes: int
12
+
13
+
14
+ __all__ = ["Array", "DTypeGeneric", "Enum", "Period"]
@@ -0,0 +1,23 @@
1
+ """Provide a way of representing the entities of a rule system."""
2
+
3
+ from . import types
4
+ from ._core_entity import CoreEntity
5
+ from .entity import Entity
6
+ from .group_entity import GroupEntity
7
+ from .helpers import build_entity, find_role
8
+ from .role import Role
9
+
10
+ SingleEntity = Entity
11
+ check_role_validity = CoreEntity.check_role_validity
12
+
13
+ __all__ = [
14
+ "CoreEntity",
15
+ "Entity",
16
+ "GroupEntity",
17
+ "Role",
18
+ "SingleEntity",
19
+ "build_entity",
20
+ "check_role_validity",
21
+ "find_role",
22
+ "types",
23
+ ]