anemoi-datasets 0.5.15__py3-none-any.whl → 0.5.17__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.
- anemoi/datasets/__init__.py +4 -1
- anemoi/datasets/__main__.py +12 -2
- anemoi/datasets/_version.py +9 -4
- anemoi/datasets/commands/cleanup.py +17 -2
- anemoi/datasets/commands/compare.py +18 -2
- anemoi/datasets/commands/copy.py +196 -14
- anemoi/datasets/commands/create.py +50 -7
- anemoi/datasets/commands/finalise-additions.py +17 -2
- anemoi/datasets/commands/finalise.py +17 -2
- anemoi/datasets/commands/init-additions.py +17 -2
- anemoi/datasets/commands/init.py +16 -2
- anemoi/datasets/commands/inspect.py +283 -62
- anemoi/datasets/commands/load-additions.py +16 -2
- anemoi/datasets/commands/load.py +16 -2
- anemoi/datasets/commands/patch.py +17 -2
- anemoi/datasets/commands/publish.py +17 -2
- anemoi/datasets/commands/scan.py +31 -3
- anemoi/datasets/compute/recentre.py +47 -11
- anemoi/datasets/create/__init__.py +612 -85
- anemoi/datasets/create/check.py +142 -20
- anemoi/datasets/create/chunks.py +64 -4
- anemoi/datasets/create/config.py +185 -21
- anemoi/datasets/create/filter.py +50 -0
- anemoi/datasets/create/filters/__init__.py +33 -0
- anemoi/datasets/create/filters/empty.py +37 -0
- anemoi/datasets/create/filters/legacy.py +93 -0
- anemoi/datasets/create/filters/noop.py +37 -0
- anemoi/datasets/create/filters/orog_to_z.py +58 -0
- anemoi/datasets/create/{functions/filters → filters}/pressure_level_relative_humidity_to_specific_humidity.py +33 -10
- anemoi/datasets/create/{functions/filters → filters}/pressure_level_specific_humidity_to_relative_humidity.py +32 -8
- anemoi/datasets/create/filters/rename.py +205 -0
- anemoi/datasets/create/{functions/filters → filters}/rotate_winds.py +43 -28
- anemoi/datasets/create/{functions/filters → filters}/single_level_dewpoint_to_relative_humidity.py +32 -9
- anemoi/datasets/create/{functions/filters → filters}/single_level_relative_humidity_to_dewpoint.py +33 -9
- anemoi/datasets/create/{functions/filters → filters}/single_level_relative_humidity_to_specific_humidity.py +55 -7
- anemoi/datasets/create/{functions/filters → filters}/single_level_specific_humidity_to_relative_humidity.py +98 -37
- anemoi/datasets/create/filters/speeddir_to_uv.py +95 -0
- anemoi/datasets/create/{functions/filters → filters}/sum.py +24 -27
- anemoi/datasets/create/filters/transform.py +53 -0
- anemoi/datasets/create/{functions/filters → filters}/unrotate_winds.py +27 -18
- anemoi/datasets/create/filters/uv_to_speeddir.py +94 -0
- anemoi/datasets/create/{functions/filters → filters}/wz_to_w.py +51 -33
- anemoi/datasets/create/input/__init__.py +76 -5
- anemoi/datasets/create/input/action.py +149 -13
- anemoi/datasets/create/input/concat.py +81 -10
- anemoi/datasets/create/input/context.py +39 -4
- anemoi/datasets/create/input/data_sources.py +72 -6
- anemoi/datasets/create/input/empty.py +21 -3
- anemoi/datasets/create/input/filter.py +60 -12
- anemoi/datasets/create/input/function.py +154 -37
- anemoi/datasets/create/input/join.py +86 -14
- anemoi/datasets/create/input/misc.py +67 -17
- anemoi/datasets/create/input/pipe.py +33 -6
- anemoi/datasets/create/input/repeated_dates.py +189 -41
- anemoi/datasets/create/input/result.py +202 -87
- anemoi/datasets/create/input/step.py +119 -22
- anemoi/datasets/create/input/template.py +100 -13
- anemoi/datasets/create/input/trace.py +62 -7
- anemoi/datasets/create/patch.py +52 -4
- anemoi/datasets/create/persistent.py +134 -17
- anemoi/datasets/create/size.py +15 -1
- anemoi/datasets/create/source.py +51 -0
- anemoi/datasets/create/sources/__init__.py +36 -0
- anemoi/datasets/create/{functions/sources → sources}/accumulations.py +296 -30
- anemoi/datasets/create/{functions/sources → sources}/constants.py +27 -2
- anemoi/datasets/create/{functions/sources → sources}/eccc_fstd.py +7 -3
- anemoi/datasets/create/sources/empty.py +37 -0
- anemoi/datasets/create/{functions/sources → sources}/forcings.py +25 -1
- anemoi/datasets/create/sources/grib.py +297 -0
- anemoi/datasets/create/{functions/sources → sources}/hindcasts.py +38 -4
- anemoi/datasets/create/sources/legacy.py +93 -0
- anemoi/datasets/create/{functions/sources → sources}/mars.py +168 -20
- anemoi/datasets/create/sources/netcdf.py +42 -0
- anemoi/datasets/create/sources/opendap.py +43 -0
- anemoi/datasets/create/{functions/sources/__init__.py → sources/patterns.py} +35 -4
- anemoi/datasets/create/sources/recentre.py +150 -0
- anemoi/datasets/create/{functions/sources → sources}/source.py +27 -5
- anemoi/datasets/create/{functions/sources → sources}/tendencies.py +64 -7
- anemoi/datasets/create/sources/xarray.py +92 -0
- anemoi/datasets/create/sources/xarray_kerchunk.py +36 -0
- anemoi/datasets/create/sources/xarray_support/README.md +1 -0
- anemoi/datasets/create/{functions/sources/xarray → sources/xarray_support}/__init__.py +109 -8
- anemoi/datasets/create/sources/xarray_support/coordinates.py +442 -0
- anemoi/datasets/create/{functions/sources/xarray → sources/xarray_support}/field.py +94 -16
- anemoi/datasets/create/{functions/sources/xarray → sources/xarray_support}/fieldlist.py +90 -25
- anemoi/datasets/create/sources/xarray_support/flavour.py +1036 -0
- anemoi/datasets/create/{functions/sources/xarray → sources/xarray_support}/grid.py +92 -31
- anemoi/datasets/create/sources/xarray_support/metadata.py +395 -0
- anemoi/datasets/create/sources/xarray_support/patch.py +91 -0
- anemoi/datasets/create/sources/xarray_support/time.py +391 -0
- anemoi/datasets/create/sources/xarray_support/variable.py +331 -0
- anemoi/datasets/create/sources/xarray_zarr.py +41 -0
- anemoi/datasets/create/{functions/sources → sources}/zenodo.py +34 -5
- anemoi/datasets/create/statistics/__init__.py +233 -44
- anemoi/datasets/create/statistics/summary.py +52 -6
- anemoi/datasets/create/testing.py +76 -0
- anemoi/datasets/create/{functions/filters/noop.py → typing.py} +6 -3
- anemoi/datasets/create/utils.py +97 -6
- anemoi/datasets/create/writer.py +26 -4
- anemoi/datasets/create/zarr.py +170 -23
- anemoi/datasets/data/__init__.py +51 -4
- anemoi/datasets/data/complement.py +191 -40
- anemoi/datasets/data/concat.py +141 -16
- anemoi/datasets/data/dataset.py +552 -61
- anemoi/datasets/data/debug.py +197 -26
- anemoi/datasets/data/ensemble.py +93 -8
- anemoi/datasets/data/fill_missing.py +165 -18
- anemoi/datasets/data/forwards.py +428 -56
- anemoi/datasets/data/grids.py +323 -97
- anemoi/datasets/data/indexing.py +112 -19
- anemoi/datasets/data/interpolate.py +92 -12
- anemoi/datasets/data/join.py +158 -19
- anemoi/datasets/data/masked.py +129 -15
- anemoi/datasets/data/merge.py +137 -23
- anemoi/datasets/data/misc.py +172 -16
- anemoi/datasets/data/missing.py +233 -29
- anemoi/datasets/data/rescale.py +111 -10
- anemoi/datasets/data/select.py +168 -26
- anemoi/datasets/data/statistics.py +67 -6
- anemoi/datasets/data/stores.py +149 -64
- anemoi/datasets/data/subset.py +159 -25
- anemoi/datasets/data/unchecked.py +168 -57
- anemoi/datasets/data/xy.py +168 -25
- anemoi/datasets/dates/__init__.py +191 -16
- anemoi/datasets/dates/groups.py +189 -47
- anemoi/datasets/grids.py +270 -31
- anemoi/datasets/testing.py +28 -1
- {anemoi_datasets-0.5.15.dist-info → anemoi_datasets-0.5.17.dist-info}/METADATA +10 -7
- anemoi_datasets-0.5.17.dist-info/RECORD +137 -0
- {anemoi_datasets-0.5.15.dist-info → anemoi_datasets-0.5.17.dist-info}/WHEEL +1 -1
- {anemoi_datasets-0.5.15.dist-info → anemoi_datasets-0.5.17.dist-info/licenses}/LICENSE +1 -1
- anemoi/datasets/create/functions/__init__.py +0 -66
- anemoi/datasets/create/functions/filters/__init__.py +0 -9
- anemoi/datasets/create/functions/filters/empty.py +0 -17
- anemoi/datasets/create/functions/filters/orog_to_z.py +0 -58
- anemoi/datasets/create/functions/filters/rename.py +0 -79
- anemoi/datasets/create/functions/filters/speeddir_to_uv.py +0 -78
- anemoi/datasets/create/functions/filters/uv_to_speeddir.py +0 -56
- anemoi/datasets/create/functions/sources/empty.py +0 -15
- anemoi/datasets/create/functions/sources/grib.py +0 -150
- anemoi/datasets/create/functions/sources/netcdf.py +0 -15
- anemoi/datasets/create/functions/sources/opendap.py +0 -15
- anemoi/datasets/create/functions/sources/recentre.py +0 -60
- anemoi/datasets/create/functions/sources/xarray/coordinates.py +0 -255
- anemoi/datasets/create/functions/sources/xarray/flavour.py +0 -472
- anemoi/datasets/create/functions/sources/xarray/metadata.py +0 -148
- anemoi/datasets/create/functions/sources/xarray/patch.py +0 -44
- anemoi/datasets/create/functions/sources/xarray/time.py +0 -177
- anemoi/datasets/create/functions/sources/xarray/variable.py +0 -188
- anemoi/datasets/create/functions/sources/xarray_kerchunk.py +0 -42
- anemoi/datasets/create/functions/sources/xarray_zarr.py +0 -15
- anemoi/datasets/utils/fields.py +0 -47
- anemoi_datasets-0.5.15.dist-info/RECORD +0 -129
- {anemoi_datasets-0.5.15.dist-info → anemoi_datasets-0.5.17.dist-info}/entry_points.txt +0 -0
- {anemoi_datasets-0.5.15.dist-info → anemoi_datasets-0.5.17.dist-info}/top_level.txt +0 -0
|
@@ -9,8 +9,12 @@
|
|
|
9
9
|
|
|
10
10
|
import logging
|
|
11
11
|
from functools import cached_property
|
|
12
|
+
from typing import Any
|
|
13
|
+
from typing import Dict
|
|
12
14
|
|
|
13
|
-
from
|
|
15
|
+
from earthkit.data import FieldList
|
|
16
|
+
|
|
17
|
+
from ...dates.groups import GroupOfDates
|
|
14
18
|
from .action import Action
|
|
15
19
|
from .misc import _tidy
|
|
16
20
|
from .misc import assert_fieldlist
|
|
@@ -30,87 +34,200 @@ class FunctionContext:
|
|
|
30
34
|
to the functions from the other actions and filters and results.
|
|
31
35
|
"""
|
|
32
36
|
|
|
33
|
-
def __init__(self, owner):
|
|
34
|
-
|
|
35
|
-
self.use_grib_paramid = owner.context.use_grib_paramid
|
|
37
|
+
def __init__(self, owner: Result) -> None:
|
|
38
|
+
"""Initializes a FunctionContext instance.
|
|
36
39
|
|
|
37
|
-
|
|
40
|
+
Parameters
|
|
41
|
+
----------
|
|
42
|
+
owner : object
|
|
43
|
+
The owner object.
|
|
44
|
+
"""
|
|
45
|
+
self.owner = owner
|
|
46
|
+
self.use_grib_paramid: bool = owner.context.use_grib_paramid
|
|
47
|
+
|
|
48
|
+
def trace(self, emoji: str, *args: Any) -> None:
|
|
49
|
+
"""Traces the given arguments with an emoji.
|
|
50
|
+
|
|
51
|
+
Parameters
|
|
52
|
+
----------
|
|
53
|
+
emoji : str
|
|
54
|
+
The emoji to use.
|
|
55
|
+
*args : Any
|
|
56
|
+
The arguments to trace.
|
|
57
|
+
"""
|
|
38
58
|
trace(emoji, *args)
|
|
39
59
|
|
|
40
|
-
def info(self, *args, **kwargs):
|
|
60
|
+
def info(self, *args: Any, **kwargs: Any) -> None:
|
|
61
|
+
"""Logs an info message.
|
|
62
|
+
|
|
63
|
+
Parameters
|
|
64
|
+
----------
|
|
65
|
+
*args : Any
|
|
66
|
+
The arguments for the log message.
|
|
67
|
+
**kwargs : Any
|
|
68
|
+
The keyword arguments for the log message.
|
|
69
|
+
"""
|
|
41
70
|
LOG.info(*args, **kwargs)
|
|
42
71
|
|
|
43
72
|
@property
|
|
44
|
-
def dates_provider(self):
|
|
73
|
+
def dates_provider(self) -> object:
|
|
74
|
+
"""Returns the dates provider."""
|
|
45
75
|
return self.owner.group_of_dates.provider
|
|
46
76
|
|
|
47
77
|
@property
|
|
48
|
-
def partial_ok(self):
|
|
78
|
+
def partial_ok(self) -> bool:
|
|
79
|
+
"""Returns whether partial results are acceptable."""
|
|
49
80
|
return self.owner.group_of_dates.partial_ok
|
|
50
81
|
|
|
51
82
|
|
|
52
83
|
class FunctionAction(Action):
|
|
53
|
-
|
|
84
|
+
"""Represents an action that executes a function.
|
|
85
|
+
|
|
86
|
+
Attributes
|
|
87
|
+
----------
|
|
88
|
+
name : str
|
|
89
|
+
The name of the function.
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
def __init__(self, context: object, action_path: list, _name: str, source, **kwargs: Dict[str, Any]) -> None:
|
|
93
|
+
"""Initializes a FunctionAction instance.
|
|
94
|
+
|
|
95
|
+
Parameters
|
|
96
|
+
----------
|
|
97
|
+
context : object
|
|
98
|
+
The context object.
|
|
99
|
+
action_path : list
|
|
100
|
+
The action path.
|
|
101
|
+
_name : str
|
|
102
|
+
The name of the function.
|
|
103
|
+
**kwargs : Dict[str, Any]
|
|
104
|
+
Additional keyword arguments.
|
|
105
|
+
"""
|
|
54
106
|
super().__init__(context, action_path, **kwargs)
|
|
55
|
-
self.name = _name
|
|
107
|
+
self.name: str = _name
|
|
108
|
+
self.source = source
|
|
56
109
|
|
|
57
110
|
@trace_select
|
|
58
|
-
def select(self, group_of_dates):
|
|
111
|
+
def select(self, group_of_dates: GroupOfDates) -> "FunctionResult":
|
|
112
|
+
"""Selects the function result for the given group of dates.
|
|
113
|
+
|
|
114
|
+
Parameters
|
|
115
|
+
----------
|
|
116
|
+
group_of_dates : GroupOfDates
|
|
117
|
+
The group of dates.
|
|
118
|
+
|
|
119
|
+
Returns
|
|
120
|
+
-------
|
|
121
|
+
FunctionResult
|
|
122
|
+
The function result instance.
|
|
123
|
+
"""
|
|
59
124
|
return FunctionResult(self.context, self.action_path, group_of_dates, action=self)
|
|
60
125
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
return import_function(self.name, "sources")
|
|
65
|
-
|
|
66
|
-
def __repr__(self):
|
|
67
|
-
content = ""
|
|
126
|
+
def __repr__(self) -> str:
|
|
127
|
+
"""Returns a string representation of the FunctionAction instance."""
|
|
128
|
+
content: str = ""
|
|
68
129
|
content += ",".join([self._short_str(a) for a in self.args])
|
|
69
130
|
content += " ".join([self._short_str(f"{k}={v}") for k, v in self.kwargs.items()])
|
|
70
131
|
content = self._short_str(content)
|
|
71
|
-
return
|
|
132
|
+
return self._repr(_inline_=content, _indent_=" ")
|
|
133
|
+
|
|
134
|
+
def _trace_select(self, group_of_dates: GroupOfDates) -> str:
|
|
135
|
+
"""Traces the selection of the function for the given group of dates.
|
|
72
136
|
|
|
73
|
-
|
|
137
|
+
Parameters
|
|
138
|
+
----------
|
|
139
|
+
group_of_dates : GroupOfDates
|
|
140
|
+
The group of dates.
|
|
141
|
+
|
|
142
|
+
Returns
|
|
143
|
+
-------
|
|
144
|
+
str
|
|
145
|
+
The trace string.
|
|
146
|
+
"""
|
|
74
147
|
return f"{self.name}({group_of_dates})"
|
|
75
148
|
|
|
76
149
|
|
|
77
150
|
class FunctionResult(Result):
|
|
78
|
-
|
|
151
|
+
"""Represents the result of executing a function.
|
|
152
|
+
|
|
153
|
+
Attributes
|
|
154
|
+
----------
|
|
155
|
+
action : Action
|
|
156
|
+
The action instance.
|
|
157
|
+
args : tuple
|
|
158
|
+
The positional arguments for the function.
|
|
159
|
+
kwargs : dict
|
|
160
|
+
The keyword arguments for the function.
|
|
161
|
+
"""
|
|
162
|
+
|
|
163
|
+
def __init__(self, context: object, action_path: list, group_of_dates: GroupOfDates, action: Action) -> None:
|
|
164
|
+
"""Initializes a FunctionResult instance.
|
|
165
|
+
|
|
166
|
+
Parameters
|
|
167
|
+
----------
|
|
168
|
+
context : object
|
|
169
|
+
The context object.
|
|
170
|
+
action_path : list
|
|
171
|
+
The action path.
|
|
172
|
+
group_of_dates : GroupOfDates
|
|
173
|
+
The group of dates.
|
|
174
|
+
action : Action
|
|
175
|
+
The action instance.
|
|
176
|
+
"""
|
|
79
177
|
super().__init__(context, action_path, group_of_dates)
|
|
80
178
|
assert isinstance(action, Action), type(action)
|
|
81
|
-
self.action = action
|
|
179
|
+
self.action: Action = action
|
|
82
180
|
|
|
83
181
|
self.args, self.kwargs = substitute(context, (self.action.args, self.action.kwargs))
|
|
84
182
|
|
|
85
|
-
def _trace_datasource(self, *args, **kwargs):
|
|
183
|
+
def _trace_datasource(self, *args: Any, **kwargs: Any) -> str:
|
|
184
|
+
"""Traces the datasource for the given arguments.
|
|
185
|
+
|
|
186
|
+
Parameters
|
|
187
|
+
----------
|
|
188
|
+
*args : Any
|
|
189
|
+
The arguments.
|
|
190
|
+
**kwargs : Any
|
|
191
|
+
The keyword arguments.
|
|
192
|
+
|
|
193
|
+
Returns
|
|
194
|
+
-------
|
|
195
|
+
str
|
|
196
|
+
The trace string.
|
|
197
|
+
"""
|
|
86
198
|
return f"{self.action.name}({self.group_of_dates})"
|
|
87
199
|
|
|
88
200
|
@cached_property
|
|
89
201
|
@assert_fieldlist
|
|
90
202
|
@notify_result
|
|
91
203
|
@trace_datasource
|
|
92
|
-
def datasource(self):
|
|
204
|
+
def datasource(self) -> FieldList:
|
|
205
|
+
"""Returns the datasource for the function result."""
|
|
93
206
|
args, kwargs = resolve(self.context, (self.args, self.kwargs))
|
|
207
|
+
self.action.source.context = FunctionContext(self)
|
|
94
208
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
self.
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
*args,
|
|
101
|
-
**kwargs,
|
|
102
|
-
)
|
|
209
|
+
return _tidy(
|
|
210
|
+
self.action.source.execute(
|
|
211
|
+
self.group_of_dates, # Will provide a list of datetime objects
|
|
212
|
+
*args,
|
|
213
|
+
**kwargs,
|
|
103
214
|
)
|
|
104
|
-
|
|
105
|
-
LOG.error(f"Error in {self.action.function.__name__}", exc_info=True)
|
|
106
|
-
raise
|
|
215
|
+
)
|
|
107
216
|
|
|
108
|
-
def __repr__(self):
|
|
217
|
+
def __repr__(self) -> str:
|
|
218
|
+
"""Returns a string representation of the FunctionResult instance."""
|
|
109
219
|
try:
|
|
110
220
|
return f"{self.action.name}({self.group_of_dates})"
|
|
111
221
|
except Exception:
|
|
112
222
|
return f"{self.__class__.__name__}(unitialised)"
|
|
113
223
|
|
|
114
224
|
@property
|
|
115
|
-
def function(self):
|
|
225
|
+
def function(self) -> None:
|
|
226
|
+
"""Raises NotImplementedError as this property is not implemented.
|
|
227
|
+
|
|
228
|
+
Raises
|
|
229
|
+
------
|
|
230
|
+
NotImplementedError
|
|
231
|
+
Always raised.
|
|
232
|
+
"""
|
|
116
233
|
raise NotImplementedError(f"Not implemented in {self.__class__.__name__}")
|
|
@@ -9,7 +9,12 @@
|
|
|
9
9
|
|
|
10
10
|
import logging
|
|
11
11
|
from functools import cached_property
|
|
12
|
+
from typing import Any
|
|
13
|
+
from typing import List
|
|
12
14
|
|
|
15
|
+
from earthkit.data import FieldList
|
|
16
|
+
|
|
17
|
+
from ...dates.groups import GroupOfDates
|
|
13
18
|
from .action import Action
|
|
14
19
|
from .action import action_factory
|
|
15
20
|
from .empty import EmptyResult
|
|
@@ -24,35 +29,102 @@ LOG = logging.getLogger(__name__)
|
|
|
24
29
|
|
|
25
30
|
|
|
26
31
|
class JoinResult(Result):
|
|
27
|
-
|
|
32
|
+
"""Represents a result that combines multiple results.
|
|
33
|
+
|
|
34
|
+
Attributes
|
|
35
|
+
----------
|
|
36
|
+
context : object
|
|
37
|
+
The context object.
|
|
38
|
+
action_path : list
|
|
39
|
+
The action path.
|
|
40
|
+
group_of_dates : GroupOfDates
|
|
41
|
+
The group of dates.
|
|
42
|
+
results : List[Result]
|
|
43
|
+
The list of results.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __init__(
|
|
47
|
+
self, context: object, action_path: list, group_of_dates: GroupOfDates, results: List[Result], **kwargs: Any
|
|
48
|
+
) -> None:
|
|
49
|
+
"""Initializes a JoinResult instance.
|
|
50
|
+
|
|
51
|
+
Parameters
|
|
52
|
+
----------
|
|
53
|
+
context : object
|
|
54
|
+
The context object.
|
|
55
|
+
action_path : list
|
|
56
|
+
The action path.
|
|
57
|
+
group_of_dates : GroupOfDates
|
|
58
|
+
The group of dates.
|
|
59
|
+
results : List[Result]
|
|
60
|
+
The list of results.
|
|
61
|
+
"""
|
|
28
62
|
super().__init__(context, action_path, group_of_dates)
|
|
29
|
-
self.results = [r for r in results if not r.empty]
|
|
63
|
+
self.results: List[Result] = [r for r in results if not r.empty]
|
|
30
64
|
|
|
31
65
|
@cached_property
|
|
32
66
|
@assert_fieldlist
|
|
33
67
|
@notify_result
|
|
34
68
|
@trace_datasource
|
|
35
|
-
def datasource(self):
|
|
36
|
-
|
|
69
|
+
def datasource(self) -> FieldList:
|
|
70
|
+
"""Returns the combined datasource from all results."""
|
|
71
|
+
ds: FieldList = EmptyResult(self.context, self.action_path, self.group_of_dates).datasource
|
|
37
72
|
for i in self.results:
|
|
38
73
|
ds += i.datasource
|
|
39
74
|
return _tidy(ds)
|
|
40
75
|
|
|
41
|
-
def __repr__(self):
|
|
42
|
-
|
|
43
|
-
|
|
76
|
+
def __repr__(self) -> str:
|
|
77
|
+
"""Returns a string representation of the JoinResult instance."""
|
|
78
|
+
content: str = "\n".join([str(i) for i in self.results])
|
|
79
|
+
return self._repr(content)
|
|
44
80
|
|
|
45
81
|
|
|
46
82
|
class JoinAction(Action):
|
|
47
|
-
|
|
83
|
+
"""Represents an action that combines multiple actions.
|
|
84
|
+
|
|
85
|
+
Attributes
|
|
86
|
+
----------
|
|
87
|
+
context : object
|
|
88
|
+
The context object.
|
|
89
|
+
action_path : list
|
|
90
|
+
The action path.
|
|
91
|
+
actions : List[Action]
|
|
92
|
+
The list of actions.
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
def __init__(self, context: object, action_path: list, *configs: dict) -> None:
|
|
96
|
+
"""Initializes a JoinAction instance.
|
|
97
|
+
|
|
98
|
+
Parameters
|
|
99
|
+
----------
|
|
100
|
+
context : object
|
|
101
|
+
The context object.
|
|
102
|
+
action_path : list
|
|
103
|
+
The action path.
|
|
104
|
+
*configs : dict
|
|
105
|
+
The configuration dictionaries.
|
|
106
|
+
"""
|
|
48
107
|
super().__init__(context, action_path, *configs)
|
|
49
|
-
self.actions = [action_factory(c, context, action_path + [str(i)]) for i, c in enumerate(configs)]
|
|
108
|
+
self.actions: List[Action] = [action_factory(c, context, action_path + [str(i)]) for i, c in enumerate(configs)]
|
|
50
109
|
|
|
51
|
-
def __repr__(self):
|
|
52
|
-
|
|
53
|
-
|
|
110
|
+
def __repr__(self) -> str:
|
|
111
|
+
"""Returns a string representation of the JoinAction instance."""
|
|
112
|
+
content: str = "\n".join([str(i) for i in self.actions])
|
|
113
|
+
return self._repr(content)
|
|
54
114
|
|
|
55
115
|
@trace_select
|
|
56
|
-
def select(self, group_of_dates):
|
|
57
|
-
|
|
116
|
+
def select(self, group_of_dates: GroupOfDates) -> JoinResult:
|
|
117
|
+
"""Selects the results for the given group of dates.
|
|
118
|
+
|
|
119
|
+
Parameters
|
|
120
|
+
----------
|
|
121
|
+
group_of_dates : GroupOfDates
|
|
122
|
+
The group of dates.
|
|
123
|
+
|
|
124
|
+
Returns
|
|
125
|
+
-------
|
|
126
|
+
JoinResult
|
|
127
|
+
The combined result for the given group of dates.
|
|
128
|
+
"""
|
|
129
|
+
results: List[Result] = [a.select(group_of_dates) for a in self.actions]
|
|
58
130
|
return JoinResult(self.context, self.action_path, group_of_dates, results)
|
|
@@ -9,17 +9,30 @@
|
|
|
9
9
|
|
|
10
10
|
import logging
|
|
11
11
|
from functools import wraps
|
|
12
|
+
from typing import Any
|
|
13
|
+
from typing import Callable
|
|
14
|
+
from typing import Tuple
|
|
15
|
+
from typing import Union
|
|
12
16
|
|
|
17
|
+
from earthkit.data import FieldList
|
|
13
18
|
from earthkit.data.core.fieldlist import MultiFieldList
|
|
14
|
-
from earthkit.data.indexing.fieldlist import FieldList
|
|
15
|
-
|
|
16
|
-
from ..functions import import_function
|
|
17
19
|
|
|
18
20
|
LOG = logging.getLogger(__name__)
|
|
19
21
|
|
|
20
22
|
|
|
21
|
-
def parse_function_name(name):
|
|
23
|
+
def parse_function_name(name: str) -> Tuple[str, Union[int, None]]:
|
|
24
|
+
"""Parses a function name to extract the base name and an optional time delta.
|
|
25
|
+
|
|
26
|
+
Parameters
|
|
27
|
+
----------
|
|
28
|
+
name : str
|
|
29
|
+
The function name to parse.
|
|
22
30
|
|
|
31
|
+
Returns
|
|
32
|
+
-------
|
|
33
|
+
tuple of (str, int or None)
|
|
34
|
+
The base name and an optional time delta.
|
|
35
|
+
"""
|
|
23
36
|
if name.endswith("h") and name[:-1].isdigit():
|
|
24
37
|
|
|
25
38
|
if "-" in name:
|
|
@@ -40,19 +53,23 @@ def parse_function_name(name):
|
|
|
40
53
|
return name, None
|
|
41
54
|
|
|
42
55
|
|
|
43
|
-
def
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
return False
|
|
56
|
+
def assert_fieldlist(method: Callable[..., Any]) -> Callable[..., Any]:
|
|
57
|
+
"""Decorator to assert that the result of a method is an instance of FieldList.
|
|
58
|
+
|
|
59
|
+
Parameters
|
|
60
|
+
----------
|
|
61
|
+
method : Callable[..., Any]
|
|
62
|
+
The method to decorate.
|
|
51
63
|
|
|
64
|
+
Returns
|
|
65
|
+
-------
|
|
66
|
+
Callable[..., Any]
|
|
67
|
+
The decorated method.
|
|
68
|
+
"""
|
|
52
69
|
|
|
53
|
-
def assert_fieldlist(method):
|
|
54
70
|
@wraps(method)
|
|
55
|
-
def wrapper(self, *args, **kwargs):
|
|
71
|
+
def wrapper(self: Any, *args: Any, **kwargs: Any) -> Any:
|
|
72
|
+
|
|
56
73
|
result = method(self, *args, **kwargs)
|
|
57
74
|
assert isinstance(result, FieldList), type(result)
|
|
58
75
|
return result
|
|
@@ -60,17 +77,50 @@ def assert_fieldlist(method):
|
|
|
60
77
|
return wrapper
|
|
61
78
|
|
|
62
79
|
|
|
63
|
-
def assert_is_fieldlist(obj):
|
|
80
|
+
def assert_is_fieldlist(obj: object) -> None:
|
|
81
|
+
"""Asserts that the given object is an instance of FieldList.
|
|
82
|
+
|
|
83
|
+
Parameters
|
|
84
|
+
----------
|
|
85
|
+
obj : object
|
|
86
|
+
The object to check.
|
|
87
|
+
"""
|
|
64
88
|
assert isinstance(obj, FieldList), type(obj)
|
|
65
89
|
|
|
66
90
|
|
|
67
|
-
def _flatten(ds):
|
|
91
|
+
def _flatten(ds: Union[MultiFieldList, FieldList]) -> list:
|
|
92
|
+
"""Flattens a MultiFieldList or FieldList into a list of FieldList objects.
|
|
93
|
+
|
|
94
|
+
Parameters
|
|
95
|
+
----------
|
|
96
|
+
ds : Union[MultiFieldList, FieldList]
|
|
97
|
+
The dataset to flatten.
|
|
98
|
+
|
|
99
|
+
Returns
|
|
100
|
+
-------
|
|
101
|
+
list
|
|
102
|
+
A list of FieldList objects.
|
|
103
|
+
"""
|
|
68
104
|
if isinstance(ds, MultiFieldList):
|
|
69
105
|
return [_tidy(f) for s in ds._indexes for f in _flatten(s)]
|
|
70
106
|
return [ds]
|
|
71
107
|
|
|
72
108
|
|
|
73
|
-
def _tidy(ds, indent=0):
|
|
109
|
+
def _tidy(ds: Union[MultiFieldList, FieldList], indent: int = 0) -> Union[MultiFieldList, FieldList]:
|
|
110
|
+
"""Tidies up a MultiFieldList or FieldList by removing empty sources.
|
|
111
|
+
|
|
112
|
+
Parameters
|
|
113
|
+
----------
|
|
114
|
+
ds : Union[MultiFieldList, FieldList]
|
|
115
|
+
The dataset to tidy.
|
|
116
|
+
indent : int, optional
|
|
117
|
+
The indentation level. Defaults to 0.
|
|
118
|
+
|
|
119
|
+
Returns
|
|
120
|
+
-------
|
|
121
|
+
Union[MultiFieldList, FieldList]
|
|
122
|
+
The tidied dataset.
|
|
123
|
+
"""
|
|
74
124
|
if isinstance(ds, MultiFieldList):
|
|
75
125
|
|
|
76
126
|
sources = [s for s in _flatten(ds) if len(s) > 0]
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import json
|
|
11
11
|
import logging
|
|
12
|
+
from typing import Any
|
|
12
13
|
|
|
13
14
|
from .action import Action
|
|
14
15
|
from .action import action_factory
|
|
@@ -19,21 +20,47 @@ LOG = logging.getLogger(__name__)
|
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
class PipeAction(Action):
|
|
22
|
-
|
|
23
|
+
"""A class to represent a pipeline of actions."""
|
|
24
|
+
|
|
25
|
+
def __init__(self, context: Any, action_path: list, *configs: dict) -> None:
|
|
26
|
+
"""Initialize the PipeAction.
|
|
27
|
+
|
|
28
|
+
Parameters
|
|
29
|
+
----------
|
|
30
|
+
context : Any
|
|
31
|
+
The context for the action.
|
|
32
|
+
action_path : list
|
|
33
|
+
The path of the action.
|
|
34
|
+
configs : dict
|
|
35
|
+
The configurations for the actions.
|
|
36
|
+
"""
|
|
23
37
|
super().__init__(context, action_path, *configs)
|
|
24
38
|
if len(configs) <= 1:
|
|
25
39
|
raise ValueError(
|
|
26
40
|
f"PipeAction requires at least two actions, got {len(configs)}\n{json.dumps(configs, indent=2)}"
|
|
27
41
|
)
|
|
28
42
|
|
|
29
|
-
current = action_factory(configs[0], context, action_path + ["0"])
|
|
43
|
+
current: Any = action_factory(configs[0], context, action_path + ["0"])
|
|
30
44
|
for i, c in enumerate(configs[1:]):
|
|
31
45
|
current = step_factory(c, context, action_path + [str(i + 1)], previous_step=current)
|
|
32
|
-
self.last_step = current
|
|
46
|
+
self.last_step: Any = current
|
|
33
47
|
|
|
34
48
|
@trace_select
|
|
35
|
-
def select(self, group_of_dates):
|
|
49
|
+
def select(self, group_of_dates: Any) -> Any:
|
|
50
|
+
"""Select data based on the group of dates.
|
|
51
|
+
|
|
52
|
+
Parameters
|
|
53
|
+
----------
|
|
54
|
+
group_of_dates : Any
|
|
55
|
+
The group of dates to select data for.
|
|
56
|
+
|
|
57
|
+
Returns
|
|
58
|
+
-------
|
|
59
|
+
Any
|
|
60
|
+
The selected data.
|
|
61
|
+
"""
|
|
36
62
|
return self.last_step.select(group_of_dates)
|
|
37
63
|
|
|
38
|
-
def __repr__(self):
|
|
39
|
-
|
|
64
|
+
def __repr__(self) -> str:
|
|
65
|
+
"""Return a string representation of the PipeAction."""
|
|
66
|
+
return f"PipeAction({self.last_step})"
|