Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion src/shade/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
from .errors import (
AuthenticationError,
InvalidRequestError,
NetworkError,
NotFoundError,
ShadeError,
)
from .gateway import Gateway

__version__ = "0.1.0"

__all__ = ["Gateway"]
__all__ = [
"AuthenticationError",
"Gateway",
"InvalidRequestError",
"NetworkError",
"NotFoundError",
"ShadeError",
]
39 changes: 39 additions & 0 deletions src/shade/errors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from __future__ import annotations

from typing import Optional


class ShadeError(Exception):
"""Base exception for all Shade SDK errors."""

def __init__(
self,
message: str,
status_code: Optional[int] = None,
response_body: Optional[str] = None,
) -> None:
self.message = message
self.status_code = status_code
self.response_body = response_body
super().__init__(message)

def __str__(self) -> str:
if self.status_code is None:
return self.message
return f"{self.message} (status code: {self.status_code})"


class AuthenticationError(ShadeError):
"""Raised when authentication fails or credentials are invalid."""


class InvalidRequestError(ShadeError):
"""Raised when a request is malformed or rejected by validation."""


class NotFoundError(ShadeError):
"""Raised when an API resource cannot be found."""


class NetworkError(ShadeError):
"""Raised when the SDK cannot complete a network request."""
51 changes: 51 additions & 0 deletions tests/test_errors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import shade
from shade import (
AuthenticationError,
InvalidRequestError,
NetworkError,
NotFoundError,
ShadeError,
)


def test_shade_error_can_be_raised_standalone():
error = ShadeError("contract rejected payment")

assert str(error) == "contract rejected payment"
assert error.message == "contract rejected payment"
assert error.status_code is None
assert error.response_body is None


def test_shade_error_includes_http_context():
error = ShadeError(
"invalid request",
status_code=422,
response_body='{"error":"missing amount"}',
)

assert str(error) == "invalid request (status code: 422)"
assert error.status_code == 422
assert error.response_body == '{"error":"missing amount"}'


def test_specific_errors_inherit_from_shade_error():
for error_type in (
AuthenticationError,
InvalidRequestError,
NetworkError,
NotFoundError,
):
error = error_type("request failed", status_code=400, response_body="raw")

assert isinstance(error, ShadeError)
assert str(error) == "request failed (status code: 400)"
assert error.response_body == "raw"


def test_package_root_exports_error_classes():
assert shade.ShadeError is ShadeError
assert shade.AuthenticationError is AuthenticationError
assert shade.InvalidRequestError is InvalidRequestError
assert shade.NetworkError is NetworkError
assert shade.NotFoundError is NotFoundError
Loading