anemoi-utils 0.4.4__tar.gz → 0.4.6__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.

Files changed (80) hide show
  1. anemoi_utils-0.4.6/.github/workflows/changelog-pr-update.yml +18 -0
  2. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/.github/workflows/changelog-release-update.yml +13 -3
  3. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/.github/workflows/ci.yml +23 -8
  4. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/.github/workflows/python-pull-request.yml +8 -3
  5. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/.pre-commit-config.yaml +3 -8
  6. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/CHANGELOG.md +14 -3
  7. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/PKG-INFO +2 -1
  8. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/pyproject.toml +3 -4
  9. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/src/anemoi/utils/__main__.py +2 -3
  10. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/src/anemoi/utils/_version.py +2 -2
  11. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/src/anemoi/utils/checkpoints.py +2 -2
  12. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/src/anemoi/utils/commands/__init__.py +2 -3
  13. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/src/anemoi/utils/commands/config.py +0 -1
  14. anemoi_utils-0.4.6/src/anemoi/utils/compatibility.py +76 -0
  15. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/src/anemoi/utils/hindcasts.py +9 -9
  16. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/src/anemoi/utils/mars/__init__.py +3 -1
  17. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/src/anemoi/utils/registry.py +52 -4
  18. anemoi_utils-0.4.6/src/anemoi/utils/remote/__init__.py +328 -0
  19. {anemoi_utils-0.4.4/src/anemoi/utils → anemoi_utils-0.4.6/src/anemoi/utils/remote}/s3.py +42 -216
  20. anemoi_utils-0.4.6/src/anemoi/utils/remote/ssh.py +133 -0
  21. anemoi_utils-0.4.6/src/anemoi/utils/s3.py +63 -0
  22. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/src/anemoi_utils.egg-info/PKG-INFO +2 -1
  23. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/src/anemoi_utils.egg-info/SOURCES.txt +12 -1
  24. anemoi_utils-0.4.6/tests/test-transfer-data/directory/b/c/x +1 -0
  25. anemoi_utils-0.4.6/tests/test-transfer-data/directory/b/y +1 -0
  26. anemoi_utils-0.4.6/tests/test-transfer-data/directory/exotic filename ;^/"'[=.,#]()/303/252/303/274/303/247/303/262/342/234/205.txt" +1 -0
  27. anemoi_utils-0.4.6/tests/test-transfer-data/directory/z +1 -0
  28. anemoi_utils-0.4.6/tests/test-transfer-data/file +1 -0
  29. anemoi_utils-0.4.6/tests/test_compatibility.py +32 -0
  30. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/tests/test_dates.py +0 -30
  31. anemoi_utils-0.4.6/tests/test_remote.py +175 -0
  32. anemoi_utils-0.4.4/.github/workflows/changelog-pr-update.yml +0 -18
  33. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/.gitattributes +0 -0
  34. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/.github/CODEOWNERS +0 -0
  35. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/.github/ci-hpc-config.yml +0 -0
  36. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/.github/workflows/label-public-pr.yml +0 -0
  37. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/.github/workflows/python-publish.yml +0 -0
  38. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/.github/workflows/readthedocs-pr-update.yml +0 -0
  39. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/.gitignore +0 -0
  40. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/.readthedocs.yaml +0 -0
  41. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/CONTRIBUTORS.md +0 -0
  42. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/LICENSE +0 -0
  43. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/README.md +0 -0
  44. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/docs/Makefile +0 -0
  45. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/docs/_static/logo.png +0 -0
  46. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/docs/_static/style.css +0 -0
  47. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/docs/_templates/.gitkeep +0 -0
  48. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/docs/conf.py +0 -0
  49. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/docs/index.rst +0 -0
  50. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/docs/installing.rst +0 -0
  51. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/docs/modules/checkpoints.rst +0 -0
  52. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/docs/modules/config.rst +0 -0
  53. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/docs/modules/dates.rst +0 -0
  54. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/docs/modules/grib.rst +0 -0
  55. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/docs/modules/humanize.rst +0 -0
  56. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/docs/modules/provenance.rst +0 -0
  57. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/docs/modules/s3.rst +0 -0
  58. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/docs/modules/text.rst +0 -0
  59. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/setup.cfg +0 -0
  60. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/src/anemoi/utils/__init__.py +0 -0
  61. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/src/anemoi/utils/caching.py +0 -0
  62. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/src/anemoi/utils/cli.py +0 -0
  63. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/src/anemoi/utils/config.py +0 -0
  64. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/src/anemoi/utils/dates.py +0 -0
  65. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/src/anemoi/utils/grib.py +0 -0
  66. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/src/anemoi/utils/humanize.py +0 -0
  67. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/src/anemoi/utils/mars/mars.yaml +0 -0
  68. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/src/anemoi/utils/provenance.py +0 -0
  69. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/src/anemoi/utils/sanitise.py +0 -0
  70. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/src/anemoi/utils/sanitize.py +0 -0
  71. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/src/anemoi/utils/text.py +0 -0
  72. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/src/anemoi/utils/timer.py +0 -0
  73. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/src/anemoi_utils.egg-info/dependency_links.txt +0 -0
  74. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/src/anemoi_utils.egg-info/entry_points.txt +0 -0
  75. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/src/anemoi_utils.egg-info/requires.txt +0 -0
  76. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/src/anemoi_utils.egg-info/top_level.txt +0 -0
  77. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/tests/test_frequency.py +0 -0
  78. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/tests/test_provenance.py +0 -0
  79. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/tests/test_sanetise.py +0 -0
  80. {anemoi_utils-0.4.4 → anemoi_utils-0.4.6}/tests/test_utils.py +0 -0
@@ -0,0 +1,18 @@
1
+ # name: Check Changelog Update on PR
2
+ # on:
3
+ # pull_request:
4
+ # types: [assigned, opened, synchronize, reopened, labeled, unlabeled]
5
+ # branches:
6
+ # - main
7
+ # - develop
8
+ # paths-ignore:
9
+ # - .pre-commit-config.yaml
10
+ # - .readthedocs.yaml
11
+ # jobs:
12
+ # Check-Changelog:
13
+ # name: Check Changelog Action
14
+ # runs-on: ubuntu-20.04
15
+ # steps:
16
+ # - uses: tarides/changelog-check-action@v2
17
+ # with:
18
+ # changelog: CHANGELOG.md
@@ -2,9 +2,11 @@
2
2
  name: "Update Changelog"
3
3
 
4
4
  on:
5
- release:
6
- types: [released]
7
- workflow_dispatch: ~
5
+ workflow_run:
6
+ workflows:
7
+ - Upload Python Package
8
+ types:
9
+ - completed
8
10
 
9
11
  permissions:
10
12
  pull-requests: write
@@ -13,6 +15,7 @@ permissions:
13
15
  jobs:
14
16
  update:
15
17
  runs-on: ubuntu-latest
18
+ if: ${{ github.event.workflow_run.conclusion == 'success' }}
16
19
 
17
20
  steps:
18
21
  - name: Checkout code
@@ -25,11 +28,18 @@ jobs:
25
28
  with:
26
29
  latest-version: ${{ github.event.release.tag_name }}
27
30
  heading-text: ${{ github.event.release.name }}
31
+ release-notes: ${{ github.event.release.body }}
28
32
 
29
33
  - name: Create Pull Request
30
34
  uses: peter-evans/create-pull-request@v6
31
35
  with:
32
36
  branch: docs/changelog-update-${{ github.event.release.tag_name }}
37
+ base: develop
33
38
  title: '[Changelog] Update to ${{ github.event.release.tag_name }}'
39
+ body: |
40
+ This PR updates the changelog to include the changes in the latest release.
41
+
42
+ > [!CAUTION]
43
+ > Merge DO NOT squash to correctly update the tag version of `develop` branch.
34
44
  add-paths: |
35
45
  CHANGELOG.md
@@ -31,13 +31,28 @@ jobs:
31
31
  with:
32
32
  anemoi-utils: ecmwf/anemoi-utils@${{ github.event.pull_request.head.sha || github.sha }}
33
33
  codecov_upload: true
34
+ skip_matrix_jobs: |
35
+ gnu@debian-11
36
+ clang@rocky-8.6
37
+ gnu@debian-11
38
+ gnu@rocky-8.6
39
+ clang@rocky-8.6
40
+ gnu@fedora-37
34
41
  secrets: inherit
35
42
 
36
- # Build downstream packages on HPC
37
- downstream-ci-hpc:
38
- name: downstream-ci-hpc
39
- if: ${{ !github.event.pull_request.head.repo.fork && github.event.action != 'labeled' || github.event.label.name == 'approved-for-ci' }}
40
- uses: ecmwf-actions/downstream-ci/.github/workflows/downstream-ci-hpc.yml@main
41
- with:
42
- anemoi-utils: ecmwf/anemoi-utils@${{ github.event.pull_request.head.sha || github.sha }}
43
- secrets: inherit
43
+ # # Build downstream packages on HPC
44
+ # downstream-ci-hpc:
45
+ # name: downstream-ci-hpc
46
+ # if: ${{ !github.event.pull_request.head.repo.fork && github.event.action != 'labeled' || github.event.label.name == 'approved-for-ci' }}
47
+ # uses: ecmwf-actions/downstream-ci/.github/workflows/downstream-ci.yml@main
48
+ # with:
49
+ # anemoi-utils: ecmwf/anemoi-utils@${{ github.event.pull_request.head.sha || github.sha }}
50
+ # codecov_upload: true
51
+ # skip_matrix_jobs: |
52
+ # gnu@debian-11
53
+ # clang@rocky-8.6
54
+ # gnu@debian-11
55
+ # gnu@rocky-8.6
56
+ # clang@rocky-8.6
57
+ # gnu@fedora-37
58
+ # secrets: inherit
@@ -1,12 +1,17 @@
1
1
  # This workflow will upload a Python Package using Twine when a release is created
2
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
3
 
4
- name: Code Quality checks for PRs
4
+ name: Test PR
5
5
 
6
6
  on:
7
- push:
8
7
  pull_request:
9
8
  types: [opened, synchronize, reopened]
9
+ push:
10
+ branches:
11
+ - develop
12
+ schedule:
13
+ - cron: "9 2 * * 0" # at 9:02 on sunday
14
+
10
15
 
11
16
  jobs:
12
17
  quality:
@@ -17,7 +22,7 @@ jobs:
17
22
  checks:
18
23
  strategy:
19
24
  matrix:
20
- python-version: ["3.9", "3.10", "3.11", "3.12"]
25
+ python-version: ["3.11"]
21
26
  uses: ecmwf-actions/reusable-workflows/.github/workflows/qa-pytest-pyproject.yml@v2
22
27
  with:
23
28
  python-version: ${{ matrix.python-version }}
@@ -27,7 +27,7 @@ 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: 24.8.0
30
+ rev: 24.10.0
31
31
  hooks:
32
32
  - id: black
33
33
  args: [--line-length=120]
@@ -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.6.9
43
+ rev: v0.7.2
44
44
  hooks:
45
45
  - id: ruff
46
46
  args:
@@ -59,13 +59,8 @@ repos:
59
59
  hooks:
60
60
  - id: rstfmt
61
61
  exclude: 'cli/.*' # Because we use argparse
62
- - repo: https://github.com/b8raoult/pre-commit-docconvert
63
- rev: "0.1.5"
64
- hooks:
65
- - id: docconvert
66
- args: ["numpy"]
67
62
  - repo: https://github.com/tox-dev/pyproject-fmt
68
- rev: "2.2.4"
63
+ rev: "v2.5.0"
69
64
  hooks:
70
65
  - id: pyproject-fmt
71
66
  - repo: https://github.com/jshwi/docsig # Check docstrings against function sig
@@ -8,19 +8,28 @@ 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
- ## [Unreleased](https://github.com/ecmwf/anemoi-utils/compare/0.4.3...HEAD)
11
+ ## [0.4.5](https://github.com/ecmwf/anemoi-utils/compare/0.4.4...0.4.5) - 2024-11-06
12
+
13
+ ### What's Changed
14
+
15
+ * upload with ssh by @floriankrb in https://github.com/ecmwf/anemoi-utils/pull/25
16
+ * feat: Add aliases decorator by @HCookie in https://github.com/ecmwf/anemoi-utils/pull/40
17
+
18
+ **Full Changelog**: https://github.com/ecmwf/anemoi-utils/compare/0.4.4...0.4.5
19
+
20
+ ## [0.4.4](https://github.com/ecmwf/anemoi-utils/compare/0.4.3...0.4.4) - 2024-11-01
12
21
 
13
22
  ## [0.4.3](https://github.com/ecmwf/anemoi-utils/compare/0.4.1...0.4.3) - 2024-10-26
14
23
 
15
24
  ## [0.4.2](https://github.com/ecmwf/anemoi-utils/compare/0.4.1...0.4.2) - 2024-10-25
16
25
 
17
26
  ### Added
27
+
18
28
  - Add supporting_arrays to checkpoints
19
29
  - Add factories registry
20
30
  - Optional renaming of subcommands via `command` attribute [#34](https://github.com/ecmwf/anemoi-utils/pull/34)
21
31
  - `skip_on_hpc` pytest marker for tests that should not be run on HPC [36](https://github.com/ecmwf/anemoi-utils/pull/36)
22
32
 
23
-
24
33
  ## [0.4.1](https://github.com/ecmwf/anemoi-utils/compare/0.4.0...0.4.1) - 2024-10-23
25
34
 
26
35
  ## Fixed
@@ -51,7 +60,9 @@ Keep it human-readable, your future self will thank you!
51
60
  - Changelog merge strategy- Codeowners file
52
61
  - Create dependency on wcwidth. MIT licence.
53
62
  - Add distribution name dictionary to provenance [#15](https://github.com/ecmwf/anemoi-utils/pull/15) & [#19](https://github.com/ecmwf/anemoi-utils/pull/19)
54
- - Add anonimize() function.
63
+ - Add anonymize() function.
64
+ - Add transfer to ssh:// target (experimental)
65
+ - Deprecated 'anemoi.utils.s3'
55
66
 
56
67
  ### Changed
57
68
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: anemoi-utils
3
- Version: 0.4.4
3
+ Version: 0.4.6
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
@@ -219,6 +219,7 @@ Classifier: Programming Language :: Python :: 3.9
219
219
  Classifier: Programming Language :: Python :: 3.10
220
220
  Classifier: Programming Language :: Python :: 3.11
221
221
  Classifier: Programming Language :: Python :: 3.12
222
+ Classifier: Programming Language :: Python :: 3.13
222
223
  Classifier: Programming Language :: Python :: Implementation :: CPython
223
224
  Classifier: Programming Language :: Python :: Implementation :: PyPy
224
225
  Requires-Python: >=3.9
@@ -1,14 +1,12 @@
1
- #!/usr/bin/env python
2
- # (C) Copyright 2024 ECMWF.
1
+ # (C) Copyright 2024 Anemoi contributors.
3
2
  #
4
3
  # This software is licensed under the terms of the Apache Licence Version 2.0
5
4
  # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
5
+ #
6
6
  # In applying this licence, ECMWF does not waive the privileges and immunities
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
- # https://packaging.python.org/en/latest/guides/writing-pyproject-toml/
11
-
12
10
  [build-system]
13
11
  requires = [ "setuptools>=60", "setuptools-scm>=8" ]
14
12
 
@@ -35,6 +33,7 @@ classifiers = [
35
33
  "Programming Language :: Python :: 3.10",
36
34
  "Programming Language :: Python :: 3.11",
37
35
  "Programming Language :: Python :: 3.12",
36
+ "Programming Language :: Python :: 3.13",
38
37
  "Programming Language :: Python :: Implementation :: CPython",
39
38
  "Programming Language :: Python :: Implementation :: PyPy",
40
39
  ]
@@ -1,12 +1,11 @@
1
- #!/usr/bin/env python
2
- # (C) Copyright 2024 ECMWF.
1
+ # (C) Copyright 2024 Anemoi contributors.
3
2
  #
4
3
  # This software is licensed under the terms of the Apache Licence Version 2.0
5
4
  # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
5
+ #
6
6
  # In applying this licence, ECMWF does not waive the privileges and immunities
7
7
  # granted to it by virtue of its status as an intergovernmental organisation
8
8
  # nor does it submit to any jurisdiction.
9
- #
10
9
 
11
10
  from anemoi.utils.cli import cli_main
12
11
  from anemoi.utils.cli import make_parser
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '0.4.4'
16
- __version_tuple__ = version_tuple = (0, 4, 4)
15
+ __version__ = version = '0.4.6'
16
+ __version_tuple__ = version_tuple = (0, 4, 6)
@@ -94,8 +94,8 @@ def load_metadata(path: str, *, supporting_arrays=False, name: str = DEFAULT_NAM
94
94
  with zipfile.ZipFile(path, "r") as f:
95
95
  metadata = json.load(f.open(metadata, "r"))
96
96
  if supporting_arrays:
97
- metadata["supporting_arrays"] = load_supporting_arrays(f, metadata.get("supporting_arrays", {}))
98
- return metadata, supporting_arrays
97
+ arrays = load_supporting_arrays(f, metadata.get("supporting_arrays_paths", {}))
98
+ return metadata, arrays
99
99
 
100
100
  return metadata
101
101
  else:
@@ -1,12 +1,11 @@
1
- #!/usr/bin/env python
2
- # (C) Copyright 2024 ECMWF.
1
+ # (C) Copyright 2024 Anemoi contributors.
3
2
  #
4
3
  # This software is licensed under the terms of the Apache Licence Version 2.0
5
4
  # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
5
+ #
6
6
  # In applying this licence, ECMWF does not waive the privileges and immunities
7
7
  # granted to it by virtue of its status as an intergovernmental organisation
8
8
  # nor does it submit to any jurisdiction.
9
- #
10
9
 
11
10
  import os
12
11
 
@@ -1,4 +1,3 @@
1
- #!/usr/bin/env python3
2
1
  # (C) Copyright 2024 Anemoi contributors.
3
2
  #
4
3
  # This software is licensed under the terms of the Apache Licence Version 2.0
@@ -0,0 +1,76 @@
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
+ from __future__ import annotations
11
+
12
+ import functools
13
+ from typing import Any
14
+ from typing import Callable
15
+
16
+
17
+ def aliases(
18
+ aliases: dict[str, str | list[str]] | None = None, **kwargs: str | list[str]
19
+ ) -> Callable[[Callable], Callable]:
20
+ """Alias keyword arguments in a function call.
21
+
22
+ Allows for dynamically renaming keyword arguments in a function call.
23
+
24
+ Parameters
25
+ ----------
26
+ aliases : dict[str, str | list[str]] | None, optional
27
+ Key, value pair of aliases, with keys being the true name, and value being a str or list of aliases,
28
+ by default None
29
+ **kwargs : str | list[str]
30
+ Kwargs form of aliases
31
+
32
+ Returns
33
+ -------
34
+ Callable
35
+ Decorator function that renames keyword arguments in a function call.
36
+
37
+ Raises
38
+ ------
39
+ ValueError
40
+ If the aliasing would result in duplicate keys.
41
+
42
+ Examples
43
+ --------
44
+ ```python
45
+ @aliases(a="b", c=["d", "e"])
46
+ def func(a, c):
47
+ return a, c
48
+
49
+ func(a=1, c=2) # (1, 2)
50
+ func(b=1, d=2) # (1, 2)
51
+ ```
52
+
53
+ """
54
+
55
+ if aliases is None:
56
+ aliases = {}
57
+ aliases.update(kwargs)
58
+
59
+ aliases = {v: k for k, vs in aliases.items() for v in (vs if isinstance(vs, list) else [vs])}
60
+
61
+ def decorator(func: Callable) -> Callable:
62
+ @functools.wraps(func)
63
+ def wrapper(*args, **kwargs) -> Any:
64
+ keys = kwargs.keys()
65
+ for k in set(keys).intersection(set(aliases.keys())):
66
+ if aliases[k] in keys:
67
+ raise ValueError(
68
+ f"When aliasing {k} with {aliases[k]} duplicate keys were present. Cannot include both."
69
+ )
70
+ kwargs[aliases[k]] = kwargs.pop(k)
71
+
72
+ return func(*args, **kwargs)
73
+
74
+ return wrapper
75
+
76
+ return decorator
@@ -27,16 +27,16 @@ class HindcastDatesTimes:
27
27
 
28
28
  self.reference_dates = reference_dates
29
29
 
30
- if isinstance(years, list):
31
- self.years = years
32
- else:
33
- self.years = range(1, years + 1)
30
+ assert isinstance(years, int), f"years must be an integer, got {years}"
31
+ assert years > 0, f"years must be greater than 0, got {years}"
32
+ self.years = years
34
33
 
35
34
  def __iter__(self):
36
35
  for reference_date in self.reference_dates:
37
- for year in self.years:
38
- if reference_date.month == 2 and reference_date.day == 29:
39
- date = datetime.datetime(reference_date.year - year, 2, 28)
40
- else:
41
- date = datetime.datetime(reference_date.year - year, reference_date.month, reference_date.day)
36
+ year, month, day = reference_date.year, reference_date.month, reference_date.day
37
+ if (month, day) == (2, 29):
38
+ day = 28
39
+
40
+ for i in range(1, self.years + 1):
41
+ date = datetime.datetime(year - i, month, day)
42
42
  yield (date, reference_date)
@@ -1,6 +1,8 @@
1
- # (C) Copyright 2024 European Centre for Medium-Range Weather Forecasts.
1
+ # (C) Copyright 2024 Anemoi contributors.
2
+ #
2
3
  # This software is licensed under the terms of the Apache Licence Version 2.0
3
4
  # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
5
+ #
4
6
  # In applying this licence, ECMWF does not waive the privileges and immunities
5
7
  # granted to it by virtue of its status as an intergovernmental organisation
6
8
  # nor does it submit to any jurisdiction.
@@ -30,14 +30,23 @@ class Wrapper:
30
30
  return factory
31
31
 
32
32
 
33
+ _BY_KIND = {}
34
+
35
+
33
36
  class Registry:
34
37
  """A registry of factories"""
35
38
 
36
- def __init__(self, package):
39
+ def __init__(self, package, key="_type"):
37
40
 
38
41
  self.package = package
39
42
  self.registered = {}
40
43
  self.kind = package.split(".")[-1]
44
+ self.key = key
45
+ _BY_KIND[self.kind] = self
46
+
47
+ @classmethod
48
+ def lookup_kind(cls, kind: str):
49
+ return _BY_KIND.get(kind)
41
50
 
42
51
  def register(self, name: str, factory: callable = None):
43
52
 
@@ -46,6 +55,9 @@ class Registry:
46
55
 
47
56
  self.registered[name] = factory
48
57
 
58
+ # def registered(self, name: str):
59
+ # return name in self.registered
60
+
49
61
  def _load(self, file):
50
62
  name, _ = os.path.splitext(file)
51
63
  try:
@@ -53,7 +65,9 @@ class Registry:
53
65
  except Exception:
54
66
  LOG.warning(f"Error loading filter '{self.package}.{name}'", exc_info=True)
55
67
 
56
- def lookup(self, name: str) -> callable:
68
+ def lookup(self, name: str, *, return_none=False) -> callable:
69
+
70
+ # print('✅✅✅✅✅✅✅✅✅✅✅✅✅', name, self.registered)
57
71
  if name in self.registered:
58
72
  return self.registered[name]
59
73
 
@@ -86,6 +100,12 @@ class Registry:
86
100
  self.registered[name] = entry_point.load()
87
101
 
88
102
  if name not in self.registered:
103
+ if return_none:
104
+ return None
105
+
106
+ for e in self.registered:
107
+ LOG.info(f"Registered: {e}")
108
+
89
109
  raise ValueError(f"Cannot load '{name}' from {self.package}")
90
110
 
91
111
  return self.registered[name]
@@ -94,5 +114,33 @@ class Registry:
94
114
  factory = self.lookup(name)
95
115
  return factory(*args, **kwargs)
96
116
 
97
- def __call__(self, name: str, *args, **kwargs):
98
- return self.create(name, *args, **kwargs)
117
+ # def __call__(self, name: str, *args, **kwargs):
118
+ # return self.create(name, *args, **kwargs)
119
+
120
+ def from_config(self, config, *args, **kwargs):
121
+ if isinstance(config, str):
122
+ config = {config: {}}
123
+
124
+ if not isinstance(config, dict):
125
+ raise ValueError(f"Invalid config: {config}")
126
+
127
+ if self.key in config:
128
+ config = config.copy()
129
+ key = config.pop(self.key)
130
+ return self.create(key, *args, **config, **kwargs)
131
+
132
+ if len(config) == 1:
133
+ key = list(config.keys())[0]
134
+ value = config[key]
135
+
136
+ if isinstance(value, dict):
137
+ return self.create(key, *args, **value, **kwargs)
138
+
139
+ if isinstance(value, list):
140
+ return self.create(key, *args, *value, **kwargs)
141
+
142
+ return self.create(key, *args, value, **kwargs)
143
+
144
+ raise ValueError(
145
+ f"Entry '{config}' must either be a string, a dictionary with a single entry, or a dictionary with a '{self.key}' key"
146
+ )