Source code for flask_jsonrpc.exceptions

# Copyright (c) 2020-2025, Cenobit Technologies, Inc. http://cenobit.es/
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
#    this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
#    this list of conditions and the following disclaimer in the documentation
#    and/or other materials provided with the distribution.
# * Neither the name of the Cenobit Technologies nor the names of
#    its contributors may be used to endorse or promote products derived from
#    this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import annotations

import sys
import typing as t
import traceback

# Added in version 3.11.
from typing_extensions import Self

from flask import current_app

try:
    from flaskext.babel import gettext as _  # type: ignore

    _("You're lazy...")  # this function lazy-loads settings (pragma: no cover)
except (ImportError, NameError):
    _ = lambda t, *a, **k: t  # noqa: E731


[docs] class JSONRPCError(Exception): """Error class based on the `JSON-RPC 2.0 specs`_. Args: message (str | None, optional): Error message. Defaults to `None`. code (int | None, optional): Error code. Defaults to `0`. data (typing.Any | None, optional): Additional error data. Defaults to `None`. status_code (int | None, optional): HTTP status code. Defaults to `400`. Examples: >>> error = JSONRPCError( ... message='An error occurred', code=1234, data={'info': 'details'} ... ) >>> assert error.jsonrpc_format == { ... 'name': 'JSONRPCError', ... 'code': 1234, ... 'message': 'An error occurred', ... 'data': {'info': 'details'}, ... } .. _JSON-RPC 2.0 specs: https://www.jsonrpc.org/specification .. _JSON-RPC Errors: https://www.jsonrpc.org/specification_v1#a2.2JSON-RPCoverHTTP """ message: str | None = None code: int = 0 data: t.Any | None = None # noqa: ANN401 status_code: int = 400 def __init__( self: Self, message: str | None = None, code: int | None = None, data: t.Any | None = None, # noqa: ANN401 status_code: int | None = None, ) -> None: """Setup the Exception and overwrite the default message""" super().__init__(message) self.message = message or self.message self.code = code or self.code self.data = data or self.data self.status_code = status_code or self.status_code @property def jsonrpc_format(self: Self) -> dict[str, t.Any]: """Return the Exception data in a format for JSON-RPC Returns: dict[str, typing.Any]: The error data formatted for JSON-RPC Examples: >>> error = JSONRPCError( ... message='An error occurred', code=1234, data={'info': 'details'} ... ) >>> assert error.jsonrpc_format == { ... 'name': 'JSONRPCError', ... 'code': 1234, ... 'message': 'An error occurred', ... 'data': {'info': 'details'}, ... } """ error = {'name': self.__class__.__name__, 'code': self.code, 'message': self.message, 'data': self.data} # RuntimeError: Working outside of application context. # This typically means that you attempted to use functionality that needed # to interface with the current application object in some way. To solve # this, set up an application context with app.app_context(). See the # documentation for more information. if current_app and current_app.config['DEBUG']: # pragma: no cover error['stack'] = traceback.format_exc() error['executable'] = sys.executable return error
# The error codes from and including -32768 to -32000 are reserved for # pre-defined errors. Any code within this range, but not defined explicitly # below is reserved for future use. The error codes are nearly the same as # those suggested for XML-RPC at the following url: # http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php
[docs] class ParseError(JSONRPCError): """Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text. """ def __init__( self: Self, message: str | None = _('Parse error'), code: int | None = -32700, data: t.Any | None = None, # noqa: ANN401 status_code: int | None = 400, ) -> None: super().__init__(message, code, data, status_code)
[docs] class InvalidRequestError(JSONRPCError): """The JSON sent is not a valid Request object.""" def __init__( self: Self, message: str | None = _('Invalid Request'), code: int | None = -32600, data: t.Any | None = None, # noqa: ANN401 status_code: int | None = 400, ) -> None: super().__init__(message, code, data, status_code)
[docs] class MethodNotFoundError(JSONRPCError): """The method does not exist / is not available.""" def __init__( self: Self, message: str | None = _('Method not found'), code: int | None = -32601, data: t.Any | None = None, # noqa: ANN401 status_code: int | None = 400, ) -> None: super().__init__(message, code, data, status_code)
[docs] class InvalidParamsError(JSONRPCError): """Invalid method parameter(s).""" def __init__( self: Self, message: str | None = _('Invalid params'), code: int | None = -32602, data: t.Any | None = None, # noqa: ANN401 status_code: int | None = 400, ) -> None: super().__init__(message, code, data, status_code)
[docs] class InternalError(JSONRPCError): """Internal JSON-RPC error.""" def __init__( self: Self, message: str | None = _('Internal error'), code: int | None = -32603, data: t.Any | None = None, # noqa: ANN401 status_code: int | None = 400, ) -> None: super().__init__(message, code, data, status_code)
[docs] class ServerError(JSONRPCError): """Reserved for implementation-defined server-errors. Args: message (str | None, optional): Error message. Defaults to 'Server error'. code (int | None, optional): Error code. Defaults to -32000. data (typing.Any | None, optional): Additional error data. Defaults to `None`. status_code (int | None, optional): HTTP status code. Defaults to `500`. original_exception (BaseException | None, optional): The original exception that caused this error. Defaults to `None`. Notes: code: -32000 to -32099 Server error. Examples: >>> try: ... 1 / 0 ... except ZeroDivisionError as e: ... error = ServerError(original_exception=e) >>> assert error.jsonrpc_format == { ... 'name': 'ServerError', ... 'code': -32000, ... 'message': 'Server error', ... 'data': None, ... } >>> assert isinstance(error.original_exception, ZeroDivisionError) """ def __init__( self: Self, message: str | None = _('Server error'), code: int | None = -32000, data: t.Any | None = None, # noqa: ANN401 status_code: int | None = 500, original_exception: BaseException | None = None, ) -> None: # The original exception that caused this 500 error. Can be # used by frameworks to provide context when handling # unexpected errors. self.original_exception = original_exception super().__init__(message, code, data, status_code)