anemoi-datasets 0.5.16__py3-none-any.whl → 0.5.18__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 (155) hide show
  1. anemoi/datasets/__init__.py +4 -1
  2. anemoi/datasets/__main__.py +12 -2
  3. anemoi/datasets/_version.py +9 -4
  4. anemoi/datasets/commands/cleanup.py +17 -2
  5. anemoi/datasets/commands/compare.py +18 -2
  6. anemoi/datasets/commands/copy.py +196 -14
  7. anemoi/datasets/commands/create.py +50 -7
  8. anemoi/datasets/commands/finalise-additions.py +17 -2
  9. anemoi/datasets/commands/finalise.py +17 -2
  10. anemoi/datasets/commands/init-additions.py +17 -2
  11. anemoi/datasets/commands/init.py +16 -2
  12. anemoi/datasets/commands/inspect.py +283 -62
  13. anemoi/datasets/commands/load-additions.py +16 -2
  14. anemoi/datasets/commands/load.py +16 -2
  15. anemoi/datasets/commands/patch.py +17 -2
  16. anemoi/datasets/commands/publish.py +17 -2
  17. anemoi/datasets/commands/scan.py +31 -3
  18. anemoi/datasets/compute/recentre.py +47 -11
  19. anemoi/datasets/create/__init__.py +612 -85
  20. anemoi/datasets/create/check.py +142 -20
  21. anemoi/datasets/create/chunks.py +64 -4
  22. anemoi/datasets/create/config.py +185 -21
  23. anemoi/datasets/create/filter.py +50 -0
  24. anemoi/datasets/create/filters/__init__.py +33 -0
  25. anemoi/datasets/create/filters/empty.py +37 -0
  26. anemoi/datasets/create/filters/legacy.py +93 -0
  27. anemoi/datasets/create/filters/noop.py +37 -0
  28. anemoi/datasets/create/filters/orog_to_z.py +58 -0
  29. anemoi/datasets/create/{functions/filters → filters}/pressure_level_relative_humidity_to_specific_humidity.py +33 -10
  30. anemoi/datasets/create/{functions/filters → filters}/pressure_level_specific_humidity_to_relative_humidity.py +32 -8
  31. anemoi/datasets/create/filters/rename.py +205 -0
  32. anemoi/datasets/create/{functions/filters → filters}/rotate_winds.py +43 -28
  33. anemoi/datasets/create/{functions/filters → filters}/single_level_dewpoint_to_relative_humidity.py +32 -9
  34. anemoi/datasets/create/{functions/filters → filters}/single_level_relative_humidity_to_dewpoint.py +33 -9
  35. anemoi/datasets/create/{functions/filters → filters}/single_level_relative_humidity_to_specific_humidity.py +55 -7
  36. anemoi/datasets/create/{functions/filters → filters}/single_level_specific_humidity_to_relative_humidity.py +98 -37
  37. anemoi/datasets/create/filters/speeddir_to_uv.py +95 -0
  38. anemoi/datasets/create/{functions/filters → filters}/sum.py +24 -27
  39. anemoi/datasets/create/filters/transform.py +53 -0
  40. anemoi/datasets/create/{functions/filters → filters}/unrotate_winds.py +27 -18
  41. anemoi/datasets/create/filters/uv_to_speeddir.py +94 -0
  42. anemoi/datasets/create/{functions/filters → filters}/wz_to_w.py +51 -33
  43. anemoi/datasets/create/input/__init__.py +76 -5
  44. anemoi/datasets/create/input/action.py +149 -13
  45. anemoi/datasets/create/input/concat.py +81 -10
  46. anemoi/datasets/create/input/context.py +39 -4
  47. anemoi/datasets/create/input/data_sources.py +72 -6
  48. anemoi/datasets/create/input/empty.py +21 -3
  49. anemoi/datasets/create/input/filter.py +60 -12
  50. anemoi/datasets/create/input/function.py +154 -37
  51. anemoi/datasets/create/input/join.py +86 -14
  52. anemoi/datasets/create/input/misc.py +67 -17
  53. anemoi/datasets/create/input/pipe.py +33 -6
  54. anemoi/datasets/create/input/repeated_dates.py +189 -41
  55. anemoi/datasets/create/input/result.py +202 -87
  56. anemoi/datasets/create/input/step.py +119 -22
  57. anemoi/datasets/create/input/template.py +100 -13
  58. anemoi/datasets/create/input/trace.py +62 -7
  59. anemoi/datasets/create/patch.py +52 -4
  60. anemoi/datasets/create/persistent.py +134 -17
  61. anemoi/datasets/create/size.py +15 -1
  62. anemoi/datasets/create/source.py +51 -0
  63. anemoi/datasets/create/sources/__init__.py +36 -0
  64. anemoi/datasets/create/{functions/sources → sources}/accumulations.py +296 -30
  65. anemoi/datasets/create/{functions/sources → sources}/constants.py +27 -2
  66. anemoi/datasets/create/{functions/sources → sources}/eccc_fstd.py +7 -3
  67. anemoi/datasets/create/sources/empty.py +37 -0
  68. anemoi/datasets/create/{functions/sources → sources}/forcings.py +25 -1
  69. anemoi/datasets/create/sources/grib.py +297 -0
  70. anemoi/datasets/create/{functions/sources → sources}/hindcasts.py +38 -4
  71. anemoi/datasets/create/sources/legacy.py +93 -0
  72. anemoi/datasets/create/{functions/sources → sources}/mars.py +168 -20
  73. anemoi/datasets/create/sources/netcdf.py +42 -0
  74. anemoi/datasets/create/sources/opendap.py +43 -0
  75. anemoi/datasets/create/{functions/sources/__init__.py → sources/patterns.py} +35 -4
  76. anemoi/datasets/create/sources/recentre.py +150 -0
  77. anemoi/datasets/create/{functions/sources → sources}/source.py +27 -5
  78. anemoi/datasets/create/{functions/sources → sources}/tendencies.py +64 -7
  79. anemoi/datasets/create/sources/xarray.py +92 -0
  80. anemoi/datasets/create/sources/xarray_kerchunk.py +36 -0
  81. anemoi/datasets/create/sources/xarray_support/README.md +1 -0
  82. anemoi/datasets/create/{functions/sources/xarray → sources/xarray_support}/__init__.py +109 -8
  83. anemoi/datasets/create/sources/xarray_support/coordinates.py +442 -0
  84. anemoi/datasets/create/{functions/sources/xarray → sources/xarray_support}/field.py +94 -16
  85. anemoi/datasets/create/{functions/sources/xarray → sources/xarray_support}/fieldlist.py +90 -25
  86. anemoi/datasets/create/sources/xarray_support/flavour.py +1036 -0
  87. anemoi/datasets/create/{functions/sources/xarray → sources/xarray_support}/grid.py +92 -31
  88. anemoi/datasets/create/sources/xarray_support/metadata.py +395 -0
  89. anemoi/datasets/create/sources/xarray_support/patch.py +91 -0
  90. anemoi/datasets/create/sources/xarray_support/time.py +391 -0
  91. anemoi/datasets/create/sources/xarray_support/variable.py +331 -0
  92. anemoi/datasets/create/sources/xarray_zarr.py +41 -0
  93. anemoi/datasets/create/{functions/sources → sources}/zenodo.py +34 -5
  94. anemoi/datasets/create/statistics/__init__.py +233 -44
  95. anemoi/datasets/create/statistics/summary.py +52 -6
  96. anemoi/datasets/create/testing.py +76 -0
  97. anemoi/datasets/create/{functions/filters/noop.py → typing.py} +6 -3
  98. anemoi/datasets/create/utils.py +97 -6
  99. anemoi/datasets/create/writer.py +26 -4
  100. anemoi/datasets/create/zarr.py +170 -23
  101. anemoi/datasets/data/__init__.py +51 -4
  102. anemoi/datasets/data/complement.py +191 -40
  103. anemoi/datasets/data/concat.py +141 -16
  104. anemoi/datasets/data/dataset.py +558 -62
  105. anemoi/datasets/data/debug.py +197 -26
  106. anemoi/datasets/data/ensemble.py +93 -8
  107. anemoi/datasets/data/fill_missing.py +165 -18
  108. anemoi/datasets/data/forwards.py +428 -56
  109. anemoi/datasets/data/grids.py +323 -97
  110. anemoi/datasets/data/indexing.py +112 -19
  111. anemoi/datasets/data/interpolate.py +92 -12
  112. anemoi/datasets/data/join.py +158 -19
  113. anemoi/datasets/data/masked.py +129 -15
  114. anemoi/datasets/data/merge.py +137 -23
  115. anemoi/datasets/data/misc.py +172 -16
  116. anemoi/datasets/data/missing.py +233 -29
  117. anemoi/datasets/data/rescale.py +111 -10
  118. anemoi/datasets/data/select.py +168 -26
  119. anemoi/datasets/data/statistics.py +67 -6
  120. anemoi/datasets/data/stores.py +149 -64
  121. anemoi/datasets/data/subset.py +159 -25
  122. anemoi/datasets/data/unchecked.py +168 -57
  123. anemoi/datasets/data/xy.py +168 -25
  124. anemoi/datasets/dates/__init__.py +191 -16
  125. anemoi/datasets/dates/groups.py +189 -47
  126. anemoi/datasets/grids.py +270 -31
  127. anemoi/datasets/testing.py +28 -1
  128. {anemoi_datasets-0.5.16.dist-info → anemoi_datasets-0.5.18.dist-info}/METADATA +9 -6
  129. anemoi_datasets-0.5.18.dist-info/RECORD +137 -0
  130. {anemoi_datasets-0.5.16.dist-info → anemoi_datasets-0.5.18.dist-info}/WHEEL +1 -1
  131. anemoi/datasets/create/functions/__init__.py +0 -66
  132. anemoi/datasets/create/functions/filters/__init__.py +0 -9
  133. anemoi/datasets/create/functions/filters/empty.py +0 -17
  134. anemoi/datasets/create/functions/filters/orog_to_z.py +0 -58
  135. anemoi/datasets/create/functions/filters/rename.py +0 -79
  136. anemoi/datasets/create/functions/filters/speeddir_to_uv.py +0 -78
  137. anemoi/datasets/create/functions/filters/uv_to_speeddir.py +0 -56
  138. anemoi/datasets/create/functions/sources/empty.py +0 -15
  139. anemoi/datasets/create/functions/sources/grib.py +0 -150
  140. anemoi/datasets/create/functions/sources/netcdf.py +0 -15
  141. anemoi/datasets/create/functions/sources/opendap.py +0 -15
  142. anemoi/datasets/create/functions/sources/recentre.py +0 -60
  143. anemoi/datasets/create/functions/sources/xarray/coordinates.py +0 -255
  144. anemoi/datasets/create/functions/sources/xarray/flavour.py +0 -472
  145. anemoi/datasets/create/functions/sources/xarray/metadata.py +0 -148
  146. anemoi/datasets/create/functions/sources/xarray/patch.py +0 -44
  147. anemoi/datasets/create/functions/sources/xarray/time.py +0 -177
  148. anemoi/datasets/create/functions/sources/xarray/variable.py +0 -188
  149. anemoi/datasets/create/functions/sources/xarray_kerchunk.py +0 -42
  150. anemoi/datasets/create/functions/sources/xarray_zarr.py +0 -15
  151. anemoi/datasets/utils/fields.py +0 -47
  152. anemoi_datasets-0.5.16.dist-info/RECORD +0 -129
  153. {anemoi_datasets-0.5.16.dist-info → anemoi_datasets-0.5.18.dist-info}/entry_points.txt +0 -0
  154. {anemoi_datasets-0.5.16.dist-info → anemoi_datasets-0.5.18.dist-info/licenses}/LICENSE +0 -0
  155. {anemoi_datasets-0.5.16.dist-info → anemoi_datasets-0.5.18.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,331 @@
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
+
11
+ import logging
12
+ import math
13
+ from functools import cached_property
14
+ from typing import Any
15
+ from typing import Dict
16
+ from typing import List
17
+ from typing import Optional
18
+ from typing import Tuple
19
+
20
+ import numpy as np
21
+ import xarray as xr
22
+
23
+ from .field import XArrayField
24
+
25
+ LOG = logging.getLogger(__name__)
26
+
27
+
28
+ class Variable:
29
+ """Represents a variable in an xarray dataset.
30
+
31
+ Attributes
32
+ ----------
33
+ ds : xr.Dataset
34
+ The xarray dataset.
35
+ variable : xr.DataArray
36
+ The data array representing the variable.
37
+ coordinates : List[Any]
38
+ List of coordinates associated with the variable.
39
+ grid : Any
40
+ The grid associated with the variable.
41
+ time : Any
42
+ The time dimension associated with the variable.
43
+ metadata : Dict[str, Any]
44
+ Metadata associated with the variable.
45
+ """
46
+
47
+ def __init__(
48
+ self,
49
+ *,
50
+ ds: xr.Dataset,
51
+ variable: xr.DataArray,
52
+ coordinates: List[Any],
53
+ grid: Any,
54
+ time: Any,
55
+ metadata: Dict[str, Any],
56
+ ):
57
+ """Initialize the Variable object.
58
+
59
+ Parameters
60
+ ----------
61
+ ds : xr.Dataset
62
+ The xarray dataset.
63
+ variable : xr.DataArray
64
+ The data array representing the variable.
65
+ coordinates : List[Any]
66
+ List of coordinates associated with the variable.
67
+ grid : Any
68
+ The grid associated with the variable.
69
+ time : Any
70
+ The time dimension associated with the variable.
71
+ metadata : Dict[str, Any]
72
+ Metadata associated with the variable.
73
+ """
74
+ self.ds = ds
75
+ self.variable = variable
76
+
77
+ self.grid = grid
78
+ self.coordinates = coordinates
79
+
80
+ self._metadata = metadata.copy()
81
+ self._metadata.update({"variable": variable.name, "param": variable.name})
82
+
83
+ self.time = time
84
+
85
+ self.shape = tuple(len(c.variable) for c in coordinates if c.is_dim and not c.scalar and not c.is_grid)
86
+ self.names = {c.variable.name: c for c in coordinates if c.is_dim and not c.scalar and not c.is_grid}
87
+ self.by_name = {c.variable.name: c for c in coordinates}
88
+
89
+ # We need that alias for the time dimension
90
+ self._aliases = dict(valid_datetime="time")
91
+
92
+ self.length = math.prod(self.shape)
93
+
94
+ @property
95
+ def name(self) -> str:
96
+ """Return the name of the variable."""
97
+ return str(self.variable.name)
98
+
99
+ def __len__(self) -> int:
100
+ """Return the length of the variable.
101
+
102
+ Returns
103
+ -------
104
+ int
105
+ The length of the variable.
106
+ """
107
+ return self.length
108
+
109
+ @property
110
+ def grid_mapping(self) -> Optional[Dict[str, Any]]:
111
+ """Return the grid mapping of the variable."""
112
+ grid_mapping = self.variable.attrs.get("grid_mapping", None)
113
+ if grid_mapping is None:
114
+ return None
115
+ return self.ds[grid_mapping].attrs
116
+
117
+ def grid_points(self) -> Any:
118
+ """Return the grid points of the variable.
119
+
120
+ Returns
121
+ -------
122
+ Any
123
+ The grid points of the variable.
124
+ """
125
+ return self.grid.grid_points
126
+
127
+ @property
128
+ def latitudes(self) -> Any:
129
+ """Return the latitudes of the variable."""
130
+ return self.grid.latitudes
131
+
132
+ @property
133
+ def longitudes(self) -> Any:
134
+ """Return the longitudes of the variable."""
135
+ return self.grid.longitudes
136
+
137
+ def __repr__(self) -> str:
138
+ """Return a string representation of the variable.
139
+
140
+ Returns
141
+ -------
142
+ str
143
+ A string representation of the variable.
144
+ """
145
+ return "Variable[name=%s,coordinates=%s,metadata=%s]" % (
146
+ self.variable.name,
147
+ self.coordinates,
148
+ self._metadata,
149
+ )
150
+
151
+ def __getitem__(self, i: int) -> "XArrayField":
152
+ """Get a 2D field from the variable.
153
+
154
+ Parameters
155
+ ----------
156
+ i : int
157
+ Index of the field.
158
+
159
+ Returns
160
+ -------
161
+ XArrayField
162
+ The 2D field at the specified index.
163
+
164
+ Raises
165
+ ------
166
+ IndexError
167
+ If the index is out of range.
168
+ """
169
+ if i >= self.length:
170
+ raise IndexError(i)
171
+
172
+ coords = np.unravel_index(i, self.shape)
173
+ kwargs = {k: v for k, v in zip(self.names, coords)}
174
+ return XArrayField(self, self.variable.isel(kwargs))
175
+
176
+ def sel(self, missing: Dict[str, Any], **kwargs: Any) -> Optional["Variable"]:
177
+ """Select a subset of the variable based on the given coordinates.
178
+
179
+ Parameters
180
+ ----------
181
+ missing : Dict[str, Any]
182
+ Dictionary to store missing coordinates.
183
+ **kwargs : Any
184
+ Coordinates to select.
185
+
186
+ Returns
187
+ -------
188
+ Optional[Variable]
189
+ The selected subset of the variable.
190
+ """
191
+ if not kwargs:
192
+ return self
193
+
194
+ k, v = kwargs.popitem()
195
+
196
+ user_provided_k = k
197
+
198
+ if k == "valid_datetime":
199
+ # Ask the Time object to select the valid datetime
200
+ k = self.time.select_valid_datetime(self)
201
+ if k is None:
202
+ return None
203
+
204
+ c = self.by_name.get(k)
205
+
206
+ # assert c is not None, f"Could not find coordinate {k} in {self.variable.name} {self.coordinates} {list(self.by_name)}"
207
+
208
+ if c is None:
209
+ missing[k] = v
210
+ return self.sel(missing, **kwargs)
211
+
212
+ i = c.index(v)
213
+ if i is None:
214
+ if k != user_provided_k:
215
+ LOG.warning(f"Could not find {user_provided_k}={v} in {c} (alias of {k})")
216
+ else:
217
+ LOG.warning(f"Could not find {k}={v} in {c}")
218
+ return None
219
+
220
+ coordinates = [x.reduced(i) if c is x else x for x in self.coordinates]
221
+
222
+ metadata = self._metadata.copy()
223
+ metadata.update({k: v})
224
+
225
+ variable = Variable(
226
+ ds=self.ds,
227
+ variable=self.variable.isel({k: i}),
228
+ coordinates=coordinates,
229
+ grid=self.grid,
230
+ time=self.time,
231
+ metadata=metadata,
232
+ )
233
+
234
+ return variable.sel(missing, **kwargs)
235
+
236
+ def match(self, **kwargs: Any) -> Tuple[bool, Optional[Dict[str, Any]]]:
237
+ """Match the variable based on the given metadata.
238
+
239
+ Parameters
240
+ ----------
241
+ **kwargs : Any
242
+ Metadata to match.
243
+
244
+ Returns
245
+ -------
246
+ Tuple[bool, Optional[Dict[str, Any]]]
247
+ A tuple containing a boolean indicating if the match was successful and the remaining metadata.
248
+ """
249
+ if "param" in kwargs:
250
+ assert "variable" not in kwargs
251
+ kwargs["variable"] = kwargs.pop("param")
252
+
253
+ if "variable" in kwargs:
254
+ name = kwargs.pop("variable")
255
+ if not isinstance(name, (list, tuple)):
256
+ name = [name]
257
+ if self.variable.name not in name:
258
+ return False, None
259
+ return True, kwargs
260
+ return True, kwargs
261
+
262
+
263
+ class FilteredVariable:
264
+ """Represents a filtered variable based on metadata.
265
+
266
+ Attributes
267
+ ----------
268
+ variable : Variable
269
+ The variable to filter.
270
+ kwargs : Any
271
+ Metadata to filter the variable.
272
+ """
273
+
274
+ def __init__(self, variable: Variable, **kwargs: Any):
275
+ """Initialize the FilteredVariable object.
276
+
277
+ Parameters
278
+ ----------
279
+ variable : Variable
280
+ The variable to filter.
281
+ **kwargs : Any
282
+ Metadata to filter the variable.
283
+ """
284
+ self.variable = variable
285
+ self.kwargs = kwargs
286
+
287
+ @cached_property
288
+ def fields(self) -> List["XArrayField"]:
289
+ """Filter the fields of a variable based on metadata."""
290
+ return [
291
+ field
292
+ for field in self.variable
293
+ if all(field.metadata(k, default=None) == v for k, v in self.kwargs.items())
294
+ ]
295
+
296
+ @property
297
+ def length(self) -> int:
298
+ """Return the length of the filtered variable."""
299
+ return len(self.fields)
300
+
301
+ def __len__(self) -> int:
302
+ """Return the length of the filtered variable.
303
+
304
+ Returns
305
+ -------
306
+ int
307
+ The length of the filtered variable.
308
+ """
309
+ return self.length
310
+
311
+ def __getitem__(self, i: int) -> "XArrayField":
312
+ """Get a field from the filtered variable.
313
+
314
+ Parameters
315
+ ----------
316
+ i : int
317
+ Index of the field.
318
+
319
+ Returns
320
+ -------
321
+ XArrayField
322
+ The field at the specified index.
323
+
324
+ Raises
325
+ ------
326
+ IndexError
327
+ If the index is out of range.
328
+ """
329
+ if i >= self.length:
330
+ raise IndexError(i)
331
+ return self.fields[i]
@@ -0,0 +1,41 @@
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
+ from typing import Any
11
+ from typing import List
12
+
13
+ import earthkit.data as ekd
14
+
15
+ from .legacy import legacy_source
16
+ from .xarray import load_many
17
+
18
+
19
+ @legacy_source(__file__)
20
+ def execute(context: Any, dates: List[str], url: str, *args: Any, **kwargs: Any) -> ekd.FieldList:
21
+ """Execute the data loading process.
22
+
23
+ Parameters
24
+ ----------
25
+ context : Any
26
+ The context in which the execution occurs.
27
+ dates : List[str]
28
+ List of dates for which data is to be loaded.
29
+ url : str
30
+ The URL from which data is to be loaded.
31
+ *args : tuple
32
+ Additional positional arguments.
33
+ **kwargs : dict
34
+ Additional keyword arguments.
35
+
36
+ Returns
37
+ -------
38
+ ekd.FieldList
39
+ The loaded data.
40
+ """
41
+ return load_many("🇿", context, url, *args, **kwargs).execute(dates)
@@ -8,25 +8,54 @@
8
8
  # nor does it submit to any jurisdiction.
9
9
 
10
10
 
11
+ from typing import Any
12
+ from typing import Dict
13
+ from typing import List
14
+
15
+ import earthkit.data as ekd
11
16
  from earthkit.data.core.fieldlist import MultiFieldList
12
17
  from earthkit.data.sources.url import download_and_cache
13
18
 
14
- from . import iterate_patterns
19
+ from .legacy import legacy_source
20
+ from .patterns import iterate_patterns
15
21
  from .xarray import load_one
16
22
 
17
23
 
18
- def execute(context, dates, record_id, file_key, *args, **kwargs):
24
+ @legacy_source(__file__)
25
+ def execute(context: Any, dates: Any, record_id: str, file_key: str, *args: Any, **kwargs: Any) -> ekd.FieldList:
26
+ """Executes the download and processing of files from Zenodo.
27
+
28
+ Parameters
29
+ ----------
30
+ context : Any
31
+ The context in which the function is executed.
32
+ dates : Any
33
+ The dates for which the data is required.
34
+ record_id : str
35
+ The Zenodo record ID.
36
+ file_key : str
37
+ The key to identify the file.
38
+ *args : Any
39
+ Additional arguments.
40
+ **kwargs : Any
41
+ Additional keyword arguments.
42
+
43
+ Returns
44
+ -------
45
+ MultiFieldList
46
+ A list of fields loaded from the downloaded files.
47
+ """
19
48
  import requests
20
49
 
21
- result = []
50
+ result: List[Any] = []
22
51
 
23
52
  URLPATTERN = "https://zenodo.org/api/records/{record_id}"
24
53
  url = URLPATTERN.format(record_id=record_id)
25
54
  r = requests.get(url)
26
55
  r.raise_for_status()
27
- record = r.json()
56
+ record: Dict[str, Any] = r.json()
28
57
 
29
- urls = {}
58
+ urls: Dict[str, str] = {}
30
59
  for file in record["files"]:
31
60
  urls[file["key"]] = file["links"]["self"]
32
61