anemoi-utils 0.3.6__py3-none-any.whl → 0.3.7__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
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '0.3.6'
16
- __version_tuple__ = version_tuple = (0, 3, 6)
15
+ __version__ = version = '0.3.7'
16
+ __version_tuple__ = version_tuple = (0, 3, 7)
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env python
2
+ # (C) Copyright 2024 ECMWF.
3
+ #
4
+ # This software is licensed under the terms of the Apache Licence Version 2.0
5
+ # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
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
+
12
+ import json
13
+
14
+ from ..config import config_path
15
+ from ..config import load_config
16
+ from . import Command
17
+
18
+
19
+ class Config(Command):
20
+
21
+ def add_arguments(self, command_parser):
22
+ command_parser.add_argument("--path", help="Print path to config file")
23
+
24
+ def run(self, args):
25
+ if args.path:
26
+ print(config_path())
27
+ else:
28
+ print(json.dumps(load_config(), indent=4))
29
+
30
+
31
+ command = Config
anemoi/utils/config.py CHANGED
@@ -102,41 +102,146 @@ class DotDict(dict):
102
102
  return f"DotDict({super().__repr__()})"
103
103
 
104
104
 
105
- CONFIG = None
105
+ CONFIG = {}
106
+ CHECKED = {}
106
107
  CONFIG_LOCK = threading.Lock()
108
+ QUIET = False
109
+
110
+
111
+ def config_path(name="settings.toml"):
112
+ global QUIET
113
+ full = os.path.join(os.path.expanduser("~"), ".config", "anemoi", name)
114
+ os.makedirs(os.path.dirname(full), exist_ok=True)
115
+
116
+ if name == "settings.toml":
117
+ old = os.path.join(os.path.expanduser("~"), ".anemoi.toml")
118
+ if not os.path.exists(full) and os.path.exists(old):
119
+ if not QUIET:
120
+ LOG.warning(
121
+ "Configuration file found at ~/.anemoi.toml. Please move it to ~/.config/anemoi/settings.toml"
122
+ )
123
+ QUIET = True
124
+ return old
125
+ else:
126
+ if os.path.exists(old):
127
+ if not QUIET:
128
+ LOG.warning(
129
+ "Configuration file found at ~/.anemoi.toml and ~/.config/anemoi/settings.toml, ignoring the former"
130
+ )
131
+ QUIET = True
132
+
133
+ return full
134
+
135
+
136
+ def _load(path):
137
+ if path.endswith(".json"):
138
+ with open(path, "rb") as f:
139
+ return json.load(f)
140
+
141
+ if path.endswith(".yaml") or path.endswith(".yml"):
142
+ with open(path, "rb") as f:
143
+ return yaml.safe_load(f)
144
+
145
+ if path.endswith(".toml"):
146
+ with open(path, "rb") as f:
147
+ return tomllib.load(f)
148
+
149
+ return open(path).read()
107
150
 
108
151
 
109
- def _load_config():
110
- global CONFIG
111
- if CONFIG is not None:
112
- return CONFIG
152
+ def _load_config(name="settings.toml"):
113
153
 
114
- conf = os.path.expanduser("~/.anemoi.toml")
154
+ if name in CONFIG:
155
+ return CONFIG[name]
156
+
157
+ conf = config_path(name)
115
158
 
116
159
  if os.path.exists(conf):
160
+ config = _load(conf)
161
+ else:
162
+ config = {}
117
163
 
118
- with open(conf, "rb") as f:
119
- CONFIG = tomllib.load(f)
164
+ if isinstance(config, dict):
165
+ CONFIG[name] = DotDict(config)
120
166
  else:
121
- CONFIG = {}
167
+ CONFIG[name] = config
168
+
169
+ return CONFIG[name]
170
+
171
+
172
+ def _save_config(name, data):
173
+ CONFIG.pop(name, None)
174
+
175
+ conf = config_path(name)
176
+
177
+ if conf.endswith(".json"):
178
+ with open(conf, "w") as f:
179
+ json.dump(data, f, indent=4)
180
+ return
122
181
 
123
- return DotDict(CONFIG)
182
+ if conf.endswith(".yaml") or conf.endswith(".yml"):
183
+ with open(conf, "w") as f:
184
+ yaml.dump(data, f)
185
+ return
124
186
 
187
+ if conf.endswith(".toml"):
188
+ raise NotImplementedError("Saving to TOML is not implemented yet")
125
189
 
126
- def load_config():
127
- """Load the configuration from `~/.anemoi.toml`.
190
+ with open(conf, "w") as f:
191
+ f.write(data)
192
+
193
+
194
+ def save_config(name, data):
195
+ """Save a configuration file.
196
+
197
+ Parameters
198
+ ----------
199
+ name : str
200
+ The name of the configuration file to save.
201
+
202
+ data : Any
203
+ The data to save.
204
+
205
+ """
206
+ with CONFIG_LOCK:
207
+ _save_config(name, data)
208
+
209
+
210
+ def load_config(name="settings.toml"):
211
+ """Read a configuration file.
212
+
213
+ Parameters
214
+ ----------
215
+ name : str, optional
216
+ The name of the config file to read, by default "settings.toml"
128
217
 
129
218
  Returns
130
219
  -------
131
- DotDict
132
- The configuration
220
+ DotDict or str
221
+ Return DotDict if it is a dictionary, otherwise the raw data
133
222
  """
134
223
  with CONFIG_LOCK:
135
- return _load_config()
224
+ return _load_config(name)
225
+
136
226
 
227
+ def check_config_mode(name="settings.toml"):
228
+ """Check that a configuration file is secure.
137
229
 
138
- def check_config_mode():
139
- conf = os.path.expanduser("~/.anemoi.toml")
140
- mode = os.stat(conf).st_mode
141
- if mode & 0o777 != 0o600:
142
- raise SystemError(f"Configuration file {conf} is not secure. " "Please run `chmod 600 ~/.anemoi.toml`.")
230
+ Parameters
231
+ ----------
232
+ name : str, optional
233
+ The name of the configuration file, by default "settings.toml"
234
+
235
+ Raises
236
+ ------
237
+ SystemError
238
+ If the configuration file is not secure.
239
+ """
240
+ with CONFIG_LOCK:
241
+ if name in CHECKED:
242
+ return
243
+ conf = config_path(name)
244
+ mode = os.stat(conf).st_mode
245
+ if mode & 0o777 != 0o600:
246
+ raise SystemError(f"Configuration file {conf} is not secure. " "Please run `chmod 600 {conf}`.")
247
+ CHECKED[name] = True
anemoi/utils/s3.py CHANGED
@@ -146,6 +146,14 @@ class Upload(Transfer):
146
146
  return os.path.getsize(local_path)
147
147
 
148
148
  def transfer_file(self, source, target, overwrite, resume, verbosity, config=None):
149
+ try:
150
+ return self._transfer_file(source, target, overwrite, resume, verbosity, config=config)
151
+ except Exception as e:
152
+ LOGGER.exception(f"Error transferring {source} to {target}")
153
+ LOGGER.error(e)
154
+ raise
155
+
156
+ def _transfer_file(self, source, target, overwrite, resume, verbosity, config=None):
149
157
 
150
158
  from botocore.exceptions import ClientError
151
159
 
@@ -208,6 +216,14 @@ class Download(Transfer):
208
216
  return s3_object["Size"]
209
217
 
210
218
  def transfer_file(self, source, target, overwrite, resume, verbosity, config=None):
219
+ try:
220
+ return self._transfer_file(source, target, overwrite, resume, verbosity, config=config)
221
+ except Exception as e:
222
+ LOGGER.exception(f"Error transferring {source} to {target}")
223
+ LOGGER.error(e)
224
+ raise
225
+
226
+ def _transfer_file(self, source, target, overwrite, resume, verbosity, config=None):
211
227
  # from boto3.s3.transfer import TransferConfig
212
228
 
213
229
  _, _, bucket, key = source.split("/", 3)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: anemoi-utils
3
- Version: 0.3.6
3
+ Version: 0.3.7
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
@@ -1,24 +1,24 @@
1
1
  anemoi/utils/__init__.py,sha256=zZZpbKIoGWwdCOuo6YSruLR7C0GzvzI1Wzhyqaa0K7M,456
2
2
  anemoi/utils/__main__.py,sha256=cLA2PidDTOUHaDGzd0_E5iioKYNe-PSTv567Y2fuwQk,723
3
- anemoi/utils/_version.py,sha256=IKAQ4gPrCQ2FWMXOFRqouULC2EQI1zCb4iXHsnfbmTQ,411
3
+ anemoi/utils/_version.py,sha256=vVN20516E2VTC9JNgtvqrQNlj5XptaB_a5z2XL8NFxg,411
4
4
  anemoi/utils/caching.py,sha256=HrC9aFHlcCTaM2Z5u0ivGIXz7eFu35UQQhUuwwuG2pk,1743
5
5
  anemoi/utils/checkpoints.py,sha256=1_3mg4B-ykTVfIvIUEv7IxGyREx_ZcilVbB3U-V6O6I,5165
6
6
  anemoi/utils/cli.py,sha256=w6YVYfJV-50Zm9FrO0KNrrIWDdgj5hPjxJvgAh391NY,3308
7
- anemoi/utils/config.py,sha256=HBU8UbT0ZSFVSgpQGY42bXukrGIJBPbdqsqK1Btx97A,3475
7
+ anemoi/utils/config.py,sha256=BKH-ZOIVZWhuvKxmhK33JVpIr3q7Tdx-tI8kwd4SvgA,6175
8
8
  anemoi/utils/dates.py,sha256=Ot9OTY1uFvHxW1EU4DPv3oUqmzvkXTwKuwhlfVlY788,8426
9
9
  anemoi/utils/grib.py,sha256=gVfo4KYQv31iRyoqRDwk5tiqZDUgOIvhag_kO0qjYD0,3067
10
10
  anemoi/utils/humanize.py,sha256=LD6dGnqChxA5j3tMhSybsAGRQzi33d_qS9pUoUHubkc,10330
11
11
  anemoi/utils/provenance.py,sha256=v54L9jF1JgYcclOhg3iojRl1v3ajbiWz_oc289xTgO4,9574
12
- anemoi/utils/s3.py,sha256=OEZGm85gzWfZ29OuCx-A2dczC00XKM_SYnkGv9vgs14,15204
12
+ anemoi/utils/s3.py,sha256=icYeOlCnb0vfKDMIR_Rq6Machl-3KB67sAFSi8OmftI,15910
13
13
  anemoi/utils/text.py,sha256=4Zlc4r9dzRjkKL9xqp2vuQsoJY15bJ3y_Xv3YW_XsmU,8510
14
14
  anemoi/utils/timer.py,sha256=JKOgFkpJxmVRn57DEBolmTGwr25P-ePTWASBd8CLeqM,972
15
15
  anemoi/utils/commands/__init__.py,sha256=qAybFZPBBQs0dyx7dZ3X5JsLpE90pwrqt1vSV7cqEIw,706
16
- anemoi/utils/commands/checkpoint.py,sha256=SEnAizU3WklqMXUjmIh4eNrgBVwmheKG9gEBS90zwYU,1741
16
+ anemoi/utils/commands/config.py,sha256=KEffXZh0ZQfn8t6LXresfd94kDY0gEyulx9Wto5ttW0,824
17
17
  anemoi/utils/mars/__init__.py,sha256=RAeY8gJ7ZvsPlcIvrQ4fy9xVHs3SphTAPw_XJDtNIKo,1750
18
18
  anemoi/utils/mars/mars.yaml,sha256=R0dujp75lLA4wCWhPeOQnzJ45WZAYLT8gpx509cBFlc,66
19
- anemoi_utils-0.3.6.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
20
- anemoi_utils-0.3.6.dist-info/METADATA,sha256=DHKiN2X6h1APP0fFqHPHGN6TNWv7X65V85PPHX5ghac,15513
21
- anemoi_utils-0.3.6.dist-info/WHEEL,sha256=mguMlWGMX-VHnMpKOjjQidIo1ssRlCFu4a4mBpz1s2M,91
22
- anemoi_utils-0.3.6.dist-info/entry_points.txt,sha256=LENOkn88xzFQo-V59AKoA_F_cfYQTJYtrNTtf37YgHY,60
23
- anemoi_utils-0.3.6.dist-info/top_level.txt,sha256=DYn8VPs-fNwr7fNH9XIBqeXIwiYYd2E2k5-dUFFqUz0,7
24
- anemoi_utils-0.3.6.dist-info/RECORD,,
19
+ anemoi_utils-0.3.7.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
20
+ anemoi_utils-0.3.7.dist-info/METADATA,sha256=TR-5CWm6hwvgDbTyu-P4cIoobZOBJmWWdrrdfQSu3eY,15513
21
+ anemoi_utils-0.3.7.dist-info/WHEEL,sha256=mguMlWGMX-VHnMpKOjjQidIo1ssRlCFu4a4mBpz1s2M,91
22
+ anemoi_utils-0.3.7.dist-info/entry_points.txt,sha256=LENOkn88xzFQo-V59AKoA_F_cfYQTJYtrNTtf37YgHY,60
23
+ anemoi_utils-0.3.7.dist-info/top_level.txt,sha256=DYn8VPs-fNwr7fNH9XIBqeXIwiYYd2E2k5-dUFFqUz0,7
24
+ anemoi_utils-0.3.7.dist-info/RECORD,,
@@ -1,61 +0,0 @@
1
- #!/usr/bin/env python
2
- # (C) Copyright 2024 ECMWF.
3
- #
4
- # This software is licensed under the terms of the Apache Licence Version 2.0
5
- # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
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
-
12
- import json
13
-
14
- from . import Command
15
-
16
-
17
- def visit(x, path, name, value):
18
- if isinstance(x, dict):
19
- for k, v in x.items():
20
- if k == name:
21
- print(".".join(path), k, v)
22
-
23
- if v == value:
24
- print(".".join(path), k, v)
25
-
26
- path.append(k)
27
- visit(v, path, name, value)
28
- path.pop()
29
-
30
- if isinstance(x, list):
31
- for i, v in enumerate(x):
32
- path.append(str(i))
33
- visit(v, path, name, value)
34
- path.pop()
35
-
36
-
37
- class Checkpoint(Command):
38
-
39
- def add_arguments(self, command_parser):
40
- command_parser.add_argument("path", help="Path to the checkpoint.")
41
- command_parser.add_argument("--name", help="Search for a specific name.")
42
- command_parser.add_argument("--value", help="Search for a specific value.")
43
-
44
- def run(self, args):
45
- from anemoi.utils.checkpoints import load_metadata
46
-
47
- checkpoint = load_metadata(args.path, "*.json")
48
-
49
- if args.name or args.value:
50
- visit(
51
- checkpoint,
52
- [],
53
- args.name if args.name is not None else object(),
54
- args.value if args.value is not None else object(),
55
- )
56
- return
57
-
58
- print(json.dumps(checkpoint, sort_keys=True, indent=4))
59
-
60
-
61
- command = Checkpoint