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.
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 +552 -61
  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.15.dist-info → anemoi_datasets-0.5.17.dist-info}/METADATA +10 -7
  129. anemoi_datasets-0.5.17.dist-info/RECORD +137 -0
  130. {anemoi_datasets-0.5.15.dist-info → anemoi_datasets-0.5.17.dist-info}/WHEEL +1 -1
  131. {anemoi_datasets-0.5.15.dist-info → anemoi_datasets-0.5.17.dist-info/licenses}/LICENSE +1 -1
  132. anemoi/datasets/create/functions/__init__.py +0 -66
  133. anemoi/datasets/create/functions/filters/__init__.py +0 -9
  134. anemoi/datasets/create/functions/filters/empty.py +0 -17
  135. anemoi/datasets/create/functions/filters/orog_to_z.py +0 -58
  136. anemoi/datasets/create/functions/filters/rename.py +0 -79
  137. anemoi/datasets/create/functions/filters/speeddir_to_uv.py +0 -78
  138. anemoi/datasets/create/functions/filters/uv_to_speeddir.py +0 -56
  139. anemoi/datasets/create/functions/sources/empty.py +0 -15
  140. anemoi/datasets/create/functions/sources/grib.py +0 -150
  141. anemoi/datasets/create/functions/sources/netcdf.py +0 -15
  142. anemoi/datasets/create/functions/sources/opendap.py +0 -15
  143. anemoi/datasets/create/functions/sources/recentre.py +0 -60
  144. anemoi/datasets/create/functions/sources/xarray/coordinates.py +0 -255
  145. anemoi/datasets/create/functions/sources/xarray/flavour.py +0 -472
  146. anemoi/datasets/create/functions/sources/xarray/metadata.py +0 -148
  147. anemoi/datasets/create/functions/sources/xarray/patch.py +0 -44
  148. anemoi/datasets/create/functions/sources/xarray/time.py +0 -177
  149. anemoi/datasets/create/functions/sources/xarray/variable.py +0 -188
  150. anemoi/datasets/create/functions/sources/xarray_kerchunk.py +0 -42
  151. anemoi/datasets/create/functions/sources/xarray_zarr.py +0 -15
  152. anemoi/datasets/utils/fields.py +0 -47
  153. anemoi_datasets-0.5.15.dist-info/RECORD +0 -129
  154. {anemoi_datasets-0.5.15.dist-info → anemoi_datasets-0.5.17.dist-info}/entry_points.txt +0 -0
  155. {anemoi_datasets-0.5.15.dist-info → anemoi_datasets-0.5.17.dist-info}/top_level.txt +0 -0
@@ -11,9 +11,14 @@
11
11
  import datetime
12
12
  import logging
13
13
  from functools import cached_property
14
+ from typing import Any
15
+ from typing import Dict
16
+ from typing import Optional
17
+ from typing import Tuple
14
18
 
15
- from earthkit.data.core.fieldlist import Field
19
+ from earthkit.data import Field
16
20
  from earthkit.data.core.fieldlist import math
21
+ from numpy.typing import NDArray
17
22
 
18
23
  from .coordinates import extract_single_value
19
24
  from .coordinates import is_scalar
@@ -23,19 +28,41 @@ LOG = logging.getLogger(__name__)
23
28
 
24
29
 
25
30
  class EmptyFieldList:
26
- def __len__(self):
31
+ """A class to represent an empty list of fields."""
32
+
33
+ def __len__(self) -> int:
34
+ """Return the length of the field list."""
27
35
  return 0
28
36
 
29
- def __getitem__(self, i):
37
+ def __getitem__(self, i: int) -> Any:
38
+ """Raise an IndexError when trying to access an item.
39
+
40
+ Returns
41
+ -------
42
+ Any
43
+ This method does not return anything as it raises an IndexError.
44
+
45
+ Raises
46
+ ------
47
+ IndexError
48
+ Always raised to indicate that the list is empty.
49
+
50
+ Args
51
+ ----
52
+ i : int
53
+ Index of the item to access.
54
+ """
30
55
  raise IndexError(i)
31
56
 
32
57
  def __repr__(self) -> str:
58
+ """Return a string representation of the EmptyFieldList."""
33
59
  return "EmptyFieldList()"
34
60
 
35
61
 
36
62
  class XArrayField(Field):
63
+ """A class to represent a field in an XArray dataset."""
37
64
 
38
- def __init__(self, owner, selection):
65
+ def __init__(self, owner: Any, selection: Any) -> None:
39
66
  """Create a new XArrayField object.
40
67
 
41
68
  Parameters
@@ -69,10 +96,29 @@ class XArrayField(Field):
69
96
  raise ValueError("Invalid shape for selection")
70
97
 
71
98
  @property
72
- def shape(self):
99
+ def shape(self) -> Tuple[int, int]:
100
+ """Return the shape of the field."""
73
101
  return self._shape
74
102
 
75
- def to_numpy(self, flatten=False, dtype=None, index=None):
103
+ def to_numpy(
104
+ self, flatten: bool = False, dtype: Optional[type] = None, index: Optional[int] = None
105
+ ) -> NDArray[Any]:
106
+ """Convert the selection to a numpy array.
107
+
108
+ Returns
109
+ -------
110
+ NDArray[Any]
111
+ The selection converted to a numpy array.
112
+
113
+ Args
114
+ ----
115
+ flatten : bool, optional
116
+ Whether to flatten the array, by default False.
117
+ dtype : Optional[type], optional
118
+ Data type of the array, by default None.
119
+ index : Optional[int], optional
120
+ Index to select a specific element, by default None.
121
+ """
76
122
  if index is not None:
77
123
  values = self.selection[index]
78
124
  else:
@@ -86,34 +132,53 @@ class XArrayField(Field):
86
132
  return values # .reshape(self.shape)
87
133
 
88
134
  @cached_property
89
- def _metadata(self):
135
+ def _metadata(self) -> XArrayMetadata:
136
+ """Return the metadata of the field."""
90
137
  return XArrayMetadata(self)
91
138
 
92
- def grid_points(self):
139
+ def grid_points(self) -> Any:
140
+ """Return the grid points of the field."""
93
141
  return self.owner.grid_points()
94
142
 
95
- def to_latlon(self, flatten=True):
143
+ def to_latlon(self, flatten: bool = True) -> Dict[str, Any]:
144
+ """Convert the selection to latitude and longitude coordinates.
145
+
146
+ Returns
147
+ -------
148
+ Dict[str, Any]
149
+ The latitude and longitude coordinates.
150
+
151
+ Args
152
+ ----
153
+ flatten : bool, optional
154
+ Whether to flatten the coordinates, by default True.
155
+ """
96
156
  assert flatten
97
157
  return dict(lat=self.latitudes, lon=self.longitudes)
98
158
 
99
159
  @property
100
- def resolution(self):
160
+ def resolution(self) -> Optional[Any]:
161
+ """Return the resolution of the field."""
101
162
  return None
102
163
 
103
164
  @property
104
- def grid_mapping(self):
165
+ def grid_mapping(self) -> Any:
166
+ """Return the grid mapping of the field."""
105
167
  return self.owner.grid_mapping
106
168
 
107
169
  @property
108
- def latitudes(self):
170
+ def latitudes(self) -> Any:
171
+ """Return the latitudes of the field."""
109
172
  return self.owner.latitudes
110
173
 
111
174
  @property
112
- def longitudes(self):
175
+ def longitudes(self) -> Any:
176
+ """Return the longitudes of the field."""
113
177
  return self.owner.longitudes
114
178
 
115
179
  @property
116
- def forecast_reference_time(self):
180
+ def forecast_reference_time(self) -> datetime.datetime:
181
+ """Return the forecast reference time of the field."""
117
182
  date, time = self.metadata("date", "time")
118
183
  assert len(time) == 4, time
119
184
  assert len(date) == 8, date
@@ -121,9 +186,22 @@ class XArrayField(Field):
121
186
  time = int(time) // 100
122
187
  return datetime.datetime(yyyymmdd // 10000, yyyymmdd // 100 % 100, yyyymmdd % 100, time)
123
188
 
124
- def __repr__(self):
189
+ def __repr__(self) -> str:
190
+ """Return a string representation of the field."""
125
191
  return repr(self._metadata)
126
192
 
127
- def _values(self, dtype=None):
193
+ def _values(self, dtype: Optional[type] = None) -> Any:
194
+ """Return the values of the selection.
195
+
196
+ Returns
197
+ -------
198
+ Any
199
+ The values of the selection.
200
+
201
+ Args
202
+ ----
203
+ dtype : Optional[type], optional
204
+ Data type of the values, by default None.
205
+ """
128
206
  # we don't use .values as this will download the data
129
207
  return self.selection
@@ -10,9 +10,15 @@
10
10
 
11
11
  import json
12
12
  import logging
13
+ from typing import Any
14
+ from typing import Dict
15
+ from typing import List
16
+ from typing import Optional
17
+ from typing import Union
13
18
 
19
+ import xarray as xr
14
20
  import yaml
15
- from earthkit.data.core.fieldlist import FieldList
21
+ from earthkit.data import FieldList
16
22
 
17
23
  from .field import EmptyFieldList
18
24
  from .flavour import CoordinateGuesser
@@ -25,19 +31,49 @@ LOG = logging.getLogger(__name__)
25
31
 
26
32
 
27
33
  class XarrayFieldList(FieldList):
28
- def __init__(self, ds, variables):
29
- self.ds = ds
30
- self.variables = variables.copy()
31
- self.total_length = sum(v.length for v in variables)
34
+ """A class to represent a list of fields from an xarray Dataset."""
32
35
 
33
- def __repr__(self):
36
+ def __init__(self, ds: xr.Dataset, variables: List[Variable]) -> None:
37
+ """Initialize the XarrayFieldList.
38
+
39
+ Parameters
40
+ ----------
41
+ ds : xr.Dataset
42
+ The xarray Dataset.
43
+ variables : List[Variable]
44
+ The list of variables.
45
+ """
46
+ self.ds: xr.Dataset = ds
47
+ self.variables: List[Variable] = variables.copy()
48
+ self.total_length: int = sum(v.length for v in variables)
49
+
50
+ def __repr__(self) -> str:
51
+ """Return a string representation of the XarrayFieldList."""
34
52
  return f"XarrayFieldList({self.total_length})"
35
53
 
36
- def __len__(self):
54
+ def __len__(self) -> int:
55
+ """Return the length of the XarrayFieldList."""
37
56
  return self.total_length
38
57
 
39
- def __getitem__(self, i):
40
- k = i
58
+ def __getitem__(self, i: int) -> Any:
59
+ """Get an item from the XarrayFieldList by index.
60
+
61
+ Parameters
62
+ ----------
63
+ i : int
64
+ The index of the item to get.
65
+
66
+ Returns
67
+ -------
68
+ Any
69
+ The item at the specified index.
70
+
71
+ Raises
72
+ ------
73
+ IndexError
74
+ If the index is out of range.
75
+ """
76
+ k: int = i
41
77
 
42
78
  if i < 0:
43
79
  i = self.total_length + i
@@ -50,12 +86,33 @@ class XarrayFieldList(FieldList):
50
86
  raise IndexError(k)
51
87
 
52
88
  @classmethod
53
- def from_xarray(cls, ds, *, flavour=None, patch=None):
89
+ def from_xarray(
90
+ cls,
91
+ ds: xr.Dataset,
92
+ *,
93
+ flavour: Optional[Union[str, Dict[str, Any]]] = None,
94
+ patch: Optional[Dict[str, Any]] = None,
95
+ ) -> "XarrayFieldList":
96
+ """Create an XarrayFieldList from an xarray Dataset.
97
+
98
+ Parameters
99
+ ----------
100
+ ds : xr.Dataset
101
+ The xarray Dataset to create the field list from.
102
+ flavour : Optional[Union[str, Dict[str, Any]]], optional
103
+ The flavour to use for guessing coordinates.
104
+ patch : Optional[Dict[str, Any]], optional
105
+ The patch to apply to the dataset.
54
106
 
107
+ Returns
108
+ -------
109
+ XarrayFieldList
110
+ The created XarrayFieldList.
111
+ """
55
112
  if patch is not None:
56
113
  ds = patch_dataset(ds, patch)
57
114
 
58
- variables = []
115
+ variables: List[Variable] = []
59
116
 
60
117
  if isinstance(flavour, str):
61
118
  with open(flavour) as f:
@@ -64,9 +121,9 @@ class XarrayFieldList(FieldList):
64
121
  else:
65
122
  flavour = json.load(f)
66
123
 
67
- if isinstance(flavour, dict):
68
- flavour_coords = [coords["name"] for coords in flavour["rules"].values()]
69
- ds_dims = [dim for dim in ds._dims]
124
+ if isinstance(flavour, Dict):
125
+ flavour_coords: List[str] = [coords["name"] for coords in flavour["rules"].values()]
126
+ ds_dims: List[str] = [dim for dim in ds._dims]
70
127
  for dim in ds_dims:
71
128
  if dim in flavour_coords and dim not in ds._coord_names:
72
129
  ds = ds.assign_coords({dim: ds[dim]})
@@ -75,10 +132,10 @@ class XarrayFieldList(FieldList):
75
132
 
76
133
  guess = CoordinateGuesser.from_flavour(ds, flavour)
77
134
 
78
- skip = set()
135
+ skip: set = set()
79
136
 
80
- def _skip_attr(v, attr_name):
81
- attr_val = getattr(v, attr_name, "")
137
+ def _skip_attr(v: Any, attr_name: str) -> None:
138
+ attr_val: str = getattr(v, attr_name, "")
82
139
  if isinstance(attr_val, str):
83
140
  skip.update(attr_val.split(" "))
84
141
 
@@ -97,7 +154,7 @@ class XarrayFieldList(FieldList):
97
154
  continue
98
155
 
99
156
  variable = ds[name]
100
- coordinates = []
157
+ coordinates: List[Any] = []
101
158
 
102
159
  for coord in variable.coords:
103
160
 
@@ -108,7 +165,7 @@ class XarrayFieldList(FieldList):
108
165
  c.is_dim = False
109
166
  coordinates.append(c)
110
167
 
111
- grid_coords = sum(1 for c in coordinates if c.is_grid and c.is_dim)
168
+ grid_coords: int = sum(1 for c in coordinates if c.is_grid and c.is_dim)
112
169
  assert grid_coords <= 2
113
170
 
114
171
  if grid_coords < 2:
@@ -128,14 +185,21 @@ class XarrayFieldList(FieldList):
128
185
 
129
186
  return cls(ds, variables)
130
187
 
131
- def sel(self, **kwargs):
132
- """Override the FieldList's sel method
188
+ def sel(self, **kwargs: Any) -> FieldList:
189
+ """Select fields from the XarrayFieldList based on criteria.
190
+
191
+ Parameters
192
+ ----------
193
+ kwargs : dict
194
+ The selection criteria.
133
195
 
134
196
  Returns
135
197
  -------
136
198
  FieldList
137
- The new FieldList
199
+ The new FieldList with selected fields.
200
+ """
138
201
 
202
+ """
139
203
  The algorithm is as follows:
140
204
  1 - Use the kwargs to select the variables that match the selection (`param` or `variable`)
141
205
  2 - For each variable, use the remaining kwargs to select the coordinates (`level`, `number`, ...)
@@ -146,19 +210,20 @@ class XarrayFieldList(FieldList):
146
210
  So we get an extra chance to filter the fields by the metadata.
147
211
  """
148
212
 
149
- variables = []
150
- count = 0
213
+ variables: List[Variable] = []
214
+ count: int = 0
151
215
 
152
216
  for v in self.variables:
153
217
 
154
218
  # First, select matching variables
219
+
155
220
  # This will consume 'param' or 'variable' from kwargs
156
221
  # and return the rest
157
222
  match, rest = v.match(**kwargs)
158
223
 
159
224
  if match:
160
225
  count += 1
161
- missing = {}
226
+ missing: Dict[str, Any] = {}
162
227
 
163
228
  # Select from the variable's coordinates (time, level, number, ....)
164
229
  # This may return a new variable with a isel() slice of the selection