anemoi-datasets 0.5.27__py3-none-any.whl → 0.5.29__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 (72) hide show
  1. anemoi/datasets/_version.py +2 -2
  2. anemoi/datasets/commands/recipe/__init__.py +93 -0
  3. anemoi/datasets/commands/recipe/format.py +55 -0
  4. anemoi/datasets/commands/recipe/migrate.py +555 -0
  5. anemoi/datasets/create/__init__.py +46 -13
  6. anemoi/datasets/create/config.py +52 -53
  7. anemoi/datasets/create/input/__init__.py +43 -63
  8. anemoi/datasets/create/input/action.py +296 -236
  9. anemoi/datasets/create/input/context/__init__.py +71 -0
  10. anemoi/datasets/create/input/context/field.py +54 -0
  11. anemoi/datasets/create/input/data_sources.py +2 -1
  12. anemoi/datasets/create/input/misc.py +0 -71
  13. anemoi/datasets/create/input/repeated_dates.py +0 -114
  14. anemoi/datasets/create/input/result/__init__.py +17 -0
  15. anemoi/datasets/create/input/{result.py → result/field.py} +10 -92
  16. anemoi/datasets/create/sources/accumulate.py +517 -0
  17. anemoi/datasets/create/sources/accumulate_utils/__init__.py +8 -0
  18. anemoi/datasets/create/sources/accumulate_utils/covering_intervals.py +221 -0
  19. anemoi/datasets/create/sources/accumulate_utils/field_to_interval.py +149 -0
  20. anemoi/datasets/create/sources/accumulate_utils/interval_generators.py +321 -0
  21. anemoi/datasets/create/sources/anemoi_dataset.py +46 -42
  22. anemoi/datasets/create/sources/constants.py +39 -38
  23. anemoi/datasets/create/sources/empty.py +26 -22
  24. anemoi/datasets/create/sources/forcings.py +29 -28
  25. anemoi/datasets/create/sources/grib.py +92 -72
  26. anemoi/datasets/create/sources/grib_index.py +102 -54
  27. anemoi/datasets/create/sources/hindcasts.py +56 -55
  28. anemoi/datasets/create/sources/legacy.py +10 -62
  29. anemoi/datasets/create/sources/mars.py +159 -154
  30. anemoi/datasets/create/sources/netcdf.py +28 -24
  31. anemoi/datasets/create/sources/opendap.py +28 -24
  32. anemoi/datasets/create/sources/recentre.py +42 -41
  33. anemoi/datasets/create/sources/repeated_dates.py +44 -0
  34. anemoi/datasets/create/sources/source.py +26 -48
  35. anemoi/datasets/create/sources/xarray_support/__init__.py +30 -24
  36. anemoi/datasets/create/sources/xarray_support/coordinates.py +1 -4
  37. anemoi/datasets/create/sources/xarray_support/field.py +4 -4
  38. anemoi/datasets/create/sources/xarray_support/flavour.py +2 -2
  39. anemoi/datasets/create/sources/xarray_support/patch.py +178 -5
  40. anemoi/datasets/create/sources/xarray_zarr.py +28 -24
  41. anemoi/datasets/create/sources/zenodo.py +43 -39
  42. anemoi/datasets/create/utils.py +0 -42
  43. anemoi/datasets/data/complement.py +26 -17
  44. anemoi/datasets/data/dataset.py +12 -0
  45. anemoi/datasets/data/grids.py +0 -152
  46. anemoi/datasets/data/masked.py +74 -13
  47. anemoi/datasets/data/missing.py +5 -0
  48. anemoi/datasets/data/rolling_average.py +141 -0
  49. anemoi/datasets/data/stores.py +7 -9
  50. anemoi/datasets/dates/__init__.py +2 -0
  51. anemoi/datasets/dumper.py +76 -0
  52. anemoi/datasets/grids.py +1 -178
  53. anemoi/datasets/schemas/recipe.json +131 -0
  54. {anemoi_datasets-0.5.27.dist-info → anemoi_datasets-0.5.29.dist-info}/METADATA +9 -6
  55. {anemoi_datasets-0.5.27.dist-info → anemoi_datasets-0.5.29.dist-info}/RECORD +59 -57
  56. {anemoi_datasets-0.5.27.dist-info → anemoi_datasets-0.5.29.dist-info}/WHEEL +1 -1
  57. anemoi/datasets/create/filter.py +0 -47
  58. anemoi/datasets/create/input/concat.py +0 -161
  59. anemoi/datasets/create/input/context.py +0 -86
  60. anemoi/datasets/create/input/empty.py +0 -53
  61. anemoi/datasets/create/input/filter.py +0 -117
  62. anemoi/datasets/create/input/function.py +0 -232
  63. anemoi/datasets/create/input/join.py +0 -129
  64. anemoi/datasets/create/input/pipe.py +0 -66
  65. anemoi/datasets/create/input/step.py +0 -173
  66. anemoi/datasets/create/input/template.py +0 -161
  67. anemoi/datasets/create/sources/accumulations.py +0 -1062
  68. anemoi/datasets/create/sources/accumulations2.py +0 -647
  69. anemoi/datasets/create/sources/tendencies.py +0 -198
  70. {anemoi_datasets-0.5.27.dist-info → anemoi_datasets-0.5.29.dist-info}/entry_points.txt +0 -0
  71. {anemoi_datasets-0.5.27.dist-info → anemoi_datasets-0.5.29.dist-info}/licenses/LICENSE +0 -0
  72. {anemoi_datasets-0.5.27.dist-info → anemoi_datasets-0.5.29.dist-info}/top_level.txt +0 -0
@@ -1,161 +0,0 @@
1
- # (C) Copyright 2024 Anemoi contributors.
2
- #
3
- # This software is licensed under the terms of the Apache Licence Version 2.0
4
- # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
5
- #
6
- # In applying this licence, ECMWF does not waive the privileges and immunities
7
- # granted to it by virtue of its status as an intergovernmental organisation
8
- # nor does it submit to any jurisdiction.
9
-
10
- import logging
11
- from copy import deepcopy
12
- from functools import cached_property
13
- from typing import Any
14
-
15
- from earthkit.data import FieldList
16
-
17
- from anemoi.datasets.dates import DatesProvider
18
-
19
- from ...dates.groups import GroupOfDates
20
- from .action import Action
21
- from .action import action_factory
22
- from .empty import EmptyResult
23
- from .misc import _tidy
24
- from .misc import assert_fieldlist
25
- from .result import Result
26
- from .template import notify_result
27
- from .trace import trace_datasource
28
- from .trace import trace_select
29
-
30
- LOG = logging.getLogger(__name__)
31
-
32
-
33
- class ConcatResult(Result):
34
- """Represents the result of concatenating multiple results."""
35
-
36
- def __init__(
37
- self,
38
- context: object,
39
- action_path: list[str],
40
- group_of_dates: GroupOfDates,
41
- results: list[Result],
42
- **kwargs: Any,
43
- ) -> None:
44
- """Initializes a ConcatResult instance.
45
-
46
- Parameters
47
- ----------
48
- context : object
49
- The context object.
50
- action_path : List[str]
51
- The action path.
52
- group_of_dates : GroupOfDates
53
- The group of dates.
54
- results : List[Result]
55
- The list of results.
56
- kwargs : Any
57
- Additional keyword arguments.
58
- """
59
- super().__init__(context, action_path, group_of_dates)
60
- self.results = [r for r in results if not r.empty]
61
-
62
- @cached_property
63
- @assert_fieldlist
64
- @notify_result
65
- @trace_datasource
66
- def datasource(self) -> FieldList:
67
- """Returns the concatenated datasource from all results."""
68
- ds = EmptyResult(self.context, self.action_path, self.group_of_dates).datasource
69
- for i in self.results:
70
- ds += i.datasource
71
- return _tidy(ds)
72
-
73
- @property
74
- def variables(self) -> list[str]:
75
- """Returns the list of variables, ensuring all results have the same variables."""
76
- variables = None
77
- for f in self.results:
78
- if f.empty:
79
- continue
80
- if variables is None:
81
- variables = f.variables
82
- assert variables == f.variables, (variables, f.variables)
83
- assert variables is not None, self.results
84
- return variables
85
-
86
- def __repr__(self) -> str:
87
- """Returns a string representation of the ConcatResult instance.
88
-
89
- Returns
90
- -------
91
- str
92
- A string representation of the ConcatResult instance.
93
- """
94
- content = "\n".join([str(i) for i in self.results])
95
- return self._repr(content)
96
-
97
-
98
- class ConcatAction(Action):
99
- """Represents an action that concatenates multiple actions based on their dates."""
100
-
101
- def __init__(self, context: object, action_path: list[str], *configs: dict[str, Any]) -> None:
102
- """Initializes a ConcatAction instance.
103
-
104
- Parameters
105
- ----------
106
- context : object
107
- The context object.
108
- action_path : List[str]
109
- The action path.
110
- configs : Dict[str, Any]
111
- The configuration dictionaries.
112
- """
113
- super().__init__(context, action_path, *configs)
114
- parts = []
115
- for i, cfg in enumerate(configs):
116
- if "dates" not in cfg:
117
- raise ValueError(f"Missing 'dates' in {cfg}")
118
- cfg = deepcopy(cfg)
119
- dates_cfg = cfg.pop("dates")
120
- assert isinstance(dates_cfg, dict), dates_cfg
121
- filtering_dates = DatesProvider.from_config(**dates_cfg)
122
- action = action_factory(cfg, context, action_path + [str(i)])
123
- parts.append((filtering_dates, action))
124
- self.parts = parts
125
-
126
- def __repr__(self) -> str:
127
- """Returns a string representation of the ConcatAction instance.
128
-
129
- Returns
130
- -------
131
- str
132
- A string representation of the ConcatAction instance.
133
- """
134
- content = "\n".join([str(i) for i in self.parts])
135
- return self._repr(content)
136
-
137
- @trace_select
138
- def select(self, group_of_dates: GroupOfDates) -> ConcatResult | EmptyResult:
139
- """Selects the concatenated result for the given group of dates.
140
-
141
- Parameters
142
- ----------
143
- group_of_dates : GroupOfDates
144
- The group of dates.
145
-
146
- Returns
147
- -------
148
- Union[ConcatResult, EmptyResult]
149
- The concatenated result or an empty result.
150
- """
151
- from anemoi.datasets.dates.groups import GroupOfDates
152
-
153
- results = []
154
- for filtering_dates, action in self.parts:
155
- newdates = GroupOfDates(sorted(set(group_of_dates) & set(filtering_dates)), group_of_dates.provider)
156
- if newdates:
157
- results.append(action.select(newdates))
158
- if not results:
159
- return EmptyResult(self.context, self.action_path, group_of_dates)
160
-
161
- return ConcatResult(self.context, self.action_path, group_of_dates, results)
@@ -1,86 +0,0 @@
1
- # (C) Copyright 2024 Anemoi contributors.
2
- #
3
- # This software is licensed under the terms of the Apache Licence Version 2.0
4
- # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
5
- #
6
- # In applying this licence, ECMWF does not waive the privileges and immunities
7
- # granted to it by virtue of its status as an intergovernmental organisation
8
- # nor does it submit to any jurisdiction.
9
-
10
- import logging
11
- import textwrap
12
- from typing import Any
13
-
14
- from anemoi.utils.humanize import plural
15
-
16
- from .trace import step
17
- from .trace import trace
18
-
19
- LOG = logging.getLogger(__name__)
20
-
21
-
22
- class Context:
23
- """Class to handle the build context in the dataset creation process."""
24
-
25
- def __init__(self) -> None:
26
- """Initializes a Context instance."""
27
- # used_references is a set of reference paths that will be needed
28
- self.used_references = set()
29
- # results is a dictionary of reference path -> obj
30
- self.results = {}
31
-
32
- def will_need_reference(self, key: list | tuple) -> None:
33
- """Marks a reference as needed.
34
-
35
- Parameters
36
- ----------
37
- key : Union[List, Tuple]
38
- The reference key.
39
- """
40
- assert isinstance(key, (list, tuple)), key
41
- key = tuple(key)
42
- self.used_references.add(key)
43
-
44
- def notify_result(self, key: list | tuple, result: Any) -> None:
45
- """Notifies that a result is available for a reference.
46
-
47
- Parameters
48
- ----------
49
- key : Union[List, Tuple]
50
- The reference key.
51
- result : Any
52
- The result object.
53
- """
54
- trace(
55
- "🎯",
56
- step(key),
57
- "notify result",
58
- textwrap.shorten(repr(result).replace(",", ", "), width=40),
59
- plural(len(result), "field"),
60
- )
61
- assert isinstance(key, (list, tuple)), key
62
- key = tuple(key)
63
- if key in self.used_references:
64
- if key in self.results:
65
- raise ValueError(f"Duplicate result {key}")
66
- self.results[key] = result
67
-
68
- def get_result(self, key: list | tuple) -> Any:
69
- """Retrieves the result for a given reference.
70
-
71
- Parameters
72
- ----------
73
- key : Union[List, Tuple]
74
- The reference key.
75
-
76
- Returns
77
- -------
78
- Any
79
- The result for the given reference.
80
- """
81
- assert isinstance(key, (list, tuple)), key
82
- key = tuple(key)
83
- if key in self.results:
84
- return self.results[key]
85
- all_keys = sorted(list(self.results.keys()))
86
- raise ValueError(f"Cannot find result {key} in {all_keys}")
@@ -1,53 +0,0 @@
1
- # (C) Copyright 2024 Anemoi contributors.
2
- #
3
- # This software is licensed under the terms of the Apache Licence Version 2.0
4
- # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
5
- #
6
- # In applying this licence, ECMWF does not waive the privileges and immunities
7
- # granted to it by virtue of its status as an intergovernmental organisation
8
- # nor does it submit to any jurisdiction.
9
-
10
- import logging
11
- from functools import cached_property
12
-
13
- from earthkit.data import FieldList
14
-
15
- from .misc import assert_fieldlist
16
- from .result import Result
17
- from .trace import trace_datasource
18
-
19
- LOG = logging.getLogger(__name__)
20
-
21
-
22
- class EmptyResult(Result):
23
- """Class to represent an empty result in the dataset creation process."""
24
-
25
- empty = True
26
-
27
- def __init__(self, context: object, action_path: list, dates: object) -> None:
28
- """Initializes an EmptyResult instance.
29
-
30
- Parameters
31
- ----------
32
- context : object
33
- The context object.
34
- action_path : list
35
- The action path.
36
- dates : object
37
- The dates object.
38
- """
39
- super().__init__(context, action_path + ["empty"], dates)
40
-
41
- @cached_property
42
- @assert_fieldlist
43
- @trace_datasource
44
- def datasource(self) -> FieldList:
45
- """Returns an empty datasource."""
46
- from earthkit.data import from_source
47
-
48
- return from_source("empty")
49
-
50
- @property
51
- def variables(self) -> list[str]:
52
- """Returns an empty list of variables."""
53
- return []
@@ -1,117 +0,0 @@
1
- # (C) Copyright 2024 Anemoi contributors.
2
- #
3
- # This software is licensed under the terms of the Apache Licence Version 2.0
4
- # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
5
- #
6
- # In applying this licence, ECMWF does not waive the privileges and immunities
7
- # granted to it by virtue of its status as an intergovernmental organisation
8
- # nor does it submit to any jurisdiction.
9
-
10
- import logging
11
- from functools import cached_property
12
- from typing import Any
13
-
14
- from earthkit.data import FieldList
15
-
16
- from .function import FunctionContext
17
- from .misc import _tidy
18
- from .misc import assert_fieldlist
19
- from .step import StepAction
20
- from .step import StepResult
21
- from .template import notify_result
22
- from .trace import trace_datasource
23
-
24
- LOG = logging.getLogger(__name__)
25
-
26
-
27
- class FilterStepResult(StepResult):
28
- @property
29
- @notify_result
30
- @assert_fieldlist
31
- @trace_datasource
32
- def datasource(self) -> FieldList:
33
- """Returns the filtered datasource."""
34
- ds: FieldList = self.upstream_result.datasource
35
- ds = ds.sel(**self.action.kwargs)
36
- return _tidy(ds)
37
-
38
-
39
- class FilterStepAction(StepAction):
40
- """Represents an action to filter a step result."""
41
-
42
- result_class: type[FilterStepResult] = FilterStepResult
43
-
44
-
45
- class StepFunctionResult(StepResult):
46
- @cached_property
47
- @assert_fieldlist
48
- @notify_result
49
- @trace_datasource
50
- def datasource(self) -> FieldList:
51
- """Returns the datasource after applying the function."""
52
-
53
- self.action.filter.context = FunctionContext(self)
54
- try:
55
- return _tidy(
56
- self.action.filter.execute(
57
- self.upstream_result.datasource,
58
- *self.action.args[1:],
59
- **self.action.kwargs,
60
- )
61
- )
62
-
63
- except Exception:
64
- LOG.error(f"Error in {self.action.name}", exc_info=True)
65
- raise
66
-
67
- def _trace_datasource(self, *args: Any, **kwargs: Any) -> str:
68
- """Traces the datasource for the given arguments.
69
-
70
- Parameters
71
- ----------
72
- *args : Any
73
- The arguments.
74
- **kwargs : Any
75
- The keyword arguments.
76
-
77
- Returns
78
- -------
79
- str
80
- A string representation of the traced datasource.
81
- """
82
- return f"{self.action.name}({self.group_of_dates})"
83
-
84
-
85
- class FunctionStepAction(StepAction):
86
- """Represents an action to apply a function to a step result."""
87
-
88
- result_class: type[StepFunctionResult] = StepFunctionResult
89
-
90
- def __init__(
91
- self,
92
- context: object,
93
- action_path: list,
94
- previous_step: StepAction,
95
- name: str,
96
- filter: Any,
97
- *args: Any,
98
- **kwargs: Any,
99
- ) -> None:
100
- """Initializes a FunctionStepAction instance.
101
-
102
- Parameters
103
- ----------
104
- context : object
105
- The context object.
106
- action_path : list
107
- The action path.
108
- previous_step : StepAction
109
- The previous step action.
110
- *args : Any
111
- Additional arguments.
112
- **kwargs : Any
113
- Additional keyword arguments.
114
- """
115
- super().__init__(context, action_path, previous_step, *args, **kwargs)
116
- self.name = name
117
- self.filter = filter
@@ -1,232 +0,0 @@
1
- # (C) Copyright 2024 Anemoi contributors.
2
- #
3
- # This software is licensed under the terms of the Apache Licence Version 2.0
4
- # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
5
- #
6
- # In applying this licence, ECMWF does not waive the privileges and immunities
7
- # granted to it by virtue of its status as an intergovernmental organisation
8
- # nor does it submit to any jurisdiction.
9
-
10
- import logging
11
- from functools import cached_property
12
- from typing import Any
13
-
14
- from earthkit.data import FieldList
15
-
16
- from ...dates.groups import GroupOfDates
17
- from .action import Action
18
- from .misc import _tidy
19
- from .misc import assert_fieldlist
20
- from .result import Result
21
- from .template import notify_result
22
- from .template import substitute
23
- from .trace import trace
24
- from .trace import trace_datasource
25
- from .trace import trace_select
26
-
27
- LOG = logging.getLogger(__name__)
28
-
29
-
30
- class FunctionContext:
31
- """A FunctionContext is passed to all functions, it will be used to pass information
32
- to the functions from the other actions and filters and results.
33
- """
34
-
35
- def __init__(self, owner: Result) -> None:
36
- """Initializes a FunctionContext instance.
37
-
38
- Parameters
39
- ----------
40
- owner : object
41
- The owner object.
42
- """
43
- self.owner = owner
44
- self.use_grib_paramid: bool = owner.context.use_grib_paramid
45
-
46
- def trace(self, emoji: str, *args: Any) -> None:
47
- """Traces the given arguments with an emoji.
48
-
49
- Parameters
50
- ----------
51
- emoji : str
52
- The emoji to use.
53
- *args : Any
54
- The arguments to trace.
55
- """
56
- trace(emoji, *args)
57
-
58
- def info(self, *args: Any, **kwargs: Any) -> None:
59
- """Logs an info message.
60
-
61
- Parameters
62
- ----------
63
- *args : Any
64
- The arguments for the log message.
65
- **kwargs : Any
66
- The keyword arguments for the log message.
67
- """
68
- LOG.info(*args, **kwargs)
69
-
70
- @property
71
- def dates_provider(self) -> object:
72
- """Returns the dates provider."""
73
- return self.owner.group_of_dates.provider
74
-
75
- @property
76
- def partial_ok(self) -> bool:
77
- """Returns whether partial results are acceptable."""
78
- return self.owner.group_of_dates.partial_ok
79
-
80
- def get_result(self, *args, **kwargs) -> Any:
81
- return self.owner.context.get_result(*args, **kwargs)
82
-
83
-
84
- class FunctionAction(Action):
85
- """Represents an action that executes a function.
86
-
87
- Attributes
88
- ----------
89
- name : str
90
- The name of the function.
91
- """
92
-
93
- def __init__(self, context: object, action_path: list, _name: str, source, **kwargs: dict[str, Any]) -> None:
94
- """Initializes a FunctionAction instance.
95
-
96
- Parameters
97
- ----------
98
- context : object
99
- The context object.
100
- action_path : list
101
- The action path.
102
- _name : str
103
- The name of the function.
104
- **kwargs : Dict[str, Any]
105
- Additional keyword arguments.
106
- """
107
- super().__init__(context, action_path, **kwargs)
108
- self.name: str = _name
109
- self.source = source
110
-
111
- @trace_select
112
- def select(self, group_of_dates: GroupOfDates) -> "FunctionResult":
113
- """Selects the function result for the given group of dates.
114
-
115
- Parameters
116
- ----------
117
- group_of_dates : GroupOfDates
118
- The group of dates.
119
-
120
- Returns
121
- -------
122
- FunctionResult
123
- The function result instance.
124
- """
125
- return FunctionResult(self.context, self.action_path, group_of_dates, action=self)
126
-
127
- def __repr__(self) -> str:
128
- """Returns a string representation of the FunctionAction instance."""
129
- content: str = ""
130
- content += ",".join([self._short_str(a) for a in self.args])
131
- content += " ".join([self._short_str(f"{k}={v}") for k, v in self.kwargs.items()])
132
- content = self._short_str(content)
133
- return self._repr(_inline_=content, _indent_=" ")
134
-
135
- def _trace_select(self, group_of_dates: GroupOfDates) -> str:
136
- """Traces the selection of the function for the given group of dates.
137
-
138
- Parameters
139
- ----------
140
- group_of_dates : GroupOfDates
141
- The group of dates.
142
-
143
- Returns
144
- -------
145
- str
146
- The trace string.
147
- """
148
- return f"{self.name}({group_of_dates})"
149
-
150
-
151
- class FunctionResult(Result):
152
- """Represents the result of executing a function.
153
-
154
- Attributes
155
- ----------
156
- action : Action
157
- The action instance.
158
- args : tuple
159
- The positional arguments for the function.
160
- kwargs : dict
161
- The keyword arguments for the function.
162
- """
163
-
164
- def __init__(self, context: object, action_path: list, group_of_dates: GroupOfDates, action: Action) -> None:
165
- """Initializes a FunctionResult instance.
166
-
167
- Parameters
168
- ----------
169
- context : object
170
- The context object.
171
- action_path : list
172
- The action path.
173
- group_of_dates : GroupOfDates
174
- The group of dates.
175
- action : Action
176
- The action instance.
177
- """
178
- super().__init__(context, action_path, group_of_dates)
179
- assert isinstance(action, Action), type(action)
180
- self.action: Action = action
181
-
182
- self.args, self.kwargs = substitute(context, (self.action.args, self.action.kwargs))
183
-
184
- def _trace_datasource(self, *args: Any, **kwargs: Any) -> str:
185
- """Traces the datasource for the given arguments.
186
-
187
- Parameters
188
- ----------
189
- *args : Any
190
- The arguments.
191
- **kwargs : Any
192
- The keyword arguments.
193
-
194
- Returns
195
- -------
196
- str
197
- The trace string.
198
- """
199
- return f"{self.action.name}({self.group_of_dates})"
200
-
201
- @cached_property
202
- @assert_fieldlist
203
- @notify_result
204
- @trace_datasource
205
- def datasource(self) -> FieldList:
206
- """Returns the datasource for the function result."""
207
- # args, kwargs = resolve(self.context, (self.args, self.kwargs))
208
- self.action.source.context = FunctionContext(self)
209
-
210
- return _tidy(
211
- self.action.source.execute(
212
- list(self.group_of_dates), # Will provide a list of datetime objects
213
- )
214
- )
215
-
216
- def __repr__(self) -> str:
217
- """Returns a string representation of the FunctionResult instance."""
218
- try:
219
- return f"{self.action.name}({self.group_of_dates})"
220
- except Exception:
221
- return f"{self.__class__.__name__}(unitialised)"
222
-
223
- @property
224
- def function(self) -> None:
225
- """Raises NotImplementedError as this property is not implemented.
226
-
227
- Raises
228
- ------
229
- NotImplementedError
230
- Always raised.
231
- """
232
- raise NotImplementedError(f"Not implemented in {self.__class__.__name__}")