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,121 @@
1
+ import copy
2
+ import os
3
+
4
+ from openfisca_core import commons, parameters, tools
5
+ from openfisca_core.errors import ParameterParsingError
6
+ from openfisca_core.parameters import AtInstantLike, config, helpers
7
+ from openfisca_core.taxscales import (
8
+ LinearAverageRateTaxScale,
9
+ MarginalAmountTaxScale,
10
+ MarginalRateTaxScale,
11
+ SingleAmountTaxScale,
12
+ )
13
+
14
+
15
+ class ParameterScale(AtInstantLike):
16
+ """A parameter scale (for instance a marginal scale)."""
17
+
18
+ # 'unit' and 'reference' are only listed here for backward compatibility
19
+ _allowed_keys = config.COMMON_KEYS.union({"brackets"})
20
+
21
+ def __init__(self, name, data, file_path) -> None:
22
+ """:param name: name of the scale, eg "taxes.some_scale"
23
+ :param data: Data loaded from a YAML file. In case of a reform, the data can also be created dynamically.
24
+ :param file_path: File the parameter was loaded from.
25
+ """
26
+ self.name: str = name
27
+ self.file_path: str = file_path
28
+ helpers._validate_parameter(
29
+ self,
30
+ data,
31
+ data_type=dict,
32
+ allowed_keys=self._allowed_keys,
33
+ )
34
+ self.description: str = data.get("description")
35
+ self.metadata: dict = {}
36
+ helpers._set_backward_compatibility_metadata(self, data)
37
+ self.metadata.update(data.get("metadata", {}))
38
+
39
+ if not isinstance(data.get("brackets", []), list):
40
+ msg = f"Property 'brackets' of scale '{self.name}' must be of type array."
41
+ raise ParameterParsingError(
42
+ msg,
43
+ self.file_path,
44
+ )
45
+
46
+ brackets = []
47
+ for i, bracket_data in enumerate(data.get("brackets", [])):
48
+ bracket_name = helpers._compose_name(name, item_name=i)
49
+ bracket = parameters.ParameterScaleBracket(
50
+ name=bracket_name,
51
+ data=bracket_data,
52
+ file_path=file_path,
53
+ )
54
+ brackets.append(bracket)
55
+ self.brackets: list[parameters.ParameterScaleBracket] = brackets
56
+
57
+ def __getitem__(self, key):
58
+ if isinstance(key, int) and key < len(self.brackets):
59
+ return self.brackets[key]
60
+ raise KeyError(key)
61
+
62
+ def __repr__(self) -> str:
63
+ return os.linesep.join(
64
+ ["brackets:"]
65
+ + [
66
+ tools.indent("-" + tools.indent(repr(bracket))[1:])
67
+ for bracket in self.brackets
68
+ ],
69
+ )
70
+
71
+ def get_descendants(self):
72
+ return iter(())
73
+
74
+ def clone(self):
75
+ clone = commons.empty_clone(self)
76
+ clone.__dict__ = self.__dict__.copy()
77
+
78
+ clone.brackets = [bracket.clone() for bracket in self.brackets]
79
+ clone.metadata = copy.deepcopy(self.metadata)
80
+
81
+ return clone
82
+
83
+ def _get_at_instant(self, instant):
84
+ brackets = [bracket.get_at_instant(instant) for bracket in self.brackets]
85
+
86
+ if self.metadata.get("type") == "single_amount":
87
+ scale = SingleAmountTaxScale()
88
+ for bracket in brackets:
89
+ if "amount" in bracket._children and "threshold" in bracket._children:
90
+ amount = bracket.amount
91
+ threshold = bracket.threshold
92
+ scale.add_bracket(threshold, amount)
93
+ return scale
94
+ if any("amount" in bracket._children for bracket in brackets):
95
+ scale = MarginalAmountTaxScale()
96
+ for bracket in brackets:
97
+ if "amount" in bracket._children and "threshold" in bracket._children:
98
+ amount = bracket.amount
99
+ threshold = bracket.threshold
100
+ scale.add_bracket(threshold, amount)
101
+ return scale
102
+ if any("average_rate" in bracket._children for bracket in brackets):
103
+ scale = LinearAverageRateTaxScale()
104
+
105
+ for bracket in brackets:
106
+ if (
107
+ "average_rate" in bracket._children
108
+ and "threshold" in bracket._children
109
+ ):
110
+ average_rate = bracket.average_rate
111
+ threshold = bracket.threshold
112
+ scale.add_bracket(threshold, average_rate)
113
+ return scale
114
+ scale = MarginalRateTaxScale()
115
+
116
+ for bracket in brackets:
117
+ if "rate" in bracket._children and "threshold" in bracket._children:
118
+ rate = bracket.rate
119
+ threshold = bracket.threshold
120
+ scale.add_bracket(threshold, rate)
121
+ return scale
@@ -0,0 +1,7 @@
1
+ from openfisca_core.parameters import ParameterNode
2
+
3
+
4
+ class ParameterScaleBracket(ParameterNode):
5
+ """A parameter scale bracket."""
6
+
7
+ _allowed_keys = {"amount", "threshold", "rate", "average_rate"}
@@ -0,0 +1,5 @@
1
+ from .parameter import Parameter
2
+
3
+
4
+ class ValuesHistory(Parameter):
5
+ """Only for backward compatibility."""
@@ -0,0 +1,179 @@
1
+ from typing import NoReturn
2
+
3
+ import numpy
4
+
5
+ from openfisca_core import parameters
6
+ from openfisca_core.indexed_enums import Enum, EnumArray
7
+
8
+
9
+ class VectorialParameterNodeAtInstant:
10
+ def __init__(self, vector, name, instant_str) -> None:
11
+ self.vector = vector
12
+ self._name = name
13
+ self._instant_str = instant_str
14
+
15
+ @staticmethod
16
+ def _get_appropriate_subnode_function(k):
17
+ if isinstance(k, str):
18
+ return VectorialParameterNodeAtInstant._get_appropriate_subnode_key_string
19
+ if isinstance(k, Enum):
20
+ return VectorialParameterNodeAtInstant._get_appropriate_subnode_key_enum
21
+ if isinstance(k, numpy.integer):
22
+ return VectorialParameterNodeAtInstant._get_appropriate_subnode_key_integer
23
+ return VectorialParameterNodeAtInstant._get_appropriate_subnode_key_date
24
+
25
+ @staticmethod
26
+ def _get_appropriate_subnode_key_string(node, k):
27
+ return k
28
+
29
+ @staticmethod
30
+ def _get_appropriate_subnode_key_enum(node, k):
31
+ return k.name
32
+
33
+ @staticmethod
34
+ def _get_appropriate_subnode_key_integer(node, k):
35
+ return str(k)
36
+
37
+ @staticmethod
38
+ def _get_appropriate_subnode_key_date(node, k):
39
+ subnodes_name = list(node._children.keys())
40
+ names = [name for name in subnodes_name if not name.startswith("before")]
41
+ points_in_time = [
42
+ numpy.datetime64("-".join(name[len("after_") :].split("_")))
43
+ for name in names
44
+ ]
45
+ index = sum([name <= k for name in points_in_time])
46
+ return subnodes_name[index]
47
+
48
+ def _get_appropriate_keys(key):
49
+ if isinstance(key, EnumArray):
50
+ enum = key.possible_values
51
+ result = numpy.select(
52
+ [key == item.index for item in enum],
53
+ [item.name for item in enum],
54
+ default="",
55
+ )
56
+ if numpy.any(result == ""):
57
+ invalid_indices = key[result == ""]
58
+ enum_name = enum.__name__ if enum is not None else "Unknown"
59
+ msg = f"Invalid enum index in EnumArray for {enum_name}: {invalid_indices}"
60
+ raise ValueError(msg)
61
+ return result
62
+ if key.dtype == object and issubclass(type(key[0]), Enum):
63
+ return key
64
+ return key
65
+
66
+ @staticmethod
67
+ def build_from_node_name(node, key):
68
+ VectorialParameterNodeAtInstant.check_node_vectorisable(node)
69
+ keys = VectorialParameterNodeAtInstant._get_appropriate_keys(key)
70
+ get_subnode_key = (
71
+ VectorialParameterNodeAtInstant._get_appropriate_subnode_function(keys[0])
72
+ )
73
+ nodes = [node[get_subnode_key(node, key)] for key in keys]
74
+ return VectorialParameterNodeAtInstant.build_from_nodes(node, nodes)
75
+
76
+ @staticmethod
77
+ def build_from_nodes(node, nodes):
78
+ if isinstance(nodes[0], parameters.ParameterNodeAtInstant):
79
+ return VectorialParameterNodeAtInstant(nodes, node._name, node._instant_str)
80
+ return numpy.array(nodes)
81
+
82
+ def __getattr__(self, attribute):
83
+ return self[attribute]
84
+
85
+ def __getitem__(self, key):
86
+ if isinstance(key, str):
87
+ keys = [key for v in self.vector]
88
+ elif isinstance(key[0], Enum):
89
+ keys = [k.name for k in key]
90
+ elif isinstance(key, EnumArray):
91
+ keys = [key.possible_values.names[v] for v in key]
92
+ else:
93
+ keys = key
94
+
95
+ nodes = [v[a] for (v, a) in zip(self.vector, keys)]
96
+ return VectorialParameterNodeAtInstant.build_from_nodes(self, nodes)
97
+
98
+ @staticmethod
99
+ def check_node_vectorisable(node) -> None:
100
+ """Check that a node can be casted to a vectorial node, in order to be able to use fancy indexing."""
101
+ MESSAGE_PART_1 = "Cannot use fancy indexing on parameter node '{}', as"
102
+ MESSAGE_PART_3 = (
103
+ "To use fancy indexing on parameter node, its children must be homogeneous."
104
+ )
105
+ MESSAGE_PART_4 = "See more at <https://openfisca.org/doc/coding-the-legislation/legislation_parameters#computing-a-parameter-that-depends-on-a-variable-fancy-indexing>."
106
+
107
+ def raise_key_inhomogeneity_error(
108
+ node_with_key, node_without_key, missing_key
109
+ ) -> NoReturn:
110
+ message = f"{MESSAGE_PART_1} '{{}}' exists, but '{{}}' doesn't. {MESSAGE_PART_3} {MESSAGE_PART_4}".format(
111
+ node._name,
112
+ f"{node_with_key}.{missing_key}",
113
+ f"{node_without_key}.{missing_key}",
114
+ )
115
+
116
+ raise ValueError(message)
117
+
118
+ def raise_type_inhomogeneity_error(node_name, non_node_name) -> NoReturn:
119
+ message = f"{MESSAGE_PART_1} '{{}}' is a node, but '{{}}' is not. {MESSAGE_PART_3} {MESSAGE_PART_4}".format(
120
+ node._name,
121
+ node_name,
122
+ non_node_name,
123
+ )
124
+
125
+ raise ValueError(message)
126
+
127
+ def raise_not_implemented(node_name, node_type) -> NoReturn:
128
+ message = f"{MESSAGE_PART_1} '{{}}' is a '{{}}', and fancy indexing has not been implemented yet on this kind of parameters. {MESSAGE_PART_4}".format(
129
+ node._name,
130
+ node_name,
131
+ node_type,
132
+ )
133
+ raise NotImplementedError(message)
134
+
135
+ def extract_named_children(node):
136
+ return {
137
+ f"{node._name}.{key}": value for key, value in node._children.items()
138
+ }
139
+
140
+ def check_nodes_homogeneous(named_nodes) -> None:
141
+ """Check than several nodes (or parameters, or baremes) have the same structure."""
142
+ names = list(named_nodes.keys())
143
+ nodes = list(named_nodes.values())
144
+ first_node = nodes[0]
145
+ first_name = names[0]
146
+ if isinstance(first_node, parameters.ParameterNodeAtInstant):
147
+ children = extract_named_children(first_node)
148
+ for subnode, name in list(zip(nodes, names))[1:]:
149
+ if not isinstance(subnode, parameters.ParameterNodeAtInstant):
150
+ raise_type_inhomogeneity_error(first_name, name)
151
+ first_node_keys = first_node._children.keys()
152
+ node_keys = subnode._children.keys()
153
+ if first_node_keys != node_keys:
154
+ missing_keys = set(first_node_keys).difference(node_keys)
155
+ if missing_keys: # If the first_node has a key that node hasn't
156
+ raise_key_inhomogeneity_error(
157
+ first_name,
158
+ name,
159
+ missing_keys.pop(),
160
+ )
161
+ else: # If If the node has a key that first_node doesn't have
162
+ missing_key = (
163
+ set(node_keys).difference(first_node_keys).pop()
164
+ )
165
+ raise_key_inhomogeneity_error(name, first_name, missing_key)
166
+ children.update(extract_named_children(subnode))
167
+ check_nodes_homogeneous(children)
168
+ elif isinstance(first_node, (float, int, str)):
169
+ for subnode, name in list(zip(nodes, names))[1:]:
170
+ if isinstance(subnode, (int, float, str)):
171
+ pass
172
+ elif isinstance(subnode, parameters.ParameterNodeAtInstant):
173
+ raise_type_inhomogeneity_error(name, first_name)
174
+ else:
175
+ raise_not_implemented(name, type(subnode).__name__)
176
+ else:
177
+ raise_not_implemented(first_name, type(first_node).__name__)
178
+
179
+ check_nodes_homogeneous(extract_named_children(node))
@@ -0,0 +1,77 @@
1
+ # Transitional imports to ensure non-breaking changes.
2
+ # Could be deprecated in the next major release.
3
+ #
4
+ # How imports are being used today:
5
+ #
6
+ # >>> from openfisca_core.module import symbol
7
+ #
8
+ # The previous example provokes cyclic dependency problems
9
+ # that prevent us from modularizing the different components
10
+ # of the library so to make them easier to test and to maintain.
11
+ #
12
+ # How could them be used after the next major release:
13
+ #
14
+ # >>> from openfisca_core import module
15
+ # >>> module.symbol()
16
+ #
17
+ # And for classes:
18
+ #
19
+ # >>> from openfisca_core.module import Symbol
20
+ # >>> Symbol()
21
+ #
22
+ # See: https://www.python.org/dev/peps/pep-0008/#imports
23
+
24
+ from ._errors import InstantError, ParserError, PeriodError
25
+ from .config import (
26
+ INSTANT_PATTERN,
27
+ date_by_instant_cache,
28
+ str_by_instant_cache,
29
+ year_or_month_or_day_re,
30
+ )
31
+ from .date_unit import DateUnit
32
+ from .helpers import (
33
+ instant,
34
+ instant_date,
35
+ key_period_size,
36
+ period,
37
+ unit_weight,
38
+ unit_weights,
39
+ )
40
+ from .instant_ import Instant
41
+ from .period_ import Period
42
+
43
+ WEEKDAY = DateUnit.WEEKDAY
44
+ WEEK = DateUnit.WEEK
45
+ DAY = DateUnit.DAY
46
+ MONTH = DateUnit.MONTH
47
+ YEAR = DateUnit.YEAR
48
+ ETERNITY = DateUnit.ETERNITY
49
+ ISOFORMAT = DateUnit.isoformat
50
+ ISOCALENDAR = DateUnit.isocalendar
51
+
52
+ __all__ = [
53
+ "DAY",
54
+ "ETERNITY",
55
+ "INSTANT_PATTERN",
56
+ "ISOCALENDAR",
57
+ "ISOFORMAT",
58
+ "MONTH",
59
+ "WEEK",
60
+ "WEEKDAY",
61
+ "YEAR",
62
+ "DateUnit",
63
+ "Instant",
64
+ "InstantError",
65
+ "ParserError",
66
+ "Period",
67
+ "PeriodError",
68
+ "date_by_instant_cache",
69
+ "instant",
70
+ "instant_date",
71
+ "key_period_size",
72
+ "period",
73
+ "str_by_instant_cache",
74
+ "unit_weight",
75
+ "unit_weights",
76
+ "year_or_month_or_day_re",
77
+ ]
@@ -0,0 +1,28 @@
1
+ from pendulum.parsing.exceptions import ParserError
2
+
3
+
4
+ class InstantError(ValueError):
5
+ """Raised when an invalid instant-like is provided."""
6
+
7
+ def __init__(self, value: str) -> None:
8
+ msg = (
9
+ f"'{value}' is not a valid instant string. Instants are described "
10
+ "using either the 'YYYY-MM-DD' format, for instance '2015-06-15', "
11
+ "or the 'YYYY-Www-D' format, for instance '2015-W24-1'."
12
+ )
13
+ super().__init__(msg)
14
+
15
+
16
+ class PeriodError(ValueError):
17
+ """Raised when an invalid period-like is provided."""
18
+
19
+ def __init__(self, value: str) -> None:
20
+ msg = (
21
+ "Expected a period (eg. '2017', 'month:2017-01', 'week:2017-W01-1:3', "
22
+ f"...); got: '{value}'. Learn more about legal period formats in "
23
+ "OpenFisca: <https://openfisca.org/doc/coding-the-legislation/35_periods.html#periods-in-simulations>."
24
+ )
25
+ super().__init__(msg)
26
+
27
+
28
+ __all__ = ["InstantError", "ParserError", "PeriodError"]
@@ -0,0 +1,122 @@
1
+ """To parse periods and instants from strings."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import datetime
6
+
7
+ import pendulum
8
+
9
+ from openfisca_core import types as t
10
+
11
+ from ._errors import InstantError, ParserError, PeriodError
12
+ from .date_unit import DateUnit
13
+ from .instant_ import Instant
14
+ from .period_ import Period
15
+
16
+
17
+ def parse_instant(value: str) -> t.Instant:
18
+ """Parse a string into an instant.
19
+
20
+ Args:
21
+ value (str): The string to parse.
22
+
23
+ Returns:
24
+ An InstantStr.
25
+
26
+ Raises:
27
+ InstantError: When the string is not a valid ISO Calendar/Format.
28
+ ParserError: When the string couldn't be parsed.
29
+
30
+ Examples:
31
+ >>> parse_instant("2022")
32
+ Instant((2022, 1, 1))
33
+
34
+ >>> parse_instant("2022-02")
35
+ Instant((2022, 2, 1))
36
+
37
+ >>> parse_instant("2022-W02-7")
38
+ Instant((2022, 1, 16))
39
+
40
+ >>> parse_instant("2022-W013")
41
+ Traceback (most recent call last):
42
+ openfisca_core.periods._errors.InstantError: '2022-W013' is not a va...
43
+
44
+ >>> parse_instant("2022-02-29")
45
+ Traceback (most recent call last):
46
+ pendulum.parsing.exceptions.ParserError: Unable to parse string [202...
47
+
48
+ """
49
+
50
+ if not isinstance(value, t.InstantStr):
51
+ raise InstantError(str(value))
52
+
53
+ date = pendulum.parse(value, exact=True)
54
+
55
+ if not isinstance(date, datetime.date):
56
+ msg = f"Unable to parse string [{value}]"
57
+ raise ParserError(msg)
58
+
59
+ return Instant((date.year, date.month, date.day))
60
+
61
+
62
+ def parse_period(value: str) -> t.Period:
63
+ """Parses ISO format/calendar periods.
64
+
65
+ Such as "2012" or "2015-03".
66
+
67
+ Examples:
68
+ >>> parse_period("2022")
69
+ Period((<DateUnit.YEAR: 'year'>, Instant((2022, 1, 1)), 1))
70
+
71
+ >>> parse_period("2022-02")
72
+ Period((<DateUnit.MONTH: 'month'>, Instant((2022, 2, 1)), 1))
73
+
74
+ >>> parse_period("2022-W02-7")
75
+ Period((<DateUnit.WEEKDAY: 'weekday'>, Instant((2022, 1, 16)), 1))
76
+
77
+ """
78
+
79
+ try:
80
+ instant = parse_instant(value)
81
+
82
+ except InstantError as error:
83
+ raise PeriodError(value) from error
84
+
85
+ unit = parse_unit(value)
86
+
87
+ return Period((unit, instant, 1))
88
+
89
+
90
+ def parse_unit(value: str) -> t.DateUnit:
91
+ """Determine the date unit of a date string.
92
+
93
+ Args:
94
+ value (str): The date string to parse.
95
+
96
+ Returns:
97
+ A DateUnit.
98
+
99
+ Raises:
100
+ InstantError: when no DateUnit can be determined.
101
+
102
+ Examples:
103
+ >>> parse_unit("2022")
104
+ <DateUnit.YEAR: 'year'>
105
+
106
+ >>> parse_unit("2022-W03-1")
107
+ <DateUnit.WEEKDAY: 'weekday'>
108
+
109
+ """
110
+
111
+ if not isinstance(value, t.InstantStr):
112
+ raise InstantError(str(value))
113
+
114
+ length = len(value.split("-"))
115
+
116
+ if isinstance(value, t.ISOCalendarStr):
117
+ return DateUnit.isocalendar[-length]
118
+
119
+ return DateUnit.isoformat[-length]
120
+
121
+
122
+ __all__ = ["parse_instant", "parse_period", "parse_unit"]
@@ -0,0 +1,20 @@
1
+ import re
2
+
3
+ import pendulum
4
+
5
+ from openfisca_core import types as t
6
+
7
+ # Matches "2015", "2015-01", "2015-01-01"
8
+ # Does not match "2015-13", "2015-12-32"
9
+ INSTANT_PATTERN = re.compile(
10
+ r"^\d{4}(-(0[1-9]|1[012]))?(-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01]))?$",
11
+ )
12
+
13
+ date_by_instant_cache: dict[t.Instant, pendulum.Date] = {}
14
+ str_by_instant_cache: dict[t.Instant, t.InstantStr] = {}
15
+ year_or_month_or_day_re = re.compile(
16
+ r"(18|19|20)\d{2}(-(0?[1-9]|1[0-2])(-([0-2]?\d|3[0-1]))?)?$",
17
+ )
18
+
19
+
20
+ __all__ = ["INSTANT_PATTERN", "date_by_instant_cache", "str_by_instant_cache"]
@@ -0,0 +1,110 @@
1
+ from __future__ import annotations
2
+
3
+ from enum import EnumMeta
4
+
5
+ from strenum import StrEnum
6
+
7
+ from openfisca_core import types as t
8
+
9
+
10
+ class DateUnitMeta(EnumMeta):
11
+ @property
12
+ def isoformat(self) -> tuple[t.DateUnit, ...]:
13
+ """Creates a :obj:`tuple` of ``key`` with isoformat items.
14
+
15
+ Returns:
16
+ tuple(str): A :obj:`tuple` containing the ``keys``.
17
+
18
+ Examples:
19
+ >>> DateUnit.isoformat
20
+ (<DateUnit.DAY: 'day'>, <DateUnit.MONTH: 'month'>, <DateUnit.YE...)
21
+
22
+ >>> DateUnit.DAY in DateUnit.isoformat
23
+ True
24
+
25
+ >>> DateUnit.WEEK in DateUnit.isoformat
26
+ False
27
+
28
+ """
29
+ return DateUnit.DAY, DateUnit.MONTH, DateUnit.YEAR
30
+
31
+ @property
32
+ def isocalendar(self) -> tuple[t.DateUnit, ...]:
33
+ """Creates a :obj:`tuple` of ``key`` with isocalendar items.
34
+
35
+ Returns:
36
+ tuple(str): A :obj:`tuple` containing the ``keys``.
37
+
38
+ Examples:
39
+ >>> DateUnit.isocalendar
40
+ (<DateUnit.WEEKDAY: 'weekday'>, <DateUnit.WEEK: 'week'>, <DateU...)
41
+
42
+ >>> DateUnit.WEEK in DateUnit.isocalendar
43
+ True
44
+
45
+ >>> "day" in DateUnit.isocalendar
46
+ False
47
+
48
+ """
49
+ return DateUnit.WEEKDAY, DateUnit.WEEK, DateUnit.YEAR
50
+
51
+
52
+ class DateUnit(StrEnum, metaclass=DateUnitMeta):
53
+ """The date units of a rule system.
54
+
55
+ Examples:
56
+ >>> repr(DateUnit)
57
+ "<enum 'DateUnit'>"
58
+
59
+ >>> repr(DateUnit.DAY)
60
+ "<DateUnit.DAY: 'day'>"
61
+
62
+ >>> str(DateUnit.DAY)
63
+ 'day'
64
+
65
+ >>> dict([(DateUnit.DAY, DateUnit.DAY.value)])
66
+ {<DateUnit.DAY: 'day'>: 'day'}
67
+
68
+ >>> list(DateUnit)
69
+ [<DateUnit.WEEKDAY: 'weekday'>, <DateUnit.WEEK: 'week'>, <DateUnit.D...
70
+
71
+ >>> len(DateUnit)
72
+ 6
73
+
74
+ >>> DateUnit["DAY"]
75
+ <DateUnit.DAY: 'day'>
76
+
77
+ >>> DateUnit(DateUnit.DAY)
78
+ <DateUnit.DAY: 'day'>
79
+
80
+ >>> DateUnit.DAY in DateUnit
81
+ True
82
+
83
+ >>> "day" in list(DateUnit)
84
+ True
85
+
86
+ >>> DateUnit.DAY == "day"
87
+ True
88
+
89
+ >>> DateUnit.DAY.name
90
+ 'DAY'
91
+
92
+ >>> DateUnit.DAY.value
93
+ 'day'
94
+
95
+ """
96
+
97
+ def __contains__(self, other: object) -> bool:
98
+ if isinstance(other, str):
99
+ return super().__contains__(other)
100
+ return NotImplemented
101
+
102
+ WEEKDAY = "weekday"
103
+ WEEK = "week"
104
+ DAY = "day"
105
+ MONTH = "month"
106
+ YEAR = "year"
107
+ ETERNITY = "eternity"
108
+
109
+
110
+ __all__ = ["DateUnit"]