anemoi-utils 0.3.15__tar.gz → 0.3.17__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.17/.gitattributes +1 -0
- anemoi_utils-0.3.17/.github/CODEOWNERS +6 -0
- anemoi_utils-0.3.17/.github/ci-hpc-config.yml +8 -0
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/.github/workflows/changelog-pr-update.yml +3 -0
- anemoi_utils-0.3.17/.github/workflows/changelog-release-update.yml +35 -0
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/.github/workflows/ci.yml +4 -1
- anemoi_utils-0.3.17/.github/workflows/python-publish.yml +27 -0
- anemoi_utils-0.3.17/.github/workflows/python-pull-request.yml +23 -0
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/.gitignore +1 -0
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/.pre-commit-config.yaml +26 -9
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/CHANGELOG.md +9 -0
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/PKG-INFO +5 -16
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/pyproject.toml +14 -48
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/src/anemoi/utils/_version.py +2 -2
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/src/anemoi/utils/caching.py +1 -0
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/src/anemoi/utils/checkpoints.py +6 -4
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/src/anemoi/utils/cli.py +2 -0
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/src/anemoi/utils/config.py +20 -6
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/src/anemoi/utils/dates.py +62 -9
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/src/anemoi/utils/grib.py +3 -3
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/src/anemoi/utils/humanize.py +24 -20
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/src/anemoi/utils/provenance.py +6 -1
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/src/anemoi/utils/s3.py +12 -8
- anemoi_utils-0.3.17/src/anemoi/utils/sanetize.py +10 -0
- anemoi_utils-0.3.17/src/anemoi/utils/sanitise.py +115 -0
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/src/anemoi/utils/text.py +3 -1
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/src/anemoi_utils.egg-info/PKG-INFO +5 -16
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/src/anemoi_utils.egg-info/SOURCES.txt +8 -0
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/src/anemoi_utils.egg-info/requires.txt +4 -15
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/tests/test_dates.py +11 -7
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/tests/test_frequency.py +4 -2
- anemoi_utils-0.3.17/tests/test_sanetise.py +69 -0
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/tests/test_utils.py +4 -1
- anemoi_utils-0.3.15/.github/workflows/python-publish.yml +0 -54
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/.github/workflows/label-public-pr.yml +0 -0
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/.github/workflows/readthedocs-pr-update.yml +0 -0
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/.readthedocs.yaml +0 -0
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/LICENSE +0 -0
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/README.md +0 -0
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/docs/Makefile +0 -0
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/docs/_static/logo.png +0 -0
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/docs/_static/style.css +0 -0
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/docs/_templates/.gitkeep +0 -0
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/docs/conf.py +0 -0
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/docs/index.rst +0 -0
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/docs/installing.rst +0 -0
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/docs/modules/checkpoints.rst +0 -0
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/docs/modules/config.rst +0 -0
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/docs/modules/dates.rst +0 -0
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/docs/modules/grib.rst +0 -0
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/docs/modules/humanize.rst +0 -0
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/docs/modules/provenance.rst +0 -0
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/docs/modules/s3.rst +0 -0
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/docs/modules/text.rst +0 -0
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/setup.cfg +0 -0
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/src/anemoi/utils/__init__.py +0 -0
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/src/anemoi/utils/__main__.py +0 -0
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/src/anemoi/utils/commands/__init__.py +0 -0
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/src/anemoi/utils/commands/config.py +0 -0
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/src/anemoi/utils/hindcasts.py +0 -0
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/src/anemoi/utils/mars/__init__.py +0 -0
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/src/anemoi/utils/mars/mars.yaml +0 -0
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/src/anemoi/utils/timer.py +0 -0
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/src/anemoi_utils.egg-info/dependency_links.txt +0 -0
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/src/anemoi_utils.egg-info/entry_points.txt +0 -0
- {anemoi_utils-0.3.15 → anemoi_utils-0.3.17}/src/anemoi_utils.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
CHANGELOG.md merge=union
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# .github/workflows/update-changelog.yaml
|
|
2
|
+
name: "Update Changelog"
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
release:
|
|
6
|
+
types: [released]
|
|
7
|
+
workflow_dispatch: ~
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
pull-requests: write
|
|
11
|
+
contents: write
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
update:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- name: Checkout code
|
|
19
|
+
uses: actions/checkout@v4
|
|
20
|
+
with:
|
|
21
|
+
ref: ${{ github.event.release.target_commitish }}
|
|
22
|
+
|
|
23
|
+
- name: Update Changelog
|
|
24
|
+
uses: stefanzweifel/changelog-updater-action@v1
|
|
25
|
+
with:
|
|
26
|
+
latest-version: ${{ github.event.release.tag_name }}
|
|
27
|
+
heading-text: ${{ github.event.release.name }}
|
|
28
|
+
|
|
29
|
+
- name: Create Pull Request
|
|
30
|
+
uses: peter-evans/create-pull-request@v6
|
|
31
|
+
with:
|
|
32
|
+
branch: docs/changelog-update-${{ github.event.release.tag_name }}
|
|
33
|
+
title: '[Changelog] Update to ${{ github.event.release.tag_name }}'
|
|
34
|
+
add-paths: |
|
|
35
|
+
CHANGELOG.md
|
|
@@ -8,6 +8,9 @@ on:
|
|
|
8
8
|
- 'develop'
|
|
9
9
|
tags-ignore:
|
|
10
10
|
- '**'
|
|
11
|
+
paths:
|
|
12
|
+
- "src/**"
|
|
13
|
+
- "tests/**"
|
|
11
14
|
|
|
12
15
|
# Trigger the workflow on pull request
|
|
13
16
|
pull_request: ~
|
|
@@ -34,7 +37,7 @@ jobs:
|
|
|
34
37
|
downstream-ci-hpc:
|
|
35
38
|
name: downstream-ci-hpc
|
|
36
39
|
if: ${{ !github.event.pull_request.head.repo.fork && github.event.action != 'labeled' || github.event.label.name == 'approved-for-ci' }}
|
|
37
|
-
uses: ecmwf-actions/downstream-ci/.github/workflows/downstream-ci.yml@main
|
|
40
|
+
uses: ecmwf-actions/downstream-ci/.github/workflows/downstream-ci-hpc.yml@main
|
|
38
41
|
with:
|
|
39
42
|
anemoi-utils: ecmwf/anemoi-utils@${{ github.event.pull_request.head.sha || github.sha }}
|
|
40
43
|
secrets: inherit
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# This workflow will upload a Python Package using Twine when a release is created
|
|
2
|
+
# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries
|
|
3
|
+
|
|
4
|
+
name: Upload Python Package
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
release:
|
|
8
|
+
types: [created]
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
quality:
|
|
12
|
+
uses: ecmwf-actions/reusable-workflows/.github/workflows/qa-precommit-run.yml@v2
|
|
13
|
+
with:
|
|
14
|
+
skip-hooks: "no-commit-to-branch"
|
|
15
|
+
|
|
16
|
+
checks:
|
|
17
|
+
strategy:
|
|
18
|
+
matrix:
|
|
19
|
+
python-version: ["3.9", "3.10"]
|
|
20
|
+
uses: ecmwf-actions/reusable-workflows/.github/workflows/qa-pytest-pyproject.yml@v2
|
|
21
|
+
with:
|
|
22
|
+
python-version: ${{ matrix.python-version }}
|
|
23
|
+
|
|
24
|
+
deploy:
|
|
25
|
+
needs: [checks, quality]
|
|
26
|
+
uses: ecmwf-actions/reusable-workflows/.github/workflows/cd-pypi.yml@v2
|
|
27
|
+
secrets: inherit
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# This workflow will upload a Python Package using Twine when a release is created
|
|
2
|
+
# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries
|
|
3
|
+
|
|
4
|
+
name: Code Quality checks for PRs
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
pull_request:
|
|
9
|
+
types: [opened, synchronize, reopened]
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
quality:
|
|
13
|
+
uses: ecmwf-actions/reusable-workflows/.github/workflows/qa-precommit-run.yml@v2
|
|
14
|
+
with:
|
|
15
|
+
skip-hooks: "no-commit-to-branch"
|
|
16
|
+
|
|
17
|
+
checks:
|
|
18
|
+
strategy:
|
|
19
|
+
matrix:
|
|
20
|
+
python-version: ["3.9", "3.10"]
|
|
21
|
+
uses: ecmwf-actions/reusable-workflows/.github/workflows/qa-pytest-pyproject.yml@v2
|
|
22
|
+
with:
|
|
23
|
+
python-version: ${{ matrix.python-version }}
|
|
@@ -20,8 +20,14 @@ repos:
|
|
|
20
20
|
- id: no-commit-to-branch # Prevent committing to main / master
|
|
21
21
|
- id: check-added-large-files # Check for large files added to git
|
|
22
22
|
- id: check-merge-conflict # Check for files that contain merge conflict
|
|
23
|
+
- repo: https://github.com/pre-commit/pygrep-hooks
|
|
24
|
+
rev: v1.10.0 # Use the ref you want to point at
|
|
25
|
+
hooks:
|
|
26
|
+
- id: python-use-type-annotations # Check for missing type annotations
|
|
27
|
+
- id: python-check-blanket-noqa # Check for # noqa: all
|
|
28
|
+
- id: python-no-log-warn # Check for log.warn
|
|
23
29
|
- repo: https://github.com/psf/black-pre-commit-mirror
|
|
24
|
-
rev: 24.
|
|
30
|
+
rev: 24.8.0
|
|
25
31
|
hooks:
|
|
26
32
|
- id: black
|
|
27
33
|
args: [--line-length=120]
|
|
@@ -34,7 +40,7 @@ repos:
|
|
|
34
40
|
- --force-single-line-imports
|
|
35
41
|
- --profile black
|
|
36
42
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
37
|
-
rev: v0.4
|
|
43
|
+
rev: v0.6.4
|
|
38
44
|
hooks:
|
|
39
45
|
- id: ruff
|
|
40
46
|
# Next line if for documenation cod snippets
|
|
@@ -45,7 +51,7 @@ repos:
|
|
|
45
51
|
- --exit-non-zero-on-fix
|
|
46
52
|
- --preview
|
|
47
53
|
- repo: https://github.com/sphinx-contrib/sphinx-lint
|
|
48
|
-
rev:
|
|
54
|
+
rev: v1.0.0
|
|
49
55
|
hooks:
|
|
50
56
|
- id: sphinx-lint
|
|
51
57
|
# For now, we use it. But it does not support a lot of sphinx features
|
|
@@ -59,12 +65,23 @@ repos:
|
|
|
59
65
|
hooks:
|
|
60
66
|
- id: docconvert
|
|
61
67
|
args: ["numpy"]
|
|
62
|
-
- repo: https://github.com/b8raoult/optional-dependencies-all
|
|
63
|
-
rev: "0.0.6"
|
|
64
|
-
hooks:
|
|
65
|
-
- id: optional-dependencies-all
|
|
66
|
-
args: ["--inplace", "--exclude-keys=dev,docs,tests", "--group=dev=all,docs,tests"]
|
|
67
68
|
- repo: https://github.com/tox-dev/pyproject-fmt
|
|
68
|
-
rev: "2.
|
|
69
|
+
rev: "2.2.3"
|
|
69
70
|
hooks:
|
|
70
71
|
- id: pyproject-fmt
|
|
72
|
+
- repo: https://github.com/jshwi/docsig # Check docstrings against function sig
|
|
73
|
+
rev: v0.60.1
|
|
74
|
+
hooks:
|
|
75
|
+
- id: docsig
|
|
76
|
+
args:
|
|
77
|
+
- --ignore-no-params # Allow docstrings without parameters
|
|
78
|
+
- --check-dunders # Check dunder methods
|
|
79
|
+
- --check-overridden # Check overridden methods
|
|
80
|
+
- --check-protected # Check protected methods
|
|
81
|
+
- --check-class # Check class docstrings
|
|
82
|
+
- --disable=E113 # Disable empty docstrings
|
|
83
|
+
- --summary # Print a summary
|
|
84
|
+
ci:
|
|
85
|
+
autoupdate_schedule: monthly
|
|
86
|
+
ci:
|
|
87
|
+
autoupdate_schedule: monthly
|
|
@@ -11,8 +11,17 @@ Keep it human-readable, your future self will thank you!
|
|
|
11
11
|
## [Unreleased]
|
|
12
12
|
|
|
13
13
|
### Added
|
|
14
|
+
- Codeowners file
|
|
15
|
+
- Pygrep precommit hooks
|
|
16
|
+
- Docsig precommit hooks
|
|
17
|
+
- Changelog merge strategy- Codeowners file
|
|
18
|
+
- Create dependency on wcwidth. MIT licence.
|
|
19
|
+
- Add anonimize() function.
|
|
14
20
|
|
|
15
21
|
### Changed
|
|
22
|
+
- downstream-ci should only runs for changes in src and tests
|
|
23
|
+
- bugfixes for CI
|
|
24
|
+
- python3.9 support
|
|
16
25
|
|
|
17
26
|
### Removed
|
|
18
27
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: anemoi-utils
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.17
|
|
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
|
|
@@ -223,26 +223,14 @@ Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
|
223
223
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
224
224
|
Requires-Python: >=3.9
|
|
225
225
|
License-File: LICENSE
|
|
226
|
-
Requires-Dist:
|
|
226
|
+
Requires-Dist: aniso8601
|
|
227
227
|
Requires-Dist: pyyaml
|
|
228
228
|
Requires-Dist: tomli
|
|
229
229
|
Requires-Dist: tqdm
|
|
230
230
|
Provides-Extra: all
|
|
231
|
-
Requires-Dist:
|
|
232
|
-
Requires-Dist: nvsmi; extra == "all"
|
|
233
|
-
Requires-Dist: requests; extra == "all"
|
|
234
|
-
Requires-Dist: termcolor; extra == "all"
|
|
231
|
+
Requires-Dist: anemoi-utils[grib,provenance,text]; extra == "all"
|
|
235
232
|
Provides-Extra: dev
|
|
236
|
-
Requires-Dist:
|
|
237
|
-
Requires-Dist: nbsphinx; extra == "dev"
|
|
238
|
-
Requires-Dist: nvsmi; extra == "dev"
|
|
239
|
-
Requires-Dist: pandoc; extra == "dev"
|
|
240
|
-
Requires-Dist: pytest; extra == "dev"
|
|
241
|
-
Requires-Dist: requests; extra == "dev"
|
|
242
|
-
Requires-Dist: sphinx; extra == "dev"
|
|
243
|
-
Requires-Dist: sphinx-argparse<0.5; extra == "dev"
|
|
244
|
-
Requires-Dist: sphinx-rtd-theme; extra == "dev"
|
|
245
|
-
Requires-Dist: termcolor; extra == "dev"
|
|
233
|
+
Requires-Dist: anemoi-utils[all,docs,tests]; extra == "dev"
|
|
246
234
|
Provides-Extra: docs
|
|
247
235
|
Requires-Dist: nbsphinx; extra == "docs"
|
|
248
236
|
Requires-Dist: pandoc; extra == "docs"
|
|
@@ -260,3 +248,4 @@ Provides-Extra: tests
|
|
|
260
248
|
Requires-Dist: pytest; extra == "tests"
|
|
261
249
|
Provides-Extra: text
|
|
262
250
|
Requires-Dist: termcolor; extra == "text"
|
|
251
|
+
Requires-Dist: wcwidth; extra == "text"
|
|
@@ -10,19 +10,13 @@
|
|
|
10
10
|
# https://packaging.python.org/en/latest/guides/writing-pyproject-toml/
|
|
11
11
|
|
|
12
12
|
[build-system]
|
|
13
|
-
requires = [
|
|
14
|
-
"setuptools>=60",
|
|
15
|
-
"setuptools-scm>=8",
|
|
16
|
-
]
|
|
13
|
+
requires = [ "setuptools>=60", "setuptools-scm>=8" ]
|
|
17
14
|
|
|
18
15
|
[project]
|
|
19
16
|
name = "anemoi-utils"
|
|
20
17
|
|
|
21
18
|
description = "A package to hold various functions to support training of ML models on ECMWF data."
|
|
22
|
-
keywords = [
|
|
23
|
-
"ai",
|
|
24
|
-
"tools",
|
|
25
|
-
]
|
|
19
|
+
keywords = [ "ai", "tools" ]
|
|
26
20
|
|
|
27
21
|
license = { file = "LICENSE" }
|
|
28
22
|
authors = [
|
|
@@ -45,35 +39,16 @@ classifiers = [
|
|
|
45
39
|
"Programming Language :: Python :: Implementation :: PyPy",
|
|
46
40
|
]
|
|
47
41
|
|
|
48
|
-
dynamic = [
|
|
49
|
-
"version",
|
|
50
|
-
]
|
|
42
|
+
dynamic = [ "version" ]
|
|
51
43
|
dependencies = [
|
|
52
|
-
"
|
|
53
|
-
|
|
44
|
+
"aniso8601",
|
|
54
45
|
"pyyaml",
|
|
55
|
-
"tomli",
|
|
46
|
+
"tomli", # Only needed before 3.11
|
|
56
47
|
"tqdm",
|
|
57
48
|
]
|
|
58
49
|
|
|
59
|
-
optional-dependencies.all = [
|
|
60
|
-
|
|
61
|
-
"nvsmi",
|
|
62
|
-
"requests",
|
|
63
|
-
"termcolor",
|
|
64
|
-
]
|
|
65
|
-
optional-dependencies.dev = [
|
|
66
|
-
"gitpython",
|
|
67
|
-
"nbsphinx",
|
|
68
|
-
"nvsmi",
|
|
69
|
-
"pandoc",
|
|
70
|
-
"pytest",
|
|
71
|
-
"requests",
|
|
72
|
-
"sphinx",
|
|
73
|
-
"sphinx-argparse<0.5",
|
|
74
|
-
"sphinx-rtd-theme",
|
|
75
|
-
"termcolor",
|
|
76
|
-
]
|
|
50
|
+
optional-dependencies.all = [ "anemoi-utils[grib,provenance,text]" ]
|
|
51
|
+
optional-dependencies.dev = [ "anemoi-utils[all,docs,tests]" ]
|
|
77
52
|
|
|
78
53
|
optional-dependencies.docs = [
|
|
79
54
|
"nbsphinx",
|
|
@@ -85,21 +60,14 @@ optional-dependencies.docs = [
|
|
|
85
60
|
"termcolor",
|
|
86
61
|
]
|
|
87
62
|
|
|
88
|
-
optional-dependencies.grib = [
|
|
89
|
-
"requests",
|
|
90
|
-
]
|
|
63
|
+
optional-dependencies.grib = [ "requests" ]
|
|
91
64
|
|
|
92
|
-
optional-dependencies.provenance = [
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
optional-dependencies.
|
|
97
|
-
"pytest",
|
|
98
|
-
]
|
|
65
|
+
optional-dependencies.provenance = [ "gitpython", "nvsmi" ]
|
|
66
|
+
|
|
67
|
+
optional-dependencies.tests = [ "pytest" ]
|
|
68
|
+
|
|
69
|
+
optional-dependencies.text = [ "termcolor", "wcwidth" ]
|
|
99
70
|
|
|
100
|
-
optional-dependencies.text = [
|
|
101
|
-
"termcolor",
|
|
102
|
-
]
|
|
103
71
|
urls.Documentation = "https://anemoi-utils.readthedocs.io/"
|
|
104
72
|
urls.Homepage = "https://github.com/ecmwf/anemoi-utils/"
|
|
105
73
|
urls.Issues = "https://github.com/ecmwf/anemoi-utils/issues"
|
|
@@ -108,9 +76,7 @@ urls.Repository = "https://github.com/ecmwf/anemoi-utils/"
|
|
|
108
76
|
scripts.anemoi-utils = "anemoi.utils.__main__:main"
|
|
109
77
|
|
|
110
78
|
[tool.setuptools.package-data]
|
|
111
|
-
"anemoi.utils.mars" = [
|
|
112
|
-
"*.yaml",
|
|
113
|
-
]
|
|
79
|
+
"anemoi.utils.mars" = [ "*.yaml" ]
|
|
114
80
|
|
|
115
81
|
[tool.setuptools_scm]
|
|
116
82
|
version_file = "src/anemoi/utils/_version.py"
|
|
@@ -47,7 +47,7 @@ def has_metadata(path: str, name: str = DEFAULT_NAME) -> bool:
|
|
|
47
47
|
return False
|
|
48
48
|
|
|
49
49
|
|
|
50
|
-
def load_metadata(path: str, name: str = DEFAULT_NAME):
|
|
50
|
+
def load_metadata(path: str, name: str = DEFAULT_NAME) -> dict:
|
|
51
51
|
"""Load metadata from a checkpoint file
|
|
52
52
|
|
|
53
53
|
Parameters
|
|
@@ -59,8 +59,8 @@ def load_metadata(path: str, name: str = DEFAULT_NAME):
|
|
|
59
59
|
|
|
60
60
|
Returns
|
|
61
61
|
-------
|
|
62
|
-
|
|
63
|
-
The content of the metadata file
|
|
62
|
+
dict
|
|
63
|
+
The content of the metadata file from JSON
|
|
64
64
|
|
|
65
65
|
Raises
|
|
66
66
|
------
|
|
@@ -82,7 +82,7 @@ def load_metadata(path: str, name: str = DEFAULT_NAME):
|
|
|
82
82
|
raise ValueError(f"Could not find '{name}' in {path}.")
|
|
83
83
|
|
|
84
84
|
|
|
85
|
-
def save_metadata(path, metadata, name=DEFAULT_NAME, folder=DEFAULT_FOLDER):
|
|
85
|
+
def save_metadata(path, metadata, name=DEFAULT_NAME, folder=DEFAULT_FOLDER) -> None:
|
|
86
86
|
"""Save metadata to a checkpoint file
|
|
87
87
|
|
|
88
88
|
Parameters
|
|
@@ -93,6 +93,8 @@ def save_metadata(path, metadata, name=DEFAULT_NAME, folder=DEFAULT_FOLDER):
|
|
|
93
93
|
A JSON serializable object
|
|
94
94
|
name : str, optional
|
|
95
95
|
The name of the metadata file in the zip archive
|
|
96
|
+
folder : str, optional
|
|
97
|
+
The folder where the metadata file will be saved
|
|
96
98
|
"""
|
|
97
99
|
with zipfile.ZipFile(path, "a") as zipf:
|
|
98
100
|
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
# granted to it by virtue of its status as an intergovernmental organisation
|
|
6
6
|
# nor does it submit to any jurisdiction.
|
|
7
7
|
|
|
8
|
+
from __future__ import annotations
|
|
8
9
|
|
|
9
10
|
import json
|
|
10
11
|
import logging
|
|
@@ -175,9 +176,14 @@ def config_path(name="settings.toml"):
|
|
|
175
176
|
return full
|
|
176
177
|
|
|
177
178
|
|
|
178
|
-
def load_any_dict_format(path):
|
|
179
|
+
def load_any_dict_format(path) -> dict:
|
|
179
180
|
"""Load a configuration file in any supported format: JSON, YAML and TOML.
|
|
180
181
|
|
|
182
|
+
Parameters
|
|
183
|
+
----------
|
|
184
|
+
path : str
|
|
185
|
+
The path to the configuration file.
|
|
186
|
+
|
|
181
187
|
Returns
|
|
182
188
|
-------
|
|
183
189
|
dict
|
|
@@ -243,7 +249,7 @@ def _load_config(name="settings.toml", secrets=None, defaults=None):
|
|
|
243
249
|
return CONFIG[key]
|
|
244
250
|
|
|
245
251
|
|
|
246
|
-
def _save_config(name, data):
|
|
252
|
+
def _save_config(name, data) -> None:
|
|
247
253
|
CONFIG.pop(name, None)
|
|
248
254
|
|
|
249
255
|
conf = config_path(name)
|
|
@@ -265,7 +271,7 @@ def _save_config(name, data):
|
|
|
265
271
|
f.write(data)
|
|
266
272
|
|
|
267
273
|
|
|
268
|
-
def save_config(name, data):
|
|
274
|
+
def save_config(name, data) -> None:
|
|
269
275
|
"""Save a configuration file.
|
|
270
276
|
|
|
271
277
|
Parameters
|
|
@@ -281,13 +287,17 @@ def save_config(name, data):
|
|
|
281
287
|
_save_config(name, data)
|
|
282
288
|
|
|
283
289
|
|
|
284
|
-
def load_config(name="settings.toml", secrets=None, defaults=None):
|
|
290
|
+
def load_config(name="settings.toml", secrets=None, defaults=None) -> DotDict | str:
|
|
285
291
|
"""Read a configuration file.
|
|
286
292
|
|
|
287
293
|
Parameters
|
|
288
294
|
----------
|
|
289
295
|
name : str, optional
|
|
290
296
|
The name of the config file to read, by default "settings.toml"
|
|
297
|
+
secrets : str or list, optional
|
|
298
|
+
The name of the secrets file, by default None
|
|
299
|
+
defaults : str or dict, optional
|
|
300
|
+
The name of the defaults file, by default None
|
|
291
301
|
|
|
292
302
|
Returns
|
|
293
303
|
-------
|
|
@@ -299,7 +309,7 @@ def load_config(name="settings.toml", secrets=None, defaults=None):
|
|
|
299
309
|
return _load_config(name, secrets, defaults)
|
|
300
310
|
|
|
301
311
|
|
|
302
|
-
def load_raw_config(name, default=None):
|
|
312
|
+
def load_raw_config(name, default=None) -> DotDict | str:
|
|
303
313
|
|
|
304
314
|
path = config_path(name)
|
|
305
315
|
if os.path.exists(path):
|
|
@@ -308,13 +318,17 @@ def load_raw_config(name, default=None):
|
|
|
308
318
|
return default
|
|
309
319
|
|
|
310
320
|
|
|
311
|
-
def check_config_mode(name="settings.toml", secrets_name=None, secrets=None):
|
|
321
|
+
def check_config_mode(name="settings.toml", secrets_name=None, secrets=None) -> None:
|
|
312
322
|
"""Check that a configuration file is secure.
|
|
313
323
|
|
|
314
324
|
Parameters
|
|
315
325
|
----------
|
|
316
326
|
name : str, optional
|
|
317
327
|
The name of the configuration file, by default "settings.toml"
|
|
328
|
+
secrets_name : str, optional
|
|
329
|
+
The name of the secrets file, by default None
|
|
330
|
+
secrets : list, optional
|
|
331
|
+
The list of secrets to check, by default None
|
|
318
332
|
|
|
319
333
|
Raises
|
|
320
334
|
------
|
|
@@ -10,12 +10,20 @@ import calendar
|
|
|
10
10
|
import datetime
|
|
11
11
|
import re
|
|
12
12
|
|
|
13
|
-
import
|
|
13
|
+
import aniso8601
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
|
|
16
|
+
def normalise_frequency(frequency):
|
|
17
|
+
if isinstance(frequency, int):
|
|
18
|
+
return frequency
|
|
19
|
+
assert isinstance(frequency, str), (type(frequency), frequency)
|
|
20
|
+
|
|
21
|
+
unit = frequency[-1].lower()
|
|
22
|
+
v = int(frequency[:-1])
|
|
23
|
+
return {"h": v, "d": v * 24}[unit]
|
|
16
24
|
|
|
17
25
|
|
|
18
|
-
def _no_time_zone(date):
|
|
26
|
+
def _no_time_zone(date) -> datetime.datetime:
|
|
19
27
|
"""Remove time zone information from a date.
|
|
20
28
|
|
|
21
29
|
Parameters
|
|
@@ -33,7 +41,7 @@ def _no_time_zone(date):
|
|
|
33
41
|
|
|
34
42
|
|
|
35
43
|
# this function is use in anemoi-datasets
|
|
36
|
-
def as_datetime(date, keep_time_zone=False):
|
|
44
|
+
def as_datetime(date, keep_time_zone=False) -> datetime.datetime:
|
|
37
45
|
"""Convert a date to a datetime object, removing any time zone information.
|
|
38
46
|
|
|
39
47
|
Parameters
|
|
@@ -63,7 +71,41 @@ def as_datetime(date, keep_time_zone=False):
|
|
|
63
71
|
raise ValueError(f"Invalid date type: {type(date)}")
|
|
64
72
|
|
|
65
73
|
|
|
66
|
-
def
|
|
74
|
+
def _as_datetime_list(date, default_increment):
|
|
75
|
+
if isinstance(date, (list, tuple)):
|
|
76
|
+
for d in date:
|
|
77
|
+
yield from _as_datetime_list(d, default_increment)
|
|
78
|
+
|
|
79
|
+
if isinstance(date, str):
|
|
80
|
+
# Check for ISO format
|
|
81
|
+
try:
|
|
82
|
+
start, end = aniso8601.parse_interval(date)
|
|
83
|
+
while start <= end:
|
|
84
|
+
yield as_datetime(start)
|
|
85
|
+
start += default_increment
|
|
86
|
+
|
|
87
|
+
return
|
|
88
|
+
|
|
89
|
+
except aniso8601.exceptions.ISOFormatError:
|
|
90
|
+
pass
|
|
91
|
+
|
|
92
|
+
try:
|
|
93
|
+
intervals = aniso8601.parse_repeating_interval(date)
|
|
94
|
+
for date in intervals:
|
|
95
|
+
yield as_datetime(date)
|
|
96
|
+
return
|
|
97
|
+
except aniso8601.exceptions.ISOFormatError:
|
|
98
|
+
pass
|
|
99
|
+
|
|
100
|
+
yield as_datetime(date)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def as_datetime_list(date, default_increment=1):
|
|
104
|
+
default_increment = frequency_to_timedelta(default_increment)
|
|
105
|
+
return list(_as_datetime_list(date, default_increment))
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def frequency_to_timedelta(frequency) -> datetime.timedelta:
|
|
67
109
|
"""Convert a frequency to a timedelta object.
|
|
68
110
|
|
|
69
111
|
Parameters
|
|
@@ -120,14 +162,14 @@ def frequency_to_timedelta(frequency):
|
|
|
120
162
|
|
|
121
163
|
# ISO8601
|
|
122
164
|
try:
|
|
123
|
-
return
|
|
124
|
-
except
|
|
165
|
+
return aniso8601.parse_duration(frequency)
|
|
166
|
+
except aniso8601.exceptions.ISOFormatError:
|
|
125
167
|
pass
|
|
126
168
|
|
|
127
169
|
raise ValueError(f"Cannot convert frequency {frequency} to timedelta")
|
|
128
170
|
|
|
129
171
|
|
|
130
|
-
def frequency_to_string(frequency):
|
|
172
|
+
def frequency_to_string(frequency) -> str:
|
|
131
173
|
"""Convert a frequency (i.e. a datetime.timedelta) to a string.
|
|
132
174
|
|
|
133
175
|
Parameters
|
|
@@ -174,7 +216,7 @@ def frequency_to_string(frequency):
|
|
|
174
216
|
return str(frequency)
|
|
175
217
|
|
|
176
218
|
|
|
177
|
-
def frequency_to_seconds(frequency):
|
|
219
|
+
def frequency_to_seconds(frequency) -> int:
|
|
178
220
|
"""Convert a frequency to seconds.
|
|
179
221
|
|
|
180
222
|
Parameters
|
|
@@ -362,6 +404,8 @@ class Autumn(DateTimes):
|
|
|
362
404
|
|
|
363
405
|
|
|
364
406
|
class ConcatDateTimes:
|
|
407
|
+
"""ConcatDateTimes is an iterator that generates datetime objects from a list of dates."""
|
|
408
|
+
|
|
365
409
|
def __init__(self, *dates):
|
|
366
410
|
if len(dates) == 1 and isinstance(dates[0], list):
|
|
367
411
|
dates = dates[0]
|
|
@@ -374,6 +418,8 @@ class ConcatDateTimes:
|
|
|
374
418
|
|
|
375
419
|
|
|
376
420
|
class EnumDateTimes:
|
|
421
|
+
"""EnumDateTimes is an iterator that generates datetime objects from a list of dates."""
|
|
422
|
+
|
|
377
423
|
def __init__(self, dates):
|
|
378
424
|
self.dates = dates
|
|
379
425
|
|
|
@@ -393,6 +439,8 @@ def datetimes_factory(*args, **kwargs):
|
|
|
393
439
|
name = kwargs.get("name")
|
|
394
440
|
|
|
395
441
|
if name == "hindcast":
|
|
442
|
+
from .hindcasts import HindcastDatesTimes
|
|
443
|
+
|
|
396
444
|
reference_dates = kwargs["reference_dates"]
|
|
397
445
|
reference_dates = datetimes_factory(reference_dates)
|
|
398
446
|
years = kwargs["years"]
|
|
@@ -416,3 +464,8 @@ def datetimes_factory(*args, **kwargs):
|
|
|
416
464
|
return datetimes_factory(*a)
|
|
417
465
|
|
|
418
466
|
return ConcatDateTimes(*[datetimes_factory(a) for a in args])
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
if __name__ == "__main__":
|
|
470
|
+
print(as_datetime_list("R10/2023-01-01T00:00:00Z/P1D"))
|
|
471
|
+
print(as_datetime_list("2007-03-01T13:00:00/2008-05-11T15:30:00", "200h"))
|
|
@@ -95,8 +95,8 @@ def units(param) -> str:
|
|
|
95
95
|
|
|
96
96
|
Parameters
|
|
97
97
|
----------
|
|
98
|
-
|
|
99
|
-
Parameter id
|
|
98
|
+
param : int or str
|
|
99
|
+
Parameter id or name.
|
|
100
100
|
|
|
101
101
|
Returns
|
|
102
102
|
-------
|
|
@@ -112,7 +112,7 @@ def units(param) -> str:
|
|
|
112
112
|
return _units()[unit_id]
|
|
113
113
|
|
|
114
114
|
|
|
115
|
-
def must_be_positive(param):
|
|
115
|
+
def must_be_positive(param) -> bool:
|
|
116
116
|
"""Check if a parameter must be positive.
|
|
117
117
|
|
|
118
118
|
Parameters
|