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,43 @@
1
+ import pytest
2
+
3
+ from openfisca_core import periods
4
+ from openfisca_core.variables import Variable
5
+
6
+
7
+ @pytest.fixture
8
+ def variable(persons):
9
+ class TestVariable(Variable):
10
+ value_type = float
11
+ entity = persons
12
+
13
+ return TestVariable
14
+
15
+
16
+ def test_weekday_variable(variable) -> None:
17
+ variable.definition_period = periods.WEEKDAY
18
+ assert variable()
19
+
20
+
21
+ def test_week_variable(variable) -> None:
22
+ variable.definition_period = periods.WEEK
23
+ assert variable()
24
+
25
+
26
+ def test_day_variable(variable) -> None:
27
+ variable.definition_period = periods.DAY
28
+ assert variable()
29
+
30
+
31
+ def test_month_variable(variable) -> None:
32
+ variable.definition_period = periods.MONTH
33
+ assert variable()
34
+
35
+
36
+ def test_year_variable(variable) -> None:
37
+ variable.definition_period = periods.YEAR
38
+ assert variable()
39
+
40
+
41
+ def test_eternity_variable(variable) -> None:
42
+ variable.definition_period = periods.ETERNITY
43
+ assert variable()
@@ -0,0 +1,474 @@
1
+ from __future__ import annotations
2
+
3
+ import datetime
4
+ import re
5
+ import textwrap
6
+ from typing import NoReturn
7
+
8
+ import numpy
9
+ import sortedcontainers
10
+
11
+ from openfisca_core import commons, periods
12
+ from openfisca_core import types as t
13
+ from openfisca_core.entities import Entity, GroupEntity
14
+ from openfisca_core.indexed_enums import Enum, EnumArray
15
+ from openfisca_core.periods import DateUnit, Period
16
+
17
+ from . import config, helpers
18
+
19
+
20
+ class Variable:
21
+ """A `variable <https://openfisca.org/doc/key-concepts/variables.html>`_ of the legislation.
22
+
23
+ Main attributes:
24
+
25
+ .. attribute:: name
26
+
27
+ Name of the variable
28
+
29
+ .. attribute:: value_type
30
+
31
+ The value type of the variable. Possible value types in OpenFisca are ``int`` ``float`` ``bool`` ``str`` ``date`` and ``Enum``.
32
+
33
+ .. attribute:: entity
34
+
35
+ `Entity <https://openfisca.org/doc/key-concepts/person,_entities,_role.html>`_ the variable is defined for. For instance : ``Person``, ``Household``.
36
+
37
+ .. attribute:: definition_period
38
+
39
+ `Period <https://openfisca.org/doc/coding-the-legislation/35_periods.html>`_ the variable is defined for. Possible value: ``DateUnit.DAY``, ``DateUnit.MONTH``, ``DateUnit.YEAR``, ``DateUnit.ETERNITY``.
40
+
41
+ .. attribute:: formulas
42
+
43
+ Formulas used to calculate the variable
44
+
45
+ .. attribute:: label
46
+
47
+ Description of the variable
48
+
49
+ .. attribute:: reference
50
+
51
+ Legislative reference describing the variable.
52
+
53
+ .. attribute:: default_value
54
+
55
+ `Default value <https://openfisca.org/doc/key-concepts/variables.html#default-values>`_ of the variable.
56
+
57
+ Secondary attributes:
58
+
59
+ .. attribute:: baseline_variable
60
+
61
+ If the variable has been introduced in a `reform <https://openfisca.org/doc/key-concepts/reforms.html>`_ to replace another variable, baseline_variable is the replaced variable.
62
+
63
+ .. attribute:: dtype
64
+
65
+ Numpy `dtype <https://docs.scipy.org/doc/numpy-1.13.0/reference/generated/numpy.dtype.html>`_ used under the hood for the variable.
66
+
67
+ .. attribute:: end
68
+
69
+ `Date <https://openfisca.org/doc/coding-the-legislation/40_legislation_evolutions.html#variable-end>`_ when the variable disappears from the legislation.
70
+
71
+ .. attribute:: is_neutralized
72
+
73
+ True if the variable is neutralized. Neutralized variables never use their formula, and only return their default values when calculated.
74
+
75
+ .. attribute:: json_type
76
+
77
+ JSON type corresponding to the variable.
78
+
79
+ .. attribute:: max_length
80
+
81
+ If the value type of the variable is ``str``, max length of the string allowed. ``None`` if there is no limit.
82
+
83
+ .. attribute:: possible_values
84
+
85
+ If the value type of the variable is ``Enum``, contains the values the variable can take.
86
+
87
+ .. attribute:: set_input
88
+
89
+ Function used to automatically process variable inputs defined for periods not matching the definition_period of the variable. See more on the `documentation <https://openfisca.org/doc/coding-the-legislation/35_periods.html#set-input-automatically-process-variable-inputs-defined-for-periods-not-matching-the-definition-period>`_. Possible values are ``set_input_dispatch_by_period``, ``set_input_divide_by_period``, or nothing.
90
+
91
+ .. attribute:: unit
92
+
93
+ Free text field describing the unit of the variable. Only used as metadata.
94
+
95
+ .. attribute:: documentation
96
+
97
+ Free multilines text field describing the variable context and usage.
98
+ """
99
+
100
+ __name__: str
101
+
102
+ def __init__(self, baseline_variable=None) -> None:
103
+ self.name = self.__class__.__name__
104
+ attr = {
105
+ name: value
106
+ for name, value in self.__class__.__dict__.items()
107
+ if not name.startswith("__")
108
+ }
109
+ self.baseline_variable = baseline_variable
110
+ self.value_type = self.set(
111
+ attr,
112
+ "value_type",
113
+ required=True,
114
+ allowed_values=config.VALUE_TYPES.keys(),
115
+ )
116
+ self.dtype = config.VALUE_TYPES[self.value_type]["dtype"]
117
+ self.json_type = config.VALUE_TYPES[self.value_type]["json_type"]
118
+ if self.value_type == Enum:
119
+ self.possible_values = self.set(
120
+ attr,
121
+ "possible_values",
122
+ required=True,
123
+ setter=self.set_possible_values,
124
+ )
125
+ if self.value_type == str:
126
+ self.max_length = self.set(attr, "max_length", allowed_type=int)
127
+ if self.max_length:
128
+ self.dtype = f"|S{self.max_length}"
129
+ if self.value_type == Enum:
130
+ self.default_value = self.set(
131
+ attr,
132
+ "default_value",
133
+ allowed_type=self.possible_values,
134
+ required=True,
135
+ )
136
+ else:
137
+ self.default_value = self.set(
138
+ attr,
139
+ "default_value",
140
+ allowed_type=self.value_type,
141
+ default=config.VALUE_TYPES[self.value_type].get("default"),
142
+ )
143
+ self.entity = self.set(attr, "entity", required=True, setter=self.set_entity)
144
+ self.definition_period = self.set(
145
+ attr,
146
+ "definition_period",
147
+ required=True,
148
+ allowed_values=DateUnit,
149
+ )
150
+ self.label = self.set(attr, "label", allowed_type=str, setter=self.set_label)
151
+ self.end = self.set(attr, "end", allowed_type=str, setter=self.set_end)
152
+ self.reference = self.set(attr, "reference", setter=self.set_reference)
153
+ self.cerfa_field = self.set(attr, "cerfa_field", allowed_type=(str, dict))
154
+ self.unit = self.set(attr, "unit", allowed_type=str)
155
+ self.documentation = self.set(
156
+ attr,
157
+ "documentation",
158
+ allowed_type=str,
159
+ setter=self.set_documentation,
160
+ )
161
+ self.set_input = self.set_set_input(attr.pop("set_input", None))
162
+ self.calculate_output = self.set_calculate_output(
163
+ attr.pop("calculate_output", None),
164
+ )
165
+ self.is_period_size_independent = self.set(
166
+ attr,
167
+ "is_period_size_independent",
168
+ allowed_type=bool,
169
+ default=config.VALUE_TYPES[self.value_type]["is_period_size_independent"],
170
+ )
171
+
172
+ self.introspection_data = self.set(
173
+ attr,
174
+ "introspection_data",
175
+ )
176
+
177
+ formulas_attr, unexpected_attrs = helpers._partition(
178
+ attr,
179
+ lambda name, value: name.startswith(config.FORMULA_NAME_PREFIX),
180
+ )
181
+ self.formulas = self.set_formulas(formulas_attr)
182
+
183
+ if unexpected_attrs:
184
+ msg = 'Unexpected attributes in definition of variable "{}": {!r}'.format(
185
+ self.name,
186
+ ", ".join(sorted(unexpected_attrs.keys())),
187
+ )
188
+ raise ValueError(
189
+ msg,
190
+ )
191
+
192
+ self.is_neutralized = False
193
+
194
+ # ----- Setters used to build the variable ----- #
195
+
196
+ def set(
197
+ self,
198
+ attributes,
199
+ attribute_name,
200
+ required=False,
201
+ allowed_values=None,
202
+ allowed_type=None,
203
+ setter=None,
204
+ default=None,
205
+ ):
206
+ value = attributes.pop(attribute_name, None)
207
+ if value is None and self.baseline_variable:
208
+ return getattr(self.baseline_variable, attribute_name)
209
+ if required and value is None:
210
+ msg = f"Missing attribute '{attribute_name}' in definition of variable '{self.name}'."
211
+ raise ValueError(
212
+ msg,
213
+ )
214
+ if allowed_values is not None and value not in allowed_values:
215
+ msg = f"Invalid value '{value}' for attribute '{attribute_name}' in variable '{self.name}'. Allowed values are '{allowed_values}'."
216
+ raise ValueError(
217
+ msg,
218
+ )
219
+ if (
220
+ allowed_type is not None
221
+ and value is not None
222
+ and not isinstance(value, allowed_type)
223
+ ):
224
+ if allowed_type == float and isinstance(value, int):
225
+ value = float(value)
226
+ else:
227
+ msg = f"Invalid value '{value}' for attribute '{attribute_name}' in variable '{self.name}'. Must be of type '{allowed_type}'."
228
+ raise ValueError(
229
+ msg,
230
+ )
231
+ if setter is not None:
232
+ value = setter(value)
233
+ if value is None and default is not None:
234
+ return default
235
+ return value
236
+
237
+ def set_entity(self, entity):
238
+ if not isinstance(entity, (Entity, GroupEntity)):
239
+ msg = (
240
+ f"Invalid value '{entity}' for attribute 'entity' in variable "
241
+ f"'{self.name}'. Must be an instance of Entity or GroupEntity."
242
+ )
243
+ raise ValueError(
244
+ msg,
245
+ )
246
+ return entity
247
+
248
+ def set_possible_values(self, possible_values):
249
+ if not issubclass(possible_values, Enum):
250
+ msg = f"Invalid value '{possible_values}' for attribute 'possible_values' in variable '{self.name}'. Must be a subclass of {Enum}."
251
+ raise ValueError(
252
+ msg,
253
+ )
254
+ return possible_values
255
+
256
+ def set_label(self, label):
257
+ if label:
258
+ return label
259
+ return None
260
+
261
+ def set_end(self, end):
262
+ if end:
263
+ try:
264
+ return datetime.datetime.strptime(end, "%Y-%m-%d").date()
265
+ except ValueError:
266
+ msg = f"Incorrect 'end' attribute format in '{self.name}'. 'YYYY-MM-DD' expected where YYYY, MM and DD are year, month and day. Found: {end}"
267
+ raise ValueError(
268
+ msg,
269
+ )
270
+ return None
271
+
272
+ def set_reference(self, reference):
273
+ if reference:
274
+ if isinstance(reference, str):
275
+ reference = [reference]
276
+ elif isinstance(reference, list):
277
+ pass
278
+ elif isinstance(reference, tuple):
279
+ reference = list(reference)
280
+ else:
281
+ msg = f"The reference of the variable {self.name} is a {type(reference)} instead of a String or a List of Strings."
282
+ raise TypeError(
283
+ msg,
284
+ )
285
+
286
+ for element in reference:
287
+ if not isinstance(element, str):
288
+ msg = f"The reference of the variable {self.name} is a {type(reference)} instead of a String or a List of Strings."
289
+ raise TypeError(
290
+ msg,
291
+ )
292
+
293
+ return reference
294
+
295
+ def set_documentation(self, documentation):
296
+ if documentation:
297
+ return textwrap.dedent(documentation)
298
+ return None
299
+
300
+ def set_set_input(self, set_input):
301
+ if not set_input and self.baseline_variable:
302
+ return self.baseline_variable.set_input
303
+ return set_input
304
+
305
+ def set_calculate_output(self, calculate_output):
306
+ if not calculate_output and self.baseline_variable:
307
+ return self.baseline_variable.calculate_output
308
+ return calculate_output
309
+
310
+ def set_formulas(self, formulas_attr):
311
+ formulas = sortedcontainers.sorteddict.SortedDict()
312
+ for formula_name, formula in formulas_attr.items():
313
+ starting_date = self.parse_formula_name(formula_name)
314
+
315
+ if self.end is not None and starting_date > self.end:
316
+ msg = f'You declared that "{self.name}" ends on "{self.end}", but you wrote a formula to calculate it from "{starting_date}" ({formula_name}). The "end" attribute of a variable must be posterior to the start dates of all its formulas.'
317
+ raise ValueError(
318
+ msg,
319
+ )
320
+
321
+ formulas[str(starting_date)] = formula
322
+
323
+ # If the variable is reforming a baseline variable, keep the formulas from the latter when they are not overridden by new formulas.
324
+ if self.baseline_variable is not None:
325
+ first_reform_formula_date = formulas.peekitem(0)[0] if formulas else None
326
+ formulas.update(
327
+ {
328
+ baseline_start_date: baseline_formula
329
+ for baseline_start_date, baseline_formula in self.baseline_variable.formulas.items()
330
+ if first_reform_formula_date is None
331
+ or baseline_start_date < first_reform_formula_date
332
+ },
333
+ )
334
+
335
+ return formulas
336
+
337
+ def parse_formula_name(self, attribute_name):
338
+ """Returns the starting date of a formula based on its name.
339
+
340
+ Valid dated name formats are : 'formula', 'formula_YYYY', 'formula_YYYY_MM' and 'formula_YYYY_MM_DD' where YYYY, MM and DD are a year, month and day.
341
+
342
+ By convention, the starting date of:
343
+ - `formula` is `0001-01-01` (minimal date in Python)
344
+ - `formula_YYYY` is `YYYY-01-01`
345
+ - `formula_YYYY_MM` is `YYYY-MM-01`
346
+ """
347
+
348
+ def raise_error() -> NoReturn:
349
+ msg = f'Unrecognized formula name in variable "{self.name}". Expecting "formula_YYYY" or "formula_YYYY_MM" or "formula_YYYY_MM_DD where YYYY, MM and DD are year, month and day. Found: "{attribute_name}".'
350
+ raise ValueError(
351
+ msg,
352
+ )
353
+
354
+ if attribute_name == config.FORMULA_NAME_PREFIX:
355
+ return datetime.date.min
356
+
357
+ FORMULA_REGEX = r"formula_(\d{4})(?:_(\d{2}))?(?:_(\d{2}))?$" # YYYY or YYYY_MM or YYYY_MM_DD
358
+
359
+ match = re.match(FORMULA_REGEX, attribute_name)
360
+ if not match:
361
+ raise_error()
362
+ date_str = "-".join(
363
+ [match.group(1), match.group(2) or "01", match.group(3) or "01"],
364
+ )
365
+
366
+ try:
367
+ return datetime.datetime.strptime(date_str, "%Y-%m-%d").date()
368
+ except ValueError: # formula_2005_99_99 for instance
369
+ raise_error()
370
+
371
+ # ----- Methods ----- #
372
+
373
+ def is_input_variable(self):
374
+ """Returns True if the variable is an input variable."""
375
+ return len(self.formulas) == 0
376
+
377
+ @classmethod
378
+ def get_introspection_data(cls):
379
+ try:
380
+ return cls.introspection_data
381
+ except AttributeError:
382
+ return "", None, 0
383
+
384
+ def get_formula(
385
+ self,
386
+ period: None | t.Instant | t.Period | str | int = None,
387
+ ) -> None | t.Formula:
388
+ """Returns the formula to compute the variable at the given period.
389
+
390
+ If no period is given and the variable has several formulas, the method
391
+ returns the oldest formula.
392
+
393
+ Args:
394
+ period: The period to get the formula.
395
+
396
+ Returns:
397
+ Formula used to compute the variable.
398
+
399
+ """
400
+ instant: None | t.Instant
401
+
402
+ if not self.formulas:
403
+ return None
404
+
405
+ if period is None:
406
+ return self.formulas.peekitem(
407
+ index=0,
408
+ )[
409
+ 1
410
+ ] # peekitem gets the 1st key-value tuple (the oldest start_date and formula). Return the formula.
411
+
412
+ if isinstance(period, Period):
413
+ instant = period.start
414
+ else:
415
+ try:
416
+ instant = periods.period(period).start
417
+ except ValueError:
418
+ instant = periods.instant(period)
419
+
420
+ if instant is None:
421
+ return None
422
+
423
+ if self.end and instant.date > self.end:
424
+ return None
425
+
426
+ instant_str = str(instant)
427
+
428
+ for start_date in reversed(self.formulas):
429
+ if start_date <= instant_str:
430
+ return self.formulas[start_date]
431
+
432
+ return None
433
+
434
+ def clone(self):
435
+ return self.__class__()
436
+
437
+ def check_set_value(self, value):
438
+ if self.value_type == Enum and isinstance(value, str):
439
+ try:
440
+ value = self.possible_values[value].index
441
+ except KeyError:
442
+ possible_values = [item.name for item in self.possible_values]
443
+ msg = "'{}' is not a known value for '{}'. Possible values are ['{}'].".format(
444
+ value,
445
+ self.name,
446
+ "', '".join(possible_values),
447
+ )
448
+ raise ValueError(
449
+ msg,
450
+ )
451
+ if self.value_type in (float, int) and isinstance(value, str):
452
+ value = commons.eval_expression(value)
453
+
454
+ try:
455
+ value = numpy.array([value], dtype=self.dtype)[0]
456
+ except (TypeError, ValueError):
457
+ if self.value_type == datetime.date:
458
+ error_message = f"Can't deal with date: '{value}'."
459
+ else:
460
+ error_message = f"Can't deal with value: expected type {self.json_type}, received '{value}'."
461
+ raise ValueError(error_message)
462
+ except OverflowError:
463
+ error_message = f"Can't deal with value: '{value}', it's too large for type '{self.json_type}'."
464
+ raise ValueError(error_message)
465
+
466
+ return value
467
+
468
+ def default_array(self, array_size):
469
+ array = numpy.empty(array_size, dtype=self.dtype)
470
+ if self.value_type == Enum:
471
+ array.fill(self.default_value.index)
472
+ return EnumArray(array, self.possible_values)
473
+ array.fill(self.default_value)
474
+ return array
@@ -0,0 +1,25 @@
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 .libyaml_warning import LibYAMLWarning # noqa: F401
25
+ from .tempfile_warning import TempfileWarning # noqa: F401
@@ -0,0 +1,2 @@
1
+ class LibYAMLWarning(UserWarning):
2
+ """Custom warning for LibYAML not installed."""
@@ -0,0 +1,2 @@
1
+ class TempfileWarning(UserWarning):
2
+ """Custom warning when using a tempfile on disk."""