1from typing import Any, Dict, List, Optional
2
3
4class SkipCollection(Exception):
5    pass
6
7
8class TartifletteError(Exception):
9    """
10    Base exceptions of all internal errors raised by the Tartiflette engine.
11    """
12
13    def __init__(
14        self,
15        message: str,
16        path: Optional[List[str]] = None,
17        locations: Optional[List["Location"]] = None,
18        user_message: Optional[str] = None,
19        more_info: Optional[str] = None,
20        extensions: Optional[Dict[str, Any]] = None,
21        original_error: Optional[Exception] = None,
22    ) -> None:
23        """
24        :param message: message explaining the error which occurred
25        :param path: path on the query where the error occurred
26        :param locations: locations on the document where the error occurred
27        :param user_message: more detailed human message explaining the error
28        :param more_info: extra information for the error
29        :param extensions: extra information for the error which will be added
30        to the `extensions` key once coerced
31        :param original_error: instance of the original exception which lead to
32        the error
33        :type message: str
34        :type path: Optional[List[str]]
35        :type locations: Optional[List[Location]]
36        :type user_message: Optional[str]
37        :type more_info: Optional[str]
38        :type extensions: Optional[Dict[str, Any]]
39        :type original_error: Optional[Exception]
40        """
41        super().__init__(message)
42        self.message = message  # Developer message by default
43        self.user_message = user_message
44        self.more_info = more_info or ""
45        self.path = path or None
46        self.locations = locations or []
47        self.extensions = extensions or {}
48        self.original_error = original_error
49
50    def __repr__(self) -> str:
51        """
52        Returns the representation of a TartifletteError instance.
53        :return: the representation of a TartifletteError instance
54        :rtype: str
55        """
56        return f"{self.__class__.__name__}(message=%r, locations=%r)" % (
57            self.user_message or self.message,
58            self.locations,
59        )
60
61    def coerce_value(
62        self,
63        *_args,
64        path: Optional[List[str]] = None,
65        locations: Optional[List["Location"]] = None,
66        **_kwargs,
67    ) -> Dict[str, Any]:
68        """
69        Converts the TartifletteError instance into a valid GraphQL error
70        output.
71        :param path: path on the query where the error occurred
72        :param locations: locations on the document where the error occurred
73        :type path: Optional[List[str]]
74        :type locations: Optional[List[Location]]
75        :return: a valid GraphQL error output
76        :rtype: Dict[str, Any]
77        """
78        # pylint: disable=missing-param-doc
79        computed_locations = []
80
81        try:
82            for location in locations or self.locations:
83                computed_locations.append(location.collect_value())
84        except (AttributeError, TypeError):
85            pass
86
87        errors = {
88            "message": self.user_message or self.message,
89            "path": path or self.path,
90            "locations": computed_locations,
91        }
92
93        if self.extensions:
94            errors["extensions"] = dict(self.extensions)
95        return errors
96
97
98class MultipleException(Exception):
99    """
100    Utility exception which allows to handle multiple errors at once.
101    """
102
103    def __init__(self, exceptions: Optional[List[Exception]] = None) -> None:
104        """
105        :param exceptions: list of exceptions to handle
106        :type exceptions: Optional[List[Exception]]
107        """
108        super().__init__()
109        self.exceptions = exceptions or []
110
111    def __bool__(self) -> bool:
112        """
113        Determines whether or not there is exceptions.
114        :return: whether or not there is exceptions
115        :rtype: bool
116        """
117        return bool(self.exceptions)
118
119    def __add__(self, other: "MultipleException") -> "MultipleException":
120        """
121        Concatenates the exception list of both MultipleException to return a
122        new MultipleException instance containing both exception list.
123        :param other: an MultipleException instance
124        :type other: MultipleException
125        :return: a new MultipleException containing both exception list
126        :rtype: MultipleException
127        """
128        return MultipleException(self.exceptions + other.exceptions)
129
130
131class ImproperlyConfigured(TartifletteError):
132    pass
133
134
135class InvalidType(TartifletteError):
136    pass
137
138
139class GraphQLSchemaError(TartifletteError):
140    pass
141
142
143class GraphQLSyntaxError(TartifletteError):
144    pass
145
146
147class NonCallable(ImproperlyConfigured):
148    pass
149
150
151class NonCoroutine(ImproperlyConfigured):
152    pass
153
154
155class NonAwaitableResolver(ImproperlyConfigured):
156    pass
157
158
159class NonAsyncGeneratorSubscription(ImproperlyConfigured):
160    pass
161
162
163class NotSubscriptionField(ImproperlyConfigured):
164    pass
165
166
167class UnknownSchemaFieldResolver(TartifletteError):
168    pass
169
170
171class UnknownDirectiveDefinition(TartifletteError):
172    pass
173
174
175class UnknownScalarDefinition(TartifletteError):
176    pass
177
178
179class UnknownFieldDefinition(TartifletteError):
180    pass
181
182
183class UnknownTypeDefinition(TartifletteError):
184    pass
185
186
187class MissingImplementation(ImproperlyConfigured):
188    pass
189
190
191class RedefinedImplementation(TartifletteError):
192    pass
193
194
195class CoercionError(TartifletteError):
196    pass
197