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.
- openfisca_core/__init__.py +0 -0
- openfisca_core/commons/__init__.py +18 -0
- openfisca_core/commons/dummy.py +25 -0
- openfisca_core/commons/formulas.py +121 -0
- openfisca_core/commons/misc.py +109 -0
- openfisca_core/commons/py.typed +0 -0
- openfisca_core/commons/rates.py +114 -0
- openfisca_core/commons/tests/__init__.py +0 -0
- openfisca_core/commons/tests/test_dummy.py +9 -0
- openfisca_core/commons/tests/test_formulas.py +75 -0
- openfisca_core/commons/tests/test_rates.py +26 -0
- openfisca_core/data_storage/__init__.py +7 -0
- openfisca_core/data_storage/in_memory_storage.py +198 -0
- openfisca_core/data_storage/on_disk_storage.py +307 -0
- openfisca_core/data_storage/types.py +14 -0
- openfisca_core/entities/__init__.py +23 -0
- openfisca_core/entities/_core_entity.py +217 -0
- openfisca_core/entities/_description.py +55 -0
- openfisca_core/entities/entity.py +60 -0
- openfisca_core/entities/group_entity.py +123 -0
- openfisca_core/entities/helpers.py +165 -0
- openfisca_core/entities/py.typed +0 -0
- openfisca_core/entities/role.py +92 -0
- openfisca_core/entities/tests/__init__.py +0 -0
- openfisca_core/entities/tests/test_entity.py +10 -0
- openfisca_core/entities/tests/test_group_entity.py +70 -0
- openfisca_core/entities/tests/test_role.py +11 -0
- openfisca_core/entities/types.py +44 -0
- openfisca_core/errors/__init__.py +64 -0
- openfisca_core/errors/cycle_error.py +2 -0
- openfisca_core/errors/empty_argument_error.py +39 -0
- openfisca_core/errors/nan_creation_error.py +2 -0
- openfisca_core/errors/parameter_not_found_error.py +16 -0
- openfisca_core/errors/parameter_parsing_error.py +18 -0
- openfisca_core/errors/period_mismatch_error.py +9 -0
- openfisca_core/errors/situation_parsing_error.py +26 -0
- openfisca_core/errors/spiral_error.py +2 -0
- openfisca_core/errors/variable_name_config_error.py +2 -0
- openfisca_core/errors/variable_not_found_error.py +29 -0
- openfisca_core/experimental/__init__.py +9 -0
- openfisca_core/experimental/_errors.py +5 -0
- openfisca_core/experimental/_memory_config.py +41 -0
- openfisca_core/formula_helpers.py +9 -0
- openfisca_core/holders/__init__.py +33 -0
- openfisca_core/holders/helpers.py +93 -0
- openfisca_core/holders/holder.py +328 -0
- openfisca_core/holders/tests/__init__.py +0 -0
- openfisca_core/holders/tests/test_helpers.py +134 -0
- openfisca_core/holders/types.py +3 -0
- openfisca_core/indexed_enums/__init__.py +18 -0
- openfisca_core/indexed_enums/_enum_type.py +79 -0
- openfisca_core/indexed_enums/_errors.py +35 -0
- openfisca_core/indexed_enums/_guards.py +209 -0
- openfisca_core/indexed_enums/_utils.py +180 -0
- openfisca_core/indexed_enums/config.py +6 -0
- openfisca_core/indexed_enums/enum.py +218 -0
- openfisca_core/indexed_enums/enum_array.py +316 -0
- openfisca_core/indexed_enums/py.typed +0 -0
- openfisca_core/indexed_enums/tests/__init__.py +0 -0
- openfisca_core/indexed_enums/tests/test_enum.py +135 -0
- openfisca_core/indexed_enums/tests/test_enum_array.py +30 -0
- openfisca_core/indexed_enums/types.py +49 -0
- openfisca_core/memory_config.py +9 -0
- openfisca_core/model_api.py +71 -0
- openfisca_core/parameters/__init__.py +71 -0
- openfisca_core/parameters/at_instant_like.py +17 -0
- openfisca_core/parameters/config.py +55 -0
- openfisca_core/parameters/helpers.py +106 -0
- openfisca_core/parameters/parameter.py +217 -0
- openfisca_core/parameters/parameter_at_instant.py +75 -0
- openfisca_core/parameters/parameter_node.py +176 -0
- openfisca_core/parameters/parameter_node_at_instant.py +57 -0
- openfisca_core/parameters/parameter_scale.py +121 -0
- openfisca_core/parameters/parameter_scale_bracket.py +7 -0
- openfisca_core/parameters/values_history.py +5 -0
- openfisca_core/parameters/vectorial_parameter_node_at_instant.py +179 -0
- openfisca_core/periods/__init__.py +77 -0
- openfisca_core/periods/_errors.py +28 -0
- openfisca_core/periods/_parsers.py +122 -0
- openfisca_core/periods/config.py +20 -0
- openfisca_core/periods/date_unit.py +110 -0
- openfisca_core/periods/helpers.py +314 -0
- openfisca_core/periods/instant_.py +226 -0
- openfisca_core/periods/period_.py +919 -0
- openfisca_core/periods/py.typed +0 -0
- openfisca_core/periods/tests/__init__.py +0 -0
- openfisca_core/periods/tests/helpers/__init__.py +0 -0
- openfisca_core/periods/tests/helpers/test_helpers.py +65 -0
- openfisca_core/periods/tests/helpers/test_instant.py +73 -0
- openfisca_core/periods/tests/helpers/test_period.py +134 -0
- openfisca_core/periods/tests/test_instant.py +32 -0
- openfisca_core/periods/tests/test_parsers.py +129 -0
- openfisca_core/periods/tests/test_period.py +283 -0
- openfisca_core/populations/__init__.py +64 -0
- openfisca_core/populations/_core_population.py +454 -0
- openfisca_core/populations/_errors.py +65 -0
- openfisca_core/populations/group_population.py +326 -0
- openfisca_core/populations/population.py +142 -0
- openfisca_core/populations/tests/__init__.py +0 -0
- openfisca_core/populations/tests/test_members_position.py +116 -0
- openfisca_core/populations/types.py +110 -0
- openfisca_core/projectors/__init__.py +39 -0
- openfisca_core/projectors/entity_to_person_projector.py +12 -0
- openfisca_core/projectors/first_person_to_entity_projector.py +13 -0
- openfisca_core/projectors/helpers.py +139 -0
- openfisca_core/projectors/projector.py +38 -0
- openfisca_core/projectors/typing.py +27 -0
- openfisca_core/projectors/unique_role_to_entity_projector.py +14 -0
- openfisca_core/rates.py +9 -0
- openfisca_core/reforms/__init__.py +24 -0
- openfisca_core/reforms/in_yaml_test_reform.py +28 -0
- openfisca_core/reforms/reform.py +86 -0
- openfisca_core/scripts/__init__.py +106 -0
- openfisca_core/scripts/__pycache__/__init__.cpython-310-pytest-8.3.3.pyc +0 -0
- openfisca_core/scripts/__pycache__/__init__.cpython-310-pytest-8.4.2.pyc +0 -0
- openfisca_core/scripts/__pycache__/__init__.cpython-310.pyc +0 -0
- openfisca_core/scripts/__pycache__/__init__.cpython-311-pytest-8.3.3.pyc +0 -0
- openfisca_core/scripts/__pycache__/__init__.cpython-311-pytest-8.4.2.pyc +0 -0
- openfisca_core/scripts/__pycache__/__init__.cpython-311.pyc +0 -0
- openfisca_core/scripts/__pycache__/__init__.cpython-312-pytest-8.4.2.pyc +0 -0
- openfisca_core/scripts/__pycache__/__init__.cpython-312.pyc +0 -0
- openfisca_core/scripts/__pycache__/__init__.cpython-313-pytest-8.3.3.pyc +0 -0
- openfisca_core/scripts/__pycache__/__init__.cpython-313-pytest-8.4.2.pyc +0 -0
- openfisca_core/scripts/__pycache__/__init__.cpython-313.pyc +0 -0
- openfisca_core/scripts/__pycache__/find_placeholders.cpython-311.pyc +0 -0
- openfisca_core/scripts/__pycache__/find_placeholders.cpython-313.pyc +0 -0
- openfisca_core/scripts/__pycache__/measure_numpy_condition_notations.cpython-311.pyc +0 -0
- openfisca_core/scripts/__pycache__/measure_numpy_condition_notations.cpython-313.pyc +0 -0
- openfisca_core/scripts/__pycache__/measure_performances.cpython-311.pyc +0 -0
- openfisca_core/scripts/__pycache__/measure_performances.cpython-313.pyc +0 -0
- openfisca_core/scripts/__pycache__/measure_performances_fancy_indexing.cpython-311.pyc +0 -0
- openfisca_core/scripts/__pycache__/measure_performances_fancy_indexing.cpython-313.pyc +0 -0
- openfisca_core/scripts/__pycache__/openfisca_command.cpython-310.pyc +0 -0
- openfisca_core/scripts/__pycache__/openfisca_command.cpython-311.pyc +0 -0
- openfisca_core/scripts/__pycache__/openfisca_command.cpython-312.pyc +0 -0
- openfisca_core/scripts/__pycache__/openfisca_command.cpython-313.pyc +0 -0
- openfisca_core/scripts/__pycache__/remove_fuzzy.cpython-311.pyc +0 -0
- openfisca_core/scripts/__pycache__/remove_fuzzy.cpython-313.pyc +0 -0
- openfisca_core/scripts/__pycache__/run_test.cpython-310.pyc +0 -0
- openfisca_core/scripts/__pycache__/run_test.cpython-311.pyc +0 -0
- openfisca_core/scripts/__pycache__/run_test.cpython-312.pyc +0 -0
- openfisca_core/scripts/__pycache__/run_test.cpython-313.pyc +0 -0
- openfisca_core/scripts/__pycache__/simulation_generator.cpython-311.pyc +0 -0
- openfisca_core/scripts/__pycache__/simulation_generator.cpython-313.pyc +0 -0
- openfisca_core/scripts/assets/__init__.py +0 -0
- openfisca_core/scripts/assets/__pycache__/__init__.cpython-311.pyc +0 -0
- openfisca_core/scripts/assets/__pycache__/__init__.cpython-313.pyc +0 -0
- openfisca_core/scripts/assets/index.html +140 -0
- openfisca_core/scripts/find_placeholders.py +51 -0
- openfisca_core/scripts/measure_numpy_condition_notations.py +137 -0
- openfisca_core/scripts/measure_performances.py +291 -0
- openfisca_core/scripts/measure_performances_fancy_indexing.py +89 -0
- openfisca_core/scripts/migrations/__init__.py +0 -0
- openfisca_core/scripts/migrations/__pycache__/__init__.cpython-311.pyc +0 -0
- openfisca_core/scripts/migrations/__pycache__/__init__.cpython-313.pyc +0 -0
- openfisca_core/scripts/migrations/__pycache__/v24_to_25.cpython-311.pyc +0 -0
- openfisca_core/scripts/migrations/__pycache__/v24_to_25.cpython-313.pyc +0 -0
- openfisca_core/scripts/migrations/v16_2_to_v17/__init__.py +0 -0
- openfisca_core/scripts/migrations/v16_2_to_v17/__pycache__/__init__.cpython-311.pyc +0 -0
- openfisca_core/scripts/migrations/v16_2_to_v17/__pycache__/__init__.cpython-313.pyc +0 -0
- openfisca_core/scripts/migrations/v16_2_to_v17/__pycache__/xml_to_yaml_country_template.cpython-311.pyc +0 -0
- openfisca_core/scripts/migrations/v16_2_to_v17/__pycache__/xml_to_yaml_country_template.cpython-313.pyc +0 -0
- openfisca_core/scripts/migrations/v16_2_to_v17/__pycache__/xml_to_yaml_extension_template.cpython-311.pyc +0 -0
- openfisca_core/scripts/migrations/v16_2_to_v17/__pycache__/xml_to_yaml_extension_template.cpython-313.pyc +0 -0
- openfisca_core/scripts/migrations/v16_2_to_v17/legislation.xsd +102 -0
- openfisca_core/scripts/migrations/v16_2_to_v17/xml_to_yaml_country_template.py +30 -0
- openfisca_core/scripts/migrations/v16_2_to_v17/xml_to_yaml_extension_template.py +26 -0
- openfisca_core/scripts/migrations/v24_to_25.py +156 -0
- openfisca_core/scripts/openfisca_command.py +192 -0
- openfisca_core/scripts/remove_fuzzy.py +213 -0
- openfisca_core/scripts/run_test.py +51 -0
- openfisca_core/scripts/simulation_generator.py +90 -0
- openfisca_core/simulation_builder.py +16 -0
- openfisca_core/simulations/__init__.py +45 -0
- openfisca_core/simulations/_build_default_simulation.py +158 -0
- openfisca_core/simulations/_build_from_variables.py +230 -0
- openfisca_core/simulations/_type_guards.py +298 -0
- openfisca_core/simulations/helpers.py +111 -0
- openfisca_core/simulations/simulation.py +618 -0
- openfisca_core/simulations/simulation_builder.py +855 -0
- openfisca_core/simulations/typing.py +203 -0
- openfisca_core/taxbenefitsystems/__init__.py +26 -0
- openfisca_core/taxbenefitsystems/tax_benefit_system.py +584 -0
- openfisca_core/taxscales/__init__.py +35 -0
- openfisca_core/taxscales/abstract_rate_tax_scale.py +41 -0
- openfisca_core/taxscales/abstract_tax_scale.py +53 -0
- openfisca_core/taxscales/amount_tax_scale_like.py +55 -0
- openfisca_core/taxscales/helpers.py +44 -0
- openfisca_core/taxscales/linear_average_rate_tax_scale.py +76 -0
- openfisca_core/taxscales/marginal_amount_tax_scale.py +34 -0
- openfisca_core/taxscales/marginal_rate_tax_scale.py +286 -0
- openfisca_core/taxscales/rate_tax_scale_like.py +214 -0
- openfisca_core/taxscales/single_amount_tax_scale.py +32 -0
- openfisca_core/taxscales/tax_scale_like.py +65 -0
- openfisca_core/tools/__init__.py +94 -0
- openfisca_core/tools/parallel_plugin.py +55 -0
- openfisca_core/tools/simulation_dumper.py +137 -0
- openfisca_core/tools/test_runner.py +802 -0
- openfisca_core/tracers/__init__.py +40 -0
- openfisca_core/tracers/computation_log.py +196 -0
- openfisca_core/tracers/flat_trace.py +87 -0
- openfisca_core/tracers/full_tracer.py +172 -0
- openfisca_core/tracers/performance_log.py +133 -0
- openfisca_core/tracers/simple_tracer.py +64 -0
- openfisca_core/tracers/trace_node.py +121 -0
- openfisca_core/tracers/tracing_parameter_node_at_instant.py +81 -0
- openfisca_core/types.py +548 -0
- openfisca_core/variables/__init__.py +26 -0
- openfisca_core/variables/config.py +53 -0
- openfisca_core/variables/helpers.py +67 -0
- openfisca_core/variables/tests/__init__.py +0 -0
- openfisca_core/variables/tests/test_definition_period.py +43 -0
- openfisca_core/variables/variable.py +474 -0
- openfisca_core/warnings/__init__.py +25 -0
- openfisca_core/warnings/libyaml_warning.py +2 -0
- openfisca_core/warnings/tempfile_warning.py +2 -0
- openfisca_core_test-46.0.0.data/data/share/openfisca/openfisca-core/CHANGELOG.md +4209 -0
- openfisca_core_test-46.0.0.data/data/share/openfisca/openfisca-core/README.md +152 -0
- openfisca_core_test-46.0.0.dist-info/METADATA +194 -0
- openfisca_core_test-46.0.0.dist-info/RECORD +237 -0
- openfisca_core_test-46.0.0.dist-info/WHEEL +5 -0
- openfisca_core_test-46.0.0.dist-info/entry_points.txt +3 -0
- openfisca_core_test-46.0.0.dist-info/licenses/LICENSE +661 -0
- openfisca_core_test-46.0.0.dist-info/top_level.txt +2 -0
- openfisca_web_api/__init__.py +0 -0
- openfisca_web_api/app.py +226 -0
- openfisca_web_api/errors.py +11 -0
- openfisca_web_api/handlers.py +90 -0
- openfisca_web_api/loader/__init__.py +22 -0
- openfisca_web_api/loader/entities.py +28 -0
- openfisca_web_api/loader/parameters.py +122 -0
- openfisca_web_api/loader/spec.py +168 -0
- openfisca_web_api/loader/tax_benefit_system.py +27 -0
- openfisca_web_api/loader/variables.py +121 -0
- openfisca_web_api/openAPI.yml +434 -0
- openfisca_web_api/scripts/__init__.py +0 -0
- 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,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"]
|