anemoi-utils 0.4.12__tar.gz → 0.4.14__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.4.12 → anemoi_utils-0.4.14}/.github/dependabot.yml +4 -4
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/.github/labeler.yml +1 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/.github/workflows/downstream-ci-hpc.yml +14 -8
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/.github/workflows/pr-label-conventional-commits.yml +4 -1
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/.github/workflows/pr-label-public.yml +1 -1
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/.github/workflows/python-publish.yml +1 -1
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/.github/workflows/python-pull-request.yml +2 -2
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/.gitignore +3 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/.pre-commit-config.yaml +13 -7
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/.readthedocs.yaml +1 -1
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/.release-please-config.json +3 -2
- anemoi_utils-0.4.14/.release-please-manifest.json +3 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/CHANGELOG.md +27 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/PKG-INFO +8 -4
- anemoi_utils-0.4.14/docs/_static/logo.png +0 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/pyproject.toml +18 -2
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/src/anemoi/utils/__init__.py +1 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/src/anemoi/utils/__main__.py +12 -2
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/src/anemoi/utils/_version.py +9 -4
- anemoi_utils-0.4.14/src/anemoi/utils/caching.py +249 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/src/anemoi/utils/checkpoints.py +81 -13
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/src/anemoi/utils/cli.py +83 -7
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/src/anemoi/utils/commands/__init__.py +4 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/src/anemoi/utils/commands/config.py +19 -2
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/src/anemoi/utils/commands/requests.py +18 -2
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/src/anemoi/utils/compatibility.py +6 -5
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/src/anemoi/utils/config.py +254 -23
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/src/anemoi/utils/dates.py +204 -50
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/src/anemoi/utils/devtools.py +68 -7
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/src/anemoi/utils/grib.py +30 -9
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/src/anemoi/utils/grids.py +85 -8
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/src/anemoi/utils/hindcasts.py +25 -8
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/src/anemoi/utils/humanize.py +357 -52
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/src/anemoi/utils/logs.py +31 -3
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/src/anemoi/utils/mars/__init__.py +46 -12
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/src/anemoi/utils/mars/requests.py +15 -1
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/src/anemoi/utils/provenance.py +189 -32
- anemoi_utils-0.4.14/src/anemoi/utils/registry.py +336 -0
- anemoi_utils-0.4.14/src/anemoi/utils/remote/__init__.py +680 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/src/anemoi/utils/remote/s3.py +252 -29
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/src/anemoi/utils/remote/ssh.py +140 -8
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/src/anemoi/utils/s3.py +77 -4
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/src/anemoi/utils/sanitise.py +52 -7
- anemoi_utils-0.4.14/src/anemoi/utils/testing.py +182 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/src/anemoi/utils/text.py +218 -54
- anemoi_utils-0.4.14/src/anemoi/utils/timer.py +150 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/src/anemoi_utils.egg-info/PKG-INFO +8 -4
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/src/anemoi_utils.egg-info/SOURCES.txt +2 -1
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/src/anemoi_utils.egg-info/requires.txt +6 -2
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/tests/test_caching.py +94 -15
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/tests/test_compatibility.py +3 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/tests/test_dates.py +21 -5
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/tests/test_frequency.py +4 -2
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/tests/test_grids.py +2 -1
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/tests/test_provenance.py +2 -2
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/tests/test_remote.py +92 -10
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/tests/test_sanetise.py +4 -2
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/tests/test_utils.py +31 -4
- anemoi_utils-0.4.12/.release-please-manifest.json +0 -3
- anemoi_utils-0.4.12/docs/_static/logo.png +0 -0
- anemoi_utils-0.4.12/src/anemoi/utils/caching.py +0 -124
- anemoi_utils-0.4.12/src/anemoi/utils/registry.py +0 -146
- anemoi_utils-0.4.12/src/anemoi/utils/remote/__init__.py +0 -332
- anemoi_utils-0.4.12/src/anemoi/utils/timer.py +0 -74
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/.gitattributes +0 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/.github/CODEOWNERS +0 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/.github/ci-hpc-config.yml +0 -0
- {anemoi_utils-0.4.12/.github/PULL_REQUEST_TEMPLATE → anemoi_utils-0.4.14/.github}/pull_request_template.md +0 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/.github/release.yml +0 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/.github/workflows/pr-conventional-commit.yml +0 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/.github/workflows/pr-label-file-based.yml +0 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/.github/workflows/readthedocs-pr-update.yml +0 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/.github/workflows/release-please.yml +0 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/CONTRIBUTORS.md +0 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/LICENSE +0 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/README.md +0 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/docs/Makefile +0 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/docs/_static/style.css +0 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/docs/_templates/.gitkeep +0 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/docs/conf.py +0 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/docs/index.rst +0 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/docs/installing.rst +0 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/docs/modules/checkpoints.rst +0 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/docs/modules/config.rst +0 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/docs/modules/dates.rst +0 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/docs/modules/grib.rst +0 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/docs/modules/humanize.rst +0 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/docs/modules/provenance.rst +0 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/docs/modules/s3.rst +0 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/docs/modules/text.rst +0 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/setup.cfg +0 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/src/anemoi/utils/mars/mars.yaml +0 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/src/anemoi/utils/sanitize.py +0 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/src/anemoi_utils.egg-info/dependency_links.txt +0 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/src/anemoi_utils.egg-info/entry_points.txt +0 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/src/anemoi_utils.egg-info/top_level.txt +0 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/tests/test-transfer-data/directory/b/c/x +0 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/tests/test-transfer-data/directory/b/y +0 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/tests/test-transfer-data/directory/exotic filename ;^/"'[=.,#]()/303/252/303/274/303/247/303/262/342/234/205.txt" +0 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/tests/test-transfer-data/directory/z +0 -0
- {anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/tests/test-transfer-data/file +0 -0
|
@@ -42,13 +42,19 @@ jobs:
|
|
|
42
42
|
with:
|
|
43
43
|
anemoi-utils: ecmwf/anemoi-utils@${{ github.event.pull_request.head.sha || github.sha }}
|
|
44
44
|
codecov_upload: true
|
|
45
|
+
# Only run on fedora
|
|
46
|
+
skip_matrix_jobs: |
|
|
47
|
+
gnu@debian-11
|
|
48
|
+
gnu@rocky-8.6
|
|
49
|
+
clang@rocky-8.6
|
|
50
|
+
gnu@ubuntu-22.04
|
|
45
51
|
secrets: inherit
|
|
46
52
|
|
|
47
|
-
|
|
48
|
-
downstream-ci-hpc:
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
# # Build downstream packages on HPC
|
|
54
|
+
# downstream-ci-hpc:
|
|
55
|
+
# name: downstream-ci-hpc
|
|
56
|
+
# if: ${{ !github.event.pull_request.head.repo.fork && github.event.action != 'labeled' || github.event.label.name == 'approved-for-ci' }}
|
|
57
|
+
# uses: ecmwf-actions/downstream-ci/.github/workflows/downstream-ci-hpc.yml@main
|
|
58
|
+
# with:
|
|
59
|
+
# anemoi-utils: ecmwf/anemoi-utils@${{ github.event.pull_request.head.sha || github.sha }}
|
|
60
|
+
# secrets: inherit
|
{anemoi_utils-0.4.12 → anemoi_utils-0.4.14}/.github/workflows/pr-label-conventional-commits.yml
RENAMED
|
@@ -8,6 +8,9 @@ on:
|
|
|
8
8
|
types:
|
|
9
9
|
[opened, reopened, labeled, unlabeled]
|
|
10
10
|
|
|
11
|
+
permissions:
|
|
12
|
+
pull-requests: write
|
|
13
|
+
|
|
11
14
|
jobs:
|
|
12
15
|
assign-labels:
|
|
13
16
|
runs-on: ubuntu-latest
|
|
@@ -41,5 +44,5 @@ jobs:
|
|
|
41
44
|
- type: 'config'
|
|
42
45
|
nouns: ['config', 'conf', 'configuration']
|
|
43
46
|
labels: ['config']
|
|
44
|
-
maintain-labels-not-matched:
|
|
47
|
+
maintain-labels-not-matched: true
|
|
45
48
|
apply-changes: true
|
|
@@ -12,7 +12,7 @@ on:
|
|
|
12
12
|
|
|
13
13
|
jobs:
|
|
14
14
|
quality:
|
|
15
|
-
uses: ecmwf
|
|
15
|
+
uses: ecmwf/reusable-workflows/.github/workflows/qa-precommit-run.yml@v2
|
|
16
16
|
with:
|
|
17
17
|
skip-hooks: "no-commit-to-branch"
|
|
18
18
|
|
|
@@ -20,6 +20,6 @@ jobs:
|
|
|
20
20
|
strategy:
|
|
21
21
|
matrix:
|
|
22
22
|
python-version: ["3.9", "3.10", "3.11", "3.12"]
|
|
23
|
-
uses: ecmwf
|
|
23
|
+
uses: ecmwf/reusable-workflows/.github/workflows/qa-pytest-pyproject.yml@v2
|
|
24
24
|
with:
|
|
25
25
|
python-version: ${{ matrix.python-version }}
|
|
@@ -27,12 +27,12 @@ repos:
|
|
|
27
27
|
- id: python-check-blanket-noqa # Check for # noqa: all
|
|
28
28
|
- id: python-no-log-warn # Check for log.warn
|
|
29
29
|
- repo: https://github.com/psf/black-pre-commit-mirror
|
|
30
|
-
rev:
|
|
30
|
+
rev: 25.1.0
|
|
31
31
|
hooks:
|
|
32
32
|
- id: black
|
|
33
33
|
args: [--line-length=120]
|
|
34
34
|
- repo: https://github.com/pycqa/isort
|
|
35
|
-
rev:
|
|
35
|
+
rev: 6.0.1
|
|
36
36
|
hooks:
|
|
37
37
|
- id: isort
|
|
38
38
|
args:
|
|
@@ -40,7 +40,7 @@ repos:
|
|
|
40
40
|
- --force-single-line-imports
|
|
41
41
|
- --profile black
|
|
42
42
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
43
|
-
rev: v0.
|
|
43
|
+
rev: v0.9.9
|
|
44
44
|
hooks:
|
|
45
45
|
- id: ruff
|
|
46
46
|
args:
|
|
@@ -58,13 +58,18 @@ repos:
|
|
|
58
58
|
rev: v0.0.14
|
|
59
59
|
hooks:
|
|
60
60
|
- id: rstfmt
|
|
61
|
-
exclude: 'cli/.*' # Because we use argparse
|
|
61
|
+
exclude: '(cli|schemas)/.*' # Because we use argparse and pydantic sphinx directives
|
|
62
|
+
- repo: https://github.com/b8raoult/pre-commit-docconvert
|
|
63
|
+
rev: "0.1.5"
|
|
64
|
+
hooks:
|
|
65
|
+
- id: docconvert
|
|
66
|
+
args: ["numpy"]
|
|
62
67
|
- repo: https://github.com/tox-dev/pyproject-fmt
|
|
63
|
-
rev: "v2.5.
|
|
68
|
+
rev: "v2.5.1"
|
|
64
69
|
hooks:
|
|
65
70
|
- id: pyproject-fmt
|
|
66
71
|
- repo: https://github.com/jshwi/docsig # Check docstrings against function sig
|
|
67
|
-
rev: v0.
|
|
72
|
+
rev: v0.69.1
|
|
68
73
|
hooks:
|
|
69
74
|
- id: docsig
|
|
70
75
|
args:
|
|
@@ -73,6 +78,7 @@ repos:
|
|
|
73
78
|
- --check-overridden # Check overridden methods
|
|
74
79
|
- --check-protected # Check protected methods
|
|
75
80
|
- --check-class # Check class docstrings
|
|
76
|
-
- --disable=
|
|
81
|
+
- --disable=SIG101,SIG102 # Disable empty docstrings
|
|
77
82
|
ci:
|
|
78
83
|
autoupdate_schedule: monthly
|
|
84
|
+
autoupdate_commit_msg: "chore(deps): pre-commit.ci autoupdate"
|
|
@@ -4,10 +4,11 @@
|
|
|
4
4
|
"bump-patch-for-minor-pre-major": true,
|
|
5
5
|
"separate-pull-requests": true,
|
|
6
6
|
"always-update": true,
|
|
7
|
-
"changelog-type": "
|
|
7
|
+
"changelog-type": "default",
|
|
8
8
|
"include-component-in-tag": false,
|
|
9
9
|
"include-v-in-tag": false,
|
|
10
|
-
"pull-request
|
|
10
|
+
"draft-pull-request": true,
|
|
11
|
+
"pull-request-title-pattern": "chore${scope}: Release${component} ${version}",
|
|
11
12
|
"pull-request-header": ":robot: Automated Release PR\n\nThis PR was created by `release-please` to prepare the next release. Once merged:\n\n1. A new version tag will be created\n2. A GitHub release will be published\n3. The changelog will be updated\n\nChanges to be included in the next release:",
|
|
12
13
|
"pull-request-footer": "> [!IMPORTANT]\n> :warning: Merging this PR will:\n> - Create a new release\n> - Trigger deployment pipelines\n> - Update package versions\n\n **Before merging:**\n - Ensure all tests pass\n - Review the changelog carefully\n - Get required approvals\n\n [Release-please documentation](https://github.com/googleapis/release-please)",
|
|
13
14
|
"packages": {
|
|
@@ -8,6 +8,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
8
8
|
Please add your functional changes to the appropriate section in the PR.
|
|
9
9
|
Keep it human-readable, your future self will thank you!
|
|
10
10
|
|
|
11
|
+
## [0.4.14](https://github.com/ecmwf/anemoi-utils/compare/0.4.13...0.4.14) (2025-03-21)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Bug Fixes
|
|
15
|
+
|
|
16
|
+
* plugin support ([#110](https://github.com/ecmwf/anemoi-utils/issues/110)) ([329395a](https://github.com/ecmwf/anemoi-utils/commit/329395a5870cbf59bacb39cb5afea6b91c465b07))
|
|
17
|
+
|
|
18
|
+
## [0.4.13](https://github.com/ecmwf/anemoi-utils/compare/0.4.12...0.4.13) (2025-03-14)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
### Features
|
|
22
|
+
|
|
23
|
+
* add robust requests ([#112](https://github.com/ecmwf/anemoi-utils/issues/112)) ([5d87227](https://github.com/ecmwf/anemoi-utils/commit/5d87227e6f0b39f087f8a34f238806a2f73480f1))
|
|
24
|
+
* bugfix ([#100](https://github.com/ecmwf/anemoi-utils/issues/100)) ([c016cb4](https://github.com/ecmwf/anemoi-utils/commit/c016cb46c23b6a0575d9d843b06fd6b9f71b9f27))
|
|
25
|
+
* keep yaml formating in error messages ([#108](https://github.com/ecmwf/anemoi-utils/issues/108)) ([3bd6682](https://github.com/ecmwf/anemoi-utils/commit/3bd66828cf19d8e3d7d3fbed27533161b6285828))
|
|
26
|
+
* re-add default values in transfer function ([#101](https://github.com/ecmwf/anemoi-utils/issues/101)) ([6462205](https://github.com/ecmwf/anemoi-utils/commit/6462205ee25fa35a71af047b1fbb04bd3c4ca2c4))
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
### Bug Fixes
|
|
30
|
+
|
|
31
|
+
* add optional dependency. boto3 <= 1.36 ([#105](https://github.com/ecmwf/anemoi-utils/issues/105)) ([c8c8393](https://github.com/ecmwf/anemoi-utils/commit/c8c8393ab1e886289541d3aa47a614afe5cd379b))
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
### Documentation
|
|
35
|
+
|
|
36
|
+
* update logo ([#96](https://github.com/ecmwf/anemoi-utils/issues/96)) ([c297127](https://github.com/ecmwf/anemoi-utils/commit/c297127e066c92023ca065b3e7d36ac4ab62527e))
|
|
37
|
+
|
|
11
38
|
## 0.4.12 (2025-01-30)
|
|
12
39
|
|
|
13
40
|
<!-- Release notes generated using configuration in .github/release.yml at main -->
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: anemoi-utils
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.14
|
|
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
|
|
@@ -226,13 +226,14 @@ Requires-Python: >=3.9
|
|
|
226
226
|
License-File: LICENSE
|
|
227
227
|
Requires-Dist: aniso8601
|
|
228
228
|
Requires-Dist: importlib-metadata; python_version < "3.10"
|
|
229
|
+
Requires-Dist: multiurl
|
|
229
230
|
Requires-Dist: numpy
|
|
230
231
|
Requires-Dist: python-dateutil
|
|
231
232
|
Requires-Dist: pyyaml
|
|
232
233
|
Requires-Dist: tomli; python_version < "3.11"
|
|
233
234
|
Requires-Dist: tqdm
|
|
234
235
|
Provides-Extra: all
|
|
235
|
-
Requires-Dist: anemoi-utils[grib,provenance,text]; extra == "all"
|
|
236
|
+
Requires-Dist: anemoi-utils[grib,provenance,s3,text]; extra == "all"
|
|
236
237
|
Provides-Extra: dev
|
|
237
238
|
Requires-Dist: anemoi-utils[all,docs,tests]; extra == "dev"
|
|
238
239
|
Provides-Extra: docs
|
|
@@ -240,7 +241,7 @@ Requires-Dist: nbsphinx; extra == "docs"
|
|
|
240
241
|
Requires-Dist: pandoc; extra == "docs"
|
|
241
242
|
Requires-Dist: requests; extra == "docs"
|
|
242
243
|
Requires-Dist: sphinx; extra == "docs"
|
|
243
|
-
Requires-Dist: sphinx-argparse
|
|
244
|
+
Requires-Dist: sphinx-argparse; extra == "docs"
|
|
244
245
|
Requires-Dist: sphinx-rtd-theme; extra == "docs"
|
|
245
246
|
Requires-Dist: termcolor; extra == "docs"
|
|
246
247
|
Provides-Extra: grib
|
|
@@ -248,8 +249,11 @@ Requires-Dist: requests; extra == "grib"
|
|
|
248
249
|
Provides-Extra: provenance
|
|
249
250
|
Requires-Dist: gitpython; extra == "provenance"
|
|
250
251
|
Requires-Dist: nvsmi; extra == "provenance"
|
|
252
|
+
Provides-Extra: s3
|
|
253
|
+
Requires-Dist: boto3<1.36; extra == "s3"
|
|
251
254
|
Provides-Extra: tests
|
|
252
255
|
Requires-Dist: pytest; extra == "tests"
|
|
253
256
|
Provides-Extra: text
|
|
254
257
|
Requires-Dist: termcolor; extra == "text"
|
|
255
258
|
Requires-Dist: wcwidth; extra == "text"
|
|
259
|
+
Dynamic: license-file
|
|
Binary file
|
|
@@ -42,6 +42,7 @@ dynamic = [ "version" ]
|
|
|
42
42
|
dependencies = [
|
|
43
43
|
"aniso8601",
|
|
44
44
|
"importlib-metadata; python_version<'3.10'",
|
|
45
|
+
"multiurl",
|
|
45
46
|
"numpy",
|
|
46
47
|
"python-dateutil",
|
|
47
48
|
"pyyaml",
|
|
@@ -49,7 +50,7 @@ dependencies = [
|
|
|
49
50
|
"tqdm",
|
|
50
51
|
]
|
|
51
52
|
|
|
52
|
-
optional-dependencies.all = [ "anemoi-utils[grib,provenance,text]" ]
|
|
53
|
+
optional-dependencies.all = [ "anemoi-utils[grib,provenance,text,s3]" ]
|
|
53
54
|
optional-dependencies.dev = [ "anemoi-utils[all,docs,tests]" ]
|
|
54
55
|
|
|
55
56
|
optional-dependencies.docs = [
|
|
@@ -57,7 +58,7 @@ optional-dependencies.docs = [
|
|
|
57
58
|
"pandoc",
|
|
58
59
|
"requests",
|
|
59
60
|
"sphinx",
|
|
60
|
-
"sphinx-argparse
|
|
61
|
+
"sphinx-argparse",
|
|
61
62
|
"sphinx-rtd-theme",
|
|
62
63
|
"termcolor",
|
|
63
64
|
]
|
|
@@ -66,6 +67,10 @@ optional-dependencies.grib = [ "requests" ]
|
|
|
66
67
|
|
|
67
68
|
optional-dependencies.provenance = [ "gitpython", "nvsmi" ]
|
|
68
69
|
|
|
70
|
+
optional-dependencies.s3 = [
|
|
71
|
+
"boto3<1.36",
|
|
72
|
+
]
|
|
73
|
+
|
|
69
74
|
optional-dependencies.tests = [ "pytest" ]
|
|
70
75
|
|
|
71
76
|
optional-dependencies.text = [ "termcolor", "wcwidth" ]
|
|
@@ -87,3 +92,14 @@ version_file = "src/anemoi/utils/_version.py"
|
|
|
87
92
|
markers = [
|
|
88
93
|
"skip_on_hpc: mark a test that should not be run on HPC",
|
|
89
94
|
]
|
|
95
|
+
|
|
96
|
+
[tool.mypy]
|
|
97
|
+
strict = false
|
|
98
|
+
exclude = [
|
|
99
|
+
"docs/**",
|
|
100
|
+
]
|
|
101
|
+
|
|
102
|
+
[tool.pydocstringformatter]
|
|
103
|
+
write = true
|
|
104
|
+
exclude = "docs/**"
|
|
105
|
+
style = "numpydoc"
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
# granted to it by virtue of its status as an intergovernmental organisation
|
|
8
8
|
# nor does it submit to any jurisdiction.
|
|
9
9
|
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
10
12
|
from anemoi.utils.cli import cli_main
|
|
11
13
|
from anemoi.utils.cli import make_parser
|
|
12
14
|
|
|
@@ -15,11 +17,19 @@ from .commands import COMMANDS
|
|
|
15
17
|
|
|
16
18
|
|
|
17
19
|
# For read-the-docs
|
|
18
|
-
def create_parser():
|
|
20
|
+
def create_parser() -> Any:
|
|
21
|
+
"""Create the argument parser for the CLI.
|
|
22
|
+
|
|
23
|
+
Returns
|
|
24
|
+
-------
|
|
25
|
+
Any
|
|
26
|
+
The argument parser
|
|
27
|
+
"""
|
|
19
28
|
return make_parser(__doc__, COMMANDS)
|
|
20
29
|
|
|
21
30
|
|
|
22
|
-
def main():
|
|
31
|
+
def main() -> None:
|
|
32
|
+
"""Main entry point for the CLI."""
|
|
23
33
|
cli_main(__version__, __doc__, COMMANDS)
|
|
24
34
|
|
|
25
35
|
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
# file generated by
|
|
1
|
+
# file generated by setuptools-scm
|
|
2
2
|
# don't change, don't track in version control
|
|
3
|
+
|
|
4
|
+
__all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
|
|
5
|
+
|
|
3
6
|
TYPE_CHECKING = False
|
|
4
7
|
if TYPE_CHECKING:
|
|
5
|
-
from typing import Tuple
|
|
8
|
+
from typing import Tuple
|
|
9
|
+
from typing import Union
|
|
10
|
+
|
|
6
11
|
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
|
7
12
|
else:
|
|
8
13
|
VERSION_TUPLE = object
|
|
@@ -12,5 +17,5 @@ __version__: str
|
|
|
12
17
|
__version_tuple__: VERSION_TUPLE
|
|
13
18
|
version_tuple: VERSION_TUPLE
|
|
14
19
|
|
|
15
|
-
__version__ = version = '0.4.
|
|
16
|
-
__version_tuple__ = version_tuple = (0, 4,
|
|
20
|
+
__version__ = version = '0.4.14'
|
|
21
|
+
__version_tuple__ = version_tuple = (0, 4, 14)
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
# (C) Copyright 2024 Anemoi contributors.
|
|
2
|
+
#
|
|
3
|
+
# This software is licensed under the terms of the Apache Licence Version 2.0
|
|
4
|
+
# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
|
|
5
|
+
#
|
|
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
|
+
import hashlib
|
|
12
|
+
import json
|
|
13
|
+
import os
|
|
14
|
+
import time
|
|
15
|
+
from threading import Lock
|
|
16
|
+
from typing import Any
|
|
17
|
+
from typing import Callable
|
|
18
|
+
from typing import Optional
|
|
19
|
+
|
|
20
|
+
import numpy as np
|
|
21
|
+
|
|
22
|
+
LOCK = Lock()
|
|
23
|
+
CACHE = {}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _get_cache_path(collection: str) -> str:
|
|
27
|
+
"""Get the cache path for a collection.
|
|
28
|
+
|
|
29
|
+
Parameters
|
|
30
|
+
----------
|
|
31
|
+
collection : str
|
|
32
|
+
The name of the collection
|
|
33
|
+
|
|
34
|
+
Returns
|
|
35
|
+
-------
|
|
36
|
+
str
|
|
37
|
+
The cache path
|
|
38
|
+
"""
|
|
39
|
+
return os.path.join(os.path.expanduser("~"), ".cache", "anemoi", collection)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def clean_cache(collection: str = "default") -> None:
|
|
43
|
+
"""Clean the cache for a collection.
|
|
44
|
+
|
|
45
|
+
Parameters
|
|
46
|
+
----------
|
|
47
|
+
collection : str, optional
|
|
48
|
+
The name of the collection, by default "default"
|
|
49
|
+
"""
|
|
50
|
+
global CACHE
|
|
51
|
+
CACHE = {}
|
|
52
|
+
path = _get_cache_path(collection)
|
|
53
|
+
if not os.path.exists(path):
|
|
54
|
+
return
|
|
55
|
+
for filename in os.listdir(path):
|
|
56
|
+
os.remove(os.path.join(path, filename))
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class Cacher:
|
|
60
|
+
"""This class implements a simple caching mechanism.
|
|
61
|
+
Private class, do not use directly.
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
def __init__(self, collection: str, expires: Optional[int]):
|
|
65
|
+
"""Initialize the Cacher.
|
|
66
|
+
|
|
67
|
+
Parameters
|
|
68
|
+
----------
|
|
69
|
+
collection : str
|
|
70
|
+
The name of the collection
|
|
71
|
+
expires : int, optional
|
|
72
|
+
The expiration time in seconds, or None for no expiration
|
|
73
|
+
"""
|
|
74
|
+
self.collection = collection
|
|
75
|
+
self.expires = expires
|
|
76
|
+
|
|
77
|
+
def __call__(self, func: Callable) -> Callable:
|
|
78
|
+
"""Wrap a function with caching.
|
|
79
|
+
|
|
80
|
+
Parameters
|
|
81
|
+
----------
|
|
82
|
+
func : Callable
|
|
83
|
+
The function to wrap
|
|
84
|
+
|
|
85
|
+
Returns
|
|
86
|
+
-------
|
|
87
|
+
Callable
|
|
88
|
+
The wrapped function
|
|
89
|
+
"""
|
|
90
|
+
full = f"{func.__module__}.{func.__name__}"
|
|
91
|
+
|
|
92
|
+
def wrapped(*args, **kwargs):
|
|
93
|
+
with LOCK:
|
|
94
|
+
return self.cache(
|
|
95
|
+
(full, args, kwargs),
|
|
96
|
+
lambda: func(*args, **kwargs),
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
return wrapped
|
|
100
|
+
|
|
101
|
+
def cache(self, key: tuple, proc: Callable) -> Any:
|
|
102
|
+
"""Cache the result of a function.
|
|
103
|
+
|
|
104
|
+
Parameters
|
|
105
|
+
----------
|
|
106
|
+
key : tuple
|
|
107
|
+
The cache key
|
|
108
|
+
proc : Callable
|
|
109
|
+
The function to call if the result is not cached
|
|
110
|
+
|
|
111
|
+
Returns
|
|
112
|
+
-------
|
|
113
|
+
Any
|
|
114
|
+
The cached result
|
|
115
|
+
"""
|
|
116
|
+
key = json.dumps(key, sort_keys=True)
|
|
117
|
+
m = hashlib.md5()
|
|
118
|
+
m.update(key.encode("utf-8"))
|
|
119
|
+
m = m.hexdigest()
|
|
120
|
+
|
|
121
|
+
if m in CACHE:
|
|
122
|
+
return CACHE[m]
|
|
123
|
+
|
|
124
|
+
path = _get_cache_path(self.collection)
|
|
125
|
+
|
|
126
|
+
filename = os.path.join(path, m) + self.ext
|
|
127
|
+
if os.path.exists(filename):
|
|
128
|
+
data = self.load(filename)
|
|
129
|
+
if self.expires is None or data["expires"] > time.time():
|
|
130
|
+
if data["key"] == key:
|
|
131
|
+
return data["value"]
|
|
132
|
+
|
|
133
|
+
value = proc()
|
|
134
|
+
data = {"key": key, "value": value}
|
|
135
|
+
if self.expires is not None:
|
|
136
|
+
data["expires"] = time.time() + self.expires
|
|
137
|
+
|
|
138
|
+
os.makedirs(path, exist_ok=True)
|
|
139
|
+
temp_filename = self.save(filename, data)
|
|
140
|
+
os.rename(temp_filename, filename)
|
|
141
|
+
|
|
142
|
+
CACHE[m] = value
|
|
143
|
+
return value
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
class JsonCacher(Cacher):
|
|
147
|
+
"""Cacher that uses JSON files."""
|
|
148
|
+
|
|
149
|
+
ext = ""
|
|
150
|
+
|
|
151
|
+
def save(self, path: str, data: dict) -> str:
|
|
152
|
+
"""Save data to a JSON file.
|
|
153
|
+
|
|
154
|
+
Parameters
|
|
155
|
+
----------
|
|
156
|
+
path : str
|
|
157
|
+
The path to the JSON file
|
|
158
|
+
data : dict
|
|
159
|
+
The data to save
|
|
160
|
+
|
|
161
|
+
Returns
|
|
162
|
+
-------
|
|
163
|
+
str
|
|
164
|
+
The temporary file path
|
|
165
|
+
"""
|
|
166
|
+
temp_path = path + ".tmp"
|
|
167
|
+
with open(temp_path, "w") as f:
|
|
168
|
+
json.dump(data, f)
|
|
169
|
+
return temp_path
|
|
170
|
+
|
|
171
|
+
def load(self, path: str) -> dict:
|
|
172
|
+
"""Load data from a JSON file.
|
|
173
|
+
|
|
174
|
+
Parameters
|
|
175
|
+
----------
|
|
176
|
+
path : str
|
|
177
|
+
The path to the JSON file
|
|
178
|
+
|
|
179
|
+
Returns
|
|
180
|
+
-------
|
|
181
|
+
dict
|
|
182
|
+
The loaded data
|
|
183
|
+
"""
|
|
184
|
+
with open(path, "r") as f:
|
|
185
|
+
return json.load(f)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
class NpzCacher(Cacher):
|
|
189
|
+
"""Cacher that uses NPZ files."""
|
|
190
|
+
|
|
191
|
+
ext = ".npz"
|
|
192
|
+
|
|
193
|
+
def save(self, path: str, data: dict) -> str:
|
|
194
|
+
"""Save data to an NPZ file.
|
|
195
|
+
|
|
196
|
+
Parameters
|
|
197
|
+
----------
|
|
198
|
+
path : str
|
|
199
|
+
The path to the NPZ file
|
|
200
|
+
data : dict
|
|
201
|
+
The data to save
|
|
202
|
+
|
|
203
|
+
Returns
|
|
204
|
+
-------
|
|
205
|
+
str
|
|
206
|
+
The temporary file path
|
|
207
|
+
"""
|
|
208
|
+
temp_path = path + ".tmp.npz"
|
|
209
|
+
np.savez(temp_path, **data)
|
|
210
|
+
return temp_path
|
|
211
|
+
|
|
212
|
+
def load(self, path: str) -> dict:
|
|
213
|
+
"""Load data from an NPZ file.
|
|
214
|
+
|
|
215
|
+
Parameters
|
|
216
|
+
----------
|
|
217
|
+
path : str
|
|
218
|
+
The path to the NPZ file
|
|
219
|
+
|
|
220
|
+
Returns
|
|
221
|
+
-------
|
|
222
|
+
dict
|
|
223
|
+
The loaded data
|
|
224
|
+
"""
|
|
225
|
+
return np.load(path, allow_pickle=True)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
# This function is the main entry point for the caching mechanism for the other anemoi packages
|
|
229
|
+
def cached(collection: str = "default", expires: Optional[int] = None, encoding: str = "json") -> Callable:
|
|
230
|
+
"""Decorator to cache the result of a function.
|
|
231
|
+
|
|
232
|
+
Default is to use a json file to store the cache, but you can also use npz files
|
|
233
|
+
to cache dict of numpy arrays.
|
|
234
|
+
|
|
235
|
+
Parameters
|
|
236
|
+
----------
|
|
237
|
+
collection : str, optional
|
|
238
|
+
The name of the collection, by default "default"
|
|
239
|
+
expires : int, optional
|
|
240
|
+
The expiration time in seconds, or None for no expiration, by default None
|
|
241
|
+
encoding : str, optional
|
|
242
|
+
The encoding type, either "json" or "npz", by default "json"
|
|
243
|
+
|
|
244
|
+
Returns
|
|
245
|
+
-------
|
|
246
|
+
Callable
|
|
247
|
+
The decorated function
|
|
248
|
+
"""
|
|
249
|
+
return dict(json=JsonCacher, npz=NpzCacher)[encoding](collection, expires)
|