From e4c8bc463f23de02d69c9d2f2a6dd860088c9527 Mon Sep 17 00:00:00 2001 From: Bart Schilperoort Date: Wed, 23 Oct 2024 13:27:49 +0200 Subject: [PATCH 01/11] Update changelog --- docs/CHANGELOG.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 6767e65..127cee3 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -7,6 +7,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +# 2.0.0 - 2024-10-23 + +Changes since v1.4.2: + +**Changed:** + + - The `splitmonths` argument now defaults to `True` for hourly requests. To not split requests by year, add `--splitmonths False`. + +**Fixed:** + + - Added support for the new climate data store. + - For authentication, the new `cads-api-client` is used, instead of a dummy request. This should avoid the dummy requests appearing in the user's queue. + +**Removed:** + + - the deprecated `orography` variable. Use `geopotential` instead. + - the deprecated `--prelimbe` argument. This one has not been required anymore, as the back-extension is part of the normal dataset now. + +**Dev changes:** + + - The pre-commit hook has been removed. Pre-commit does not play well with hatch: it would need to be installed system-wide. No hatch-specific hooks are available. + + + ## 2.0.0b2 - 2024-09-20 **Fixed:** From 0015c24294d6574013d5b9cedd773b46a9e9737e Mon Sep 17 00:00:00 2001 From: Bart Schilperoort Date: Wed, 23 Oct 2024 13:27:55 +0200 Subject: [PATCH 02/11] Fix dependencies --- pyproject.toml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index c2bf7ca..daecea2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,7 +13,7 @@ name = "era5cli" description = "A command line interface to download ERA5 data from the Copernicus Climate Data Store. https://climate.copernicus.eu/.." readme = "README.md" license = { text = "Apache Software License" } -requires-python = ">=3.8, <3.12" +requires-python = ">=3.9, <3.13" authors = [ {name = "Ronald van Haren"}, {name = "Jaro Camphuijsen"}, @@ -51,14 +51,13 @@ classifiers = [ "Operating System :: POSIX", "Programming Language :: Python", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", ] dependencies = [ - "cdsapi>=0.7.1", - "cads-api-client==1.3.0", # Pin because API is unstable: github.com/ecmwf-projects/cads-api-client/issues/81 + "cads-api-client>=1.4", "pathos", "PTable", "netCDF4" @@ -111,7 +110,7 @@ serve = ["mkdocs serve",] [tool.black] line-length = 88 -target-version = ['py38', 'py39', 'py310', 'py311'] +target-version = ['py39', 'py310', 'py311', 'py312'] include = '\.pyi?$' [tool.isort] From 4d327d27a8e40d9ebd24cb7b550d25ecd923055d Mon Sep 17 00:00:00 2001 From: Bart Schilperoort Date: Wed, 23 Oct 2024 13:29:57 +0200 Subject: [PATCH 03/11] Update notices on readme --- README.md | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index dd24331..90b18b3 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,3 @@ -Logo - -> [!IMPORTANT] -> The old Climate Data Store (CDS) will be shut down on 3 September 2024. -> All era5cli versions up to v1.4.2 will no longer work. -> -> For more information see: https://forum.ecmwf.int/t/the-new-climate-data-store-beta-cds-beta-is-now-live/3315 -> -> -> To continue using era5cli, you will need to re-register at ECMWF and get a new API key, -> and transition to the era5cli v2 beta. This can be installed with: -> `pip install era5cli==2.0.0b2` - -> [!WARNING] -> netCDF files from the new Climate Data Store Beta are not formatted the same as the -> old CDS. Some variables might be missing. -> See the open issue [here](https://github.com/eWaterCycle/era5cli/issues/165), as well as the [ECMWF discussion forum](https://forum.ecmwf.int/). - - [![github license badge](https://img.shields.io/github/license/eWaterCycle/era5cli)](https://github.com/eWaterCycle/era5cli) [![rsd badge](https://img.shields.io/badge/RSD-era5cli-blue)](https://research-software-directory.org/software/era5cli) [![fair-software.eu](https://img.shields.io/badge/fair--software.eu-%E2%97%8F%20%20%E2%97%8F%20%20%E2%97%8F%20%20%E2%97%8F%20%20%E2%97%8B-yellow)](https://fair-software.eu) @@ -27,6 +8,22 @@ [![Test Coverage](https://codecov.io/gh/eWaterCycle/era5cli/branch/main/graph/badge.svg?token=qeZXgGASBK)](https://codecov.io/gh/eWaterCycle/era5cli) [![PyPI](https://badge.fury.io/py/era5cli.svg)](https://badge.fury.io/py/era5cli) +Logo + +> [!IMPORTANT] +> The old Climate Data Store (CDS) has been shut down. All era5cli versions up to v1.4.2 will no longer work. +> +> For more information see: +> https://forum.ecmwf.int/t/the-new-climate-data-store-beta-cds-beta-is-now-live/3315 +> +> To continue using era5cli, you will need to re-register at ECMWF and get a new API key, +> and transition to the era5cli v2 beta. This can be installed with: +> `pip install era5cli==2.0.0` + +> [!WARNING] +> netCDF files from the new Climate Data Store Beta are not formatted the same as the +> old CDS. Some variables might be missing. +> See the open issue [here](https://github.com/eWaterCycle/era5cli/issues/165), as well as the [ECMWF discussion forum](https://forum.ecmwf.int/). A command line interface to download ERA5 data from the [Copernicus Climate Data Store](). From cc5d01472af847e91188fe9567815f535c10c770 Mon Sep 17 00:00:00 2001 From: Bart Schilperoort Date: Wed, 23 Oct 2024 13:32:18 +0200 Subject: [PATCH 04/11] Add py3.12 to changelog --- docs/CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 127cee3..ebe12a1 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 Changes since v1.4.2: +**Added:** + + - support for Python 3.12. + **Changed:** - The `splitmonths` argument now defaults to `True` for hourly requests. To not split requests by year, add `--splitmonths False`. @@ -24,6 +28,7 @@ Changes since v1.4.2: - the deprecated `orography` variable. Use `geopotential` instead. - the deprecated `--prelimbe` argument. This one has not been required anymore, as the back-extension is part of the normal dataset now. + - support for Python 3.8. **Dev changes:** From 0bf53bae21dcd1b6bec3de6c5a40d951b30b624d Mon Sep 17 00:00:00 2001 From: Bart Schilperoort Date: Wed, 23 Oct 2024 13:37:03 +0200 Subject: [PATCH 05/11] Make newest cads-api-client version work --- era5cli/key_management.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/era5cli/key_management.py b/era5cli/key_management.py index 5b8d037..cde5d1e 100644 --- a/era5cli/key_management.py +++ b/era5cli/key_management.py @@ -34,7 +34,7 @@ def attempt_cds_login(url: str, key: str) -> True: InvalidRequestError: If the test request failed, likely due to changes in the CDS API's variable naming. """ - client = cads_api_client.ApiClient(key, url) + client = cads_api_client.ApiClient(key=key, url=url) try: client.check_authentication() except ConnectionError as err: diff --git a/pyproject.toml b/pyproject.toml index daecea2..d5f5eae 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,7 +57,7 @@ classifiers = [ "Programming Language :: Python :: 3.12", ] dependencies = [ - "cads-api-client>=1.4", + "cads-api-client>=1.4.6", "pathos", "PTable", "netCDF4" From 316885fcb361f68aa4cf0502f61afac0434e9393 Mon Sep 17 00:00:00 2001 From: Bart Schilperoort Date: Wed, 23 Oct 2024 13:38:10 +0200 Subject: [PATCH 06/11] Bump version to 2.0.0 --- .bumpversion.cfg | 2 +- CITATION.cff | 2 +- docs/index.md | 2 +- era5cli/__version__.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 79a6e2d..b913a03 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 2.0.0b2 +current_version = 2.0.0 [comment] comment = The contents of this file cannot be merged with that of pyproject.toml until https://github.com/c4urself/bump2version/issues/42 is resolved diff --git a/CITATION.cff b/CITATION.cff index 23c92b4..5bb4373 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -97,4 +97,4 @@ license: Apache-2.0 message: "If you use this software, please cite it using these metadata." repository-code: "https://github.com/ewatercycle/era5cli" title: era5cli -version: "2.0.0b2" +version: "2.0.0" diff --git a/docs/index.md b/docs/index.md index 46323ef..d8721e6 100644 --- a/docs/index.md +++ b/docs/index.md @@ -10,7 +10,7 @@ A command line interface to download ERA5 data from the [Copernicus Climate Data To continue using era5cli, you will need to re-register at ECMWF and get a new API key, and transition to the era5cli v2 beta. This can be installed with: - `pip install era5cli==2.0.0b2` + `pip install era5cli>=2.0.0` ???+ warning netCDF files from the new Climate Data Store Beta are not formatted the same as the diff --git a/era5cli/__version__.py b/era5cli/__version__.py index 0c29e2c..ea0c345 100644 --- a/era5cli/__version__.py +++ b/era5cli/__version__.py @@ -26,4 +26,4 @@ "Bart Schilperoort", ) __email__ = "ewatercycle@esciencecenter.nl" -__version__ = "2.0.0b2" +__version__ = "2.0.0" From 0cd5940f295dc0de401afed11a4acd261270da98 Mon Sep 17 00:00:00 2001 From: Bart Schilperoort Date: Wed, 23 Oct 2024 13:54:32 +0200 Subject: [PATCH 07/11] Drop py38 add py312 for the testing workflow --- .github/workflows/test_and_build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test_and_build.yml b/.github/workflows/test_and_build.yml index cb52656..dcaee25 100644 --- a/.github/workflows/test_and_build.yml +++ b/.github/workflows/test_and_build.yml @@ -12,7 +12,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.8", "3.9", "3.10", "3.11"] + python-version: ["3.9", "3.10", "3.11", "3.12"] name: Run tests for ${{ matrix.python-version }} steps: - uses: actions/checkout@v3 From 008eb97741487c1835a3e508212401252fce0346 Mon Sep 17 00:00:00 2001 From: Bart Schilperoort Date: Wed, 23 Oct 2024 13:57:57 +0200 Subject: [PATCH 08/11] Remove reference to old cdsapi package --- era5cli/fetch.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/era5cli/fetch.py b/era5cli/fetch.py index d6a4d9b..f3b9195 100644 --- a/era5cli/fetch.py +++ b/era5cli/fetch.py @@ -4,7 +4,7 @@ import logging import os import sys -import cdsapi +from cads_api_client import legacy_api_client from pathos.threading import ThreadPool as Pool import era5cli.inputref as ref import era5cli.utils @@ -499,7 +499,7 @@ def _getdata(self, variables: list, years: list, outputfile: str, months=None): "please do not kill this process in the meantime.", os.linesep, ) - connection = cdsapi.Client( + connection = legacy_api_client.LegacyApiClient( url=self.url, key=self.key, verify=True, From c506b2ef97cc07ff6d7dc7cbd290a72de2c3e69a Mon Sep 17 00:00:00 2001 From: Bart Schilperoort Date: Wed, 23 Oct 2024 14:01:23 +0200 Subject: [PATCH 09/11] Turns out the the cads-api-client still needs the cdsapi --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index d5f5eae..a52a1c8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -58,6 +58,7 @@ classifiers = [ ] dependencies = [ "cads-api-client>=1.4.6", + "cdsapi", "pathos", "PTable", "netCDF4" From 8ac52aa692ff477d32f41d0f4f181dfb36bb5919 Mon Sep 17 00:00:00 2001 From: Bart Schilperoort Date: Mon, 10 Feb 2025 13:12:55 +0100 Subject: [PATCH 10/11] Patch new LegacyApiClient class instead of old client --- tests/test_fetch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_fetch.py b/tests/test_fetch.py index 04ec51c..493a7f6 100644 --- a/tests/test_fetch.py +++ b/tests/test_fetch.py @@ -134,7 +134,7 @@ def test_init(mockpatch): ) -@mock.patch("cdsapi.Client", autospec=True) +@mock.patch("cads_api_client.legacy_api_client.LegacyApiClient", autospec=True) @mock.patch("era5cli.utils.append_history", autospec=True) def test_fetch_nodryrun(cds, era5cli_utilsappend_history): """Test fetch function of Fetch class.""" From 900c281d9363545d50a77861f6068cab53c7c38d Mon Sep 17 00:00:00 2001 From: Bart Schilperoort Date: Mon, 10 Feb 2025 14:02:17 +0100 Subject: [PATCH 11/11] Fix API again, add 3.13 support. --- .github/workflows/test_and_build.yml | 2 +- README.md | 4 +-- docs/CHANGELOG.md | 6 ++--- era5cli/fetch.py | 4 +-- era5cli/key_management.py | 34 ++++++++++++++++++----- pyproject.toml | 8 +++--- tests/test_config.py | 40 ++++++++++++++++++---------- tests/test_fetch.py | 2 +- 8 files changed, 66 insertions(+), 34 deletions(-) diff --git a/.github/workflows/test_and_build.yml b/.github/workflows/test_and_build.yml index dcaee25..491769a 100644 --- a/.github/workflows/test_and_build.yml +++ b/.github/workflows/test_and_build.yml @@ -12,7 +12,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.9", "3.10", "3.11", "3.12"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] name: Run tests for ${{ matrix.python-version }} steps: - uses: actions/checkout@v3 diff --git a/README.md b/README.md index 90b18b3..7498a4a 100644 --- a/README.md +++ b/README.md @@ -14,10 +14,10 @@ > The old Climate Data Store (CDS) has been shut down. All era5cli versions up to v1.4.2 will no longer work. > > For more information see: -> https://forum.ecmwf.int/t/the-new-climate-data-store-beta-cds-beta-is-now-live/3315 +> https://forum.ecmwf.int/t/goodbye-legacy-climate-data-store-hello-new-climate-data-store-cds/6380/14 > > To continue using era5cli, you will need to re-register at ECMWF and get a new API key, -> and transition to the era5cli v2 beta. This can be installed with: +> and transition to era5cli version 2. This can be installed with: > `pip install era5cli==2.0.0` > [!WARNING] diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index ebe12a1..a4b1858 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -7,22 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased -# 2.0.0 - 2024-10-23 +# 2.0.0 - 2025-02-12 Changes since v1.4.2: **Added:** - - support for Python 3.12. + - support for Python 3.12, 3.13. **Changed:** - The `splitmonths` argument now defaults to `True` for hourly requests. To not split requests by year, add `--splitmonths False`. + - The 'cads-api-client' used in the 2.0 beta versions is already deprecated, the backend now uses the 'cdsapi' again, which uses ["datapi"](https://github.com/ecmwf-projects/datapi). **Fixed:** - Added support for the new climate data store. - - For authentication, the new `cads-api-client` is used, instead of a dummy request. This should avoid the dummy requests appearing in the user's queue. **Removed:** diff --git a/era5cli/fetch.py b/era5cli/fetch.py index f3b9195..d6a4d9b 100644 --- a/era5cli/fetch.py +++ b/era5cli/fetch.py @@ -4,7 +4,7 @@ import logging import os import sys -from cads_api_client import legacy_api_client +import cdsapi from pathos.threading import ThreadPool as Pool import era5cli.inputref as ref import era5cli.utils @@ -499,7 +499,7 @@ def _getdata(self, variables: list, years: list, outputfile: str, months=None): "please do not kill this process in the meantime.", os.linesep, ) - connection = legacy_api_client.LegacyApiClient( + connection = cdsapi.Client( url=self.url, key=self.key, verify=True, diff --git a/era5cli/key_management.py b/era5cli/key_management.py index cde5d1e..9c1451c 100644 --- a/era5cli/key_management.py +++ b/era5cli/key_management.py @@ -2,15 +2,16 @@ import sys from pathlib import Path from typing import Tuple -import cads_api_client +import cdsapi from requests.exceptions import ConnectionError # pylint: disable=redefined-builtin ERA5CLI_CONFIG_PATH = Path.home() / ".config" / "era5cli" / "cds_key.txt" CDSAPI_CONFIG_PATH = Path.home() / ".cdsapirc" -DEFAULT_CDS_URL = "https://cds-beta.climate.copernicus.eu/api" +DEFAULT_CDS_URL = "https://cds.climate.copernicus.eu/api" -AUTH_ERR_MSG = "401 Client Error" +AUTH_ERR_MSG = "401" +NO_DATA_ERR_MSG = "There is no data matching your request" class InvalidRequestError(Exception): @@ -34,9 +35,23 @@ def attempt_cds_login(url: str, key: str) -> True: InvalidRequestError: If the test request failed, likely due to changes in the CDS API's variable naming. """ - client = cads_api_client.ApiClient(key=key, url=url) + client = cdsapi.Client(key=key, url=url, verify=True) try: - client.check_authentication() + # Check the URL + client.status() # pragma: no cover + + # Checks if the authentication works, without downloading data + client.retrieve( # pragma: no cover + "reanalysis-era5-single-levels", + { + "variable": "2t", + "product_type": "reanalysis", + "date": "2012-12-01", + "time": "14:00", + "format": "netcdf", + }, + ) + return True except ConnectionError as err: raise ConnectionError( f"{os.linesep}Failed to connect to CDS. Please check your internet " @@ -54,6 +69,11 @@ def attempt_cds_login(url: str, key: str) -> True: f"{ERA5CLI_CONFIG_PATH.resolve()}{os.linesep}" "Or redefine your configuration with 'era5cli config'" ) from err + if NO_DATA_ERR_MSG in str(err): + raise InvalidRequestError( + f"{os.linesep}Something changed in the CDS API. Please raise an issue " + "on https://www.github.com/eWaterCycle/era5cli" + ) from err raise err # pragma: no cover @@ -123,7 +143,7 @@ def load_era5cli_config() -> Tuple[str, str]: "Old config detected. In the new CDS API only a key is required.\n" "Please look at the new CDS website, and reconfigure your login in " "era5cli\n" - " https://cds-beta.climate.copernicus.eu/" + " https://cds.climate.copernicus.eu/" ) raise InvalidLoginError(msg) @@ -148,7 +168,7 @@ def load_cdsapi_config() -> Tuple[str, str]: msg = ( "Your CDS API configuration file contains a UID entry/incorrect URL.\n" "Please look at the new CDS website, and reconfigure your key:\n" - " https://cds-beta.climate.copernicus.eu/" + " https://cds.climate.copernicus.eu/" ) raise InvalidLoginError(msg) return url, key diff --git a/pyproject.toml b/pyproject.toml index a52a1c8..52e27ec 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,7 +13,7 @@ name = "era5cli" description = "A command line interface to download ERA5 data from the Copernicus Climate Data Store. https://climate.copernicus.eu/.." readme = "README.md" license = { text = "Apache Software License" } -requires-python = ">=3.9, <3.13" +requires-python = ">=3.9" authors = [ {name = "Ronald van Haren"}, {name = "Jaro Camphuijsen"}, @@ -55,10 +55,10 @@ classifiers = [ "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", ] dependencies = [ - "cads-api-client>=1.4.6", - "cdsapi", + "cdsapi>=0.7.4", "pathos", "PTable", "netCDF4" @@ -111,7 +111,7 @@ serve = ["mkdocs serve",] [tool.black] line-length = 88 -target-version = ['py39', 'py310', 'py311', 'py312'] +target-version = ['py39', 'py310', 'py311', 'py312', 'py313'] include = '\.pyi?$' [tool.isort] diff --git a/tests/test_config.py b/tests/test_config.py index 29bd27d..2838830 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -122,29 +122,41 @@ class TestAttemptCdsLogin: expected. """ - @pytest.mark.xfail(reason="broken by new cads-client api") def test_status_fail(self): - with patch( - "cads_api_client.ApiClient.check_authentication", - side_effect=rex.ConnectionError, - ): + with patch("cdsapi.Client.status", side_effect=rex.ConnectionError): with pytest.raises(rex.ConnectionError, match="Failed to connect to CDS"): - key_management.attempt_cds_login( - url="https://www.github.com/", key="def" - ) + key_management.attempt_cds_login(url="test", key="abc:def") def test_connection_fail(self): - mp = patch( - "cads_api_client.ApiClient.check_authentication", - side_effect=Exception("401 Client Error"), + mp1 = patch("cdsapi.Client.status") + mp2 = patch( + "cdsapi.Client.retrieve", + side_effect=Exception("401"), ) - with mp: + with mp1, mp2: with pytest.raises( key_management.InvalidLoginError, match="Authorization with the CDS served failed", + ): + key_management.attempt_cds_login( + url="https://www.github.com/", key="abc:def" + ) + + def test_retrieve_fail(self): + mp1 = patch("cdsapi.Client.status") + mp2 = patch( + "cdsapi.Client.retrieve", + side_effect=Exception("There is no data matching your request"), + ) + with mp1, mp2: + with pytest.raises( + key_management.InvalidRequestError, + match="Something changed in the CDS API", ): key_management.attempt_cds_login(url="test", key="abc:def") def test_all_pass(self): - with patch("cads_api_client.ApiClient.check_authentication"): - key_management.attempt_cds_login(url="test", key="abc:def") + mp1 = patch("cdsapi.Client.status") + mp2 = patch("cdsapi.Client.retrieve") + with mp1, mp2: + assert key_management.attempt_cds_login(url="test", key="abc:def") is True diff --git a/tests/test_fetch.py b/tests/test_fetch.py index 493a7f6..04ec51c 100644 --- a/tests/test_fetch.py +++ b/tests/test_fetch.py @@ -134,7 +134,7 @@ def test_init(mockpatch): ) -@mock.patch("cads_api_client.legacy_api_client.LegacyApiClient", autospec=True) +@mock.patch("cdsapi.Client", autospec=True) @mock.patch("era5cli.utils.append_history", autospec=True) def test_fetch_nodryrun(cds, era5cli_utilsappend_history): """Test fetch function of Fetch class."""