From caa8406c071d048e61b63a47709051df4c96c410 Mon Sep 17 00:00:00 2001 From: Jules Lasne Date: Mon, 10 Jun 2024 13:51:21 +0200 Subject: [PATCH] Docs: Added documentation for custom errors --- README.md | 5 ++- docs/source/api.rst | 8 ++++ flask_utils/__init__.py | 2 +- flask_utils/errors/__init__.py | 26 ++++++------- flask_utils/errors/_error_template.py | 23 ++++++++++- flask_utils/errors/badrequest.py | 42 +++++++++++++++++++-- flask_utils/errors/base_class.py | 2 +- flask_utils/errors/conflict.py | 42 +++++++++++++++++++-- flask_utils/errors/failed_dependency.py | 42 +++++++++++++++++++-- flask_utils/errors/forbidden.py | 42 +++++++++++++++++++-- flask_utils/errors/gone.py | 42 +++++++++++++++++++-- flask_utils/errors/notfound.py | 42 +++++++++++++++++++-- flask_utils/errors/origin_is_unreachable.py | 42 +++++++++++++++++++-- flask_utils/errors/service_unavailable.py | 42 +++++++++++++++++++-- flask_utils/errors/unauthorized.py | 42 +++++++++++++++++++-- flask_utils/errors/unprocessableentity.py | 42 +++++++++++++++++++-- flask_utils/errors/web_server_is_down.py | 42 +++++++++++++++++++-- 17 files changed, 465 insertions(+), 63 deletions(-) diff --git a/README.md b/README.md index 68b9a32..c005aa0 100644 --- a/README.md +++ b/README.md @@ -76,9 +76,10 @@ tox -p # TODO -- [ ] Documentation -- [ ] Licence +- [ ] Move todo-list to GitHub issues - [ ] Badges - [ ] Automatic build/deployment (https://github.com/pypa/cibuildwheel) - [ ] https://github.com/PyCQA/flake8-bugbear - [ ] Versioning of docs in Read the Docs +- [ ] Refactor documentation to avoid full links in docs (have `BadRequestError` instead of `flask_utils.errors.BadRequestError`) +- [ ] Add usage examples to documentation in the Usage section diff --git a/docs/source/api.rst b/docs/source/api.rst index adb2993..9385024 100644 --- a/docs/source/api.rst +++ b/docs/source/api.rst @@ -9,6 +9,14 @@ This part of the documentation covers all the interfaces of Flask-Utils Custom exceptions ----------------- +.. warning:: For any of these errors to work, you need to register the error handlers in your Flask app. + To do this, you can call :meth:`flask_utils.errors.register_error_handlers` with your Flask app as an argument. + + .. code-block:: python + + from flask_utils import register_error_handlers + register_error_handlers(app) + .. automodule:: flask_utils.errors :members: diff --git a/flask_utils/__init__.py b/flask_utils/__init__.py index 543f898..c5fcf4f 100644 --- a/flask_utils/__init__.py +++ b/flask_utils/__init__.py @@ -1,5 +1,5 @@ # Increment versions here according to SemVer -__version__ = "0.2.4" +__version__ = "0.2.5" from flask_utils.errors import ConflictError from flask_utils.errors import ForbiddenError diff --git a/flask_utils/errors/__init__.py b/flask_utils/errors/__init__.py index 9a67938..733356b 100644 --- a/flask_utils/errors/__init__.py +++ b/flask_utils/errors/__init__.py @@ -1,7 +1,7 @@ from flask import Flask from flask import Response -from flask_utils.errors._error_template import generate_error_json +from flask_utils.errors._error_template import _generate_error_json from flask_utils.errors.badrequest import BadRequestError from flask_utils.errors.conflict import ConflictError from flask_utils.errors.failed_dependency import FailedDependencyError @@ -32,7 +32,7 @@ def generate_badrequest(error: BadRequestError) -> Response: :param error: The error body :return: Returns the response formatted """ - return generate_error_json(error, 400) + return _generate_error_json(error, 400) @application.errorhandler(ConflictError) def generate_conflict(error: ConflictError) -> Response: @@ -44,7 +44,7 @@ def generate_conflict(error: ConflictError) -> Response: :return: Returns the response formatted """ - return generate_error_json(error, 409) + return _generate_error_json(error, 409) @application.errorhandler(ForbiddenError) def generate_forbidden(error: ForbiddenError) -> Response: @@ -56,7 +56,7 @@ def generate_forbidden(error: ForbiddenError) -> Response: :return: Returns the response formatted """ - return generate_error_json(error, 403) + return _generate_error_json(error, 403) @application.errorhandler(NotFoundError) def generate_notfound(error: NotFoundError) -> Response: @@ -68,7 +68,7 @@ def generate_notfound(error: NotFoundError) -> Response: :return: Returns the response formatted """ - return generate_error_json(error, 404) + return _generate_error_json(error, 404) @application.errorhandler(UnauthorizedError) def generate_unauthorized(error: UnauthorizedError) -> Response: @@ -80,7 +80,7 @@ def generate_unauthorized(error: UnauthorizedError) -> Response: :return: Returns the response formatted """ - return generate_error_json(error, 401) + return _generate_error_json(error, 401) @application.errorhandler(OriginIsUnreachableError) def generate_origin_is_unreachable(error: OriginIsUnreachableError) -> Response: @@ -92,7 +92,7 @@ def generate_origin_is_unreachable(error: OriginIsUnreachableError) -> Response: :return: Returns the response formatted """ - return generate_error_json(error, 523) + return _generate_error_json(error, 523) @application.errorhandler(WebServerIsDownError) def generate_web_server_is_down(error: WebServerIsDownError) -> Response: @@ -104,7 +104,7 @@ def generate_web_server_is_down(error: WebServerIsDownError) -> Response: :return: Returns the response formatted """ - return generate_error_json(error, 521) + return _generate_error_json(error, 521) @application.errorhandler(FailedDependencyError) def generate_failed_dependency(error: FailedDependencyError) -> Response: @@ -116,7 +116,7 @@ def generate_failed_dependency(error: FailedDependencyError) -> Response: :return: Returns the response formatted """ - return generate_error_json(error, 424) + return _generate_error_json(error, 424) @application.errorhandler(GoneError) def generate_gone(error: GoneError) -> Response: @@ -128,7 +128,7 @@ def generate_gone(error: GoneError) -> Response: :return: Returns the response formatted """ - return generate_error_json(error, 410) + return _generate_error_json(error, 410) @application.errorhandler(UnprocessableEntityError) def generate_unprocessable_entity(error: UnprocessableEntityError) -> Response: @@ -140,7 +140,7 @@ def generate_unprocessable_entity(error: UnprocessableEntityError) -> Response: :return: Returns the response formatted """ - return generate_error_json(error, 422) + return _generate_error_json(error, 422) @application.errorhandler(ServiceUnavailableError) def generate_service_unavailable(error: ServiceUnavailableError) -> Response: @@ -152,7 +152,7 @@ def generate_service_unavailable(error: ServiceUnavailableError) -> Response: :return: Returns the response formatted """ - return generate_error_json(error, 503) + return _generate_error_json(error, 503) __all__ = [ @@ -161,7 +161,7 @@ def generate_service_unavailable(error: ServiceUnavailableError) -> Response: "ForbiddenError", "NotFoundError", "UnauthorizedError", - "generate_error_json", + "_generate_error_json", "FailedDependencyError", "WebServerIsDownError", "OriginIsUnreachableError", diff --git a/flask_utils/errors/_error_template.py b/flask_utils/errors/_error_template.py index ea9c94a..91014b7 100644 --- a/flask_utils/errors/_error_template.py +++ b/flask_utils/errors/_error_template.py @@ -1,16 +1,35 @@ from flask import jsonify from flask import Response -from flask_utils.errors.base_class import BaseFlaskException +from flask_utils.errors.base_class import _BaseFlaskException -def generate_error_json(error: BaseFlaskException, status_code: int) -> Response: +def _generate_error_json(error: _BaseFlaskException, status_code: int) -> Response: """ This function is used to generate a json of the error passed :param error: The error containing the message and solution :param status_code: The status code of the error. :return: Returns a json containing all the info + + :Example: + + .. code-block:: python + + from flask_utils.errors import _BaseFlaskException + from flask_utils.errors._error_template import _generate_error_json + + class MyError(_BaseFlaskException): + self.name = "MyError" + self.msg = msg + self.solution = solution + self.status_code = 666 + + error = MyError("This is an error", "This is the solution") + + json = _generate_error_json(error, 666) + + .. versionadded:: 0.1.0 """ success = False json = { diff --git a/flask_utils/errors/badrequest.py b/flask_utils/errors/badrequest.py index 627b828..19bc2fd 100644 --- a/flask_utils/errors/badrequest.py +++ b/flask_utils/errors/badrequest.py @@ -1,9 +1,43 @@ -from flask_utils.errors.base_class import BaseFlaskException +from flask_utils.errors.base_class import _BaseFlaskException -class BadRequestError(BaseFlaskException): - """ - This is the BadRequestError class for the Exception. +class BadRequestError(_BaseFlaskException): + """This is the BadRequestError exception class. + + When raised, it will return a 400 status code with the message and solution provided. + + :param msg: The message to be displayed in the error. + :param solution: The solution to the error. + + :Example: + + .. code-block:: python + + from flask_utils.errors import BadRequestError + + # Inside a Flask route + @app.route('/example', methods=['POST']) + def example_route(): + ... + if some_condition: + raise BadRequestError("This is a bad request error.") + + The above code would return the following JSON response from Flask: + + .. code-block:: json + + { + "success": false, + "error": { + "type": "BadRequestError", + "name": "Bad Request", + "message": "This is a bad request error.", + "solution": "Try again." + }, + "code": 400 + } + + .. versionadded:: 0.1.0 """ def __init__(self, msg: str, solution: str = "Try again.") -> None: diff --git a/flask_utils/errors/base_class.py b/flask_utils/errors/base_class.py index e5216dc..61e3050 100644 --- a/flask_utils/errors/base_class.py +++ b/flask_utils/errors/base_class.py @@ -1,7 +1,7 @@ from typing import Optional -class BaseFlaskException(Exception): +class _BaseFlaskException(Exception): name: Optional[str] = None msg: Optional[str] = None solution: str = "Try again." diff --git a/flask_utils/errors/conflict.py b/flask_utils/errors/conflict.py index 47691bb..e788e1b 100644 --- a/flask_utils/errors/conflict.py +++ b/flask_utils/errors/conflict.py @@ -1,9 +1,43 @@ -from flask_utils.errors.base_class import BaseFlaskException +from flask_utils.errors.base_class import _BaseFlaskException -class ConflictError(BaseFlaskException): - """ - This is the ConflictError class for the Exception. +class ConflictError(_BaseFlaskException): + """This is the ConflictError exception class. + + When raised, it will return a 409 status code with the message and solution provided. + + :param msg: The message to be displayed in the error. + :param solution: The solution to the error. + + :Example: + + .. code-block:: python + + from flask_utils.errors import ConflictError + + # Inside a Flask route + @app.route('/example', methods=['POST']) + def example_route(): + ... + if some_condition: + raise ConflictError("This is a conflict error.") + + The above code would return the following JSON response from Flask: + + .. code-block:: json + + { + "success": false, + "error": { + "type": "ConflictError", + "name": "Conflict", + "message": "This is a conflict error.", + "solution": "Try again." + }, + "code": 409 + } + + .. versionadded:: 0.1.0 """ def __init__(self, msg: str, solution: str = "Try again.") -> None: diff --git a/flask_utils/errors/failed_dependency.py b/flask_utils/errors/failed_dependency.py index a427b2b..d538574 100644 --- a/flask_utils/errors/failed_dependency.py +++ b/flask_utils/errors/failed_dependency.py @@ -1,9 +1,43 @@ -from flask_utils.errors.base_class import BaseFlaskException +from flask_utils.errors.base_class import _BaseFlaskException -class FailedDependencyError(BaseFlaskException): - """ - This is the FailedDependencyError class for the Exception. +class FailedDependencyError(_BaseFlaskException): + """This is the FailedDependencyError exception class. + + When raised, it will return a 424 status code with the message and solution provided. + + :param msg: The message to be displayed in the error. + :param solution: The solution to the error. + + :Example: + + .. code-block:: python + + from flask_utils.errors import FailedDependencyError + + # Inside a Flask route + @app.route('/example', methods=['POST']) + def example_route(): + ... + if some_condition: + raise FailedDependencyError("This is a failed dependency error.") + + The above code would return the following JSON response from Flask: + + .. code-block:: json + + { + "success": false, + "error": { + "type": "FailedDependencyError", + "name": "Failed Dependency", + "message": "This is a failed dependency error.", + "solution": "Try again later." + }, + "code": 424 + } + + .. versionadded:: 0.1.0 """ def __init__(self, msg: str, solution: str = "Try again later.") -> None: diff --git a/flask_utils/errors/forbidden.py b/flask_utils/errors/forbidden.py index c281dac..65ccca4 100644 --- a/flask_utils/errors/forbidden.py +++ b/flask_utils/errors/forbidden.py @@ -1,9 +1,43 @@ -from flask_utils.errors.base_class import BaseFlaskException +from flask_utils.errors.base_class import _BaseFlaskException -class ForbiddenError(BaseFlaskException): - """ - This is the ForbiddenError class for the Exception. +class ForbiddenError(_BaseFlaskException): + """This is the ForbiddenError exception class. + + When raised, it will return a 403 status code with the message and solution provided. + + :param msg: The message to be displayed in the error. + :param solution: The solution to the error. + + :Example: + + .. code-block:: python + + from flask_utils.errors import ForbiddenError + + # Inside a Flask route + @app.route('/example', methods=['POST']) + def example_route(): + ... + if some_condition: + raise ForbiddenError("This is a forbidden error.") + + The above code would return the following JSON response from Flask: + + .. code-block:: json + + { + "success": false, + "error": { + "type": "ForbiddenError", + "name": "Forbidden", + "message": "This is a forbidden error.", + "solution": "Try again." + }, + "code": 403 + } + + .. versionadded:: 0.1.0 """ def __init__(self, msg: str, solution: str = "Try again.") -> None: diff --git a/flask_utils/errors/gone.py b/flask_utils/errors/gone.py index ee0287c..1dd1ef7 100644 --- a/flask_utils/errors/gone.py +++ b/flask_utils/errors/gone.py @@ -1,9 +1,43 @@ -from flask_utils.errors.base_class import BaseFlaskException +from flask_utils.errors.base_class import _BaseFlaskException -class GoneError(BaseFlaskException): - """ - This is the GoneError class for the Exception. +class GoneError(_BaseFlaskException): + """This is the GoneError exception class. + + When raised, it will return a 410 status code with the message and solution provided. + + :param msg: The message to be displayed in the error. + :param solution: The solution to the error. + + :Example: + + .. code-block:: python + + from flask_utils.errors import GoneError + + # Inside a Flask route + @app.route('/example', methods=['POST']) + def example_route(): + ... + if some_condition: + raise GoneError("This is a gone error.") + + The above code would return the following JSON response from Flask: + + .. code-block:: json + + { + "success": false, + "error": { + "type": "GoneError", + "name": "Ressource is gone.", + "message": "This is a gone error.", + "solution": "Try again later." + }, + "code": 410 + } + + .. versionadded:: 0.1.0 """ def __init__(self, msg: str, solution: str = "Try again later.") -> None: diff --git a/flask_utils/errors/notfound.py b/flask_utils/errors/notfound.py index c75fb1b..0985163 100644 --- a/flask_utils/errors/notfound.py +++ b/flask_utils/errors/notfound.py @@ -1,9 +1,43 @@ -from flask_utils.errors.base_class import BaseFlaskException +from flask_utils.errors.base_class import _BaseFlaskException -class NotFoundError(BaseFlaskException): - """ - This is the NotFoundError class for the Exception. +class NotFoundError(_BaseFlaskException): + """This is the NotFoundError exception class. + + When raised, it will return 400 status code with the message and solution provided. + + :param msg: The message to be displayed in the error. + :param solution: The solution to the error. + + :Example: + + .. code-block:: python + + from flask_utils.errors import NotFoundError + + # Inside a Flask route + @app.route('/example', methods=['POST']) + def example_route(): + ... + if some_condition: + raise NotFoundError("This is a not found error.") + + The above code would return the following JSON response from Flask: + + .. code-block:: json + + { + "success": false, + "error": { + "type": "NotFoundError", + "name": "Not Found", + "message": "This is a not found error.", + "solution": "Try again." + }, + "code": 404 + } + + .. versionadded:: 0.1.0 """ def __init__(self, msg: str, solution: str = "Try again.") -> None: diff --git a/flask_utils/errors/origin_is_unreachable.py b/flask_utils/errors/origin_is_unreachable.py index 3dfbd5a..6c9acbf 100644 --- a/flask_utils/errors/origin_is_unreachable.py +++ b/flask_utils/errors/origin_is_unreachable.py @@ -1,9 +1,43 @@ -from flask_utils.errors.base_class import BaseFlaskException +from flask_utils.errors.base_class import _BaseFlaskException -class OriginIsUnreachableError(BaseFlaskException): - """ - This is the OriginIsUnreachableError class for the Exception. +class OriginIsUnreachableError(_BaseFlaskException): + """This is the OriginIsUnreachableError exception class. + + When raised, it will return a 523 status code with the message and solution provided. + + :param msg: The message to be displayed in the error. + :param solution: The solution to the error. + + :Example: + + .. code-block:: python + + from flask_utils.errors import OriginIsUnreachableError + + # Inside a Flask route + @app.route('/example', methods=['POST']) + def example_route(): + ... + if some_condition: + raise OriginIsUnreachableError("The origin is unreachable.") + + The above code would return the following JSON response from Flask: + + .. code-block:: json + + { + "success": false, + "error": { + "type": "OriginIsUnreachableError", + "name": "Origin is unreachable", + "message": "The origin is unreachable.", + "solution": "Try again later." + }, + "code": 523 + } + + .. versionadded:: 0.1.0 """ def __init__(self, msg: str, solution: str = "Try again later.") -> None: diff --git a/flask_utils/errors/service_unavailable.py b/flask_utils/errors/service_unavailable.py index 1d63ac3..2ddb55b 100644 --- a/flask_utils/errors/service_unavailable.py +++ b/flask_utils/errors/service_unavailable.py @@ -1,9 +1,43 @@ -from flask_utils.errors.base_class import BaseFlaskException +from flask_utils.errors.base_class import _BaseFlaskException -class ServiceUnavailableError(BaseFlaskException): - """ - This is the ServiceUnavailable class for the Exception. +class ServiceUnavailableError(_BaseFlaskException): + """This is the ServiceUnavailableError exception class. + + When raised, it will return a 503 status code with the message and solution provided. + + :param msg: The message to be displayed in the error. + :param solution: The solution to the error. + + :Example: + + .. code-block:: python + + from flask_utils.errors import ServiceUnavailableError + + # Inside a Flask route + @app.route('/example', methods=['POST']) + def example_route(): + ... + if some_condition: + raise ServiceUnavailableError("This is a service unavailable error.") + + The above code would return the following JSON response from Flask: + + .. code-block:: json + + { + "success": false, + "error": { + "type": "ServiceUnavailableError", + "name": "Service Unavailable", + "message": "This is a service unavailable error.", + "solution": "Try again later." + }, + "code": 503 + } + + .. versionadded:: 0.1.0 """ def __init__(self, msg: str, solution: str = "Try again later.") -> None: diff --git a/flask_utils/errors/unauthorized.py b/flask_utils/errors/unauthorized.py index 97aeddb..2e78d6d 100644 --- a/flask_utils/errors/unauthorized.py +++ b/flask_utils/errors/unauthorized.py @@ -1,9 +1,43 @@ -from flask_utils.errors.base_class import BaseFlaskException +from flask_utils.errors.base_class import _BaseFlaskException -class UnauthorizedError(BaseFlaskException): - """ - This is the UnauthorizedError class for the Exception. +class UnauthorizedError(_BaseFlaskException): + """This is the UnauthorizedError exception class. + + When raised, it will return a 401 status code with the message and solution provided. + + :param msg: The message to be displayed in the error. + :param solution: The solution to the error. + + :Example: + + .. code-block:: python + + from flask_utils.errors import UnauthorizedError + + # Inside a Flask route + @app.route('/example', methods=['POST']) + def example_route(): + ... + if some_condition: + raise UnauthorizedError("This is an unauthorized error.") + + The above code would return the following JSON response from Flask: + + .. code-block:: json + + { + "success": false, + "error": { + "type": "UnauthorizedError", + "name": "Unauthorized", + "message": "This is an unauthorized error.", + "solution": "Try again." + }, + "code": 401 + } + + .. versionadded:: 0.1.0 """ def __init__(self, msg: str, solution: str = "Try again.") -> None: diff --git a/flask_utils/errors/unprocessableentity.py b/flask_utils/errors/unprocessableentity.py index 4677348..38b8fd7 100644 --- a/flask_utils/errors/unprocessableentity.py +++ b/flask_utils/errors/unprocessableentity.py @@ -1,9 +1,43 @@ -from flask_utils.errors.base_class import BaseFlaskException +from flask_utils.errors.base_class import _BaseFlaskException -class UnprocessableEntityError(BaseFlaskException): - """ - This is the UnprocessableEntityError class for the Exception. +class UnprocessableEntityError(_BaseFlaskException): + """This is the UnprocessableEntityError exception class. + + When raised, it will return a 422 status code with the message and solution provided. + + :param msg: The message to be displayed in the error. + :param solution: The solution to the error. + + :Example: + + .. code-block:: python + + from flask_utils.errors import UnprocessableEntityError + + # Inside a Flask route + @app.route('/example', methods=['POST']) + def example_route(): + ... + if some_condition: + raise UnprocessableEntityError("This is an unprocessable entity error.") + + The above code would return the following JSON response from Flask: + + .. code-block:: json + + { + "success": false, + "error": { + "type": "UnprocessableEntityError", + "name": "Unprocessable Entity", + "message": "This is an unprocessable entity error.", + "solution": "Try again." + }, + "code": 422 + } + + .. versionadded:: 0.1.0 """ def __init__(self, msg: str, solution: str = "Try again.") -> None: diff --git a/flask_utils/errors/web_server_is_down.py b/flask_utils/errors/web_server_is_down.py index 92b83c3..8557ac2 100644 --- a/flask_utils/errors/web_server_is_down.py +++ b/flask_utils/errors/web_server_is_down.py @@ -1,9 +1,43 @@ -from flask_utils.errors.base_class import BaseFlaskException +from flask_utils.errors.base_class import _BaseFlaskException -class WebServerIsDownError(BaseFlaskException): - """ - This is the WebServerIsDownError class for the Exception. +class WebServerIsDownError(_BaseFlaskException): + """This is the WebServerIsDownError exception class. + + When raised, it will return a 521 status code with the message and solution provided. + + :param msg: The message to be displayed in the error. + :param solution: The solution to the error. + + :Example: + + .. code-block:: python + + from flask_utils.errors import WebServerIsDownError + + # Inside a Flask route + @app.route('/example', methods=['POST']) + def example_route(): + ... + if some_condition: + raise WebServerIsDownError("The web server is down.") + + The above code would return the following JSON response from Flask: + + .. code-block:: json + + { + "success": false, + "error": { + "type": "WebServerIsDownError", + "name": "Web Server Is Down", + "message": "The web server is down.", + "solution": "Try again later." + }, + "code": 521 + } + + .. versionadded:: 0.1.0 """ def __init__(self, msg: str, solution: str = "Try again later.") -> None: