Coverage for src/paperap/exceptions.py: 88%
48 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-03-20 13:17 -0400
« prev ^ index » next coverage.py v7.6.12, created at 2025-03-20 13:17 -0400
1"""
2----------------------------------------------------------------------------
4 METADATA:
6 File: exceptions.py
7 Project: paperap
8 Created: 2025-03-04
9 Version: 0.0.8
10 Author: Jess Mann
11 Email: jess@jmann.me
12 Copyright (c) 2025 Jess Mann
14----------------------------------------------------------------------------
16 LAST MODIFIED:
18 2025-03-04 By Jess Mann
20"""
22from __future__ import annotations
24from string import Template
26import pydantic
29class PaperapError(Exception):
30 """Base exception for all paperless client errors."""
33class ModelValidationError(PaperapError, ValueError):
34 """Raised when a model fails validation."""
36 def __init__(self, message: str | None = None, model: pydantic.BaseModel | None = None) -> None:
37 if not message:
38 message = f"Model failed validation for {model.__class__.__name__}."
39 super().__init__(message)
42class ReadOnlyFieldError(ModelValidationError):
43 """Raised when a read-only field is set."""
46class ConfigurationError(PaperapError):
47 """Raised when the configuration is invalid."""
50class PaperlessError(PaperapError):
51 """Raised due to a feature or error of paperless ngx"""
54class APIError(PaperlessError):
55 """Raised when the API returns an error."""
57 status_code: int | None = None
59 def __init__(self, message: str | None = None, status_code: int | None = None) -> None:
60 self.status_code = status_code
61 if not message:
62 message = "An error occurred."
63 message = f"API Error {status_code}: {message}"
64 message = Template(message).safe_substitute(status_code=status_code)
65 super().__init__(message)
68class AuthenticationError(APIError):
69 """Raised when authentication fails."""
72class InsufficientPermissionError(APIError):
73 """Raised when a user does not have permission to perform an action."""
76class FeatureNotAvailableError(APIError):
77 """Raised when a feature is not available."""
80class FilterDisabledError(FeatureNotAvailableError):
81 """Raised when a filter is not available."""
84class RequestError(APIError):
85 """Raised when an error occurs while making a request."""
88class BadResponseError(APIError):
89 """Raised when a response is returned, but the status code is not 200."""
92class ResponseParsingError(APIError):
93 """Raised when the response can't be parsed."""
96class ResourceNotFoundError(APIError):
97 """Raised when a requested resource is not found."""
99 resource_name: str | None = None
101 def __init__(self, message: str | None = None, resource_name: str | None = None) -> None:
102 self.resource_name = resource_name
103 if not message:
104 message = "Resource ${resource} not found."
105 message = Template(message).safe_substitute(resource=resource_name)
106 super().__init__(message, 404)
109class ObjectNotFoundError(ResourceNotFoundError):
110 """Raised when a requested object is not found."""
112 model_id: int | None = None
114 def __init__(
115 self, message: str | None = None, resource_name: str | None = None, model_id: int | None = None
116 ) -> None:
117 self.model_id = model_id
118 if not message:
119 message = "Resource ${resource} (#${pk}) not found."
120 message = Template(message).safe_substitute(resource=resource_name, pk=model_id)
121 super().__init__(message, resource_name)
124class MultipleObjectsFoundError(APIError):
125 """Raised when multiple objects are found when only one was expected."""
128class DocumentError(PaperapError):
129 """Raised when an error occurs with a local document."""
132class NoImagesError(DocumentError):
133 """Raised when no images are found in a pdf."""
136class DocumentParsingError(DocumentError):
137 """Raised when a document cannot be parsed."""