| from collections.abc import Sequence | |
| from typing import Annotated, Any, Optional, TypedDict, Union | |
| from annotated_doc import Doc | |
| from pydantic import BaseModel, create_model | |
| from starlette.exceptions import HTTPException as StarletteHTTPException | |
| from starlette.exceptions import WebSocketException as StarletteWebSocketException | |
| class EndpointContext(TypedDict, total=False): | |
| function: str | |
| path: str | |
| file: str | |
| line: int | |
| class HTTPException(StarletteHTTPException): | |
| """ | |
| An HTTP exception you can raise in your own code to show errors to the client. | |
| This is for client errors, invalid authentication, invalid data, etc. Not for server | |
| errors in your code. | |
| Read more about it in the | |
| [FastAPI docs for Handling Errors](https://fastapi.tiangolo.com/tutorial/handling-errors/). | |
| ## Example | |
| ```python | |
| from fastapi import FastAPI, HTTPException | |
| app = FastAPI() | |
| items = {"foo": "The Foo Wrestlers"} | |
| @app.get("/items/{item_id}") | |
| async def read_item(item_id: str): | |
| if item_id not in items: | |
| raise HTTPException(status_code=404, detail="Item not found") | |
| return {"item": items[item_id]} | |
| ``` | |
| """ | |
| def __init__( | |
| self, | |
| status_code: Annotated[ | |
| int, | |
| Doc( | |
| """ | |
| HTTP status code to send to the client. | |
| Read more about it in the | |
| [FastAPI docs for Handling Errors](https://fastapi.tiangolo.com/tutorial/handling-errors/#use-httpexception) | |
| """ | |
| ), | |
| ], | |
| detail: Annotated[ | |
| Any, | |
| Doc( | |
| """ | |
| Any data to be sent to the client in the `detail` key of the JSON | |
| response. | |
| Read more about it in the | |
| [FastAPI docs for Handling Errors](https://fastapi.tiangolo.com/tutorial/handling-errors/#use-httpexception) | |
| """ | |
| ), | |
| ] = None, | |
| headers: Annotated[ | |
| Optional[dict[str, str]], | |
| Doc( | |
| """ | |
| Any headers to send to the client in the response. | |
| Read more about it in the | |
| [FastAPI docs for Handling Errors](https://fastapi.tiangolo.com/tutorial/handling-errors/#add-custom-headers) | |
| """ | |
| ), | |
| ] = None, | |
| ) -> None: | |
| super().__init__(status_code=status_code, detail=detail, headers=headers) | |
| class WebSocketException(StarletteWebSocketException): | |
| """ | |
| A WebSocket exception you can raise in your own code to show errors to the client. | |
| This is for client errors, invalid authentication, invalid data, etc. Not for server | |
| errors in your code. | |
| Read more about it in the | |
| [FastAPI docs for WebSockets](https://fastapi.tiangolo.com/advanced/websockets/). | |
| ## Example | |
| ```python | |
| from typing import Annotated | |
| from fastapi import ( | |
| Cookie, | |
| FastAPI, | |
| WebSocket, | |
| WebSocketException, | |
| status, | |
| ) | |
| app = FastAPI() | |
| @app.websocket("/items/{item_id}/ws") | |
| async def websocket_endpoint( | |
| *, | |
| websocket: WebSocket, | |
| session: Annotated[str | None, Cookie()] = None, | |
| item_id: str, | |
| ): | |
| if session is None: | |
| raise WebSocketException(code=status.WS_1008_POLICY_VIOLATION) | |
| await websocket.accept() | |
| while True: | |
| data = await websocket.receive_text() | |
| await websocket.send_text(f"Session cookie is: {session}") | |
| await websocket.send_text(f"Message text was: {data}, for item ID: {item_id}") | |
| ``` | |
| """ | |
| def __init__( | |
| self, | |
| code: Annotated[ | |
| int, | |
| Doc( | |
| """ | |
| A closing code from the | |
| [valid codes defined in the specification](https://datatracker.ietf.org/doc/html/rfc6455#section-7.4.1). | |
| """ | |
| ), | |
| ], | |
| reason: Annotated[ | |
| Union[str, None], | |
| Doc( | |
| """ | |
| The reason to close the WebSocket connection. | |
| It is UTF-8-encoded data. The interpretation of the reason is up to the | |
| application, it is not specified by the WebSocket specification. | |
| It could contain text that could be human-readable or interpretable | |
| by the client code, etc. | |
| """ | |
| ), | |
| ] = None, | |
| ) -> None: | |
| super().__init__(code=code, reason=reason) | |
| RequestErrorModel: type[BaseModel] = create_model("Request") | |
| WebSocketErrorModel: type[BaseModel] = create_model("WebSocket") | |
| class FastAPIError(RuntimeError): | |
| """ | |
| A generic, FastAPI-specific error. | |
| """ | |
| class DependencyScopeError(FastAPIError): | |
| """ | |
| A dependency declared that it depends on another dependency with an invalid | |
| (narrower) scope. | |
| """ | |
| class ValidationException(Exception): | |
| def __init__( | |
| self, | |
| errors: Sequence[Any], | |
| *, | |
| endpoint_ctx: Optional[EndpointContext] = None, | |
| ) -> None: | |
| self._errors = errors | |
| self.endpoint_ctx = endpoint_ctx | |
| ctx = endpoint_ctx or {} | |
| self.endpoint_function = ctx.get("function") | |
| self.endpoint_path = ctx.get("path") | |
| self.endpoint_file = ctx.get("file") | |
| self.endpoint_line = ctx.get("line") | |
| def errors(self) -> Sequence[Any]: | |
| return self._errors | |
| def _format_endpoint_context(self) -> str: | |
| if not (self.endpoint_file and self.endpoint_line and self.endpoint_function): | |
| if self.endpoint_path: | |
| return f"\n Endpoint: {self.endpoint_path}" | |
| return "" | |
| context = f'\n File "{self.endpoint_file}", line {self.endpoint_line}, in {self.endpoint_function}' | |
| if self.endpoint_path: | |
| context += f"\n {self.endpoint_path}" | |
| return context | |
| def __str__(self) -> str: | |
| message = f"{len(self._errors)} validation error{'s' if len(self._errors) != 1 else ''}:\n" | |
| for err in self._errors: | |
| message += f" {err}\n" | |
| message += self._format_endpoint_context() | |
| return message.rstrip() | |
| class RequestValidationError(ValidationException): | |
| def __init__( | |
| self, | |
| errors: Sequence[Any], | |
| *, | |
| body: Any = None, | |
| endpoint_ctx: Optional[EndpointContext] = None, | |
| ) -> None: | |
| super().__init__(errors, endpoint_ctx=endpoint_ctx) | |
| self.body = body | |
| class WebSocketRequestValidationError(ValidationException): | |
| def __init__( | |
| self, | |
| errors: Sequence[Any], | |
| *, | |
| endpoint_ctx: Optional[EndpointContext] = None, | |
| ) -> None: | |
| super().__init__(errors, endpoint_ctx=endpoint_ctx) | |
| class ResponseValidationError(ValidationException): | |
| def __init__( | |
| self, | |
| errors: Sequence[Any], | |
| *, | |
| body: Any = None, | |
| endpoint_ctx: Optional[EndpointContext] = None, | |
| ) -> None: | |
| super().__init__(errors, endpoint_ctx=endpoint_ctx) | |
| self.body = body | |
| class PydanticV1NotSupportedError(FastAPIError): | |
| """ | |
| A pydantic.v1 model is used, which is no longer supported. | |
| """ | |
| class FastAPIDeprecationWarning(UserWarning): | |
| """ | |
| A custom deprecation warning as DeprecationWarning is ignored | |
| Ref: https://sethmlarson.dev/deprecations-via-warnings-dont-work-for-python-libraries | |
| """ | |