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

1""" 

2---------------------------------------------------------------------------- 

3 

4 METADATA: 

5 

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 

13 

14---------------------------------------------------------------------------- 

15 

16 LAST MODIFIED: 

17 

18 2025-03-04 By Jess Mann 

19 

20""" 

21 

22from __future__ import annotations 

23 

24from string import Template 

25 

26import pydantic 

27 

28 

29class PaperapError(Exception): 

30 """Base exception for all paperless client errors.""" 

31 

32 

33class ModelValidationError(PaperapError, ValueError): 

34 """Raised when a model fails validation.""" 

35 

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) 

40 

41 

42class ReadOnlyFieldError(ModelValidationError): 

43 """Raised when a read-only field is set.""" 

44 

45 

46class ConfigurationError(PaperapError): 

47 """Raised when the configuration is invalid.""" 

48 

49 

50class PaperlessError(PaperapError): 

51 """Raised due to a feature or error of paperless ngx""" 

52 

53 

54class APIError(PaperlessError): 

55 """Raised when the API returns an error.""" 

56 

57 status_code: int | None = None 

58 

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) 

66 

67 

68class AuthenticationError(APIError): 

69 """Raised when authentication fails.""" 

70 

71 

72class InsufficientPermissionError(APIError): 

73 """Raised when a user does not have permission to perform an action.""" 

74 

75 

76class FeatureNotAvailableError(APIError): 

77 """Raised when a feature is not available.""" 

78 

79 

80class FilterDisabledError(FeatureNotAvailableError): 

81 """Raised when a filter is not available.""" 

82 

83 

84class RequestError(APIError): 

85 """Raised when an error occurs while making a request.""" 

86 

87 

88class BadResponseError(APIError): 

89 """Raised when a response is returned, but the status code is not 200.""" 

90 

91 

92class ResponseParsingError(APIError): 

93 """Raised when the response can't be parsed.""" 

94 

95 

96class ResourceNotFoundError(APIError): 

97 """Raised when a requested resource is not found.""" 

98 

99 resource_name: str | None = None 

100 

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) 

107 

108 

109class ObjectNotFoundError(ResourceNotFoundError): 

110 """Raised when a requested object is not found.""" 

111 

112 model_id: int | None = None 

113 

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) 

122 

123 

124class MultipleObjectsFoundError(APIError): 

125 """Raised when multiple objects are found when only one was expected.""" 

126 

127 

128class DocumentError(PaperapError): 

129 """Raised when an error occurs with a local document.""" 

130 

131 

132class NoImagesError(DocumentError): 

133 """Raised when no images are found in a pdf.""" 

134 

135 

136class DocumentParsingError(DocumentError): 

137 """Raised when a document cannot be parsed."""