Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved exception handling #8

Merged
merged 10 commits into from
Nov 5, 2024
2 changes: 1 addition & 1 deletion cs3client/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from cs3.rpc.v1beta1.code_pb2 import CODE_OK

from .cs3client import CS3Client
from .exceptions.exceptions import AuthenticationException, SecretNotSetException
from .exceptions import AuthenticationException, SecretNotSetException
from .config import Config


Expand Down
74 changes: 74 additions & 0 deletions cs3client/exceptions/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
"""
exceptions

Custom exception classes for the CS3 client.
Where applicable, the default values correspond to the Linux standard error strings.

Authors: Rasmus Welander, Diogo Castro, Giuseppe Lo Presti.
Emails: [email protected], [email protected], [email protected]
Last updated: 01/08/2024
"""


class AuthenticationException(Exception):
"""
Standard error thrown when attempting an operation without the required access rights
"""

def __init__(self, message: str = "Operation not permitted"):
super().__init__(message)


class NotFoundException(IOError):
"""
Standard file missing message
"""

def __init__(self, message: str = "No such file or directory"):
super().__init__(message)


class SecretNotSetException(Exception):
"""
Standard file missing message
"""

def __init__(self, message: str = "Secret was not set, unable to authenticate"):
super().__init__(message)


class FileLockedException(IOError):
"""
Standard error thrown when attempting to overwrite a file/xattr with a mistmatched lock,
or when a lock operation cannot be performed because of failed preconditions
"""

def __init__(self, message: str = "Lock mismatch"):
super().__init__(message)


class UnknownException(Exception):
"""
Standard exception to be thrown when we get an error that is unknown, e.g. not defined in the cs3api
"""

def __init__(self, message: str = ""):
super().__init__(message)


class AlreadyExistsException(IOError):
"""
Standard error thrown when attempting to create a resource that already exists
"""

def __init__(self, message: str = "File exists"):
super().__init__(message)


class UnimplementedException(Exception):
"""
Standard error thrown when attempting to use a feature that is not implemented
"""

def __init__(self, message: str = "Not implemented"):
super().__init__(message)
73 changes: 0 additions & 73 deletions cs3client/exceptions/exceptions.py

This file was deleted.

12 changes: 6 additions & 6 deletions cs3client/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@


from .config import Config
from .exceptions.exceptions import AuthenticationException, FileLockedException
from .exceptions import AuthenticationException, FileLockedException
from .cs3resource import Resource
from .statuscodehandler import StatusCodeHandler

Expand Down Expand Up @@ -86,7 +86,7 @@ def set_xattr(self, auth_token: tuple, resource: Resource, key: str, value: str,
:raises: UnknownException (Unknown error)
"""
md = cs3spr.ArbitraryMetadata()
md.metadata.update({key: value}) # pylint: disable=no-member
md.metadata.update({key: str(value)}) # pylint: disable=no-member
glpatcern marked this conversation as resolved.
Show resolved Hide resolved
req = cs3sp.SetArbitraryMetadataRequest(ref=resource.ref, arbitrary_metadata=md, lock_id=lock_id)
res = self._gateway.SetArbitraryMetadata(request=req, metadata=[auth_token])
# CS3 storages may refuse to set an xattr in case of lock mismatch: this is an overprotection,
Expand Down Expand Up @@ -171,7 +171,7 @@ def touch_file(self, auth_token: tuple, resource: Resource) -> None:

def write_file(
self, auth_token: tuple, resource: Resource, content: Union[str, bytes], size: int,
lock_md: tuple = ('', '')
app_name: str = None, lock_id: str = None
glpatcern marked this conversation as resolved.
Show resolved Hide resolved
) -> None:
"""
Write a file using the given userid as access token. The entire content is written
Expand All @@ -183,14 +183,14 @@ def write_file(
:param resource: Resource to write content to
:param content: content to write
:param size: size of content (optional)
:param lock_md: tuple (<app_name>, <lock_id>)
:param app_name: application name (optional)
:param lock_id: lock id (optional)
:return: None (Success)
:raises: FileLockedException (File is locked),
:raises: AuthenticationException (Authentication failed)
:raises: UnknownException (Unknown error)

"""
app_name, lock_id = lock_md
tstart = time.time()
# prepare endpoint
if size == -1:
Expand Down Expand Up @@ -228,7 +228,7 @@ def write_file(
"X-Reva-Transfer": protocol.token,
**dict([auth_token]),
"X-Lock-Id": lock_id,
"X-Lock_Holder": app_name,
"X-Lock-Holder": app_name,
}
putres = requests.put(
url=protocol.upload_endpoint,
Expand Down
22 changes: 7 additions & 15 deletions cs3client/statuscodehandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import cs3.rpc.v1beta1.code_pb2 as cs3code
import cs3.rpc.v1beta1.status_pb2 as cs3status

from .exceptions.exceptions import AuthenticationException, NotFoundException, \
from .exceptions import AuthenticationException, NotFoundException, \
UnknownException, AlreadyExistsException, FileLockedException, UnimplementedException
from .config import Config

Expand Down Expand Up @@ -75,31 +75,23 @@ def handle_errors(self, status: cs3status.Status, operation: str, msg: str = Non

if status.code == cs3code.CODE_FAILED_PRECONDITION or status.code == cs3code.CODE_ABORTED:
self._log_precondition_info(status, operation, status_message, msg)
raise FileLockedException(f'Failed precondition: operation="{operation}" '
f'status_code="{status.code}" message="{status.message}"')
raise FileLockedException
if status.code == cs3code.CODE_ALREADY_EXISTS:
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}"')
raise AlreadyExistsException
if status.code == cs3code.CODE_UNIMPLEMENTED:
self._log.info(f'msg="Invoked {operation} on unimplemented feature" ')
raise UnimplementedException(f'Unimplemented feature: operation="{operation}" '
f'status_code="{status.code}" message="{status.message}"')
raise UnimplementedException
if status.code == cs3code.CODE_NOT_FOUND:
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}"')
raise NotFoundException
if status.code == cs3code.CODE_UNAUTHENTICATED:
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}"')
raise AuthenticationException
if status.code != cs3code.CODE_OK:
if "path not found" in str(status.message).lower():
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}"'
)
raise NotFoundException
self._log_unknown_error(status, operation, status_message, msg)
raise UnknownException(f'Unknown Error: operation="{operation}" status_code="{status.code}" '
f'message="{status.message}"')
2 changes: 1 addition & 1 deletion docs/source/exceptions.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
exceptions Module
=================

.. automodule:: exceptions.exceptions
.. automodule:: exceptions
:members:
:undoc-members:
:show-inheritance:
2 changes: 1 addition & 1 deletion tests/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from unittest.mock import Mock, patch
import pytest

from cs3client.exceptions.exceptions import (
from cs3client.exceptions import (
AuthenticationException,
NotFoundException,
UnknownException,
Expand Down
2 changes: 1 addition & 1 deletion tests/test_checkpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import pytest
import cs3.rpc.v1beta1.code_pb2 as cs3code

from cs3client.exceptions.exceptions import (
from cs3client.exceptions import (
AuthenticationException,
NotFoundException,
UnknownException,
Expand Down
2 changes: 1 addition & 1 deletion tests/test_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import cs3.rpc.v1beta1.code_pb2 as cs3code

from cs3client.cs3resource import Resource
from cs3client.exceptions.exceptions import (
from cs3client.exceptions import (
AuthenticationException,
NotFoundException,
FileLockedException,
Expand Down
2 changes: 1 addition & 1 deletion tests/test_share.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import cs3.storage.provider.v1beta1.resources_pb2 as cs3spr
import cs3.rpc.v1beta1.code_pb2 as cs3code

from cs3client.exceptions.exceptions import (
from cs3client.exceptions import (
AuthenticationException,
NotFoundException,
UnknownException,
Expand Down
2 changes: 1 addition & 1 deletion tests/test_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from unittest.mock import Mock, patch
import cs3.rpc.v1beta1.code_pb2 as cs3code

from cs3client.exceptions.exceptions import (
from cs3client.exceptions import (
AuthenticationException,
NotFoundException,
UnknownException,
Expand Down
Loading