Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Core: Flask-Utils is now an extension #26

Merged
merged 1 commit into from
Jun 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
flask>=2.2.0
Pallets-Sphinx-Themes
sphinx==7.1.2
sphinx-notfound-page
sphinx-rtd-theme==1.3.0rc1
8 changes: 8 additions & 0 deletions docs/source/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ API

This part of the documentation covers all the interfaces of Flask-Utils

Extension
---------

.. automodule:: flask_utils.extension
:members:

Custom exceptions
-----------------

Expand Down Expand Up @@ -42,3 +48,5 @@ Private API
.. autofunction:: flask_utils.decorators._check_type

.. autofunction:: flask_utils.errors._error_template._generate_error_json

.. autofunction:: flask_utils.errors._register_error_handlers
3 changes: 2 additions & 1 deletion docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"sphinx.ext.intersphinx",
"sphinx.ext.viewcode",
"notfound.extension",
"pallets_sphinx_themes",
]

autodoc_default_options = {
Expand All @@ -43,7 +44,7 @@

# -- Options for HTML output

html_theme = "sphinx_rtd_theme"
html_theme = "flask"

# -- Options for EPUB output
epub_show_urls = "footnote"
7 changes: 3 additions & 4 deletions flask_utils/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Increment versions here according to SemVer
__version__ = "0.4.0"
__version__ = "0.5.0"

from flask_utils.errors import ConflictError
from flask_utils.errors import ForbiddenError
Expand All @@ -14,18 +14,16 @@
from flask_utils.errors import ServiceUnavailableError

from flask_utils.decorators import validate_params

from flask_utils.utils import is_it_true

from flask_utils.errors import register_error_handlers
from flask_utils.extension import FlaskUtils

__all__ = [
"ConflictError",
"ForbiddenError",
"UnauthorizedError",
"NotFoundError",
"BadRequestError",
"register_error_handlers",
"FailedDependencyError",
"OriginIsUnreachableError",
"WebServerIsDownError",
Expand All @@ -34,4 +32,5 @@
"ServiceUnavailableError",
"validate_params",
"is_it_true",
"FlaskUtils",
]
4 changes: 2 additions & 2 deletions flask_utils/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@

from flask_utils.errors import BadRequestError

# TODO: Turn flask-utils into a class that registers the app (like Flask-Cors for example)
# and the error handlers optionally, that way we can either use BadRequestError or just return a 400
# TODO: Change validate_params to either use BadRequestError or just return a 400 depending
# on if the error handler is registered or not in the FlaskUtils class

VALIDATE_PARAMS_MAX_DEPTH = 4

Expand Down
24 changes: 22 additions & 2 deletions flask_utils/errors/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,32 @@
from flask_utils.errors.web_server_is_down import WebServerIsDownError


def register_error_handlers(application: Flask) -> None:
def _register_error_handlers(application: Flask) -> None:
"""
This function will register all the error handlers for the application

:param application: The Flask application to register the error handlers
:return: None

.. versionchanged:: 0.5.0
Made the function private. If you want to register the custom error handlers, you need to
pass `register_error_handlers=True` to the :class:`flask_utils.extension.FlaskUtils` class
or to :meth:`flask_utils.extension.FlaskUtils.init_app`

.. code-block:: python

from flask import Flask
from flask_utils import FlaskUtils

app = Flask(__name__)
utils = FlaskUtils(app)

# OR

utils = FlaskUtils()
utils.init_app(app)

.. versionadded:: 0.1.0
"""

@application.errorhandler(BadRequestError)
Expand Down Expand Up @@ -168,5 +188,5 @@ def generate_service_unavailable(error: ServiceUnavailableError) -> Response:
"GoneError",
"UnprocessableEntityError",
"ServiceUnavailableError",
"register_error_handlers",
"_register_error_handlers",
]
69 changes: 69 additions & 0 deletions flask_utils/extension.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
from typing import Optional

from flask import Flask

from flask_utils.errors import _register_error_handlers


class FlaskUtils(object):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove redundant inheritance from object.

- class FlaskUtils(object):
+ class FlaskUtils:
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
class FlaskUtils(object):
class FlaskUtils:
Tools
Ruff

8-8: Class FlaskUtils inherits from object (UP004)

Remove object inheritance

"""
FlaskUtils extension class.

This class currently optionally register the custom error handlers found in :mod:`flask_utils.errors`.
Call :meth:`init_app` to configure the extension on an application.

:param app: Flask application instance.
:param register_error_handlers: Register the custom error handlers. Default is True.

:Example:

.. code-block:: python

from flask import Flask
from flask_utils import FlaskUtils

app = Flask(__name__)
fu = FlaskUtils(app)

# or

fu = FlaskUtils()
fu.init_app(app)

.. versionadded:: 0.5.0
"""

def __init__(self, app: Optional[Flask] = None, register_error_handlers: bool = True):
if app is not None:
self.init_app(app, register_error_handlers)

def init_app(self, app: Flask, register_error_handlers: bool = True):
"""Initialize a Flask application for use with this extension instance. This
must be called before any request is handled by the application.

If the app is created with the factory pattern, this should be called after the app
is created to configure the extension.

If `register_error_handlers` is True, the custom error handlers will be registered and
can then be used in routes to raise errors.

:param app: The Flask application to initialize.
:param register_error_handlers: Register the custom error handlers. Default is True.

:Example:

.. code-block:: python

from flask import Flask
from flask_utils import FlaskUtils

app = Flask(__name__)
fu = FlaskUtils()
fu.init_app(app)

.. versionadded:: 0.5.0
"""
if register_error_handlers:
_register_error_handlers(app)

app.extensions["flask_utils"] = self
4 changes: 2 additions & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import pytest
from flask import Flask

from flask_utils import register_error_handlers # Adjust import according to your package structure
from flask_utils import FlaskUtils # Adjust import according to your package structure


@pytest.fixture
def flask_client():
app = Flask(__name__)
register_error_handlers(app)
FlaskUtils(app)
return app


Expand Down
File renamed without changes.
49 changes: 49 additions & 0 deletions tests/test_extension.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
from flask import Flask

from flask_utils import BadRequestError
from flask_utils import FlaskUtils


class TestExtension:
def test_init_app(self):
app = Flask(__name__)
assert "flask_utils" not in app.extensions
fu = FlaskUtils()

fu.init_app(app)
assert "flask_utils" in app.extensions

def test_normal_instantiation(self):
app = Flask(__name__)

assert "flask_utils" not in app.extensions

FlaskUtils(app)

assert "flask_utils" in app.extensions

def test_error_handlers_not_registered(self):
app = Flask(__name__)

FlaskUtils(app, register_error_handlers=False)

@app.route("/")
def index():
raise BadRequestError("Bad Request")

with app.test_client() as client:
response = client.get("/")
assert response.status_code == 500

def test_error_handlers_registered(self):
app = Flask(__name__)

FlaskUtils(app, register_error_handlers=True)

@app.route("/")
def index():
raise BadRequestError("Bad Request")

with app.test_client() as client:
response = client.get("/")
assert response.status_code == 400
Loading