anemoi-utils 0.4.18__py3-none-any.whl → 0.4.20__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.

Potentially problematic release.


This version of anemoi-utils might be problematic. Click here for more details.

anemoi/utils/_version.py CHANGED
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '0.4.18'
21
- __version_tuple__ = version_tuple = (0, 4, 18)
20
+ __version__ = version = '0.4.20'
21
+ __version_tuple__ = version_tuple = (0, 4, 20)
anemoi/utils/grids.py CHANGED
@@ -13,6 +13,9 @@
13
13
  import logging
14
14
  import os
15
15
  from io import BytesIO
16
+ from typing import List
17
+ from typing import Tuple
18
+ from typing import Union
16
19
 
17
20
  import numpy as np
18
21
  import requests
@@ -121,7 +124,7 @@ def nearest_grid_points(
121
124
 
122
125
 
123
126
  @cached(collection="grids", encoding="npz")
124
- def _grids(name: str) -> bytes:
127
+ def _grids(name: Union[str, List[float], Tuple[float, ...]]) -> bytes:
125
128
  """Get grid data by name.
126
129
 
127
130
  Parameters
@@ -136,6 +139,15 @@ def _grids(name: str) -> bytes:
136
139
  """
137
140
  from anemoi.utils.config import load_config
138
141
 
142
+ if isinstance(name, (tuple, list)):
143
+ assert len(name) == 2, "Grid name must be a list or a tuple of length 2"
144
+ assert all(isinstance(i, (int, float)) for i in name), "Grid name must be a list or a tuple of numbers"
145
+ if name[0] == name[1]:
146
+ name = str(float(name[0]))
147
+ else:
148
+ name = str(float(name[0])) + "x" + str(float(name[1]))
149
+ name = name.replace(".", "p")
150
+
139
151
  user_path = load_config().get("utils", {}).get("grids_path")
140
152
  if user_path:
141
153
  path = os.path.expanduser(os.path.join(user_path, f"grid-{name}.npz"))
@@ -146,6 +158,10 @@ def _grids(name: str) -> bytes:
146
158
  else:
147
159
  LOG.warning("Custom user path %s does not exist", path)
148
160
 
161
+ # To add a grid
162
+ # anemoi-transform get-grid --source mars grid=o400,levtype=sfc,param=2t grid-o400.npz
163
+ # nexus-cli -u xxxx -p yyyy -s GET_INSTANCE --repository anemoi upload --remote-path grids --local-path grid-o400.npz
164
+
149
165
  url = GRIDS_URL_PATTERN.format(name=name.lower())
150
166
  LOG.warning("Downloading grids from %s", url)
151
167
  response = requests.get(url)
@@ -153,7 +169,7 @@ def _grids(name: str) -> bytes:
153
169
  return response.content
154
170
 
155
171
 
156
- def grids(name: str) -> dict:
172
+ def grids(name: Union[str, List[float], Tuple[float, ...]]) -> dict:
157
173
  """Load grid data by name.
158
174
 
159
175
  Parameters
@@ -166,7 +182,7 @@ def grids(name: str) -> dict:
166
182
  dict
167
183
  The grid data
168
184
  """
169
- if name.endswith(".npz"):
185
+ if isinstance(name, str) and name.endswith(".npz"):
170
186
  return dict(np.load(name))
171
187
 
172
188
  data = _grids(name)
anemoi/utils/registry.py CHANGED
@@ -17,8 +17,10 @@ from functools import cached_property
17
17
  from typing import Any
18
18
  from typing import Callable
19
19
  from typing import Dict
20
+ from typing import Generic
20
21
  from typing import List
21
22
  from typing import Optional
23
+ from typing import TypeVar
22
24
  from typing import Union
23
25
 
24
26
  import entrypoints
@@ -78,8 +80,10 @@ class Error:
78
80
 
79
81
  _BY_KIND = {}
80
82
 
83
+ T = TypeVar("T")
81
84
 
82
- class Registry:
85
+
86
+ class Registry(Generic[T]):
83
87
  """A registry of factories.
84
88
 
85
89
  Parameters
@@ -287,7 +291,7 @@ class Registry:
287
291
 
288
292
  return sorted(self.factories.keys())
289
293
 
290
- def create(self, name: str, *args: Any, **kwargs: Any) -> Any:
294
+ def create(self, name: str, *args: Any, **kwargs: Any) -> T:
291
295
  """Create an instance using a factory.
292
296
 
293
297
  Parameters
@@ -310,7 +314,7 @@ class Registry:
310
314
  factory = self.lookup(name)
311
315
  return factory(*args, **kwargs)
312
316
 
313
- def from_config(self, config: Union[str, Dict[str, Any]], *args: Any, **kwargs: Any) -> Any:
317
+ def from_config(self, config: Union[str, Dict[str, Any]], *args: Any, **kwargs: Any) -> T:
314
318
  """Create an instance from a configuration.
315
319
 
316
320
  Parameters
anemoi/utils/testing.py CHANGED
@@ -13,9 +13,14 @@ import os
13
13
  import shutil
14
14
  import tempfile
15
15
  import threading
16
+ import warnings
17
+ from functools import lru_cache
16
18
 
19
+ import pytest
17
20
  from multiurl import download
18
21
 
22
+ from anemoi.utils.humanize import list_to_human
23
+
19
24
  LOG = logging.getLogger(__name__)
20
25
 
21
26
  TEST_DATA_URL = "https://object-store.os-api.cci1.ecmwf.int/ml-tests/test-data/samples/"
@@ -98,6 +103,9 @@ def get_test_data(path: str, gzipped=False) -> str:
98
103
  """
99
104
  _check_path(path)
100
105
 
106
+ if _offline():
107
+ raise RuntimeError("Offline mode: cannot download test data, add @pytest.mark.skipif(not offline(),...)")
108
+
101
109
  target = os.path.normpath(os.path.join(_temporary_directory(), path))
102
110
  with lock:
103
111
  if os.path.exists(target):
@@ -174,9 +182,85 @@ def packages_installed(*names) -> bool:
174
182
  Flag indicating if all the packages are installed."
175
183
  """
176
184
 
185
+ warnings.warn(
186
+ "The 'packages_installed' function is deprecated. Use '@skip_if_missing' instead.",
187
+ DeprecationWarning,
188
+ stacklevel=2,
189
+ )
190
+
177
191
  for name in names:
178
192
  try:
179
193
  __import__(name)
180
194
  except ImportError:
181
195
  return False
182
196
  return True
197
+
198
+
199
+ def _missing_packages(*names) -> list[str]:
200
+ """Check if the given packages are missing.
201
+
202
+ Use this function to check if the required packages are missing before running tests.
203
+
204
+ >>> @pytest.mark.skipif(missing_packages("foo", "bar"), reason="Packages 'foo' and 'bar' are not installed")
205
+ >>> def test_foo_bar() -> None:
206
+ >>> ...
207
+
208
+ Parameters
209
+ ----------
210
+ names : str
211
+ The names of the packages to check.
212
+
213
+ Returns
214
+ -------
215
+ list[str]:
216
+ List of missing packages.
217
+ """
218
+
219
+ missing = []
220
+ for name in names:
221
+ try:
222
+ __import__(name)
223
+ except ImportError:
224
+ missing.append(name)
225
+ return missing
226
+
227
+
228
+ def _run_slow_tests() -> bool:
229
+ """Check if the SLOW_TESTS environment variable is set.
230
+
231
+ Returns
232
+ -------
233
+ bool
234
+ True if the SLOW_TESTS environment variable is set, False otherwise.
235
+ """
236
+ return int(os.environ.get("SLOW_TESTS", 0))
237
+
238
+
239
+ @lru_cache(maxsize=None)
240
+ def _offline() -> bool:
241
+ """Check if we are offline."""
242
+
243
+ import socket
244
+
245
+ try:
246
+ socket.create_connection(("anemoi.ecmwf.int", 443), timeout=5)
247
+ except OSError:
248
+ return True
249
+
250
+ return False
251
+
252
+
253
+ skip_if_offline = pytest.mark.skipif(_offline(), reason="No internet connection")
254
+ skip_slow_tests = pytest.mark.skipif(not _run_slow_tests(), reason="Skipping slow tests")
255
+
256
+
257
+ def skip_missing_packages(*names):
258
+ missing = [f"'{p}'" for p in _missing_packages(*names)]
259
+
260
+ if len(missing) == 0:
261
+ return lambda f: f
262
+
263
+ if len(missing) == 1:
264
+ return pytest.mark.skipif(True, reason=f"Package {missing[0]} is not installed")
265
+
266
+ return pytest.mark.skipif(True, reason=f"Packages {list_to_human(missing)} are not installed")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: anemoi-utils
3
- Version: 0.4.18
3
+ Version: 0.4.20
4
4
  Summary: A package to hold various functions to support training of ML models on ECMWF data.
5
5
  Author-email: "European Centre for Medium-Range Weather Forecasts (ECMWF)" <software.support@ecmwf.int>
6
6
  License: Apache License
@@ -237,6 +237,7 @@ Requires-Dist: anemoi-utils[grib,provenance,s3,text]; extra == "all"
237
237
  Provides-Extra: dev
238
238
  Requires-Dist: anemoi-utils[all,docs,tests]; extra == "dev"
239
239
  Provides-Extra: docs
240
+ Requires-Dist: anemoi-utils[all]; extra == "docs"
240
241
  Requires-Dist: nbsphinx; extra == "docs"
241
242
  Requires-Dist: pandoc; extra == "docs"
242
243
  Requires-Dist: requests; extra == "docs"
@@ -1,6 +1,6 @@
1
1
  anemoi/utils/__init__.py,sha256=uVhpF-VjIl_4mMywOVtgTutgsdIsqz-xdkwxeMhzuag,730
2
2
  anemoi/utils/__main__.py,sha256=6LlE4MYrPvqqrykxXh7XMi50UZteUY59NeM8P9Zs2dU,910
3
- anemoi/utils/_version.py,sha256=k9MQ8BJ_knOnasrd-KVQ3E0l9YzHo7DymdQPhaKLLNU,513
3
+ anemoi/utils/_version.py,sha256=a0Lwyg5BR-wqrpFfOdiqEhJkQTdIGbZv9WA1aUNeyF0,513
4
4
  anemoi/utils/caching.py,sha256=rXbeAmpBcMbbfN4EVblaHWKicsrtx1otER84FEBtz98,6183
5
5
  anemoi/utils/checkpoints.py,sha256=N4WpAZXa4etrpSEKhHqUUtG2-x9w3FJMHcLO-dDAXPY,9600
6
6
  anemoi/utils/cli.py,sha256=IyZfnSw0u0yYnrjOrzvm2RuuKvDk4cVb8pf8BkaChgA,6209
@@ -9,17 +9,17 @@ anemoi/utils/config.py,sha256=UtxlkSMhqZR0LAKi19aG6jaaNiSmsXCzE6v2AWHhx5E,16861
9
9
  anemoi/utils/dates.py,sha256=CnY6JOdpk0T-waPEowMRTkcMzxcN0GcjPVtLkwH_byw,17196
10
10
  anemoi/utils/devtools.py,sha256=W3OBu96MkXRIl7Qh1SE5Zd6aB1R0QlnmlrlpBYM0fVY,3527
11
11
  anemoi/utils/grib.py,sha256=201WcxjjAl92Y2HX2kZ2S8Qr5dN-oG7nV-vQLaybzP4,3610
12
- anemoi/utils/grids.py,sha256=edTrMK8hpE9ZBzSfwcRftgk0jljNAK3i8CraadILQoM,4427
12
+ anemoi/utils/grids.py,sha256=uYgkU_KIg8FTUiuKV0Pho2swMMeXcSQ9CQe0MFlRr_I,5262
13
13
  anemoi/utils/hindcasts.py,sha256=iYVIxSNFL2HJcc_k1abCFLkpJFGHT8WKRIR4wcAwA3s,2144
14
14
  anemoi/utils/humanize.py,sha256=pjnFJAKHbEAOfcvn8c48kt-8eFy6FGW_U2ruJvfamrA,25189
15
15
  anemoi/utils/logs.py,sha256=naTgrmPwWHD4eekFttXftS4gtcAGYHpCqG4iwYprNDA,1804
16
16
  anemoi/utils/provenance.py,sha256=xC6mTstF7f_asqtPSrulC7c34xjOSuAxWhkwc3yKhHg,14629
17
- anemoi/utils/registry.py,sha256=vIaHMT66m0_w8lrY3u5GmT70tIRNyA_Z_p_T7pTFx_k,9637
17
+ anemoi/utils/registry.py,sha256=e3nOIRyMYQ-mpEvaHAv5tuvMYNbkJ5yz94ns7BnvkjM,9717
18
18
  anemoi/utils/rules.py,sha256=xYCiUV_HXTGFe93diqMLQsMJCWGi5umd_bWEeYP8XFY,6318
19
19
  anemoi/utils/s3.py,sha256=xMT48kbcelcjjqsaU567WI3oZ5eqo88Rlgyx5ECszAU,4074
20
20
  anemoi/utils/sanitise.py,sha256=ZYGdSX6qihQANr3pHZjbKnoapnzP1KcrWdW1Ul1mOGk,3668
21
21
  anemoi/utils/sanitize.py,sha256=43ZKDcfVpeXSsJ9TFEc9aZnD6oe2cUh151XnDspM98M,462
22
- anemoi/utils/testing.py,sha256=N1y4dfZLE9zqOhIR3o-933fdAdd9BxDvjcJx7SwFC9A,4803
22
+ anemoi/utils/testing.py,sha256=7wDIpiNIhXhHv5rN-LjgTY9DqMMMVpwPbzvYpWTKfLg,6947
23
23
  anemoi/utils/text.py,sha256=HkzIvi24obDceFLpJEwBJ9PmPrJUkQN2TrElJ-A87gU,14441
24
24
  anemoi/utils/timer.py,sha256=_leKMYza2faM7JKlGE7LCNy13rbdPnwaCF7PSrI_NmI,3895
25
25
  anemoi/utils/commands/__init__.py,sha256=5u_6EwdqYczIAgJfCwRSyQAYFEqh2ZuHHT57g9g7sdI,808
@@ -31,9 +31,9 @@ anemoi/utils/mars/requests.py,sha256=VFMHBVAAl0_2lOcMBa1lvaKHctN0lDJsI6_U4BucGew
31
31
  anemoi/utils/remote/__init__.py,sha256=swPWHQoh-B6Xq9R489tPw0FykMue7f-bJ8enneFYSYE,20776
32
32
  anemoi/utils/remote/s3.py,sha256=spQ8l0rwQjLZh9dZu5cOsYIvNwKihQfCJ6YsFYegeqI,17339
33
33
  anemoi/utils/remote/ssh.py,sha256=xNtsawh8okytCKRehkRCVExbHZj-CRUQNormEHglfuw,8088
34
- anemoi_utils-0.4.18.dist-info/licenses/LICENSE,sha256=8HznKF1Vi2IvfLsKNE5A2iVyiri3pRjRPvPC9kxs6qk,11354
35
- anemoi_utils-0.4.18.dist-info/METADATA,sha256=JfNRSTBhVM7aDRWBmhpDWtrkCoTVT1fUwGYyWcR45BU,15360
36
- anemoi_utils-0.4.18.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
37
- anemoi_utils-0.4.18.dist-info/entry_points.txt,sha256=LENOkn88xzFQo-V59AKoA_F_cfYQTJYtrNTtf37YgHY,60
38
- anemoi_utils-0.4.18.dist-info/top_level.txt,sha256=DYn8VPs-fNwr7fNH9XIBqeXIwiYYd2E2k5-dUFFqUz0,7
39
- anemoi_utils-0.4.18.dist-info/RECORD,,
34
+ anemoi_utils-0.4.20.dist-info/licenses/LICENSE,sha256=8HznKF1Vi2IvfLsKNE5A2iVyiri3pRjRPvPC9kxs6qk,11354
35
+ anemoi_utils-0.4.20.dist-info/METADATA,sha256=3fUbrATAsi8f1GCjTqsfLS3ND00wSzfpfjmzOvqVljQ,15410
36
+ anemoi_utils-0.4.20.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
37
+ anemoi_utils-0.4.20.dist-info/entry_points.txt,sha256=LENOkn88xzFQo-V59AKoA_F_cfYQTJYtrNTtf37YgHY,60
38
+ anemoi_utils-0.4.20.dist-info/top_level.txt,sha256=DYn8VPs-fNwr7fNH9XIBqeXIwiYYd2E2k5-dUFFqUz0,7
39
+ anemoi_utils-0.4.20.dist-info/RECORD,,