ecmwf-datastores-client 0.1.0__tar.gz → 0.3.0__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 ecmwf-datastores-client might be problematic. Click here for more details.
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/.cruft.json +2 -2
- ecmwf_datastores_client-0.3.0/.github/workflows/on-pr-closed.yml +18 -0
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/.github/workflows/on-push.yml +15 -4
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/.pre-commit-config.yaml +7 -2
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/Makefile +1 -0
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/PKG-INFO +15 -29
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/README.md +13 -27
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/ci/environment-ci.yml +3 -2
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/docs/conf.py +1 -0
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/docs/index.md +1 -0
- ecmwf_datastores_client-0.3.0/docs/notebooks/index.md +7 -0
- ecmwf_datastores_client-0.3.0/docs/notebooks/quick_start.ipynb +45 -0
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/ecmwf/datastores/catalogue.py +0 -11
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/ecmwf/datastores/client.py +21 -4
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/ecmwf/datastores/processing.py +8 -69
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/ecmwf/datastores/version.py +1 -1
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/ecmwf_datastores_client.egg-info/PKG-INFO +15 -29
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/ecmwf_datastores_client.egg-info/SOURCES.txt +5 -2
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/ecmwf_datastores_client.egg-info/requires.txt +1 -1
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/pyproject.toml +1 -1
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/tests/conftest.py +1 -4
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/tests/integration_test_20_processing.py +7 -6
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/tests/integration_test_30_remote.py +0 -6
- ecmwf_datastores_client-0.1.0/tests/integration_test_60_api_client.py → ecmwf_datastores_client-0.3.0/tests/integration_test_60_client.py +9 -9
- ecmwf_datastores_client-0.1.0/tests/integration_test_70_legacy_api_client.py → ecmwf_datastores_client-0.3.0/tests/integration_test_70_legacy_client.py +13 -13
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/tests/test_20_processing.py +6 -28
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/.gitignore +0 -0
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/.pre-commit-config-cruft.yaml +0 -0
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/Dockerfile +0 -0
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/LICENSE +0 -0
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/ci/environment-integration.yml +0 -0
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/docs/Makefile +0 -0
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/docs/_static/.gitkeep +0 -0
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/docs/_templates/.gitkeep +0 -0
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/docs/make.bat +0 -0
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/ecmwf/datastores/__init__.py +0 -0
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/ecmwf/datastores/config.py +0 -0
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/ecmwf/datastores/legacy_client.py +0 -0
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/ecmwf/datastores/profile.py +0 -0
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/ecmwf/datastores/py.typed +0 -0
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/ecmwf/datastores/utils.py +0 -0
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/ecmwf_datastores_client.egg-info/dependency_links.txt +0 -0
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/ecmwf_datastores_client.egg-info/top_level.txt +0 -0
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/environment.yml +0 -0
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/setup.cfg +0 -0
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/tests/integration_test_10_catalogue.py +0 -0
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/tests/integration_test_40_results.py +0 -0
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/tests/integration_test_50_profile.py +0 -0
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/tests/integration_test_80_adaptors.py +0 -0
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/tests/integration_test_90_features.py +0 -0
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/tests/test_00_version.py +0 -0
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/tests/test_01_config.py +0 -0
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/tests/test_10_catalogue.py +0 -0
- {ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/tests/test_40_results.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"template": "https://github.com/ecmwf-projects/cookiecutter-conda-package",
|
|
3
|
-
"commit": "
|
|
3
|
+
"commit": "0b4d61da26c3aacfc1778716cb36749d16846c51",
|
|
4
4
|
"checkout": null,
|
|
5
5
|
"context": {
|
|
6
6
|
"cookiecutter": {
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"integration_tests": "True",
|
|
14
14
|
"pypi": true,
|
|
15
15
|
"_template": "https://github.com/ecmwf-projects/cookiecutter-conda-package",
|
|
16
|
-
"_commit": "
|
|
16
|
+
"_commit": "0b4d61da26c3aacfc1778716cb36749d16846c51"
|
|
17
17
|
}
|
|
18
18
|
},
|
|
19
19
|
"directory": null
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
name: on-pr-closed
|
|
2
|
+
on:
|
|
3
|
+
pull_request:
|
|
4
|
+
types:
|
|
5
|
+
- closed
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
remove-preview:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
permissions:
|
|
11
|
+
contents: write
|
|
12
|
+
pull-requests: write
|
|
13
|
+
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: rossjrw/pr-preview-action@v1
|
|
17
|
+
with:
|
|
18
|
+
action: "remove"
|
{ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/.github/workflows/on-push.yml
RENAMED
|
@@ -117,6 +117,7 @@ jobs:
|
|
|
117
117
|
runs-on: macos-latest
|
|
118
118
|
permissions:
|
|
119
119
|
contents: write
|
|
120
|
+
pull-requests: write
|
|
120
121
|
|
|
121
122
|
steps:
|
|
122
123
|
- uses: actions/checkout@v4
|
|
@@ -140,16 +141,26 @@ jobs:
|
|
|
140
141
|
run: |
|
|
141
142
|
python -m pip install --no-deps -e .
|
|
142
143
|
- name: Build documentation
|
|
144
|
+
timeout-minutes: 10
|
|
145
|
+
env:
|
|
146
|
+
ECMWF_DATASTORES_URL: ${{ secrets.ECMWF_DATASTORES_URL }}
|
|
147
|
+
ECMWF_DATASTORES_KEY: ${{ secrets.ECMWF_DATASTORES_KEY }}
|
|
148
|
+
ANONYMOUS_PAT: ${{ secrets.ECMWF_DATASTORES_ANON_KEY }}
|
|
143
149
|
run: |
|
|
144
150
|
make docs-build
|
|
151
|
+
- uses: rossjrw/pr-preview-action@v1
|
|
152
|
+
with:
|
|
153
|
+
source-dir: "docs/_build/html"
|
|
145
154
|
- name: Deploy documentation
|
|
146
|
-
uses:
|
|
155
|
+
uses: JamesIves/github-pages-deploy-action@v4
|
|
147
156
|
if: |
|
|
148
157
|
github.event_name == 'push' &&
|
|
149
158
|
startsWith(github.ref, 'refs/tags')
|
|
150
159
|
with:
|
|
151
|
-
|
|
152
|
-
|
|
160
|
+
folder: ./docs/_build/html/
|
|
161
|
+
# See: https://github.com/marketplace/actions/deploy-pr-preview#ensure-your-main-deployment-is-compatible
|
|
162
|
+
clean-exclude: pr-preview/
|
|
163
|
+
force: false
|
|
153
164
|
|
|
154
165
|
integration-tests:
|
|
155
166
|
needs: [combine-environments, unit-tests]
|
|
@@ -188,7 +199,7 @@ jobs:
|
|
|
188
199
|
env:
|
|
189
200
|
ECMWF_DATASTORES_URL: ${{ secrets.ECMWF_DATASTORES_URL }}
|
|
190
201
|
ECMWF_DATASTORES_KEY: ${{ secrets.ECMWF_DATASTORES_KEY }}
|
|
191
|
-
|
|
202
|
+
ANONYMOUS_PAT: ${{ secrets.ECMWF_DATASTORES_ANON_KEY }}
|
|
192
203
|
run: |
|
|
193
204
|
make ci-integration-tests COV_REPORT=xml
|
|
194
205
|
|
|
@@ -17,7 +17,7 @@ repos:
|
|
|
17
17
|
- id: blackdoc
|
|
18
18
|
additional_dependencies: [black==23.11.0]
|
|
19
19
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
20
|
-
rev: v0.11.
|
|
20
|
+
rev: v0.11.12
|
|
21
21
|
hooks:
|
|
22
22
|
- id: ruff
|
|
23
23
|
args: [--fix, --show-fixes]
|
|
@@ -34,6 +34,11 @@ repos:
|
|
|
34
34
|
- id: pretty-format-toml
|
|
35
35
|
args: [--autofix]
|
|
36
36
|
- repo: https://github.com/gitleaks/gitleaks
|
|
37
|
-
rev: v8.
|
|
37
|
+
rev: v8.27.0
|
|
38
38
|
hooks:
|
|
39
39
|
- id: gitleaks
|
|
40
|
+
- repo: https://github.com/kynan/nbstripout
|
|
41
|
+
rev: 0.8.1
|
|
42
|
+
hooks:
|
|
43
|
+
- id: nbstripout
|
|
44
|
+
args: [--drop-empty-cells]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ecmwf-datastores-client
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: ECMWF Data Stores Service (DSS) API Python client
|
|
5
5
|
License: Apache License
|
|
6
6
|
Version 2.0, January 2004
|
|
@@ -228,25 +228,20 @@ Requires-Dist: multiurl>=0.3.2
|
|
|
228
228
|
Requires-Dist: requests
|
|
229
229
|
Requires-Dist: typing-extensions
|
|
230
230
|
Provides-Extra: legacy
|
|
231
|
-
Requires-Dist: cdsapi>=0.7.
|
|
231
|
+
Requires-Dist: cdsapi>=0.7.6; extra == "legacy"
|
|
232
232
|
Dynamic: license-file
|
|
233
233
|
|
|
234
234
|
<p align="center">
|
|
235
235
|
<a href="https://github.com/ecmwf/codex/raw/refs/heads/main/ESEE">
|
|
236
|
-
<img src="https://github.com/ecmwf/codex/raw/refs/heads/main/ESEE/data_provision_badge.svg" alt="ECMWF Software EnginE">
|
|
237
|
-
</a>
|
|
236
|
+
<img src="https://github.com/ecmwf/codex/raw/refs/heads/main/ESEE/data_provision_badge.svg" alt="ECMWF Software EnginE"></a>
|
|
238
237
|
<a href="https://github.com/ecmwf/codex/raw/refs/heads/main/Project Maturity">
|
|
239
|
-
<img src="https://github.com/ecmwf/codex/raw/refs/heads/main/Project Maturity/incubating_badge.svg" alt="Maturity Level">
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
<img src="https://codecov.io/gh/ecmwf/ecmwf-datastores-client/branch/main/graph/badge.svg" alt="Code Coverage">
|
|
243
|
-
</a> -->
|
|
238
|
+
<img src="https://github.com/ecmwf/codex/raw/refs/heads/main/Project Maturity/incubating_badge.svg" alt="Maturity Level"></a>
|
|
239
|
+
<!-- <a href="https://codecov.io/gh/ecmwf/ecmwf-datastores-client">
|
|
240
|
+
<img src="https://codecov.io/gh/ecmwf/ecmwf-datastores-client/branch/main/graph/badge.svg" alt="Code Coverage"></a> -->
|
|
244
241
|
<a href="https://opensource.org/licenses/apache-2-0">
|
|
245
|
-
<img src="https://img.shields.io/badge/Licence-Apache 2.0-blue.svg" alt="Licence">
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
<img src="https://img.shields.io/github/v/release/ecmwf/ecmwf-datastores-client?color=purple&label=Release" alt="Latest Release">
|
|
249
|
-
</a>
|
|
242
|
+
<img src="https://img.shields.io/badge/Licence-Apache 2.0-blue.svg" alt="Licence"></a>
|
|
243
|
+
<a href="https://github.com/ecmwf/ecmwf-datastores-client/releases">
|
|
244
|
+
<img src="https://img.shields.io/github/v/release/ecmwf/ecmwf-datastores-client?color=purple&label=Release" alt="Latest Release"></a>
|
|
250
245
|
</p>
|
|
251
246
|
|
|
252
247
|
<p align="center">
|
|
@@ -282,8 +277,11 @@ $ pip install ecmwf-datastores-client
|
|
|
282
277
|
|
|
283
278
|
## Configuration
|
|
284
279
|
|
|
285
|
-
The `Client` requires the `url` to the API root and a valid API `key`.
|
|
286
|
-
|
|
280
|
+
The `Client` requires the `url` to the API root and a valid API `key`. These can be provided in three ways, in order of precedence:
|
|
281
|
+
|
|
282
|
+
1. As keyword arguments when instantiating the `Client`.
|
|
283
|
+
1. Via the `ECMWF_DATASTORES_URL` and `ECMWF_DATASTORES_KEY` environment variables.
|
|
284
|
+
1. From a configuration file, which must be located at `~/.ecmwfdatastoresrc` or at the path specified by the `ECMWF_DATASTORES_RC_FILE` environment variable.
|
|
287
285
|
|
|
288
286
|
```
|
|
289
287
|
$ cat $HOME/.ecmwfdatastoresrc
|
|
@@ -291,14 +289,6 @@ url: https://cds.climate.copernicus.eu/api
|
|
|
291
289
|
key: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
|
292
290
|
```
|
|
293
291
|
|
|
294
|
-
It is possible (though not recommended) to use the API key of one of the test users:
|
|
295
|
-
|
|
296
|
-
```
|
|
297
|
-
00112233-4455-6677-c899-aabbccddeeff
|
|
298
|
-
```
|
|
299
|
-
|
|
300
|
-
This key is used for anonymous tests and is designed to be the least performant option for accessing the system.
|
|
301
|
-
|
|
302
292
|
## Quick Start
|
|
303
293
|
|
|
304
294
|
Configure the logging level to display INFO messages:
|
|
@@ -312,12 +302,8 @@ Configure the logging level to display INFO messages:
|
|
|
312
302
|
Instantiate the API client and optionally verify authentication:
|
|
313
303
|
|
|
314
304
|
```python
|
|
315
|
-
>>> import os
|
|
316
305
|
>>> from ecmwf.datastores import Client
|
|
317
|
-
>>> client = Client(
|
|
318
|
-
... url=os.getenv("ECMWF_DATASTORES_URL"),
|
|
319
|
-
... key=os.getenv("ECMWF_DATASTORES_KEY"),
|
|
320
|
-
... )
|
|
306
|
+
>>> client = Client()
|
|
321
307
|
>>> client.check_authentication() # optional check
|
|
322
308
|
{...}
|
|
323
309
|
|
|
@@ -1,19 +1,14 @@
|
|
|
1
1
|
<p align="center">
|
|
2
2
|
<a href="https://github.com/ecmwf/codex/raw/refs/heads/main/ESEE">
|
|
3
|
-
<img src="https://github.com/ecmwf/codex/raw/refs/heads/main/ESEE/data_provision_badge.svg" alt="ECMWF Software EnginE">
|
|
4
|
-
</a>
|
|
3
|
+
<img src="https://github.com/ecmwf/codex/raw/refs/heads/main/ESEE/data_provision_badge.svg" alt="ECMWF Software EnginE"></a>
|
|
5
4
|
<a href="https://github.com/ecmwf/codex/raw/refs/heads/main/Project Maturity">
|
|
6
|
-
<img src="https://github.com/ecmwf/codex/raw/refs/heads/main/Project Maturity/incubating_badge.svg" alt="Maturity Level">
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
<img src="https://codecov.io/gh/ecmwf/ecmwf-datastores-client/branch/main/graph/badge.svg" alt="Code Coverage">
|
|
10
|
-
</a> -->
|
|
5
|
+
<img src="https://github.com/ecmwf/codex/raw/refs/heads/main/Project Maturity/incubating_badge.svg" alt="Maturity Level"></a>
|
|
6
|
+
<!-- <a href="https://codecov.io/gh/ecmwf/ecmwf-datastores-client">
|
|
7
|
+
<img src="https://codecov.io/gh/ecmwf/ecmwf-datastores-client/branch/main/graph/badge.svg" alt="Code Coverage"></a> -->
|
|
11
8
|
<a href="https://opensource.org/licenses/apache-2-0">
|
|
12
|
-
<img src="https://img.shields.io/badge/Licence-Apache 2.0-blue.svg" alt="Licence">
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
<img src="https://img.shields.io/github/v/release/ecmwf/ecmwf-datastores-client?color=purple&label=Release" alt="Latest Release">
|
|
16
|
-
</a>
|
|
9
|
+
<img src="https://img.shields.io/badge/Licence-Apache 2.0-blue.svg" alt="Licence"></a>
|
|
10
|
+
<a href="https://github.com/ecmwf/ecmwf-datastores-client/releases">
|
|
11
|
+
<img src="https://img.shields.io/github/v/release/ecmwf/ecmwf-datastores-client?color=purple&label=Release" alt="Latest Release"></a>
|
|
17
12
|
</p>
|
|
18
13
|
|
|
19
14
|
<p align="center">
|
|
@@ -49,8 +44,11 @@ $ pip install ecmwf-datastores-client
|
|
|
49
44
|
|
|
50
45
|
## Configuration
|
|
51
46
|
|
|
52
|
-
The `Client` requires the `url` to the API root and a valid API `key`.
|
|
53
|
-
|
|
47
|
+
The `Client` requires the `url` to the API root and a valid API `key`. These can be provided in three ways, in order of precedence:
|
|
48
|
+
|
|
49
|
+
1. As keyword arguments when instantiating the `Client`.
|
|
50
|
+
1. Via the `ECMWF_DATASTORES_URL` and `ECMWF_DATASTORES_KEY` environment variables.
|
|
51
|
+
1. From a configuration file, which must be located at `~/.ecmwfdatastoresrc` or at the path specified by the `ECMWF_DATASTORES_RC_FILE` environment variable.
|
|
54
52
|
|
|
55
53
|
```
|
|
56
54
|
$ cat $HOME/.ecmwfdatastoresrc
|
|
@@ -58,14 +56,6 @@ url: https://cds.climate.copernicus.eu/api
|
|
|
58
56
|
key: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
|
59
57
|
```
|
|
60
58
|
|
|
61
|
-
It is possible (though not recommended) to use the API key of one of the test users:
|
|
62
|
-
|
|
63
|
-
```
|
|
64
|
-
00112233-4455-6677-c899-aabbccddeeff
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
This key is used for anonymous tests and is designed to be the least performant option for accessing the system.
|
|
68
|
-
|
|
69
59
|
## Quick Start
|
|
70
60
|
|
|
71
61
|
Configure the logging level to display INFO messages:
|
|
@@ -79,12 +69,8 @@ Configure the logging level to display INFO messages:
|
|
|
79
69
|
Instantiate the API client and optionally verify authentication:
|
|
80
70
|
|
|
81
71
|
```python
|
|
82
|
-
>>> import os
|
|
83
72
|
>>> from ecmwf.datastores import Client
|
|
84
|
-
>>> client = Client(
|
|
85
|
-
... url=os.getenv("ECMWF_DATASTORES_URL"),
|
|
86
|
-
... key=os.getenv("ECMWF_DATASTORES_KEY"),
|
|
87
|
-
... )
|
|
73
|
+
>>> client = Client()
|
|
88
74
|
>>> client.check_authentication() # optional check
|
|
89
75
|
{...}
|
|
90
76
|
|
|
@@ -14,8 +14,9 @@ dependencies:
|
|
|
14
14
|
- sphinx
|
|
15
15
|
- sphinx-autoapi
|
|
16
16
|
# DO NOT EDIT ABOVE THIS LINE, ADD DEPENDENCIES BELOW
|
|
17
|
+
- ipykernel
|
|
18
|
+
- nbsphinx
|
|
17
19
|
- types-requests
|
|
18
20
|
- pip:
|
|
19
|
-
|
|
20
|
-
- git+https://github.com/bopen/cdsapi.git@COPDS-2640
|
|
21
|
+
- cdsapi >= 0.7.6
|
|
21
22
|
- responses
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cells": [
|
|
3
|
+
{
|
|
4
|
+
"cell_type": "markdown",
|
|
5
|
+
"id": "0",
|
|
6
|
+
"metadata": {},
|
|
7
|
+
"source": [
|
|
8
|
+
"# Quick start"
|
|
9
|
+
]
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"cell_type": "code",
|
|
13
|
+
"execution_count": null,
|
|
14
|
+
"id": "1",
|
|
15
|
+
"metadata": {},
|
|
16
|
+
"outputs": [],
|
|
17
|
+
"source": [
|
|
18
|
+
"from ecmwf.datastores import Client\n",
|
|
19
|
+
"\n",
|
|
20
|
+
"client = Client()"
|
|
21
|
+
]
|
|
22
|
+
}
|
|
23
|
+
],
|
|
24
|
+
"metadata": {
|
|
25
|
+
"kernelspec": {
|
|
26
|
+
"display_name": "Python 3 (ipykernel)",
|
|
27
|
+
"language": "python",
|
|
28
|
+
"name": "python3"
|
|
29
|
+
},
|
|
30
|
+
"language_info": {
|
|
31
|
+
"codemirror_mode": {
|
|
32
|
+
"name": "ipython",
|
|
33
|
+
"version": 3
|
|
34
|
+
},
|
|
35
|
+
"file_extension": ".py",
|
|
36
|
+
"mimetype": "text/x-python",
|
|
37
|
+
"name": "python",
|
|
38
|
+
"nbconvert_exporter": "python",
|
|
39
|
+
"pygments_lexer": "ipython3",
|
|
40
|
+
"version": "3.11.11"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"nbformat": 4,
|
|
44
|
+
"nbformat_minor": 5
|
|
45
|
+
}
|
{ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/ecmwf/datastores/catalogue.py
RENAMED
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
from __future__ import annotations
|
|
16
16
|
|
|
17
17
|
import datetime
|
|
18
|
-
import warnings
|
|
19
18
|
from typing import Any, Callable
|
|
20
19
|
|
|
21
20
|
import attrs
|
|
@@ -93,16 +92,6 @@ class Collection(ApiResponse):
|
|
|
93
92
|
url = self._get_link_href(rel="retrieve")
|
|
94
93
|
return datastores.Process.from_request("get", url, **self._request_kwargs)
|
|
95
94
|
|
|
96
|
-
@property
|
|
97
|
-
def process(self) -> datastores.Process:
|
|
98
|
-
warnings.warn(
|
|
99
|
-
"`process` has been deprecated, and in the future will raise an error."
|
|
100
|
-
"Please use `submit` and `apply_constraints` from now on.",
|
|
101
|
-
DeprecationWarning,
|
|
102
|
-
stacklevel=2,
|
|
103
|
-
)
|
|
104
|
-
return self._process
|
|
105
|
-
|
|
106
95
|
@property
|
|
107
96
|
def form(self) -> list[dict[str, Any]]:
|
|
108
97
|
url = f"{self.url}/form.json"
|
|
@@ -28,6 +28,8 @@ from ecmwf.datastores.catalogue import Catalogue
|
|
|
28
28
|
from ecmwf.datastores.processing import Processing, RequestKwargs
|
|
29
29
|
from ecmwf.datastores.profile import Profile
|
|
30
30
|
|
|
31
|
+
T_STATUS = Literal["accepted", "running", "successful", "failed", "rejected"]
|
|
32
|
+
|
|
31
33
|
|
|
32
34
|
@attrs.define(slots=False)
|
|
33
35
|
class Client:
|
|
@@ -179,6 +181,21 @@ class Client:
|
|
|
179
181
|
"""
|
|
180
182
|
return self._profile_api.check_authentication()
|
|
181
183
|
|
|
184
|
+
def delete(self, *request_ids: str) -> dict[str, Any]:
|
|
185
|
+
"""Delete requests.
|
|
186
|
+
|
|
187
|
+
Parameters
|
|
188
|
+
----------
|
|
189
|
+
*request_ids: str
|
|
190
|
+
Request IDs.
|
|
191
|
+
|
|
192
|
+
Returns
|
|
193
|
+
-------
|
|
194
|
+
dict[str,Any]
|
|
195
|
+
Content of the response.
|
|
196
|
+
"""
|
|
197
|
+
return self._retrieve_api.delete(*request_ids)
|
|
198
|
+
|
|
182
199
|
def download_results(self, request_id: str, target: str | None = None) -> str:
|
|
183
200
|
"""Download the results of a request.
|
|
184
201
|
|
|
@@ -234,7 +251,7 @@ class Client:
|
|
|
234
251
|
Parameters
|
|
235
252
|
----------
|
|
236
253
|
limit: int | None
|
|
237
|
-
Number of
|
|
254
|
+
Number of collections per page.
|
|
238
255
|
sortby: {None, 'id', 'relevance', 'title', 'update'}
|
|
239
256
|
Field to sort results by.
|
|
240
257
|
query: str or None
|
|
@@ -259,17 +276,17 @@ class Client:
|
|
|
259
276
|
self,
|
|
260
277
|
limit: int | None = None,
|
|
261
278
|
sortby: Literal[None, "created", "-created"] = None,
|
|
262
|
-
status:
|
|
279
|
+
status: None | T_STATUS | list[T_STATUS] = None,
|
|
263
280
|
) -> datastores.Jobs:
|
|
264
281
|
"""Retrieve submitted jobs.
|
|
265
282
|
|
|
266
283
|
Parameters
|
|
267
284
|
----------
|
|
268
285
|
limit: int or None
|
|
269
|
-
Number of
|
|
286
|
+
Number of jobs per page.
|
|
270
287
|
sortby: {None, 'created', '-created'}
|
|
271
288
|
Field to sort results by.
|
|
272
|
-
status:
|
|
289
|
+
status: None or {'accepted', 'running', 'successful', 'failed', 'rejected'} or list
|
|
273
290
|
Status of the results.
|
|
274
291
|
|
|
275
292
|
Returns
|
{ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/ecmwf/datastores/processing.py
RENAMED
|
@@ -400,16 +400,6 @@ class Remote:
|
|
|
400
400
|
"""Request ID."""
|
|
401
401
|
return self.url.rpartition("/")[2]
|
|
402
402
|
|
|
403
|
-
@property
|
|
404
|
-
def request_uid(self) -> str:
|
|
405
|
-
warnings.warn(
|
|
406
|
-
"`request_uid` has been deprecated, and in the future will raise an error."
|
|
407
|
-
"Please use `request_id` from now on.",
|
|
408
|
-
DeprecationWarning,
|
|
409
|
-
stacklevel=2,
|
|
410
|
-
)
|
|
411
|
-
return self.request_id
|
|
412
|
-
|
|
413
403
|
@property
|
|
414
404
|
def json(self) -> dict[str, Any]:
|
|
415
405
|
"""Content of the response."""
|
|
@@ -450,48 +440,18 @@ class Remote:
|
|
|
450
440
|
"""When the job was created."""
|
|
451
441
|
return utils.string_to_datetime(self.json["created"])
|
|
452
442
|
|
|
453
|
-
@property
|
|
454
|
-
def creation_datetime(self) -> datetime.datetime:
|
|
455
|
-
warnings.warn(
|
|
456
|
-
"`creation_datetime` has been deprecated, and in the future will raise an error."
|
|
457
|
-
"Please use `created_at` from now on.",
|
|
458
|
-
DeprecationWarning,
|
|
459
|
-
stacklevel=2,
|
|
460
|
-
)
|
|
461
|
-
return self.created_at
|
|
462
|
-
|
|
463
443
|
@property
|
|
464
444
|
def started_at(self) -> datetime.datetime | None:
|
|
465
445
|
"""When the job started. If None, the job has not started."""
|
|
466
446
|
value = self.json.get("started")
|
|
467
447
|
return value if value is None else utils.string_to_datetime(value)
|
|
468
448
|
|
|
469
|
-
@property
|
|
470
|
-
def start_datetime(self) -> datetime.datetime | None:
|
|
471
|
-
warnings.warn(
|
|
472
|
-
"`start_datetime` has been deprecated, and in the future will raise an error."
|
|
473
|
-
"Please use `started_at` from now on.",
|
|
474
|
-
DeprecationWarning,
|
|
475
|
-
stacklevel=2,
|
|
476
|
-
)
|
|
477
|
-
return self.started_at
|
|
478
|
-
|
|
479
449
|
@property
|
|
480
450
|
def finished_at(self) -> datetime.datetime | None:
|
|
481
451
|
"""When the job finished. If None, the job has not finished."""
|
|
482
452
|
value = self.json.get("finished")
|
|
483
453
|
return value if value is None else utils.string_to_datetime(value)
|
|
484
454
|
|
|
485
|
-
@property
|
|
486
|
-
def end_datetime(self) -> datetime.datetime | None:
|
|
487
|
-
warnings.warn(
|
|
488
|
-
"`end_datetime` has been deprecated, and in the future will raise an error."
|
|
489
|
-
"Please use `finished_at` from now on.",
|
|
490
|
-
DeprecationWarning,
|
|
491
|
-
stacklevel=2,
|
|
492
|
-
)
|
|
493
|
-
return self.finished_at
|
|
494
|
-
|
|
495
455
|
def _wait_on_results(self) -> None:
|
|
496
456
|
sleep = 1.0
|
|
497
457
|
while not self.results_ready:
|
|
@@ -507,7 +467,7 @@ class Remote:
|
|
|
507
467
|
return True
|
|
508
468
|
if status in ("accepted", "running"):
|
|
509
469
|
return False
|
|
510
|
-
if status
|
|
470
|
+
if status in ("failed", "rejected"):
|
|
511
471
|
results = self._make_results(wait=False)
|
|
512
472
|
raise ProcessingFailedError(error_json_to_message(results._json_dict))
|
|
513
473
|
if status in ("dismissed", "deleted"):
|
|
@@ -525,15 +485,6 @@ class Remote:
|
|
|
525
485
|
results = Results.from_request("get", results_url, **self._request_kwargs)
|
|
526
486
|
return results
|
|
527
487
|
|
|
528
|
-
def make_results(self, wait: bool = True) -> Results:
|
|
529
|
-
warnings.warn(
|
|
530
|
-
"`make_results` has been deprecated, and in the future will raise an error."
|
|
531
|
-
"Please use `get_results` from now on.",
|
|
532
|
-
DeprecationWarning,
|
|
533
|
-
stacklevel=2,
|
|
534
|
-
)
|
|
535
|
-
return self._make_results(wait)
|
|
536
|
-
|
|
537
488
|
def get_results(self) -> Results:
|
|
538
489
|
"""Retrieve results.
|
|
539
490
|
|
|
@@ -641,15 +592,6 @@ class Job(ApiResponse):
|
|
|
641
592
|
url = self._get_link_href(rel="self")
|
|
642
593
|
return Remote(url, **self._request_kwargs)
|
|
643
594
|
|
|
644
|
-
def make_remote(self) -> Remote:
|
|
645
|
-
warnings.warn(
|
|
646
|
-
"`make_remote` has been deprecated, and in the future will raise an error."
|
|
647
|
-
"Please use `get_remote` from now on.",
|
|
648
|
-
DeprecationWarning,
|
|
649
|
-
stacklevel=2,
|
|
650
|
-
)
|
|
651
|
-
return self.get_remote()
|
|
652
|
-
|
|
653
595
|
|
|
654
596
|
@attrs.define
|
|
655
597
|
class Jobs(ApiResponsePaginated):
|
|
@@ -660,16 +602,6 @@ class Jobs(ApiResponsePaginated):
|
|
|
660
602
|
"""List of request IDs."""
|
|
661
603
|
return [job["jobID"] for job in self._json_dict["jobs"]]
|
|
662
604
|
|
|
663
|
-
@property
|
|
664
|
-
def request_uids(self) -> list[str]:
|
|
665
|
-
warnings.warn(
|
|
666
|
-
"`request_uids` has been deprecated, and in the future will raise an error."
|
|
667
|
-
"Please use `request_ids` from now on.",
|
|
668
|
-
DeprecationWarning,
|
|
669
|
-
stacklevel=2,
|
|
670
|
-
)
|
|
671
|
-
return self.request_ids
|
|
672
|
-
|
|
673
605
|
|
|
674
606
|
@attrs.define
|
|
675
607
|
class Results(ApiResponse):
|
|
@@ -773,6 +705,13 @@ class Processing:
|
|
|
773
705
|
log_callback=self.log_callback,
|
|
774
706
|
)
|
|
775
707
|
|
|
708
|
+
def delete(self, *job_ids: str) -> dict[str, Any]:
|
|
709
|
+
url = f"{self.url}/jobs/delete"
|
|
710
|
+
response = ApiResponse.from_request(
|
|
711
|
+
"post", url, json={"job_ids": job_ids}, **self._request_kwargs
|
|
712
|
+
)
|
|
713
|
+
return response._json_dict
|
|
714
|
+
|
|
776
715
|
def get_processes(self, **params: Any) -> Processes:
|
|
777
716
|
url = f"{self.url}/processes"
|
|
778
717
|
return Processes.from_request("get", url, params=params, **self._request_kwargs)
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
# Do not change! Do not track in version control!
|
|
2
|
-
__version__ = "0.
|
|
2
|
+
__version__ = "0.3.0"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ecmwf-datastores-client
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: ECMWF Data Stores Service (DSS) API Python client
|
|
5
5
|
License: Apache License
|
|
6
6
|
Version 2.0, January 2004
|
|
@@ -228,25 +228,20 @@ Requires-Dist: multiurl>=0.3.2
|
|
|
228
228
|
Requires-Dist: requests
|
|
229
229
|
Requires-Dist: typing-extensions
|
|
230
230
|
Provides-Extra: legacy
|
|
231
|
-
Requires-Dist: cdsapi>=0.7.
|
|
231
|
+
Requires-Dist: cdsapi>=0.7.6; extra == "legacy"
|
|
232
232
|
Dynamic: license-file
|
|
233
233
|
|
|
234
234
|
<p align="center">
|
|
235
235
|
<a href="https://github.com/ecmwf/codex/raw/refs/heads/main/ESEE">
|
|
236
|
-
<img src="https://github.com/ecmwf/codex/raw/refs/heads/main/ESEE/data_provision_badge.svg" alt="ECMWF Software EnginE">
|
|
237
|
-
</a>
|
|
236
|
+
<img src="https://github.com/ecmwf/codex/raw/refs/heads/main/ESEE/data_provision_badge.svg" alt="ECMWF Software EnginE"></a>
|
|
238
237
|
<a href="https://github.com/ecmwf/codex/raw/refs/heads/main/Project Maturity">
|
|
239
|
-
<img src="https://github.com/ecmwf/codex/raw/refs/heads/main/Project Maturity/incubating_badge.svg" alt="Maturity Level">
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
<img src="https://codecov.io/gh/ecmwf/ecmwf-datastores-client/branch/main/graph/badge.svg" alt="Code Coverage">
|
|
243
|
-
</a> -->
|
|
238
|
+
<img src="https://github.com/ecmwf/codex/raw/refs/heads/main/Project Maturity/incubating_badge.svg" alt="Maturity Level"></a>
|
|
239
|
+
<!-- <a href="https://codecov.io/gh/ecmwf/ecmwf-datastores-client">
|
|
240
|
+
<img src="https://codecov.io/gh/ecmwf/ecmwf-datastores-client/branch/main/graph/badge.svg" alt="Code Coverage"></a> -->
|
|
244
241
|
<a href="https://opensource.org/licenses/apache-2-0">
|
|
245
|
-
<img src="https://img.shields.io/badge/Licence-Apache 2.0-blue.svg" alt="Licence">
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
<img src="https://img.shields.io/github/v/release/ecmwf/ecmwf-datastores-client?color=purple&label=Release" alt="Latest Release">
|
|
249
|
-
</a>
|
|
242
|
+
<img src="https://img.shields.io/badge/Licence-Apache 2.0-blue.svg" alt="Licence"></a>
|
|
243
|
+
<a href="https://github.com/ecmwf/ecmwf-datastores-client/releases">
|
|
244
|
+
<img src="https://img.shields.io/github/v/release/ecmwf/ecmwf-datastores-client?color=purple&label=Release" alt="Latest Release"></a>
|
|
250
245
|
</p>
|
|
251
246
|
|
|
252
247
|
<p align="center">
|
|
@@ -282,8 +277,11 @@ $ pip install ecmwf-datastores-client
|
|
|
282
277
|
|
|
283
278
|
## Configuration
|
|
284
279
|
|
|
285
|
-
The `Client` requires the `url` to the API root and a valid API `key`.
|
|
286
|
-
|
|
280
|
+
The `Client` requires the `url` to the API root and a valid API `key`. These can be provided in three ways, in order of precedence:
|
|
281
|
+
|
|
282
|
+
1. As keyword arguments when instantiating the `Client`.
|
|
283
|
+
1. Via the `ECMWF_DATASTORES_URL` and `ECMWF_DATASTORES_KEY` environment variables.
|
|
284
|
+
1. From a configuration file, which must be located at `~/.ecmwfdatastoresrc` or at the path specified by the `ECMWF_DATASTORES_RC_FILE` environment variable.
|
|
287
285
|
|
|
288
286
|
```
|
|
289
287
|
$ cat $HOME/.ecmwfdatastoresrc
|
|
@@ -291,14 +289,6 @@ url: https://cds.climate.copernicus.eu/api
|
|
|
291
289
|
key: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
|
292
290
|
```
|
|
293
291
|
|
|
294
|
-
It is possible (though not recommended) to use the API key of one of the test users:
|
|
295
|
-
|
|
296
|
-
```
|
|
297
|
-
00112233-4455-6677-c899-aabbccddeeff
|
|
298
|
-
```
|
|
299
|
-
|
|
300
|
-
This key is used for anonymous tests and is designed to be the least performant option for accessing the system.
|
|
301
|
-
|
|
302
292
|
## Quick Start
|
|
303
293
|
|
|
304
294
|
Configure the logging level to display INFO messages:
|
|
@@ -312,12 +302,8 @@ Configure the logging level to display INFO messages:
|
|
|
312
302
|
Instantiate the API client and optionally verify authentication:
|
|
313
303
|
|
|
314
304
|
```python
|
|
315
|
-
>>> import os
|
|
316
305
|
>>> from ecmwf.datastores import Client
|
|
317
|
-
>>> client = Client(
|
|
318
|
-
... url=os.getenv("ECMWF_DATASTORES_URL"),
|
|
319
|
-
... key=os.getenv("ECMWF_DATASTORES_KEY"),
|
|
320
|
-
... )
|
|
306
|
+
>>> client = Client()
|
|
321
307
|
>>> client.check_authentication() # optional check
|
|
322
308
|
{...}
|
|
323
309
|
|
|
@@ -8,6 +8,7 @@ Makefile
|
|
|
8
8
|
README.md
|
|
9
9
|
environment.yml
|
|
10
10
|
pyproject.toml
|
|
11
|
+
.github/workflows/on-pr-closed.yml
|
|
11
12
|
.github/workflows/on-push.yml
|
|
12
13
|
ci/environment-ci.yml
|
|
13
14
|
ci/environment-integration.yml
|
|
@@ -17,6 +18,8 @@ docs/index.md
|
|
|
17
18
|
docs/make.bat
|
|
18
19
|
docs/_static/.gitkeep
|
|
19
20
|
docs/_templates/.gitkeep
|
|
21
|
+
docs/notebooks/index.md
|
|
22
|
+
docs/notebooks/quick_start.ipynb
|
|
20
23
|
ecmwf/datastores/__init__.py
|
|
21
24
|
ecmwf/datastores/catalogue.py
|
|
22
25
|
ecmwf/datastores/client.py
|
|
@@ -38,8 +41,8 @@ tests/integration_test_20_processing.py
|
|
|
38
41
|
tests/integration_test_30_remote.py
|
|
39
42
|
tests/integration_test_40_results.py
|
|
40
43
|
tests/integration_test_50_profile.py
|
|
41
|
-
tests/
|
|
42
|
-
tests/
|
|
44
|
+
tests/integration_test_60_client.py
|
|
45
|
+
tests/integration_test_70_legacy_client.py
|
|
43
46
|
tests/integration_test_80_adaptors.py
|
|
44
47
|
tests/integration_test_90_features.py
|
|
45
48
|
tests/test_00_version.py
|
|
@@ -21,10 +21,7 @@ def api_root_url() -> str:
|
|
|
21
21
|
|
|
22
22
|
@pytest.fixture
|
|
23
23
|
def api_anon_key() -> str:
|
|
24
|
-
return os.getenv(
|
|
25
|
-
"ECMWF_DATASTORES_ANON_KEY",
|
|
26
|
-
"00112233-4455-6677-c899-aabbccddeeff", # gitleaks:allow
|
|
27
|
-
)
|
|
24
|
+
return os.getenv("ANONYMOUS_PAT", "00112233-4455-6677-c899-aabbccddeeff")
|
|
28
25
|
|
|
29
26
|
|
|
30
27
|
@pytest.fixture
|
|
@@ -75,9 +75,10 @@ def test_processing_get_jobs_sortby(api_anon_client: Client) -> None:
|
|
|
75
75
|
assert [id2] != api_anon_client.get_jobs(sortby="created", limit=1).request_ids
|
|
76
76
|
|
|
77
77
|
|
|
78
|
-
def
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
78
|
+
def test_processing_delete(api_anon_client: Client) -> None:
|
|
79
|
+
id1 = api_anon_client.submit("test-adaptor-dummy", {}).request_id
|
|
80
|
+
id2 = api_anon_client.submit("test-adaptor-dummy", {}).request_id
|
|
81
|
+
job1, job2 = api_anon_client.delete(id1, id2)["jobs"]
|
|
82
|
+
assert job1["status"] == job2["status"] == "dismissed"
|
|
83
|
+
assert job1["jobID"] == id1
|
|
84
|
+
assert job2["jobID"] == id2
|
{ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/tests/integration_test_30_remote.py
RENAMED
|
@@ -106,9 +106,3 @@ def test_remote_datetimes(api_anon_client: Client) -> None:
|
|
|
106
106
|
assert remote.finished_at is not None
|
|
107
107
|
assert remote.created_at < remote.started_at < remote.finished_at
|
|
108
108
|
assert remote.finished_at == remote.updated_at
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
def test_make_results_deprecation(api_anon_client: Client) -> None:
|
|
112
|
-
remote = api_anon_client.submit("test-adaptor-dummy", {})
|
|
113
|
-
with pytest.warns(DeprecationWarning, match="`make_results` has been deprecated"):
|
|
114
|
-
remote.make_results()
|
|
@@ -9,7 +9,7 @@ from urllib3.exceptions import InsecureRequestWarning
|
|
|
9
9
|
from ecmwf.datastores import Client, Remote, Results, processing
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
def
|
|
12
|
+
def test_client_download_results(
|
|
13
13
|
api_anon_client: Client, tmp_path: pathlib.Path
|
|
14
14
|
) -> None:
|
|
15
15
|
remote = api_anon_client.submit("test-adaptor-dummy", {})
|
|
@@ -20,27 +20,27 @@ def test_api_client_download_results(
|
|
|
20
20
|
assert os.path.exists(result)
|
|
21
21
|
|
|
22
22
|
|
|
23
|
-
def
|
|
23
|
+
def test_client_get_process(api_anon_client: Client) -> None:
|
|
24
24
|
process = api_anon_client.get_process("test-adaptor-dummy")
|
|
25
25
|
assert isinstance(process, processing.Process)
|
|
26
26
|
assert process.id == "test-adaptor-dummy"
|
|
27
27
|
assert set(process.headers) == {"User-Agent", "PRIVATE-TOKEN"}
|
|
28
28
|
|
|
29
29
|
|
|
30
|
-
def
|
|
30
|
+
def test_client_get_remote(api_anon_client: Client) -> None:
|
|
31
31
|
request_id = api_anon_client.submit("test-adaptor-dummy", {}).request_id
|
|
32
32
|
remote = api_anon_client.get_remote(request_id)
|
|
33
33
|
assert remote.request_id == request_id
|
|
34
34
|
assert set(remote.headers) == {"User-Agent", "PRIVATE-TOKEN"}
|
|
35
35
|
|
|
36
36
|
|
|
37
|
-
def
|
|
37
|
+
def test_client_get_results(api_anon_client: Client) -> None:
|
|
38
38
|
request_id = api_anon_client.submit("test-adaptor-dummy", {}).request_id
|
|
39
39
|
results = api_anon_client.get_results(request_id)
|
|
40
40
|
assert isinstance(results, Results)
|
|
41
41
|
|
|
42
42
|
|
|
43
|
-
def
|
|
43
|
+
def test_client_retrieve(
|
|
44
44
|
api_anon_client: Client,
|
|
45
45
|
tmp_path: pathlib.Path,
|
|
46
46
|
) -> None:
|
|
@@ -54,24 +54,24 @@ def test_api_client_retrieve(
|
|
|
54
54
|
assert os.path.getsize(actual_target) == 1
|
|
55
55
|
|
|
56
56
|
|
|
57
|
-
def
|
|
57
|
+
def test_client_submit(api_anon_client: Client) -> None:
|
|
58
58
|
remote = api_anon_client.submit("test-adaptor-dummy", {})
|
|
59
59
|
assert isinstance(remote, Remote)
|
|
60
60
|
|
|
61
61
|
|
|
62
|
-
def
|
|
62
|
+
def test_client_submit_and_wait_on_results(api_anon_client: Client) -> None:
|
|
63
63
|
results = api_anon_client.submit_and_wait_on_results("test-adaptor-dummy", {})
|
|
64
64
|
assert isinstance(results, Results)
|
|
65
65
|
|
|
66
66
|
|
|
67
|
-
def
|
|
67
|
+
def test_client_verify(api_root_url: str, api_anon_key: str) -> None:
|
|
68
68
|
if not api_root_url.startswith("https"):
|
|
69
69
|
pytest.skip(f"{api_root_url=} does not use https protocol")
|
|
70
70
|
with pytest.warns(InsecureRequestWarning):
|
|
71
71
|
Client(url=api_root_url, key=api_anon_key, verify=False, maximum_tries=0)
|
|
72
72
|
|
|
73
73
|
|
|
74
|
-
def
|
|
74
|
+
def test_client_timeout(
|
|
75
75
|
api_root_url: str,
|
|
76
76
|
api_anon_key: str,
|
|
77
77
|
tmp_path: pathlib.Path,
|
|
@@ -54,7 +54,7 @@ def legacy_update(remote: processing.Remote) -> None:
|
|
|
54
54
|
)
|
|
55
55
|
|
|
56
56
|
|
|
57
|
-
def
|
|
57
|
+
def test_legacy_client_retrieve(
|
|
58
58
|
tmp_path: pathlib.Path, legacy_client: LegacyClient
|
|
59
59
|
) -> None:
|
|
60
60
|
collection_id = "test-adaptor-dummy"
|
|
@@ -65,7 +65,7 @@ def test_legacy_api_client_retrieve(
|
|
|
65
65
|
assert os.path.getsize(target) == 1
|
|
66
66
|
|
|
67
67
|
|
|
68
|
-
def
|
|
68
|
+
def test_legacy_client_result(
|
|
69
69
|
monkeypatch: pytest.MonkeyPatch,
|
|
70
70
|
tmp_path: pathlib.Path,
|
|
71
71
|
legacy_client: LegacyClient,
|
|
@@ -92,7 +92,7 @@ def test_legacy_api_client_result(
|
|
|
92
92
|
|
|
93
93
|
|
|
94
94
|
@pytest.mark.parametrize("quiet", [True, False])
|
|
95
|
-
def
|
|
95
|
+
def test_legacy_client_quiet(
|
|
96
96
|
caplog: pytest.LogCaptureFixture,
|
|
97
97
|
api_root_url: str,
|
|
98
98
|
api_anon_key: str,
|
|
@@ -105,7 +105,7 @@ def test_legacy_api_client_quiet(
|
|
|
105
105
|
|
|
106
106
|
|
|
107
107
|
@pytest.mark.parametrize("debug", [True, False])
|
|
108
|
-
def
|
|
108
|
+
def test_legacy_client_debug(
|
|
109
109
|
caplog: pytest.LogCaptureFixture,
|
|
110
110
|
api_root_url: str,
|
|
111
111
|
api_anon_key: str,
|
|
@@ -120,7 +120,7 @@ def test_legacy_api_client_debug(
|
|
|
120
120
|
"wait_until_complete,expected_type",
|
|
121
121
|
[(True, processing.Results), (False, processing.Remote)],
|
|
122
122
|
)
|
|
123
|
-
def
|
|
123
|
+
def test_legacy_client_wait_until_complete(
|
|
124
124
|
tmp_path: pathlib.Path,
|
|
125
125
|
api_root_url: str,
|
|
126
126
|
api_anon_key: str,
|
|
@@ -156,7 +156,7 @@ def test_legacy_api_client_wait_until_complete(
|
|
|
156
156
|
),
|
|
157
157
|
],
|
|
158
158
|
)
|
|
159
|
-
def
|
|
159
|
+
def test_legacy_client_update(
|
|
160
160
|
api_root_url: str,
|
|
161
161
|
api_anon_key: str,
|
|
162
162
|
collection_id: str,
|
|
@@ -173,7 +173,7 @@ def test_legacy_api_client_update(
|
|
|
173
173
|
|
|
174
174
|
|
|
175
175
|
@pytest.mark.filterwarnings("ignore:Unverified HTTPS")
|
|
176
|
-
def
|
|
176
|
+
def test_legacy_client_kwargs(api_root_url: str, api_anon_key: str) -> None:
|
|
177
177
|
session = requests.Session()
|
|
178
178
|
client = LegacyClient(
|
|
179
179
|
url=api_root_url,
|
|
@@ -198,7 +198,7 @@ def test_legacy_api_client_kwargs(api_root_url: str, api_anon_key: str) -> None:
|
|
|
198
198
|
assert client.client.session is session
|
|
199
199
|
|
|
200
200
|
|
|
201
|
-
def
|
|
201
|
+
def test_legacy_client_logging(
|
|
202
202
|
caplog: pytest.LogCaptureFixture,
|
|
203
203
|
api_root_url: str,
|
|
204
204
|
api_anon_key: str,
|
|
@@ -226,7 +226,7 @@ def test_legacy_api_client_logging(
|
|
|
226
226
|
]
|
|
227
227
|
|
|
228
228
|
|
|
229
|
-
def
|
|
229
|
+
def test_legacy_client_download(
|
|
230
230
|
tmp_path: pathlib.Path,
|
|
231
231
|
monkeypatch: pytest.MonkeyPatch,
|
|
232
232
|
api_root_url: str,
|
|
@@ -258,7 +258,7 @@ def test_legacy_api_client_download(
|
|
|
258
258
|
assert all(os.path.getsize(target) == 1 for target in targets)
|
|
259
259
|
|
|
260
260
|
|
|
261
|
-
def
|
|
261
|
+
def test_legacy_client_status(legacy_client: LegacyClient) -> None:
|
|
262
262
|
status = legacy_client.status()
|
|
263
263
|
assert set(status) <= {
|
|
264
264
|
"critical",
|
|
@@ -277,7 +277,7 @@ def test_legacy_api_client_status(legacy_client: LegacyClient) -> None:
|
|
|
277
277
|
)
|
|
278
278
|
|
|
279
279
|
|
|
280
|
-
def
|
|
280
|
+
def test_legacy_client_remote(
|
|
281
281
|
legacy_client: LegacyClient, tmp_path: pathlib.Path
|
|
282
282
|
) -> None:
|
|
283
283
|
results = legacy_client.retrieve("test-adaptor-dummy", {"size": 1})
|
|
@@ -288,7 +288,7 @@ def test_legacy_api_client_remote(
|
|
|
288
288
|
assert os.path.getsize(target) == 1
|
|
289
289
|
|
|
290
290
|
|
|
291
|
-
def
|
|
291
|
+
def test_legacy_client_warning(
|
|
292
292
|
api_root_url: str,
|
|
293
293
|
api_anon_key: str,
|
|
294
294
|
) -> None:
|
|
@@ -305,7 +305,7 @@ def test_legacy_api_client_warning(
|
|
|
305
305
|
)
|
|
306
306
|
|
|
307
307
|
|
|
308
|
-
def
|
|
308
|
+
def test_legacy_client_toolbox(legacy_client: LegacyClient) -> None:
|
|
309
309
|
with pytest.raises(NotImplementedError):
|
|
310
310
|
legacy_client.service("service")
|
|
311
311
|
with pytest.raises(NotImplementedError):
|
|
@@ -389,33 +389,6 @@ def test_submit(cat: catalogue.Catalogue) -> None:
|
|
|
389
389
|
assert remote.finished_at.isoformat() == "2022-09-02T17:32:54.308120+00:00"
|
|
390
390
|
|
|
391
391
|
|
|
392
|
-
@responses.activate
|
|
393
|
-
def test_depracations(cat: catalogue.Catalogue) -> None:
|
|
394
|
-
responses_add()
|
|
395
|
-
|
|
396
|
-
collection = cat.get_collection(COLLECTION_ID)
|
|
397
|
-
with pytest.warns(DeprecationWarning, match="`process` has been deprecated"):
|
|
398
|
-
assert isinstance(collection.process, processing.Process)
|
|
399
|
-
|
|
400
|
-
request = {"variable": "temperature", "year": "2022"}
|
|
401
|
-
remote = collection.submit(request)
|
|
402
|
-
|
|
403
|
-
with pytest.warns(
|
|
404
|
-
DeprecationWarning, match="`creation_datetime` has been deprecated"
|
|
405
|
-
):
|
|
406
|
-
assert (
|
|
407
|
-
remote.creation_datetime.isoformat() == "2022-09-02T17:30:48.201213+00:00"
|
|
408
|
-
)
|
|
409
|
-
|
|
410
|
-
with pytest.warns(DeprecationWarning, match="`start_datetime` has been deprecated"):
|
|
411
|
-
assert remote.start_datetime is not None
|
|
412
|
-
assert remote.start_datetime.isoformat() == "2022-09-02T17:32:43.890617+00:00"
|
|
413
|
-
|
|
414
|
-
with pytest.warns(DeprecationWarning, match="`end_datetime` has been deprecated"):
|
|
415
|
-
assert remote.end_datetime is not None
|
|
416
|
-
assert remote.end_datetime.isoformat() == "2022-09-02T17:32:54.308120+00:00"
|
|
417
|
-
|
|
418
|
-
|
|
419
392
|
@responses.activate
|
|
420
393
|
@pytest.mark.parametrize("method", ("submit", "apply_constraints", "estimate_costs"))
|
|
421
394
|
def test_process_docstrings(cat: catalogue.Catalogue, method: str) -> None:
|
|
@@ -438,8 +411,12 @@ def test_wait_on_result(cat: catalogue.Catalogue) -> None:
|
|
|
438
411
|
remote._wait_on_results()
|
|
439
412
|
|
|
440
413
|
|
|
414
|
+
@pytest.mark.parametrize("status", ["failed", "rejected"])
|
|
441
415
|
@responses.activate
|
|
442
|
-
def test_wait_on_result_failed(
|
|
416
|
+
def test_wait_on_result_failed(
|
|
417
|
+
cat: catalogue.Catalogue, status: str, monkeypatch: pytest.MonkeyPatch
|
|
418
|
+
) -> None:
|
|
419
|
+
monkeypatch.setitem(JOB_FAILED_JSON, "status", status)
|
|
443
420
|
responses_add()
|
|
444
421
|
|
|
445
422
|
collection = cat.get_collection(COLLECTION_ID)
|
|
@@ -451,6 +428,7 @@ def test_wait_on_result_failed(cat: catalogue.Catalogue) -> None:
|
|
|
451
428
|
):
|
|
452
429
|
remote._wait_on_results()
|
|
453
430
|
|
|
431
|
+
assert remote.status == status
|
|
454
432
|
assert remote.created_at.isoformat() == "2022-09-02T17:30:48.201213+00:00"
|
|
455
433
|
assert remote.started_at is not None
|
|
456
434
|
assert remote.started_at.isoformat() == "2022-09-02T17:32:43.890617+00:00"
|
|
File without changes
|
{ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/.pre-commit-config-cruft.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/ci/environment-integration.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/ecmwf/datastores/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/ecmwf/datastores/legacy_client.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/tests/integration_test_40_results.py
RENAMED
|
File without changes
|
{ecmwf_datastores_client-0.1.0 → ecmwf_datastores_client-0.3.0}/tests/integration_test_50_profile.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|