anemoi-utils 0.3.7__tar.gz → 0.3.8__tar.gz
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-0.3.7/src/anemoi_utils.egg-info → anemoi_utils-0.3.8}/PKG-INFO +1 -1
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/src/anemoi/utils/_version.py +2 -2
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/src/anemoi/utils/cli.py +9 -2
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/src/anemoi/utils/config.py +24 -11
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/src/anemoi/utils/dates.py +2 -31
- anemoi_utils-0.3.8/src/anemoi/utils/hindcasts.py +40 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/src/anemoi/utils/humanize.py +55 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8/src/anemoi_utils.egg-info}/PKG-INFO +1 -1
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/src/anemoi_utils.egg-info/SOURCES.txt +1 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/.github/workflows/python-publish.yml +0 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/.gitignore +0 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/.pre-commit-config.yaml +0 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/.readthedocs.yaml +0 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/LICENSE +0 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/README.md +0 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/docs/Makefile +0 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/docs/_static/logo.png +0 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/docs/_static/style.css +0 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/docs/_templates/.gitkeep +0 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/docs/conf.py +0 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/docs/index.rst +0 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/docs/installing.rst +0 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/docs/modules/checkpoints.rst +0 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/docs/modules/config.rst +0 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/docs/modules/dates.rst +0 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/docs/modules/grib.rst +0 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/docs/modules/humanize.rst +0 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/docs/modules/provenance.rst +0 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/docs/modules/s3.rst +0 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/docs/modules/text.rst +0 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/docs/requirements.txt +0 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/pyproject.toml +0 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/setup.cfg +0 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/src/anemoi/utils/__init__.py +0 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/src/anemoi/utils/__main__.py +0 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/src/anemoi/utils/caching.py +0 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/src/anemoi/utils/checkpoints.py +0 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/src/anemoi/utils/commands/__init__.py +0 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/src/anemoi/utils/commands/config.py +0 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/src/anemoi/utils/grib.py +0 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/src/anemoi/utils/mars/__init__.py +0 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/src/anemoi/utils/mars/mars.yaml +0 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/src/anemoi/utils/provenance.py +0 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/src/anemoi/utils/s3.py +0 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/src/anemoi/utils/text.py +0 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/src/anemoi/utils/timer.py +0 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/src/anemoi_utils.egg-info/dependency_links.txt +0 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/src/anemoi_utils.egg-info/entry_points.txt +0 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/src/anemoi_utils.egg-info/requires.txt +0 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/src/anemoi_utils.egg-info/top_level.txt +0 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/tests/test_dates.py +0 -0
- {anemoi_utils-0.3.7 → anemoi_utils-0.3.8}/tests/test_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: anemoi-utils
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.8
|
|
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
|
|
@@ -97,7 +97,7 @@ def register_commands(here, package, select, fail=None):
|
|
|
97
97
|
|
|
98
98
|
def cli_main(version, description, commands):
|
|
99
99
|
parser = make_parser(description, commands)
|
|
100
|
-
args = parser.
|
|
100
|
+
args, unknown = parser.parse_known_args()
|
|
101
101
|
|
|
102
102
|
if args.version:
|
|
103
103
|
print(version)
|
|
@@ -115,8 +115,15 @@ def cli_main(version, description, commands):
|
|
|
115
115
|
level=logging.DEBUG if args.debug else logging.INFO,
|
|
116
116
|
)
|
|
117
117
|
|
|
118
|
+
if unknown and not cmd.accept_unknown_args:
|
|
119
|
+
# This should trigger an error
|
|
120
|
+
parser.parse_args()
|
|
121
|
+
|
|
118
122
|
try:
|
|
119
|
-
|
|
123
|
+
if unknown:
|
|
124
|
+
cmd.run(args, unknown)
|
|
125
|
+
else:
|
|
126
|
+
cmd.run(args)
|
|
120
127
|
except ValueError as e:
|
|
121
128
|
traceback.print_exc()
|
|
122
129
|
LOG.error("\n💣 %s", str(e).lstrip())
|
|
@@ -134,17 +134,21 @@ def config_path(name="settings.toml"):
|
|
|
134
134
|
|
|
135
135
|
|
|
136
136
|
def _load(path):
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
137
|
+
try:
|
|
138
|
+
if path.endswith(".json"):
|
|
139
|
+
with open(path, "rb") as f:
|
|
140
|
+
return json.load(f)
|
|
141
|
+
|
|
142
|
+
if path.endswith(".yaml") or path.endswith(".yml"):
|
|
143
|
+
with open(path, "rb") as f:
|
|
144
|
+
return yaml.safe_load(f)
|
|
145
|
+
|
|
146
|
+
if path.endswith(".toml"):
|
|
147
|
+
with open(path, "rb") as f:
|
|
148
|
+
return tomllib.load(f)
|
|
149
|
+
except (json.JSONDecodeError, yaml.YAMLError, tomllib.TOMLDecodeError) as e:
|
|
150
|
+
LOG.warning(f"Failed to parse config file {path}", exc_info=e)
|
|
151
|
+
return ValueError(f"Failed to parse config file {path} [{e}]")
|
|
148
152
|
|
|
149
153
|
return open(path).read()
|
|
150
154
|
|
|
@@ -224,6 +228,15 @@ def load_config(name="settings.toml"):
|
|
|
224
228
|
return _load_config(name)
|
|
225
229
|
|
|
226
230
|
|
|
231
|
+
def load_raw_config(name, default=None):
|
|
232
|
+
|
|
233
|
+
path = config_path(name)
|
|
234
|
+
if os.path.exists(path):
|
|
235
|
+
return _load(path)
|
|
236
|
+
|
|
237
|
+
return default
|
|
238
|
+
|
|
239
|
+
|
|
227
240
|
def check_config_mode(name="settings.toml"):
|
|
228
241
|
"""Check that a configuration file is secure.
|
|
229
242
|
|
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
import calendar
|
|
10
10
|
import datetime
|
|
11
11
|
|
|
12
|
+
from .hindcasts import HindcastDatesTimes
|
|
13
|
+
|
|
12
14
|
|
|
13
15
|
def normalise_frequency(frequency):
|
|
14
16
|
if isinstance(frequency, int):
|
|
@@ -158,37 +160,6 @@ class DateTimes:
|
|
|
158
160
|
date += self.increment
|
|
159
161
|
|
|
160
162
|
|
|
161
|
-
class HindcastDatesTimes:
|
|
162
|
-
"""The HindcastDatesTimes class is an iterator that generates datetime objects within a given range."""
|
|
163
|
-
|
|
164
|
-
def __init__(self, reference_dates, years=20):
|
|
165
|
-
"""_summary_
|
|
166
|
-
|
|
167
|
-
Parameters
|
|
168
|
-
----------
|
|
169
|
-
reference_dates : _type_
|
|
170
|
-
_description_
|
|
171
|
-
years : int, optional
|
|
172
|
-
_description_, by default 20
|
|
173
|
-
"""
|
|
174
|
-
|
|
175
|
-
self.reference_dates = reference_dates
|
|
176
|
-
|
|
177
|
-
if isinstance(years, list):
|
|
178
|
-
self.years = years
|
|
179
|
-
else:
|
|
180
|
-
self.years = range(1, years + 1)
|
|
181
|
-
|
|
182
|
-
def __iter__(self):
|
|
183
|
-
for reference_date in self.reference_dates:
|
|
184
|
-
for year in self.years:
|
|
185
|
-
if reference_date.month == 2 and reference_date.day == 29:
|
|
186
|
-
date = datetime.datetime(reference_date.year - year, 2, 28)
|
|
187
|
-
else:
|
|
188
|
-
date = datetime.datetime(reference_date.year - year, reference_date.month, reference_date.day)
|
|
189
|
-
yield (date, reference_date)
|
|
190
|
-
|
|
191
|
-
|
|
192
163
|
class Year(DateTimes):
|
|
193
164
|
"""Year is defined as the months of January to December."""
|
|
194
165
|
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# (C) Copyright 2024 European Centre for Medium-Range Weather Forecasts.
|
|
2
|
+
# This software is licensed under the terms of the Apache Licence Version 2.0
|
|
3
|
+
# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
|
|
4
|
+
# In applying this licence, ECMWF does not waive the privileges and immunities
|
|
5
|
+
# granted to it by virtue of its status as an intergovernmental organisation
|
|
6
|
+
# nor does it submit to any jurisdiction.
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
import datetime
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class HindcastDatesTimes:
|
|
13
|
+
"""The HindcastDatesTimes class is an iterator that generates datetime objects within a given range."""
|
|
14
|
+
|
|
15
|
+
def __init__(self, reference_dates, years=20):
|
|
16
|
+
"""_summary_
|
|
17
|
+
|
|
18
|
+
Parameters
|
|
19
|
+
----------
|
|
20
|
+
reference_dates : _type_
|
|
21
|
+
_description_
|
|
22
|
+
years : int, optional
|
|
23
|
+
_description_, by default 20
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
self.reference_dates = reference_dates
|
|
27
|
+
|
|
28
|
+
if isinstance(years, list):
|
|
29
|
+
self.years = years
|
|
30
|
+
else:
|
|
31
|
+
self.years = range(1, years + 1)
|
|
32
|
+
|
|
33
|
+
def __iter__(self):
|
|
34
|
+
for reference_date in self.reference_dates:
|
|
35
|
+
for year in self.years:
|
|
36
|
+
if reference_date.month == 2 and reference_date.day == 29:
|
|
37
|
+
date = datetime.datetime(reference_date.year - year, 2, 28)
|
|
38
|
+
else:
|
|
39
|
+
date = datetime.datetime(reference_date.year - year, reference_date.month, reference_date.day)
|
|
40
|
+
yield (date, reference_date)
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"""Generate human readable strings"""
|
|
11
11
|
|
|
12
12
|
import datetime
|
|
13
|
+
import json
|
|
13
14
|
import re
|
|
14
15
|
from collections import defaultdict
|
|
15
16
|
|
|
@@ -472,3 +473,57 @@ def rounded_datetime(d):
|
|
|
472
473
|
d = d + datetime.timedelta(seconds=1)
|
|
473
474
|
d = d.replace(microsecond=0)
|
|
474
475
|
return d
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
def json_pretty_dump(obj, max_line_length=120, default=str):
|
|
479
|
+
"""Custom JSON dump function that keeps dicts and lists on one line if they are short enough.
|
|
480
|
+
|
|
481
|
+
Parameters
|
|
482
|
+
----------
|
|
483
|
+
obj
|
|
484
|
+
The object to be dumped as JSON.
|
|
485
|
+
max_line_length
|
|
486
|
+
Maximum allowed line length for pretty-printing.
|
|
487
|
+
|
|
488
|
+
Returns
|
|
489
|
+
-------
|
|
490
|
+
unknown
|
|
491
|
+
JSON string.
|
|
492
|
+
"""
|
|
493
|
+
|
|
494
|
+
def _format_json(obj, indent_level=0):
|
|
495
|
+
"""Helper function to format JSON objects with custom pretty-print rules.
|
|
496
|
+
|
|
497
|
+
Parameters
|
|
498
|
+
----------
|
|
499
|
+
obj
|
|
500
|
+
The object to format.
|
|
501
|
+
indent_level
|
|
502
|
+
Current indentation level.
|
|
503
|
+
|
|
504
|
+
Returns
|
|
505
|
+
-------
|
|
506
|
+
unknown
|
|
507
|
+
Formatted JSON string.
|
|
508
|
+
"""
|
|
509
|
+
indent = " " * 4 * indent_level
|
|
510
|
+
if isinstance(obj, dict):
|
|
511
|
+
items = []
|
|
512
|
+
for key, value in obj.items():
|
|
513
|
+
items.append(f'"{key}": {_format_json(value, indent_level + 1)}')
|
|
514
|
+
line = "{" + ", ".join(items) + "}"
|
|
515
|
+
if len(line) <= max_line_length:
|
|
516
|
+
return line
|
|
517
|
+
else:
|
|
518
|
+
return "{\n" + ",\n".join([f"{indent} {item}" for item in items]) + "\n" + indent + "}"
|
|
519
|
+
elif isinstance(obj, list):
|
|
520
|
+
items = [_format_json(item, indent_level + 1) for item in obj]
|
|
521
|
+
line = "[" + ", ".join(items) + "]"
|
|
522
|
+
if len(line) <= max_line_length:
|
|
523
|
+
return line
|
|
524
|
+
else:
|
|
525
|
+
return "[\n" + ",\n".join([f"{indent} {item}" for item in items]) + "\n" + indent + "]"
|
|
526
|
+
else:
|
|
527
|
+
return json.dumps(obj, default=default)
|
|
528
|
+
|
|
529
|
+
return _format_json(obj)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: anemoi-utils
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.8
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|