From 37d97ae61113e99cba37a00f8a447e0245a5bc21 Mon Sep 17 00:00:00 2001 From: Mattia Almansi Date: Tue, 27 Aug 2024 11:27:18 +0200 Subject: [PATCH] add delete option (#60) * add delete option * add tests --- cads_api_client/api_client.py | 2 ++ cads_api_client/legacy_api_client.py | 3 ++ cads_api_client/processing.py | 41 +++++++++++++++++++------ tests/integration_test_40_api_client.py | 15 +++++++++ 4 files changed, 52 insertions(+), 9 deletions(-) diff --git a/cads_api_client/api_client.py b/cads_api_client/api_client.py index d6280eb..b7ee453 100644 --- a/cads_api_client/api_client.py +++ b/cads_api_client/api_client.py @@ -13,6 +13,7 @@ class ApiClient: url: Optional[str] = None session: requests.Session = attrs.field(factory=requests.Session) sleep_max: int = 120 + cleanup: bool = False def get_url(self) -> str: return self.url or config.get_config("url") @@ -39,6 +40,7 @@ def retrieve_api(self) -> processing.Processing: headers=self._headers(), session=self.session, sleep_max=self.sleep_max, + cleanup=self.cleanup, ) @functools.cached_property diff --git a/cads_api_client/legacy_api_client.py b/cads_api_client/legacy_api_client.py index b4e7be1..15bad22 100644 --- a/cads_api_client/legacy_api_client.py +++ b/cads_api_client/legacy_api_client.py @@ -80,11 +80,13 @@ def __init__( self.url, self.key, _ = cdsapi.api.get_url_key_verify(url, key, None) self.session = kwargs.pop("session", requests.Session()) self.sleep_max = kwargs.pop("sleep_max", 120) + self.delete = kwargs.pop("delete", True) self.client = api_client.ApiClient( url=self.url, key=self.key, session=self.session, sleep_max=self.sleep_max, + cleanup=self.delete, ) self.timeout = kwargs.pop("timeout", 60) @@ -115,6 +117,7 @@ def __init__( "timeout": self.timeout, "sleep_max": self.sleep_max, "retry_max": self.retry_max, + "delete": self.delete, }, ) diff --git a/cads_api_client/processing.py b/cads_api_client/processing.py index a96545e..6ed118c 100644 --- a/cads_api_client/processing.py +++ b/cads_api_client/processing.py @@ -180,13 +180,15 @@ def __init__( sleep_max: int = 120, headers: Dict[str, Any] = {}, session: requests.Session = requests.api, # type: ignore + cleanup: bool = False, ): self.url = url self.sleep_max = sleep_max self.headers = headers self.session = session + self.cleanup = cleanup self.log_start_time = None - logger.info(f"Request ID is {self.request_uid}") + self.info(f"Request ID is {self.request_uid}") def log_metadata(self, metadata: dict[str, Any]) -> None: logs = metadata.get("log", []) @@ -213,9 +215,9 @@ def _get_reply(self, robust: bool, **retry_options: Any) -> dict[str, Any]: if self.log_start_time: params["logStartTime"] = self.log_start_time - logger.debug(f"GET {self.url}") + self.debug(f"GET {self.url}") requests_response = get(url=self.url, headers=self.headers, params=params) - logger.debug(f"REPLY {requests_response.text}") + self.debug(f"REPLY {requests_response.text}") requests_response.raise_for_status() return dict(requests_response.json()) @@ -236,7 +238,7 @@ def wait_on_result(self, retry_options: Dict[str, Any] = {}) -> None: status = None while True: if status != (status := self._robust_status(retry_options=retry_options)): - logger.info(f"status has been updated to {status}") + self.info(f"status has been updated to {status}") if status == "successful": break elif status == "failed": @@ -248,7 +250,7 @@ def wait_on_result(self, retry_options: Dict[str, Any] = {}) -> None: sleep = self.sleep_max else: raise ProcessingFailedError(f"Unknown API state {status!r}") - logger.debug(f"result not ready, waiting for {sleep} seconds") + self.debug(f"result not ready, waiting for {sleep} seconds") time.sleep(sleep) def build_status_info(self) -> StatusInfo: @@ -263,9 +265,9 @@ def make_results(self, url: Optional[str] = None) -> Results: if status not in ("successful", "failed"): raise ValueError(f"Result not ready, job is {status}") - logger.debug(f"GET {url}") + self.debug(f"GET {url}") request_response = self.session.get(url, headers=self.headers) - logger.debug(f"REPLY {request_response.text}") + self.debug(f"REPLY {request_response.text}") response = ApiResponse(request_response, session=self.session) try: @@ -301,6 +303,14 @@ def download( target, timeout=timeout, retry_options=retry_options ) + def delete(self) -> dict[str, Any]: + self.debug(f"DELETE {self.url}") + requests_response = self.session.delete(url=self.url, headers=self.headers) + self.debug(f"REPLY {requests_response.text}") + requests_response.raise_for_status() + self.cleanup = False + return dict(requests_response.json()) + def _warn(self) -> None: message = ( ".update and .reply are available for backward compatibility." @@ -350,6 +360,13 @@ def error(self, *args: Any, **kwargs: Any) -> None: def debug(self, *args: Any, **kwargs: Any) -> None: logger.debug(*args, **kwargs) + def __del__(self) -> None: + if self.cleanup: + try: + self.delete() + except Exception as exc: + warnings.warn(str(exc), UserWarning) + @attrs.define class StatusInfo(ApiResponse): @@ -454,6 +471,7 @@ def __init__( headers: Dict[str, Any] = {}, session: requests.Session = requests.api, # type: ignore sleep_max: int = 120, + cleanup: bool = False, ) -> None: if not force_exact_url: url = f"{url}/{self.supported_api_version}" @@ -461,6 +479,7 @@ def __init__( self.headers = headers self.session = session self.sleep_max = sleep_max + self.cleanup = cleanup def processes(self, params: Dict[str, Any] = {}) -> ProcessList: url = f"{self.url}/processes" @@ -518,7 +537,7 @@ def submit( status_info = self.process_execute( collection_id, request, retry_options=retry_options ) - return status_info.make_remote(sleep_max=self.sleep_max) + return status_info.make_remote(sleep_max=self.sleep_max, cleanup=self.cleanup) def submit_and_wait_on_result( self, collection_id: str, retry_options: Dict[str, Any] = {}, **request: Any @@ -530,7 +549,11 @@ def submit_and_wait_on_result( def make_remote(self, job_id: str) -> Remote: url = f"{self.url}/jobs/{job_id}" return Remote( - url, headers=self.headers, session=self.session, sleep_max=self.sleep_max + url, + headers=self.headers, + session=self.session, + sleep_max=self.sleep_max, + cleanup=self.cleanup, ) def download_result( diff --git a/tests/integration_test_40_api_client.py b/tests/integration_test_40_api_client.py index 0d70e31..2ce5a59 100644 --- a/tests/integration_test_40_api_client.py +++ b/tests/integration_test_40_api_client.py @@ -1,3 +1,8 @@ +import datetime + +import pytest +import requests + from cads_api_client import ApiClient @@ -16,3 +21,13 @@ def test_accept_licence() -> None: licence["id"] == licence_id and licence["revision"] == licence_revision for licence in client.accepted_licences["licences"] ) + + +def test_delete_request(api_anon_client: ApiClient) -> None: + remote = api_anon_client.submit( + "test-adaptor-dummy", _timestamp=datetime.datetime.now().isoformat() + ) + reply = remote.delete() + assert reply["status"] == "dismissed" + with pytest.raises(requests.exceptions.HTTPError): + remote.status