anemoi-utils 0.4.23__tar.gz → 0.4.25__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 (104) hide show
  1. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/.github/CODEOWNERS +0 -1
  2. anemoi_utils-0.4.25/.github/pull_request_template.md +13 -0
  3. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/.github/workflows/downstream-ci-hpc.yml +1 -1
  4. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/.github/workflows/pr-conventional-commit.yml +1 -1
  5. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/.github/workflows/pr-label-conventional-commits.yml +1 -1
  6. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/.github/workflows/pr-label-file-based.yml +1 -1
  7. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/.github/workflows/pr-label-public.yml +1 -1
  8. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/.github/workflows/python-pull-request.yml +1 -1
  9. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/.github/workflows/readthedocs-pr-update.yml +1 -1
  10. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/.gitignore +7 -1
  11. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/.pre-commit-config.yaml +3 -2
  12. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/.release-please-config.json +6 -1
  13. anemoi_utils-0.4.25/.release-please-manifest.json +3 -0
  14. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/CHANGELOG.md +16 -0
  15. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/PKG-INFO +3 -1
  16. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/pyproject.toml +2 -0
  17. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/src/anemoi/utils/_version.py +2 -2
  18. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/src/anemoi/utils/cli.py +14 -4
  19. anemoi_utils-0.4.25/src/anemoi/utils/commands/transfer.py +74 -0
  20. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/src/anemoi/utils/grids.py +27 -0
  21. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/src/anemoi/utils/remote/__init__.py +8 -3
  22. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/src/anemoi/utils/remote/s3.py +107 -26
  23. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/src/anemoi/utils/testing.py +101 -0
  24. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/src/anemoi_utils.egg-info/PKG-INFO +3 -1
  25. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/src/anemoi_utils.egg-info/SOURCES.txt +1 -2
  26. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/src/anemoi_utils.egg-info/requires.txt +2 -0
  27. anemoi_utils-0.4.23/.github/pull_request_template.md +0 -46
  28. anemoi_utils-0.4.23/.github/release.yml +0 -23
  29. anemoi_utils-0.4.23/.release-please-manifest.json +0 -3
  30. anemoi_utils-0.4.23/tests/test_grids.py +0 -29
  31. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/.gitattributes +0 -0
  32. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/.github/ci-hpc-config.yml +0 -0
  33. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/.github/dependabot.yml +0 -0
  34. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/.github/labeler.yml +0 -0
  35. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/.github/workflows/python-publish.yml +0 -0
  36. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/.github/workflows/release-please.yml +0 -0
  37. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/.readthedocs.yaml +0 -0
  38. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/CONTRIBUTORS.md +0 -0
  39. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/LICENSE +0 -0
  40. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/README.md +0 -0
  41. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/docs/Makefile +0 -0
  42. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/docs/_static/logo.png +0 -0
  43. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/docs/_static/style.css +0 -0
  44. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/docs/_templates/.gitkeep +0 -0
  45. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/docs/_templates/apidoc/package.rst.jinja +0 -0
  46. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/docs/conf.py +0 -0
  47. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/docs/index.rst +0 -0
  48. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/docs/installing.rst +0 -0
  49. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/docs/modules/checkpoints.rst +0 -0
  50. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/docs/modules/config.rst +0 -0
  51. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/docs/modules/dates.rst +0 -0
  52. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/docs/modules/grib.rst +0 -0
  53. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/docs/modules/humanize.rst +0 -0
  54. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/docs/modules/provenance.rst +0 -0
  55. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/docs/modules/s3.rst +0 -0
  56. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/docs/modules/testing.rst +0 -0
  57. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/docs/modules/text.rst +0 -0
  58. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/docs/scripts/api_build.sh +0 -0
  59. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/setup.cfg +0 -0
  60. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/src/anemoi/utils/__init__.py +0 -0
  61. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/src/anemoi/utils/__main__.py +0 -0
  62. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/src/anemoi/utils/caching.py +0 -0
  63. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/src/anemoi/utils/checkpoints.py +0 -0
  64. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/src/anemoi/utils/commands/__init__.py +0 -0
  65. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/src/anemoi/utils/commands/config.py +0 -0
  66. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/src/anemoi/utils/commands/requests.py +0 -0
  67. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/src/anemoi/utils/compatibility.py +0 -0
  68. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/src/anemoi/utils/config.py +0 -0
  69. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/src/anemoi/utils/dates.py +0 -0
  70. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/src/anemoi/utils/devtools.py +0 -0
  71. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/src/anemoi/utils/grib.py +0 -0
  72. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/src/anemoi/utils/hindcasts.py +0 -0
  73. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/src/anemoi/utils/humanize.py +0 -0
  74. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/src/anemoi/utils/logs.py +0 -0
  75. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/src/anemoi/utils/mars/__init__.py +0 -0
  76. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/src/anemoi/utils/mars/mars.yaml +0 -0
  77. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/src/anemoi/utils/mars/requests.py +0 -0
  78. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/src/anemoi/utils/provenance.py +0 -0
  79. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/src/anemoi/utils/registry.py +0 -0
  80. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/src/anemoi/utils/remote/ssh.py +0 -0
  81. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/src/anemoi/utils/rules.py +0 -0
  82. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/src/anemoi/utils/s3.py +0 -0
  83. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/src/anemoi/utils/sanitise.py +0 -0
  84. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/src/anemoi/utils/sanitize.py +0 -0
  85. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/src/anemoi/utils/schemas/__init__.py +0 -0
  86. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/src/anemoi/utils/schemas/errors.py +0 -0
  87. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/src/anemoi/utils/text.py +0 -0
  88. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/src/anemoi/utils/timer.py +0 -0
  89. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/src/anemoi_utils.egg-info/dependency_links.txt +0 -0
  90. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/src/anemoi_utils.egg-info/entry_points.txt +0 -0
  91. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/src/anemoi_utils.egg-info/top_level.txt +0 -0
  92. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/tests/test-transfer-data/directory/b/c/x +0 -0
  93. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/tests/test-transfer-data/directory/b/y +0 -0
  94. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/tests/test-transfer-data/directory/exotic filename ;^/"'[=.,#]()/303/252/303/274/303/247/303/262/342/234/205.txt" +0 -0
  95. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/tests/test-transfer-data/directory/z +0 -0
  96. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/tests/test-transfer-data/file +0 -0
  97. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/tests/test_caching.py +0 -0
  98. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/tests/test_compatibility.py +0 -0
  99. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/tests/test_dates.py +0 -0
  100. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/tests/test_frequency.py +0 -0
  101. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/tests/test_provenance.py +0 -0
  102. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/tests/test_remote.py +0 -0
  103. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/tests/test_sanetise.py +0 -0
  104. {anemoi_utils-0.4.23 → anemoi_utils-0.4.25}/tests/test_utils.py +0 -0
@@ -1,4 +1,3 @@
1
-
2
1
  # Workflows
3
2
  /.github/ @ecmwf/AnemoiSecurity
4
3
 
@@ -0,0 +1,13 @@
1
+ ## Description
2
+ <!-- What issue or task does this change relate to? -->
3
+
4
+ ## What problem does this change solve?
5
+ <!-- Describe if it's a bugfix, new feature, doc update, or breaking change -->
6
+
7
+ ## What issue or task does this change relate to?
8
+ <!-- link to Issue Number -->
9
+
10
+ ## Additional notes ##
11
+ <!-- Include any additional information, caveats, or considerations that the reviewer should be aware of. -->
12
+
13
+ ***As a contributor to the Anemoi framework, please ensure that your changes include unit tests, updates to any affected dependencies and documentation, and have been tested in a parallel setting (i.e., with multiple GPUs). As a reviewer, you are also responsible for verifying these aspects and requesting changes if they are not adequately addressed. For guidelines about those please refer to https://anemoi.readthedocs.io/en/latest/***
@@ -1,6 +1,6 @@
1
1
  # This workflow triggers tests on dependent packages.
2
2
  # The dependency tree itself is defined in ecmwf/downstream-ci/
3
- name: Test downstream dependent packages on HPC
3
+ name: Test downstream dependent packages
4
4
 
5
5
  on:
6
6
  # Trigger the workflow on push to main or develop, except tag creation
@@ -1,5 +1,5 @@
1
1
  # This workflow ensures that the PR title follows the Conventional Commit format.
2
- name: "[Pull Request] Ensure Conventional Commit in PR title"
2
+ name: "[PR] Ensure Conventional Commit Title"
3
3
 
4
4
  on:
5
5
  pull_request_target:
@@ -1,6 +1,6 @@
1
1
  # This workflow assigns labels to a pull request based on the Conventional Commits format.
2
2
  # This is necessary for release-please to work properly.
3
- name: "[Pull Request] Label Conventional Commits"
3
+ name: "[PR] Label Conventional Commits"
4
4
 
5
5
  on:
6
6
  pull_request:
@@ -1,6 +1,6 @@
1
1
  # This workflow assigns labels to a pull request based on the files changed in the PR.
2
2
  # The labels are defined in the `.github/labels.yml` file.
3
- name: "[Pull Request] Label File-based"
3
+ name: "[PR] Label File-based"
4
4
  on:
5
5
  pull_request_target:
6
6
  types: [opened, synchronize]
@@ -1,5 +1,5 @@
1
1
  # Manage labels of pull requests that originate from forks
2
- name: "[Pull Request] Label PRs from public forks"
2
+ name: "[PR] Label Forks"
3
3
 
4
4
  on:
5
5
  pull_request_target:
@@ -1,5 +1,5 @@
1
1
  # This workflow runs pre-commit checks and pytest tests against multiple platforms and Python versions.
2
- name: Code Quality checks and Testing
2
+ name: Code Quality and Testing
3
3
 
4
4
  on:
5
5
  pull_request:
@@ -1,6 +1,6 @@
1
1
  # This workflow adds a link to the experimental documentation build to the PR.
2
2
  # This does NOT trigger a build of the documentation, this is handled through webhooks.
3
- name: Read the Docs PR Preview
3
+ name: "[PR] Read the Docs Preview"
4
4
  on:
5
5
  pull_request_target:
6
6
  types:
@@ -94,6 +94,11 @@ dmypy.json
94
94
  *.csv
95
95
  *.xlsx
96
96
  *.xls
97
+ *.json
98
+ *.txt
99
+ *.zip
100
+ *.db
101
+ *.tgz
97
102
 
98
103
  # ML artifacts
99
104
  wandb/
@@ -120,7 +125,8 @@ tmp/
120
125
  temp/
121
126
  logs/
122
127
  _dev/
123
- outputs
128
+ _api/
129
+ ./outputs
124
130
  *tmp_data/
125
131
 
126
132
  # Project specific
@@ -39,8 +39,9 @@ repos:
39
39
  - -l 120
40
40
  - --force-single-line-imports
41
41
  - --profile black
42
+ - --project anemoi
42
43
  - repo: https://github.com/astral-sh/ruff-pre-commit
43
- rev: v0.11.8
44
+ rev: v0.11.12
44
45
  hooks:
45
46
  - id: ruff
46
47
  args:
@@ -64,7 +65,7 @@ repos:
64
65
  - id: docconvert
65
66
  args: ["numpy"]
66
67
  - repo: https://github.com/tox-dev/pyproject-fmt
67
- rev: "v2.5.1"
68
+ rev: "v2.6.0"
68
69
  hooks:
69
70
  - id: pyproject-fmt
70
71
  - repo: https://github.com/jshwi/docsig # Check docstrings against function sig
@@ -10,11 +10,16 @@
10
10
  "draft-pull-request": true,
11
11
  "pull-request-title-pattern": "chore${scope}: Release${component} ${version}",
12
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:",
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
+ "pull-request-footer": "> [!IMPORTANT]\n> Please do not change the PR title, manifest file, or any other automatically generated content in this PR unless you understand the implications. Changes here can break the release process.\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)",
14
14
  "packages": {
15
15
  ".": {
16
16
  "package-name": "anemoi-utils"
17
17
  }
18
18
  },
19
+ "plugins": [
20
+ {
21
+ "type": "sentence-case"
22
+ }
23
+ ],
19
24
  "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json"
20
25
  }
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "0.4.25"
3
+ }
@@ -8,6 +8,22 @@ 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.25](https://github.com/ecmwf/anemoi-utils/compare/0.4.24...0.4.25) (2025-06-24)
12
+
13
+
14
+ ### Features
15
+
16
+ * Add a CLI to transfer data ([#164](https://github.com/ecmwf/anemoi-utils/issues/164)) ([3a845ca](https://github.com/ecmwf/anemoi-utils/commit/3a845ca0c31d115e6b3d0496d862a3eaee5fb236))
17
+ * Add function to test cli ([#168](https://github.com/ecmwf/anemoi-utils/issues/168)) ([9ac9b06](https://github.com/ecmwf/anemoi-utils/commit/9ac9b06b8fd0a62cad33ea5de6a6b482f0a13656))
18
+
19
+ ## [0.4.24](https://github.com/ecmwf/anemoi-utils/compare/0.4.23...0.4.24) (2025-06-06)
20
+
21
+
22
+ ### Features
23
+
24
+ * Add s3.object_exists() function ([#157](https://github.com/ecmwf/anemoi-utils/issues/157)) ([d898811](https://github.com/ecmwf/anemoi-utils/commit/d8988116320265dc6dfe467c57e0b6f29f76a2c1))
25
+ * Allow wildcard in config for matching s3 buckets to end points ([#160](https://github.com/ecmwf/anemoi-utils/issues/160)) ([ab20da7](https://github.com/ecmwf/anemoi-utils/commit/ab20da7e9497435a7183705b02dcbb7317d2700b))
26
+
11
27
  ## [0.4.23](https://github.com/ecmwf/anemoi-utils/compare/0.4.22...0.4.23) (2025-05-20)
12
28
 
13
29
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: anemoi-utils
3
- Version: 0.4.23
3
+ Version: 0.4.25
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
@@ -225,12 +225,14 @@ Classifier: Programming Language :: Python :: Implementation :: PyPy
225
225
  Requires-Python: >=3.9
226
226
  License-File: LICENSE
227
227
  Requires-Dist: aniso8601
228
+ Requires-Dist: deprecation
228
229
  Requires-Dist: importlib-metadata; python_version < "3.10"
229
230
  Requires-Dist: multiurl
230
231
  Requires-Dist: numpy
231
232
  Requires-Dist: pydantic>=2.9
232
233
  Requires-Dist: python-dateutil
233
234
  Requires-Dist: pyyaml
235
+ Requires-Dist: rich
234
236
  Requires-Dist: tomli; python_version < "3.11"
235
237
  Requires-Dist: tqdm
236
238
  Provides-Extra: all
@@ -41,12 +41,14 @@ classifiers = [
41
41
  dynamic = [ "version" ]
42
42
  dependencies = [
43
43
  "aniso8601",
44
+ "deprecation",
44
45
  "importlib-metadata; python_version<'3.10'",
45
46
  "multiurl",
46
47
  "numpy",
47
48
  "pydantic>=2.9",
48
49
  "python-dateutil",
49
50
  "pyyaml",
51
+ "rich",
50
52
  "tomli; python_version<'3.11'",
51
53
  "tqdm",
52
54
  ]
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '0.4.23'
21
- __version_tuple__ = version_tuple = (0, 4, 23)
20
+ __version__ = version = '0.4.25'
21
+ __version_tuple__ = version_tuple = (0, 4, 25)
@@ -15,6 +15,7 @@ import os
15
15
  import sys
16
16
  import traceback
17
17
  from typing import Callable
18
+ from typing import Optional
18
19
 
19
20
  try:
20
21
  import argcomplete
@@ -185,7 +186,9 @@ def register_commands(here: str, package: str, select: Callable, fail: Callable
185
186
  return result
186
187
 
187
188
 
188
- def cli_main(version: str, description: str, commands: dict[str, Command]) -> None:
189
+ def cli_main(
190
+ version: str, description: str, commands: dict[str, Command], test_arguments: Optional[list[str]] = None
191
+ ) -> None:
189
192
  """Main entry point for the CLI.
190
193
 
191
194
  Parameters
@@ -196,9 +199,11 @@ def cli_main(version: str, description: str, commands: dict[str, Command]) -> No
196
199
  The description of the CLI
197
200
  commands : dict[str, Command]
198
201
  A dictionary of command names to Command instances
202
+ test_arguments : list[str], optional
203
+ The command line arguments to parse, used for testing purposes, by default None
199
204
  """
200
205
  parser = make_parser(description, commands)
201
- args, unknown = parser.parse_known_args()
206
+ args, unknown = parser.parse_known_args(test_arguments)
202
207
  if argcomplete:
203
208
  argcomplete.autocomplete(parser)
204
209
 
@@ -220,7 +225,7 @@ def cli_main(version: str, description: str, commands: dict[str, Command]) -> No
220
225
 
221
226
  if unknown and not cmd.accept_unknown_args:
222
227
  # This should trigger an error
223
- parser.parse_args()
228
+ parser.parse_args(test_arguments)
224
229
 
225
230
  try:
226
231
  if unknown:
@@ -228,9 +233,14 @@ def cli_main(version: str, description: str, commands: dict[str, Command]) -> No
228
233
  else:
229
234
  cmd.run(args)
230
235
  except ValueError as e:
236
+
237
+ if test_arguments:
238
+ raise
239
+
231
240
  traceback.print_exc()
232
241
  LOG.error("\n💣 %s", str(e).lstrip())
233
242
  LOG.error("💣 Exiting")
234
243
  sys.exit(1)
235
244
 
236
- sys.exit(0)
245
+ if not test_arguments:
246
+ sys.exit(0)
@@ -0,0 +1,74 @@
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
+ from argparse import ArgumentParser
12
+ from argparse import Namespace
13
+
14
+ from anemoi.utils.remote import transfer
15
+
16
+ from . import Command
17
+
18
+
19
+ class Transfer(Command):
20
+ """Handle configuration related commands."""
21
+
22
+ def add_arguments(self, command_parser: ArgumentParser) -> None:
23
+ """Add arguments to the command parser.
24
+
25
+ Parameters
26
+ ----------
27
+ command_parser : ArgumentParser
28
+ The argument parser to which the arguments will be added.
29
+ """
30
+ command_parser.add_argument(
31
+ "--source", help="A path to a local file or folder or a URL to a file or a folder on S3."
32
+ )
33
+ command_parser.add_argument(
34
+ "--target", help="A path to a local file or folder or a URL to a file or a folder on S3 or a remote folder."
35
+ )
36
+ command_parser.add_argument(
37
+ "--overwrite",
38
+ action="store_true",
39
+ help="If the data is already on in the target location it will be overwritten..",
40
+ )
41
+ command_parser.add_argument(
42
+ "--resume",
43
+ action="store_true",
44
+ help="If the data is already on S3 it will not be uploaded, unless the remote file has a different size.",
45
+ )
46
+ command_parser.add_argument("--verbosity", default=1, help="The level of verbosity, by default 1.")
47
+ command_parser.add_argument(
48
+ "--progress", default=None, help="A callable that will be called with the number of files."
49
+ )
50
+ command_parser.add_argument(
51
+ "--threads", default=1, help="The number of threads to use when uploading a directory, by default 1."
52
+ )
53
+
54
+ def run(self, args: Namespace) -> None:
55
+ """Execute the command with the provided arguments.
56
+
57
+ Parameters
58
+ ----------
59
+ args : Namespace
60
+ The arguments passed to the command.
61
+ """
62
+ transfer(
63
+ source=args.source,
64
+ target=args.target,
65
+ overwrite=args.overwrite,
66
+ resume=args.resume,
67
+ verbosity=args.verbosity,
68
+ progress=args.progress,
69
+ threads=args.threads,
70
+ temporary_target=False,
71
+ )
72
+
73
+
74
+ command = Transfer
@@ -17,9 +17,12 @@ from typing import List
17
17
  from typing import Tuple
18
18
  from typing import Union
19
19
 
20
+ import deprecation
20
21
  import numpy as np
21
22
  import requests
22
23
 
24
+ from anemoi.utils._version import __version__
25
+
23
26
  from .caching import cached
24
27
 
25
28
  LOG = logging.getLogger(__name__)
@@ -28,6 +31,12 @@ LOG = logging.getLogger(__name__)
28
31
  GRIDS_URL_PATTERN = "https://get.ecmwf.int/repository/anemoi/grids/grid-{name}.npz"
29
32
 
30
33
 
34
+ @deprecation.deprecated(
35
+ deprecated_in="0.4.25",
36
+ removed_in="0.5.0",
37
+ current_version=__version__,
38
+ details="Use anemoi.transform.spatial.xyz_to_latlon instead.",
39
+ )
31
40
  def xyz_to_latlon(x: np.ndarray, y: np.ndarray, z: np.ndarray) -> tuple[np.ndarray, np.ndarray]:
32
41
  """Convert Cartesian coordinates to latitude and longitude.
33
42
 
@@ -51,6 +60,12 @@ def xyz_to_latlon(x: np.ndarray, y: np.ndarray, z: np.ndarray) -> tuple[np.ndarr
51
60
  )
52
61
 
53
62
 
63
+ @deprecation.deprecated(
64
+ deprecated_in="0.4.25",
65
+ removed_in="0.5.0",
66
+ current_version=__version__,
67
+ details="Use anemoi.transform.spatial.xyz_to_latlon instead.",
68
+ )
54
69
  def latlon_to_xyz(lat: np.ndarray, lon: np.ndarray, radius: float = 1.0) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
55
70
  """Convert latitude and longitude to Cartesian coordinates.
56
71
 
@@ -87,6 +102,12 @@ def latlon_to_xyz(lat: np.ndarray, lon: np.ndarray, radius: float = 1.0) -> tupl
87
102
  return x, y, z
88
103
 
89
104
 
105
+ @deprecation.deprecated(
106
+ deprecated_in="0.4.25",
107
+ removed_in="0.5.0",
108
+ current_version=__version__,
109
+ details="Use anemoi.transform.spatial.nearest_grid_points instead.",
110
+ )
90
111
  def nearest_grid_points(
91
112
  source_latitudes: np.ndarray,
92
113
  source_longitudes: np.ndarray,
@@ -169,6 +190,12 @@ def _grids(name: Union[str, List[float], Tuple[float, ...]]) -> bytes:
169
190
  return response.content
170
191
 
171
192
 
193
+ @deprecation.deprecated(
194
+ deprecated_in="0.4.25",
195
+ removed_in="0.5.0",
196
+ current_version=__version__,
197
+ details="Use anemoi.transform.grids.named.lookup instead.",
198
+ )
172
199
  def grids(name: Union[str, List[float], Tuple[float, ...]]) -> dict:
173
200
  """Load grid data by name.
174
201
 
@@ -371,7 +371,12 @@ class BaseDownload(Loader):
371
371
  The target path.
372
372
  """
373
373
  if os.path.exists(target):
374
- shutil.rmtree(target)
374
+ if os.path.isfile(target):
375
+ LOGGER.info(f"Deleting file {target}")
376
+ os.remove(target)
377
+ else:
378
+ LOGGER.info(f"Deleting directory {target}")
379
+ shutil.rmtree(target)
375
380
 
376
381
 
377
382
  class BaseUpload(Loader):
@@ -642,10 +647,10 @@ def transfer(
642
647
  A path to a local file or folder or a URL to a file or a folder on S3 or a remote folder.
643
648
  The url should start with 's3://' or 'ssh://'.
644
649
  overwrite : bool, optional
645
- If the data is alreay on in the target location it will be overwritten.
650
+ If the data is already on in the target location it will be overwritten.
646
651
  By default False
647
652
  resume : bool, optional
648
- If the data is alreay on S3 it will not be uploaded, unless the remote file has a different size
653
+ If the data is already on S3 it will not be uploaded, unless the remote file has a different size
649
654
  Ignored if the target is an SSH remote folder (ssh://).
650
655
  By default False
651
656
  verbosity : int, optional