Skip to content

Commit

Permalink
Refactor exception handling: Define custom exceptions as subclasses o…
Browse files Browse the repository at this point in the history
…f a base Errors class (#465)

* Refactor exception handling: Define custom exceptions as subclasses of a base Errors class

* Update errors.py

* format code, fix and sort imports

* remove unused error class imports across project

---------

Co-authored-by: Daniel Fernau <[email protected]>
  • Loading branch information
castanley and danielfernau authored Sep 18, 2024
1 parent 226fdf4 commit 73b2d12
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 28 deletions.
4 changes: 2 additions & 2 deletions protect_archiver/cli/download.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from protect_archiver.client import ProtectClient
from protect_archiver.config import Config
from protect_archiver.downloader import Downloader
from protect_archiver.errors import Errors
from protect_archiver.errors import ProtectError
from protect_archiver.utils import print_download_stats


Expand Down Expand Up @@ -300,5 +300,5 @@ def download(

print_download_stats(client)

except Errors.ProtectError as e:
except ProtectError as e:
exit(e.code)
4 changes: 2 additions & 2 deletions protect_archiver/cli/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from protect_archiver.client import ProtectClient
from protect_archiver.config import Config
from protect_archiver.downloader import Downloader
from protect_archiver.errors import Errors
from protect_archiver.errors import ProtectError
from protect_archiver.utils import print_download_stats


Expand Down Expand Up @@ -264,5 +264,5 @@ def events(

print_download_stats(client)

except Errors.ProtectError as e:
except ProtectError as e:
exit(e.code)
4 changes: 2 additions & 2 deletions protect_archiver/client/legacy.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import requests

from protect_archiver.errors import Errors
from protect_archiver.errors import ProtectError


class LegacyClient:
Expand Down Expand Up @@ -52,7 +52,7 @@ def fetch_api_token(self) -> str:
f"with status {response.status_code} {response.reason}"
)
# Downloader.print_download_stats() # TODO
raise Errors.ProtectError(2)
raise ProtectError(2)

logging.info(f"Successfully authenticated as user {self.username}")

Expand Down
4 changes: 2 additions & 2 deletions protect_archiver/client/unifi_os.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import requests

from protect_archiver.errors import Errors
from protect_archiver.errors import ProtectError


class UniFiOSClient:
Expand Down Expand Up @@ -44,7 +44,7 @@ def fetch_session_cookie_token(self) -> str:
f"Authentication failed with status code {response.status_code}! Check username and"
" password."
)
raise Errors.ProtectError(2)
raise ProtectError(2)

logging.debug("Successfully authenticated user using a session cookie")

Expand Down
25 changes: 13 additions & 12 deletions protect_archiver/downloader/download_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@

import requests

from protect_archiver.errors import Errors
from protect_archiver.errors import DownloadFailed
from protect_archiver.errors import ProtectError
from protect_archiver.utils import format_bytes
from protect_archiver.utils import print_download_stats

Expand Down Expand Up @@ -80,10 +81,10 @@ def download_file(client: Any, query: str, filename: str) -> None:
if response.status_code != 200:
try:
data = json.loads(response.content)
error_message = data.get("error") or data or "(no information available)"
except Exception:
data = None

error_message = data.get("error") or data or "(no information available)"
error_message = "(no information available)"

# TODO
logging.exception(
Expand Down Expand Up @@ -142,14 +143,14 @@ def download_file(client: Any, query: str, filename: str) -> None:
os.remove(filename)
logging.exception(f"Download failed: {request_exception}")
exit_code = 5
# except Errors.DownloadFailed:
# # clean up
# if os.path.exists(filename):
# os.remove(filename)
# logging.exception(
# f"Download failed with status {response.status_code} {response.reason}"
# )
# exit_code = 4
except DownloadFailed:
# clean up
if os.path.exists(filename):
os.remove(filename)
logging.exception(
f"Download failed with status {response.status_code} {response.reason}"
)
exit_code = 4
else:
return

Expand All @@ -162,7 +163,7 @@ def download_file(client: Any, query: str, filename: str) -> None:
" '--ignore-failed-downloads'"
)
print_download_stats(client)
raise Errors.ProtectError(exit_code)
raise ProtectError(exit_code)
else:
logging.info(
"Argument '--ignore-failed-downloads' is present, continue downloading files..."
Expand Down
52 changes: 44 additions & 8 deletions protect_archiver/errors.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,44 @@
class Errors:
def __init__(self) -> None:
pass

class ProtectError(Exception):
def __init__(self, code: int) -> None:
self.code = code
super().__init__(f"ProtectError with code: {code}")
class Error(Exception):
"""Base class for all custom exceptions in this project.
This class can be used as a catch-all for project-specific exceptions, allowing
for uniform handling or logging of errors unique to this application.
"""

pass


class ProtectError(Error):
"""Handles errors related to core application functionality.
Attributes:
code (int): Numeric identifier for the type of error encountered, useful for
diagnostics or user feedback.
"""

def __init__(self, code: int):
# Assign the error code for further reference
self.code = code
# Construct the base exception message with the specific error code
super().__init__(f"ProtectError with error code: {code}")


class DownloadFailed(Error):
"""Signifies that a file download process has been unsuccessful.
Raises when there are issues with downloading files, excluding authorization
problems, which are handled by `AuthorizationFailed`. Common causes might include
network failures or file access issues.
"""

pass


class AuthorizationFailed(Error):
"""Represents failures in the authorization or authentication process.
This should be used when access is denied due to invalid credentials, expired sessions,
or any other issues specifically related to gaining authorized access.
"""

pass

0 comments on commit 73b2d12

Please sign in to comment.