From 52fdebc751cf2944ffab425f6c2eee37b6042cef Mon Sep 17 00:00:00 2001 From: Rasmus Oscar Welander Date: Wed, 4 Sep 2024 16:43:56 +0200 Subject: [PATCH] [CI] use Python 3.9 as that is what we have in Alma9 --- .github/workflows/ci-tests.yml | 4 ++-- cs3client/auth.py | 7 +++--- cs3client/cs3resource.py | 27 +++++++++++---------- cs3client/file.py | 27 +++++++++++++-------- cs3client/statuscodehandler.py | 44 ++++++++++++++++++++-------------- 5 files changed, 63 insertions(+), 46 deletions(-) diff --git a/.github/workflows/ci-tests.yml b/.github/workflows/ci-tests.yml index 456d92e..56d2173 100644 --- a/.github/workflows/ci-tests.yml +++ b/.github/workflows/ci-tests.yml @@ -15,10 +15,10 @@ jobs: - name: Checkout uses: actions/checkout@v4 - - name: Set up Python 3.12 + - name: Set up Python 3.9 uses: actions/setup-python@v3 with: - python-version: "3.12" + python-version: "3.9" - name: Install dependencies run: | diff --git a/cs3client/auth.py b/cs3client/auth.py index fed31fe..1406960 100644 --- a/cs3client/auth.py +++ b/cs3client/auth.py @@ -10,6 +10,7 @@ import jwt import datetime import logging +from typing import Union from cs3.gateway.v1beta1.gateway_api_pb2 import AuthenticateRequest from cs3.auth.registry.v1beta1.registry_api_pb2 import ListAuthProvidersRequest from cs3.gateway.v1beta1.gateway_api_pb2_grpc import GatewayAPIStub @@ -38,9 +39,9 @@ def __init__(self, cs3_client: CS3Client) -> None: self._log: logging.Logger = cs3_client._log self._config: Config = cs3_client._config # The user should be able to change the client secret (e.g. token) and client id at runtime - self._client_secret: str | None = self._config.auth_client_secret - self._client_id: str | None = self._config.auth_client_id - self._token: str | None = None + self._client_secret: Union[str, None] = self._config.auth_client_secret + self._client_id: Union[str, None] = self._config.auth_client_id + self._token: Union[str, None] = None def set_client_secret(self, token: str) -> None: """ diff --git a/cs3client/cs3resource.py b/cs3client/cs3resource.py index 6a19ee3..91097b5 100644 --- a/cs3client/cs3resource.py +++ b/cs3client/cs3resource.py @@ -7,6 +7,7 @@ """ import cs3.storage.provider.v1beta1.resources_pb2 as cs3spr +from typing import Union class Resource: @@ -26,12 +27,12 @@ class Resource: def __init__( self, - abs_path: str | None = None, - rel_path: str | None = None, - opaque_id: str | None = None, - parent_id: str | None = None, - storage_id: str | None = None, - space_id: str | None = None, + abs_path: Union[str, None] = None, + rel_path: Union[str, None] = None, + opaque_id: Union[str, None] = None, + parent_id: Union[str, None] = None, + storage_id: Union[str, None] = None, + space_id: Union[str, None] = None, ) -> None: """ initializes the Resource class, either abs_path, rel_path or opaque_id is required @@ -45,15 +46,15 @@ def __init__( :param storage_id: storage id (optional) :param space_id: space id (optional) """ - self._abs_path: str | None = abs_path - self._rel_path: str | None = rel_path - self._parent_id: str | None = parent_id - self._opaque_id: str | None = opaque_id - self._space_id: str | None = space_id - self._storage_id: str | None = storage_id + self._abs_path: Union[str, None] = abs_path + self._rel_path: Union[str, None] = rel_path + self._parent_id: Union[str, None] = parent_id + self._opaque_id: Union[str, None] = opaque_id + self._space_id: Union[str, None] = space_id + self._storage_id: Union[str, None] = storage_id @classmethod - def from_file_ref_and_endpoint(cls, file: str, endpoint: str | None = None) -> "Resource": + def from_file_ref_and_endpoint(cls, file: str, endpoint: Union[str, None] = None) -> "Resource": """ Extracts the attributes from the file and endpoint and returns a resource. diff --git a/cs3client/file.py b/cs3client/file.py index 3092260..4525aa5 100644 --- a/cs3client/file.py +++ b/cs3client/file.py @@ -10,6 +10,7 @@ import logging import http import requests +from typing import Union from typing import Generator import cs3.storage.provider.v1beta1.resources_pb2 as cs3spr import cs3.storage.provider.v1beta1.provider_api_pb2 as cs3sp @@ -169,7 +170,8 @@ def touch_file(self, auth_token: tuple, resource: Resource) -> None: self._log.debug(f'msg="Invoked TouchFile" trace="{res.status.trace}"') def write_file( - self, auth_token: tuple, resource: Resource, content: str | bytes, size: int, lock_md: tuple = ('', '') + self, auth_token: tuple, resource: Resource, content: Union[str, bytes], size: int, + lock_md: tuple = ('', '') ) -> None: """ Write a file using the given userid as access token. The entire content is written @@ -307,9 +309,11 @@ def read_file(self, auth_token: tuple, resource: Resource, lock_id: str = None) raise IOError(e) data = fileget.iter_content(self._config.chunk_size) if fileget.status_code != http.client.OK: + # status.message.replace('"', "'") is not allowed inside f strings python<3.12 + status_msg = fileget.reason.replace('"', "'") self._log.error( f'msg="Error downloading file from Reva" code="{fileget.status_code}" ' - f'reason="{fileget.reason.replace('"', "'")}"' + f'reason="{status_msg}"' ) raise IOError(fileget.reason) else: @@ -355,7 +359,7 @@ def list_dir( for info in res.infos: yield info - def _set_lock_using_xattr(self, auth_token, resource: Resource, app_name: str, lock_id: int | str) -> None: + def _set_lock_using_xattr(self, auth_token, resource: Resource, app_name: str, lock_id: Union[int, str]) -> None: """" Set a lock to a resource with the given value metadata and appname as holder @@ -376,7 +380,7 @@ def _set_lock_using_xattr(self, auth_token, resource: Resource, app_name: str, l self.set_xattr(auth_token, resource, LOCK_ATTR_KEY, f"{app_name}!{lock_id}!{expiration}", None) return - def set_lock(self, auth_token: tuple, resource: Resource, app_name: str, lock_id: int | str) -> None: + def set_lock(self, auth_token: tuple, resource: Resource, app_name: str, lock_id: Union[int, str]) -> None: """ Set a lock to a resource with the given value and appname as holder @@ -439,7 +443,7 @@ def _get_lock_using_xattr(self, auth_token: tuple, resource: Resource) -> dict: except KeyError: return None - def get_lock(self, auth_token: tuple, resource: Resource) -> cs3spr.Lock | dict | None: + def get_lock(self, auth_token: tuple, resource: Resource) -> Union[cs3spr.Lock, dict, None]: """ Get the lock for the given filepath @@ -480,7 +484,8 @@ def get_lock(self, auth_token: tuple, resource: Resource) -> cs3spr.Lock | dict } def _refresh_lock_using_xattr( - self, auth_token: tuple, resource: Resource, app_name: str, lock_id: str | int, existing_lock_id: str | int = None + self, auth_token: tuple, resource: Resource, app_name: str, lock_id: Union[str, int], + existing_lock_id: Union[str, int] = None ) -> None: """ Refresh the lock metadata for the given filepath @@ -513,8 +518,8 @@ def _refresh_lock_using_xattr( return def refresh_lock( - self, auth_token: tuple, resource: Resource, app_name: str, lock_id: str | int, - existing_lock_id: str | int = None + self, auth_token: tuple, resource: Resource, app_name: str, lock_id: Union[str, int], + existing_lock_id: Union[str, int] = None ): """ Refresh the lock for the given filepath @@ -553,7 +558,9 @@ def refresh_lock( self._log.debug(f'msg="Invoked RefreshLock" {resource.get_file_ref_str()} result="{res.status.trace}" ' f'value="{lock_id}" old_value="{existing_lock_id}"') - def _unlock_using_xattr(self, auth_token: tuple, resource: Resource, app_name: str, lock_id: str | int) -> None: + def _unlock_using_xattr( + self, auth_token: tuple, resource: Resource, app_name: str, lock_id: Union[str, int] + ) -> None: """ Remove the lock for the given filepath @@ -581,7 +588,7 @@ def _unlock_using_xattr(self, auth_token: tuple, resource: Resource, app_name: s self.remove_xattr(resource, LOCK_ATTR_KEY, None) return - def unlock(self, auth_token: tuple, resource: Resource, app_name, lock_id: str | int): + def unlock(self, auth_token: tuple, resource: Resource, app_name, lock_id: Union[str, int]): """ Remove the lock for the given filepath diff --git a/cs3client/statuscodehandler.py b/cs3client/statuscodehandler.py index bed57f5..c468212 100644 --- a/cs3client/statuscodehandler.py +++ b/cs3client/statuscodehandler.py @@ -20,57 +20,65 @@ def __init__(self, log: logging.Logger, config: Config) -> None: self._log = log self._config = config - def _log_not_found_info(self, status: cs3status.Status, operation: str, msg: str = None) -> None: + def _log_not_found_info(self, status: cs3status.Status, operation: str, status_msg: str, msg: str = None) -> None: self._log.info( f'msg="Not found on {operation}" {msg + " " if msg else ""} ' f'userid="{self._config.auth_client_id if self._config.auth_client_id else "no_id_set"}" ' - f'trace="{status.trace}" reason="{status.message.replace('"', "'")}"' + f'trace="{status.trace}" reason="{status_msg}"' ) - def _log_authentication_error(self, status: cs3status.Status, operation: str, msg: str = None) -> None: + def _log_authentication_error( + self, status: cs3status.Status, operation: str, status_msg: str, msg: str = None + ) -> None: self._log.error( f'msg="Authentication failed on {operation}" {msg + " " if msg else ""}' f'userid="{self._config.auth_client_id if self._config.auth_client_id else "no_id_set"}" ' - f'trace="{status.trace}" reason="{status.message.replace('"', "'")}"' + f'trace="{status.trace}" reason="{status_msg}"' ) - def _log_unknown_error(self, status: cs3status.Status, operation: str, msg: str = None) -> None: + def _log_unknown_error(self, status: cs3status.Status, operation: str, status_msg: str, msg: str = None) -> None: self._log.error( f'msg="Failed to {operation}, unknown error" {msg + " " if msg else ""}' f'userid="{self._config.auth_client_id if self._config.auth_client_id else "no_id_set"}" ' - f'trace="{status.trace}" reason="{status.message.replace('"', "'")}"' + f'trace="{status.trace}" reason="{status_msg}"' ) - def _log_precondition_info(self, status: cs3status.Status, operation: str, msg: str = None) -> None: + def _log_precondition_info( + self, status: cs3status.Status, operation: str, status_msg: str, msg: str = None + ) -> None: self._log.info( f'msg="Failed precondition on {operation}" {msg + " " if msg else ""}' f'userid="{self._config.auth_client_id if self._config.auth_client_id else "no_id_set"}" ' - f'trace="{status.trace}" reason="{status.message.replace('"', "'")}"' + f'trace="{status.trace}" reason="{status_msg}"' ) - def _log_already_exists(self, status: cs3status.Status, operation: str, msg: str = None) -> None: + def _log_already_exists(self, status: cs3status.Status, operation: str, status_msg: str, msg: str = None) -> None: self._log.info( f'msg="Already exists on {operation}" {msg + " " if msg else ""}' f'userid="{self._config.auth_client_id if self._config.auth_client_id else "no_id_set"}" ' - f'trace="{status.trace}" reason="{status.message.replace('"', "'")}"' + f'trace="{status.trace}" reason="{status_msg}"' ) - def _log_unimplemented(self, status: cs3status.Status, operation: str, msg: str = None) -> None: + def _log_unimplemented(self, status: cs3status.Status, operation: str, status_msg: str, msg: str = None) -> None: self._log.info( f'msg="Invoked {operation} on unimplemented feature" {msg + " " if msg else ""}' f'userid="{self._config.auth_client_id if self._config.auth_client_id else "no_id_set"}" ' - f'trace="{status.trace}" reason="{status.message.replace('"', "'")}"' + f'trace="{status.trace}" reason="{status_msg}"' ) def handle_errors(self, status: cs3status.Status, operation: str, msg: str = None) -> None: + if status.code == cs3code.CODE_OK: return + # status.message.replace('"', "'") is not allowed inside f strings python<3.12 + status_message = status.message.replace('"', "'") + if status.code == cs3code.CODE_FAILED_PRECONDITION or status.code == cs3code.CODE_ABORTED: - self._log_precondition_info(status, operation, msg) + self._log_precondition_info(status, operation, status_message, msg) raise FileLockedException(f'Failed precondition: operation="{operation}" ' f'status_code="{status.code}" message="{status.message}"') if status.code == cs3code.CODE_ALREADY_EXISTS: - self._log_already_exists(status, operation, msg) + self._log_already_exists(status, operation, status_message, msg) raise AlreadyExistsException(f'Resource already exists: operation="{operation}" ' f'status_code="{status.code}" message="{status.message}"') if status.code == cs3code.CODE_UNIMPLEMENTED: @@ -78,11 +86,11 @@ def handle_errors(self, status: cs3status.Status, operation: str, msg: str = Non raise UnimplementedException(f'Unimplemented feature: operation="{operation}" ' f'status_code="{status.code}" message="{status.message}"') if status.code == cs3code.CODE_NOT_FOUND: - self._log_not_found_info(status, operation, msg) + self._log_not_found_info(status, operation, status_message, msg) raise NotFoundException(f'Not found: operation="{operation}" ' f'status_code="{status.code}" message="{status.message}"') if status.code == cs3code.CODE_UNAUTHENTICATED: - self._log_authentication_error(status, operation, msg) + self._log_authentication_error(status, operation, status_message, msg) raise AuthenticationException(f'Operation not permitted: operation="{operation}" ' f'status_code="{status.code}" message="{status.message}"') if status.code != cs3code.CODE_OK: @@ -90,8 +98,8 @@ def handle_errors(self, status: cs3status.Status, operation: str, msg: str = Non self._log.info(f'msg="Invoked {operation} on missing file" ') raise NotFoundException( message=f'No such file or directory: operation="{operation}" ' - f'status_code="{status.code}" message="{status.message}"' + f'status_code="{status.code}" message="{status.message}"' ) - self._log_unknown_error(status, operation, msg) + self._log_unknown_error(status, operation, status_message, msg) raise UnknownException(f'Unknown Error: operation="{operation}" status_code="{status.code}" ' f'message="{status.message}"')