From d0f69602680f42d802040f7caa5d74cf5b8f7074 Mon Sep 17 00:00:00 2001 From: tarsil Date: Tue, 9 Apr 2024 17:20:10 +0100 Subject: [PATCH 1/5] Fixing tests --- esmerald_admin/application.py | 10 +++++----- esmerald_admin/backends/base.py | 2 +- pyproject.toml | 3 ++- scripts/build | 1 - 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/esmerald_admin/application.py b/esmerald_admin/application.py index 50259c4..03d0aeb 100644 --- a/esmerald_admin/application.py +++ b/esmerald_admin/application.py @@ -2,13 +2,13 @@ from esmerald import Esmerald, HTTPException, Request, status from esmerald.exceptions import ImproperlyConfigured +from lilya.middleware import Middleware from sqladmin import Admin as SQLAdmin # noqa from sqladmin import ModelView as SQLAModelView # noqa from sqladmin._types import ENGINE_TYPE from sqladmin.authentication import AuthenticationBackend from sqladmin.models import BaseView as SQLAdminBaseView # noqa from sqlalchemy.orm.session import sessionmaker -from starlette.middleware import Middleware class EsmeraldBase: @@ -28,11 +28,11 @@ def has_permission(self, request: Request) -> bool: return True -class BaseView(SQLAdminBaseView, EsmeraldBase): +class BaseView(SQLAdminBaseView, EsmeraldBase): # type: ignore ... -class ModelView(SQLAModelView, BaseView): +class ModelView(SQLAModelView, BaseView): # type: ignore ... @@ -55,13 +55,13 @@ def __init__( authentication_backend: Optional[AuthenticationBackend] = None, ) -> None: super().__init__( - app=app, + app=app, # type: ignore engine=engine, session_maker=session_maker, base_url=base_url, title=title, logo_url=logo_url, - middlewares=middlewares, + middlewares=middlewares, # type: ignore debug=debug, templates_dir=templates_dir, authentication_backend=authentication_backend, diff --git a/esmerald_admin/backends/base.py b/esmerald_admin/backends/base.py index 8adf6ac..e615ac4 100644 --- a/esmerald_admin/backends/base.py +++ b/esmerald_admin/backends/base.py @@ -5,8 +5,8 @@ from esmerald.exceptions import AuthenticationError from esmerald.security.jwt.token import Token from jose import JWSError, JWTError +from lilya.responses import RedirectResponse from sqladmin.authentication import AuthenticationBackend -from starlette.responses import RedirectResponse DEFAULT_HEADER = "Bearer" diff --git a/pyproject.toml b/pyproject.toml index a84db97..8535cf1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,7 +38,7 @@ classifiers = [ "Topic :: Internet :: WWW/HTTP :: HTTP Servers", "Topic :: Internet :: WWW/HTTP", ] -dependencies = ["esmerald[jwt]>=2.2.0", "sqladmin>=0.15.2,<1.0"] +dependencies = ["esmerald[jwt]>=3.1.0", "sqladmin>=0.15.2,<1.0"] keywords = ["esmerald_admin"] [project.urls] @@ -59,6 +59,7 @@ test = [ "pytest-cov>=4.0.0,<5.0.0", "requests>=2.28.2", "ruff>=0.0.256,<1.0.0", + "httpx", "saffier[postgres,testing]>=1.0.0", "edgy[postgres,testing]>=0.5.1", ] diff --git a/scripts/build b/scripts/build index fd5b226..957d9e4 100755 --- a/scripts/build +++ b/scripts/build @@ -12,4 +12,3 @@ set -x ${PREFIX}python -m build ${PREFIX}twine check dist/* -${PREFIX}mkdocs build From c8f98ff40a12777a2a3437ea8d4add330d956d09 Mon Sep 17 00:00:00 2001 From: tarsil Date: Tue, 9 Apr 2024 17:25:37 +0100 Subject: [PATCH 2/5] Fixing tests module --- docs/authentication.md | 6 +++--- docs_src/auth/edgy.py | 4 ++-- docs_src/auth/example.py | 4 ++-- tests/test_integrations/test_edgy_authentication.py | 4 ++-- tests/test_integrations/test_saffier_authentication.py | 4 ++-- tests/test_views/test_edgy_views_async.py | 2 +- tests/test_views/test_saffier_views_async.py | 2 +- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/authentication.md b/docs/authentication.md index 0569271..bf3a715 100644 --- a/docs/authentication.md +++ b/docs/authentication.md @@ -18,13 +18,13 @@ and apply logins using `email` or `username`, your choice. * `secret_key` - The secret to be used with your auth. * `auth_model` - The class object of your `User`, usually derived from the [User model][user_model] for Saffier or [User model][user_model_edgy] for Edgy. -* `config` - The application settings object. It can be the [settings config][settings_config] or +* `config` - The application settings object. It can be the [settings config][settings_module] or the application `settings` from `esmerald.conf`. It all depends of which one you use. ### How to use This is how you could use the backends in your Esmerald application. This example is very simple -and it will use the [settings_config][settings_config] from Esmerald to simplify. This is not +and it will use the [settings_module][settings_module] from Esmerald to simplify. This is not mandatory and you can use your preferred way. === "Saffier" @@ -103,5 +103,5 @@ Like SQLAdmin, both methods implement the same signature and should return a `bo [user_model]: https://esmerald.dev/databases/saffier/models/#user [user_model_edgy]: https://esmerald.dev/databases/edgy/models/#user [sqladmin_models]: https://aminalaee.dev/sqladmin/authentication/ -[settings_config]: https://esmerald.dev/application/settings/#the-settings_config +[settings_module]: https://esmerald.dev/application/settings/#the-settings_module [permissions]: https://esmerald.dymmond.com/permissions/ diff --git a/docs_src/auth/edgy.py b/docs_src/auth/edgy.py index 46a840a..73c3828 100644 --- a/docs_src/auth/edgy.py +++ b/docs_src/auth/edgy.py @@ -23,7 +23,7 @@ class Meta: registry = registry -# You can use the `settings_config` directly or ESMERALD_SETTINGS_MODULE +# You can use the `settings_module` directly or ESMERALD_SETTINGS_MODULE settings = AppSettings() @@ -36,7 +36,7 @@ def get_application(): routes=[Include(namespace="linezap.urls")], on_startup=[database.connect], on_shutdown=[database.disconnect], - settings_config=settings, + settings_module=settings, ) # EmailAdminAuth or UsernameAdminAuth diff --git a/docs_src/auth/example.py b/docs_src/auth/example.py index eadf529..2600862 100644 --- a/docs_src/auth/example.py +++ b/docs_src/auth/example.py @@ -23,7 +23,7 @@ class Meta: registry = registry -# You can use the `settings_config` directly or ESMERALD_SETTINGS_MODULE +# You can use the `settings_module` directly or ESMERALD_SETTINGS_MODULE settings = AppSettings() @@ -36,7 +36,7 @@ def get_application(): routes=[Include(namespace="linezap.urls")], on_startup=[database.connect], on_shutdown=[database.disconnect], - settings_config=settings, + settings_module=settings, ) # EmailAdminAuth or UsernameAdminAuth diff --git a/tests/test_integrations/test_edgy_authentication.py b/tests/test_integrations/test_edgy_authentication.py index b78a109..db2d2ae 100644 --- a/tests/test_integrations/test_edgy_authentication.py +++ b/tests/test_integrations/test_edgy_authentication.py @@ -60,7 +60,7 @@ async def rollback_transactions(): def get_email_backend_admin() -> Admin: app = Esmerald( - settings_config=settings, on_startup=[database.connect], on_shutdown=[database.disconnect] + settings_module=settings, on_startup=[database.connect], on_shutdown=[database.disconnect] ) email_authentication_backend = EmailAdminAuth( @@ -74,7 +74,7 @@ def get_email_backend_admin() -> Admin: def get_username_backend_admin() -> Admin: app = Esmerald( - settings_config=settings, on_startup=[database.connect], on_shutdown=[database.disconnect] + settings_module=settings, on_startup=[database.connect], on_shutdown=[database.disconnect] ) user_authentication_backend = UsernameAdminAuth( diff --git a/tests/test_integrations/test_saffier_authentication.py b/tests/test_integrations/test_saffier_authentication.py index 80a78ea..29aa164 100644 --- a/tests/test_integrations/test_saffier_authentication.py +++ b/tests/test_integrations/test_saffier_authentication.py @@ -60,7 +60,7 @@ async def rollback_transactions(): def get_email_backend_admin() -> Admin: app = Esmerald( - settings_config=settings, on_startup=[database.connect], on_shutdown=[database.disconnect] + settings_module=settings, on_startup=[database.connect], on_shutdown=[database.disconnect] ) email_authentication_backend = EmailAdminAuth( @@ -74,7 +74,7 @@ def get_email_backend_admin() -> Admin: def get_username_backend_admin() -> Admin: app = Esmerald( - settings_config=settings, on_startup=[database.connect], on_shutdown=[database.disconnect] + settings_module=settings, on_startup=[database.connect], on_shutdown=[database.disconnect] ) user_authentication_backend = UsernameAdminAuth( diff --git a/tests/test_views/test_edgy_views_async.py b/tests/test_views/test_edgy_views_async.py index 8224ebe..195a3cd 100644 --- a/tests/test_views/test_edgy_views_async.py +++ b/tests/test_views/test_edgy_views_async.py @@ -80,7 +80,7 @@ def has_permission(self, request: Request) -> bool: settings = TestSettings() app = Esmerald( - settings_config=settings, on_startup=[database.connect], on_shutdown=[database.disconnect] + settings_module=settings, on_startup=[database.connect], on_shutdown=[database.disconnect] ) admin = Admin(app=app, engine=models.engine) diff --git a/tests/test_views/test_saffier_views_async.py b/tests/test_views/test_saffier_views_async.py index 1ba7ca8..de22cb5 100644 --- a/tests/test_views/test_saffier_views_async.py +++ b/tests/test_views/test_saffier_views_async.py @@ -80,7 +80,7 @@ def has_permission(self, request: Request) -> bool: settings = TestSettings() app = Esmerald( - settings_config=settings, on_startup=[database.connect], on_shutdown=[database.disconnect] + settings_module=settings, on_startup=[database.connect], on_shutdown=[database.disconnect] ) admin = Admin(app=app, engine=models.engine) From 3d4d9b138a89281dad0a9b5284c0d4b6f05888dc Mon Sep 17 00:00:00 2001 From: tarsil Date: Wed, 10 Apr 2024 10:28:10 +0100 Subject: [PATCH 3/5] Applying new structure --- .pre-commit-config.yaml | 21 +++---------- esmerald_admin/application.py | 55 +++++++++++++++++++++++++++++++++-- pyproject.toml | 17 +++-------- scripts/check | 2 +- scripts/lint | 2 +- 5 files changed, 62 insertions(+), 35 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0ca605d..82445ff 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,7 +4,7 @@ default_language_version: python: python3.10 repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.3.0 + rev: v4.4.0 hooks: - id: check-added-large-files - id: check-toml @@ -12,6 +12,7 @@ repos: args: - --unsafe - id: end-of-file-fixer + - id: debug-statements - id: trailing-whitespace - repo: https://github.com/asottile/pyupgrade rev: v2.37.3 @@ -21,29 +22,15 @@ repos: - --py3-plus - --keep-runtime-typing - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.0.254 + rev: v0.3.0 hooks: - id: ruff args: ["--fix", "--line-length=99"] - repo: https://github.com/psf/black - rev: 22.8.0 + rev: 24.3.0 hooks: - id: black args: ["--line-length=99"] - - repo: https://github.com/pycqa/isort - rev: 5.12.0 - hooks: - - id: isort - name: isort (python) - args: ["--project=esmerald_admin", "--line-length=99"] - - id: isort - name: isort (cython) - types: [cython] - args: ["--project=esmerald_admin", "--line-length=99"] - - id: isort - name: isort (pyi) - types: [pyi] - args: ["--project=esmerald_admin", "--line-length=99"] ci: autofix_commit_msg: 🎨 [pre-commit.ci] Auto format from pre-commit.com hooks autoupdate_commit_msg: ⬆ [pre-commit.ci] pre-commit autoupdate diff --git a/esmerald_admin/application.py b/esmerald_admin/application.py index 03d0aeb..cde9af6 100644 --- a/esmerald_admin/application.py +++ b/esmerald_admin/application.py @@ -1,8 +1,11 @@ -from typing import Optional, Sequence +from typing import Awaitable, Optional, Sequence, Union from esmerald import Esmerald, HTTPException, Request, status from esmerald.exceptions import ImproperlyConfigured from lilya.middleware import Middleware +from lilya.responses import Response +from lilya.routing import Include, Path +from lilya.staticfiles import StaticFiles from sqladmin import Admin as SQLAdmin # noqa from sqladmin import ModelView as SQLAModelView # noqa from sqladmin._types import ENGINE_TYPE @@ -61,12 +64,58 @@ def __init__( base_url=base_url, title=title, logo_url=logo_url, - middlewares=middlewares, # type: ignore - debug=debug, templates_dir=templates_dir, + middlewares=middlewares, # type: ignore authentication_backend=authentication_backend, ) + statics = StaticFiles(packages=["sqladmin"]) + + async def http_exception( + request: Request, exc: Exception + ) -> Union[Response, Awaitable[Response]]: + assert isinstance(exc, HTTPException) + context = { + "status_code": exc.status_code, + "message": exc.detail, + } + return await self.templates.TemplateResponse( # type: ignore + request, "error.html", context, status_code=exc.status_code # type: ignore + ) + + routes = [ + Include("/statics", app=statics, name="statics"), + Path("/", handler=self.index, name="index"), + Path("/{identity}/list", handler=self.list, name="list"), + Path("/{identity}/details/{pk:path}", handler=self.details, name="details"), + Path( + "/{identity}/delete", + handler=self.delete, + name="delete", + methods=["DELETE"], + ), + Path( + "/{identity}/create", + handler=self.create, + name="create", + methods=["GET", "POST"], + ), + Path( + "/{identity}/edit/{pk:path}", + handler=self.edit, + name="edit", + methods=["GET", "POST"], + ), + Path("/{identity}/export/{export_type}", handler=self.export, name="export"), + Path("/{identity}/ajax/lookup", handler=self.ajax_lookup, name="ajax_lookup"), + Path("/login", handler=self.login, name="login", methods=["GET", "POST"]), + Path("/logout", handler=self.logout, name="logout", methods=["GET"]), + ] + + self.admin.router.routes = routes # type: ignore + self.admin.exception_handlers = {HTTPException: http_exception} + self.admin.debug = debug + self.app.include(base_url, app=self.admin, name="admin") self.app.router.activate() # type: ignore def _find_model_view(self, identity: str) -> ModelView: diff --git a/pyproject.toml b/pyproject.toml index 8535cf1..eaee053 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -103,19 +103,10 @@ disallow_untyped_defs = true ignore_missing_imports = true no_implicit_optional = true -[tool.ruff] -select = [ - "E", # pycodestyle errors - "W", # pycodestyle warnings - "F", # pyflakes - "C", # flake8-comprehensions - "B", # flake8-bugbear -] -ignore = [ - "E501", # line too long, handled by black - "B008", # do not perform function calls in argument defaults - "C901", # too complex -] +[tool.ruff.lint] +select = ["E", "W", "F", "C", "B", "I"] +ignore = ["E501", "B008", "C901", "B026"] + [[tool.mypy.overrides]] module = "esmerald_admin.tests.*" diff --git a/scripts/check b/scripts/check index 8507c26..718f8aa 100755 --- a/scripts/check +++ b/scripts/check @@ -12,7 +12,7 @@ export MAIN="esmerald_admin" set -x -${PREFIX}mypy $MAIN +${PREFIX}mypy check $MAIN ${PREFIX}ruff $SOURCE_FILES --line-length 99 ${PREFIX}black $SOURCE_FILES --check --diff --check --line-length 99 ${PREFIX}isort $SOURCE_FILES --check --diff --project=esmerald_admin --line-length 99 diff --git a/scripts/lint b/scripts/lint index b30dfbc..7f207e3 100755 --- a/scripts/lint +++ b/scripts/lint @@ -10,7 +10,7 @@ fi export SOURCE_FILES="esmerald_admin tests" set -x -${PREFIX}ruff $SOURCE_FILES --fix --line-length 99 +${PREFIX}ruff check $SOURCE_FILES --fix --line-length 99 ${PREFIX}black $SOURCE_FILES --line-length 99 ${PREFIX}isort $SOURCE_FILES --project=esmerald_admin --line-length 99 ${PREFIX}mypy esmerald_admin From 751a12c69e6c4bd6856fe9f0914c562b3af581b2 Mon Sep 17 00:00:00 2001 From: tarsil Date: Wed, 10 Apr 2024 10:31:41 +0100 Subject: [PATCH 4/5] Applying new structure with ruff --- scripts/check | 7 +++---- scripts/lint | 1 - tests/test_integrations/test_edgy_authentication.py | 2 +- tests/test_integrations/test_saffier_authentication.py | 2 +- tests/test_views/test_edgy_views_async.py | 2 +- tests/test_views/test_saffier_views_async.py | 2 +- 6 files changed, 7 insertions(+), 9 deletions(-) diff --git a/scripts/check b/scripts/check index 718f8aa..3535592 100755 --- a/scripts/check +++ b/scripts/check @@ -12,7 +12,6 @@ export MAIN="esmerald_admin" set -x -${PREFIX}mypy check $MAIN -${PREFIX}ruff $SOURCE_FILES --line-length 99 -${PREFIX}black $SOURCE_FILES --check --diff --check --line-length 99 -${PREFIX}isort $SOURCE_FILES --check --diff --project=esmerald_admin --line-length 99 +${PREFIX}ruff check $SOURCE_FILES --fix --line-length 99 +${PREFIX}black $SOURCE_FILES --line-length 99 +${PREFIX}mypy esmerald_admin diff --git a/scripts/lint b/scripts/lint index 7f207e3..61a45d5 100755 --- a/scripts/lint +++ b/scripts/lint @@ -12,5 +12,4 @@ set -x ${PREFIX}ruff check $SOURCE_FILES --fix --line-length 99 ${PREFIX}black $SOURCE_FILES --line-length 99 -${PREFIX}isort $SOURCE_FILES --project=esmerald_admin --line-length 99 ${PREFIX}mypy esmerald_admin diff --git a/tests/test_integrations/test_edgy_authentication.py b/tests/test_integrations/test_edgy_authentication.py index db2d2ae..f9bc6a2 100644 --- a/tests/test_integrations/test_edgy_authentication.py +++ b/tests/test_integrations/test_edgy_authentication.py @@ -9,10 +9,10 @@ from esmerald.contrib.auth.edgy.base_user import AbstractUser from esmerald.testclient import EsmeraldTestClient from httpx import AsyncClient -from tests.settings import DATABASE_URL from esmerald_admin import Admin from esmerald_admin.backends.edgy import EmailAdminAuth, UsernameAdminAuth +from tests.settings import DATABASE_URL pytestmark = pytest.mark.anyio diff --git a/tests/test_integrations/test_saffier_authentication.py b/tests/test_integrations/test_saffier_authentication.py index 29aa164..d35800a 100644 --- a/tests/test_integrations/test_saffier_authentication.py +++ b/tests/test_integrations/test_saffier_authentication.py @@ -9,10 +9,10 @@ from esmerald.testclient import EsmeraldTestClient from httpx import AsyncClient from saffier.testclient import DatabaseTestClient as Database -from tests.settings import DATABASE_URL from esmerald_admin import Admin from esmerald_admin.backends.saffier import EmailAdminAuth, UsernameAdminAuth +from tests.settings import DATABASE_URL pytestmark = pytest.mark.anyio diff --git a/tests/test_views/test_edgy_views_async.py b/tests/test_views/test_edgy_views_async.py index 195a3cd..e50e944 100644 --- a/tests/test_views/test_edgy_views_async.py +++ b/tests/test_views/test_edgy_views_async.py @@ -8,9 +8,9 @@ from esmerald.config.jwt import JWTConfig from esmerald.contrib.auth.edgy.base_user import AbstractUser from httpx import AsyncClient -from tests.settings import DATABASE_URL from esmerald_admin import Admin, ModelView +from tests.settings import DATABASE_URL pytestmark = pytest.mark.anyio diff --git a/tests/test_views/test_saffier_views_async.py b/tests/test_views/test_saffier_views_async.py index de22cb5..91cb216 100644 --- a/tests/test_views/test_saffier_views_async.py +++ b/tests/test_views/test_saffier_views_async.py @@ -8,9 +8,9 @@ from esmerald.contrib.auth.saffier.base_user import AbstractUser from httpx import AsyncClient from saffier.testclient import DatabaseTestClient as Database -from tests.settings import DATABASE_URL from esmerald_admin import Admin, ModelView +from tests.settings import DATABASE_URL pytestmark = pytest.mark.anyio From d229455dc92d1b9fd0891713cae81f1529b55b25 Mon Sep 17 00:00:00 2001 From: tarsil Date: Wed, 10 Apr 2024 10:35:03 +0100 Subject: [PATCH 5/5] Update application base --- esmerald_admin/application.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/esmerald_admin/application.py b/esmerald_admin/application.py index cde9af6..8341f35 100644 --- a/esmerald_admin/application.py +++ b/esmerald_admin/application.py @@ -2,6 +2,7 @@ from esmerald import Esmerald, HTTPException, Request, status from esmerald.exceptions import ImproperlyConfigured +from lilya.apps import Lilya from lilya.middleware import Middleware from lilya.responses import Response from lilya.routing import Include, Path @@ -9,6 +10,7 @@ from sqladmin import Admin as SQLAdmin # noqa from sqladmin import ModelView as SQLAModelView # noqa from sqladmin._types import ENGINE_TYPE +from sqladmin.application import BaseAdminView from sqladmin.authentication import AuthenticationBackend from sqladmin.models import BaseView as SQLAdminBaseView # noqa from sqlalchemy.orm.session import sessionmaker @@ -39,7 +41,7 @@ class ModelView(SQLAModelView, BaseView): # type: ignore ... -class Admin(SQLAdmin): +class Admin(SQLAdmin, BaseAdminView): """ Tha base inherited for Saffier which inherits from the base of sqladmin package. """ @@ -57,7 +59,7 @@ def __init__( templates_dir: str = "templates", authentication_backend: Optional[AuthenticationBackend] = None, ) -> None: - super().__init__( + super(BaseAdminView, self).__init__( app=app, # type: ignore engine=engine, session_maker=session_maker, @@ -112,7 +114,7 @@ async def http_exception( Path("/logout", handler=self.logout, name="logout", methods=["GET"]), ] - self.admin.router.routes = routes # type: ignore + self.admin = Lilya(middleware=middlewares, routes=routes) # type: ignore self.admin.exception_handlers = {HTTPException: http_exception} self.admin.debug = debug self.app.include(base_url, app=self.admin, name="admin")