From d3b256803d184b487e6c7d70195e18cc4e756f05 Mon Sep 17 00:00:00 2001 From: Tiago Silva Date: Mon, 25 Mar 2024 10:22:24 +0000 Subject: [PATCH] V3 (#277) * Merge v3 into main and support for Lilya. --- .pre-commit-config.yaml | 2 +- README.md | 6 +- docs/application/applications.md | 4 +- docs/application/settings.md | 13 +- docs/background-tasks.md | 13 +- docs/configurations/staticfiles.md | 2 +- docs/datastructures.md | 8 +- docs/external.md | 12 +- docs/lifespan-events.md | 29 +- docs/middleware/middleware.md | 44 +- docs/permissions.md | 5 +- docs/references/background.md | 2 +- docs/references/status-codes.md | 4 +- docs/references/test-client.md | 2 +- docs/release-notes.md | 43 +- docs/requests.md | 6 +- docs/responses.md | 10 +- docs/routing/handlers.md | 2 +- docs/routing/router.md | 6 +- docs/routing/routes.md | 28 +- docs/sponsorship.md | 2 + docs/testclient.md | 4 +- docs/vendors.md | 4 +- docs/wsgi.md | 5 +- docs_src/_shared/databases_important_note.md | 4 +- .../app/permissions_and_middlewares.py | 12 +- .../configurations/jwt/claims/middleware.py | 12 +- docs_src/configurations/jwt/example1.py | 6 +- docs_src/configurations/jwt/settings.py | 7 +- docs_src/databases/edgy/example/assemble.py | 7 +- docs_src/databases/edgy/example/home.py | 2 +- docs_src/databases/edgy/jwt/settings.py | 7 +- .../databases/edgy/middleware/example1.py | 7 +- .../databases/edgy/middleware/example2.py | 5 +- .../databases/edgy/middleware/example3.py | 5 +- docs_src/databases/mongoz/example/assemble.py | 7 +- docs_src/databases/mongoz/example/home.py | 2 +- docs_src/databases/mongoz/jwt/settings.py | 7 +- .../databases/mongoz/middleware/example1.py | 7 +- .../databases/mongoz/middleware/example2.py | 5 +- .../databases/mongoz/middleware/example3.py | 5 +- .../databases/saffier/example/assemble.py | 7 +- docs_src/databases/saffier/example/home.py | 2 +- docs_src/databases/saffier/jwt/settings.py | 7 +- .../databases/saffier/middleware/example1.py | 7 +- .../databases/saffier/middleware/example2.py | 5 +- .../databases/saffier/middleware/example3.py | 5 +- docs_src/exception_handlers/precedent.py | 2 +- docs_src/exceptions/custom_exception.py | 2 +- docs_src/exceptions/overriding.py | 12 +- docs_src/interceptors/cookie_interceptor.py | 3 +- docs_src/interceptors/custom/logging.py | 3 +- docs_src/interceptors/logging.py | 2 +- docs_src/interceptors/request_interceptor.py | 3 +- .../middleware/auth_middleware_example.py | 9 +- docs_src/middleware/available/cors.py | 5 +- docs_src/middleware/available/csrf.py | 5 +- docs_src/middleware/available/gzip.py | 5 +- docs_src/middleware/available/https.py | 7 +- .../available/request_settings_middleware.py | 5 +- docs_src/middleware/available/sessions.py | 5 +- .../middleware/available/trusted_hosts.py | 5 +- docs_src/middleware/settings.py | 5 +- docs_src/middleware/starlette_middleware.py | 7 +- docs_src/routing/handlers/websocket.py | 2 +- docs_src/routing/routes/converter_example.py | 6 +- docs_src/routing/routes/middleware.py | 12 +- docs_src/settings/custom/base.py | 5 +- docs_src/testclient/example1.py | 3 +- docs_src/testclient/example2.py | 3 +- docs_src/testclient/example3.py | 3 +- docs_src/testclient/example4.py | 2 +- esmerald/__init__.py | 4 +- esmerald/applications.py | 70 +-- esmerald/backgound.py | 13 +- esmerald/conf/__init__.py | 3 +- esmerald/conf/enums.py | 13 +- esmerald/conf/global_settings.py | 10 +- esmerald/config/csrf.py | 8 +- esmerald/config/static_files.py | 7 +- esmerald/context.py | 6 +- esmerald/contrib/auth/common/middleware.py | 8 +- esmerald/contrib/auth/edgy/middleware.py | 2 +- esmerald/contrib/auth/hashers.py | 2 +- esmerald/contrib/auth/mongoz/middleware.py | 2 +- esmerald/contrib/auth/saffier/middleware.py | 2 +- esmerald/core/di/provider.py | 3 +- esmerald/core/directives/base.py | 60 +-- esmerald/core/directives/exceptions.py | 7 +- esmerald/core/directives/operations/run.py | 4 +- .../core/directives/operations/shell/base.py | 40 +- .../core/directives/operations/shell/utils.py | 2 +- .../core/directives/operations/show_urls.py | 6 +- esmerald/core/directives/parsers.py | 10 +- esmerald/datastructures/__init__.py | 10 +- esmerald/datastructures/base.py | 61 +-- esmerald/datastructures/file.py | 4 +- esmerald/datastructures/redirect.py | 2 +- esmerald/datastructures/stream.py | 2 +- esmerald/enums.py | 20 +- esmerald/exception_handlers.py | 18 +- esmerald/exceptions.py | 18 +- esmerald/interceptors/interceptor.py | 10 +- esmerald/middleware/__init__.py | 2 - esmerald/middleware/_exception_handlers.py | 20 +- esmerald/middleware/asyncexitstack.py | 2 +- esmerald/middleware/authentication.py | 10 +- esmerald/middleware/basic.py | 22 - esmerald/middleware/cors.py | 2 +- esmerald/middleware/csrf.py | 150 +----- esmerald/middleware/errors.py | 39 +- esmerald/middleware/exceptions.py | 32 +- esmerald/middleware/gzip.py | 2 +- esmerald/middleware/https.py | 2 +- esmerald/middleware/sessions.py | 2 +- esmerald/middleware/settings_middleware.py | 2 +- esmerald/middleware/trustedhost.py | 2 +- esmerald/openapi/docs.py | 2 +- esmerald/openapi/openapi.py | 22 +- esmerald/parsers.py | 10 +- esmerald/permissions/base.py | 10 +- esmerald/protocols/extension.py | 2 +- esmerald/protocols/interceptor.py | 6 +- esmerald/protocols/middleware.py | 2 +- esmerald/protocols/template.py | 2 +- esmerald/requests.py | 19 +- esmerald/responses/__init__.py | 11 +- esmerald/responses/base.py | 22 +- esmerald/responses/encoders.py | 6 +- esmerald/responses/template.py | 2 +- esmerald/routing/apis/_mixins.py | 10 +- esmerald/routing/apis/base.py | 13 +- esmerald/routing/base.py | 56 ++- esmerald/routing/events.py | 14 +- esmerald/routing/gateways.py | 99 ++-- esmerald/routing/handlers.py | 60 +-- esmerald/routing/router.py | 203 ++++---- esmerald/routing/webhooks/handlers.py | 56 +-- esmerald/staticfiles.py | 2 +- esmerald/template/jinja.py | 4 +- esmerald/testclient.py | 2 +- esmerald/transformers/utils.py | 2 +- esmerald/types.py | 8 +- esmerald/utils/constants.py | 2 +- esmerald/utils/crypto.py | 23 +- esmerald/utils/helpers.py | 2 +- esmerald/utils/module_loading.py | 23 +- esmerald/utils/sync.py | 19 +- esmerald/websockets.py | 8 +- mkdocs.yml | 2 +- pyproject.toml | 9 +- scripts/{enforce => write} | 0 tests/app_settings/test_settings.py | 12 +- tests/applications/test_applications.py | 85 ++-- tests/applications/test_events.py | 35 +- tests/databases/edgy/test_middleware_data.py | 10 +- .../databases/edgy/test_middleware_payload.py | 10 +- .../databases/mongoz/test_middleware_data.py | 10 +- .../test_middleware_payload_on_gateway.py | 12 +- .../test_middleware_payload_on_handler.py | 10 +- .../databases/saffier/test_middleware_data.py | 10 +- .../saffier/test_middleware_payload.py | 10 +- .../test_http_handler_dependency_injection.py | 2 +- .../test_injection_of_generic_models.py | 2 +- tests/dependencies/test_inter_dependencies.py | 2 +- ..._websocket_handler_dependency_injection.py | 2 +- .../test_exception_handlers.py | 15 +- tests/handlers/test_defaults.py | 2 +- tests/handlers/test_handlers.py | 0 tests/handlers/test_http_and_route.py | 2 +- tests/handlers/test_http_defaults.py | 2 +- tests/handlers/test_to_response_data.py | 38 +- tests/interceptors/test_interceptors.py | 2 +- .../interceptors/test_interceptors_payload.py | 2 +- tests/middleware/complex/test_complex.py | 2 +- tests/middleware/complex/test_with_other.py | 6 +- tests/middleware/test_basic.py | 15 - tests/middleware/test_cors.py | 110 +++-- tests/middleware/test_csrf.py | 4 +- .../test_exception_handler_middleware.py | 8 +- tests/middleware/test_http_redirect.py | 10 +- .../test_middleware_handling_data.py | 432 +----------------- .../test_middleware_handling_payload.py | 2 +- tests/middleware/test_session_middleware.py | 2 +- tests/middleware/test_settings_middleware.py | 4 +- tests/middleware/test_trusted_host.py | 20 +- tests/msgspec/test_msgspec.py | 2 +- .../test_openapi_include_middleware.py | 2 +- tests/permissions/test_permissions.py | 4 +- tests/permissions/test_permissions_async.py | 4 +- ...p_starlette.py => test_base_http_lilya.py} | 16 +- .../requests/test_base_websocket_starlette.py | 8 +- tests/requests/test_request.py | 2 +- tests/requests/test_state_from_middleware.py | 2 +- tests/requests/test_websocket.py | 4 +- tests/routing/test_include_errors.py | 2 +- tests/routing/test_routing.py | 140 +++--- tests/test_apiviews.py | 2 +- tests/test_child_esmerald.py | 2 +- tests/test_injects.py | 2 +- tests/test_responses.py | 2 +- tests/test_router.py | 2 +- tests/test_routes.py | 2 +- tests/test_static_files.py | 28 -- tests/utils/test_module_loading.py | 2 +- tests/utils/test_sync.py | 8 +- tests/utils/test_url.py | 2 +- 207 files changed, 1060 insertions(+), 2034 deletions(-) delete mode 100644 esmerald/middleware/basic.py rename scripts/{enforce => write} (100%) delete mode 100644 tests/handlers/test_handlers.py delete mode 100644 tests/middleware/test_basic.py rename tests/requests/{test_base_http_starlette.py => test_base_http_lilya.py} (97%) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 22a3de2b..82445ff3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -27,7 +27,7 @@ repos: - id: ruff args: ["--fix", "--line-length=99"] - repo: https://github.com/psf/black - rev: 24.2.0 + rev: 24.3.0 hooks: - id: black args: ["--line-length=99"] diff --git a/README.md b/README.md index c6cba900..b1eaa09e 100644 --- a/README.md +++ b/README.md @@ -35,8 +35,8 @@ Esmerald is a modern, powerful, flexible, high performant, web framework designed to build not only APIs but also full scalable applications from the smallest to enterprise level. -Esmerald is designed to build with python 3.8+ and based on standard python type hints and on the top of -the heavily known [Starlette](https://github.com/encode/starlette) and [Pydantic](https://github.com/samuelcolvin/pydantic)/[msgspec](https://jcristharif.com/msgspec/). +Esmerald is designed to build with python 3.8+ and based on standard python type hints. Initially +built on the top of [Starlette](https://github.com/encode/starlette) and later on moved to [Lilya](https://lilya.dev) and [Pydantic](https://github.com/samuelcolvin/pydantic)/[msgspec](https://jcristharif.com/msgspec/). Check out the [Esmerald documentation 📚](https://esmerald.dev) @@ -62,7 +62,7 @@ For a job to be done properly, usually it is never done alone and there is alway Esmerald wouldn't be possible without two colossals: -* Starlette +* Starlette * Pydantic ## Installation diff --git a/docs/application/applications.md b/docs/application/applications.md index e63df353..4d55f024 100644 --- a/docs/application/applications.md +++ b/docs/application/applications.md @@ -1,6 +1,6 @@ # Applications -Esmerald runs Starlette under the hood and therefore includes an application class **Esmerald** that ties +Esmerald runs Lilya under the hood and therefore includes an application class **Esmerald** that ties of its functionality. ## The Esmerald class @@ -79,7 +79,7 @@ requests (HTTP and Websockets). * **middleware** - A list of middleware to run for every request. A Esmerald application will always include the middlewares from the configurations passed (CSRF, CORS, JWT...) and the custom user middleware. The middlewares can be subclasses of the [MiddlewareProtocol](../protocols.md). -or Starlette Middleware as they are both converted +or Lilya Middleware as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). * **dependencies** - A dictionary of string and [Inject](.././dependencies.md) instances enable application level dependency injection. diff --git a/docs/application/settings.md b/docs/application/settings.md index 1f44f397..d94659b2 100644 --- a/docs/application/settings.md +++ b/docs/application/settings.md @@ -293,8 +293,9 @@ Passing parameters in the object will always override the values from the defaul ```python from esmerald import EsmeraldAPISettings from esmerald.conf.enums import EnvironmentType -from esmerald.middleware.basic import BasicHTTPMiddleware +from esmerald.middleware.https import HTTPSRedirectMiddleware from esmerald.types import Middleware +from lilya.middleware import DefineMiddleware class AppSettings(EsmeraldAPISettings): @@ -302,16 +303,16 @@ class AppSettings(EsmeraldAPISettings): @property def middleware(self) -> List[Middleware]: - return [BasicHTTPMiddleware] + return [DefineMiddleware(HTTPSRedirectMiddleware)] ``` The application will: 1. Start with `debug` as `False`. -2. Will start with a middleware `BasicHTTPMiddleware`. +2. Will start with a middleware `HTTPSRedirectMiddleware`. -Starting the application with the above settings will make sure that has an initial `BasicHTTPMiddleware` and `debug` +Starting the application with the above settings will make sure that has an initial `HTTPSRedirectMiddleware` and `debug` set with values **but** what happens if you use the settings + parameters on instantiation? ```python @@ -323,9 +324,9 @@ app = Esmerald(debug=True, middleware=[]) The application will: 1. Start with `debug` as `True`. -2. Will start without custom middlewares it the `BasicHTTPMiddleware` it was overridden by `[]`. +2. Will start without custom middlewares it the `HTTPSRedirectMiddleware` it was overridden by `[]`. -Although it was set in the settings to start with `BasicHTTPMiddleware` and debug as `False`, once you pass different +Although it was set in the settings to start with `HTTPSRedirectMiddleware` and debug as `False`, once you pass different values in the moment of instantiating an `Esmerald` object, those will become the values to be used. **Declaring parameters in the instance will always precede the values from your settings**. diff --git a/docs/background-tasks.md b/docs/background-tasks.md index b431985e..48c8f5cc 100644 --- a/docs/background-tasks.md +++ b/docs/background-tasks.md @@ -1,7 +1,6 @@ # Background Tasks -Like Starlette and any other Starlette based frameworks, in Esmerald you can define background -tasks to run **after** the returning response. +Like Lilya, in Esmerald you can define background tasks to run **after** the returning response. This can be useful for those operations that need to happen after the request without blocking the client (the client doesn't have to wait to complete) from receiving that same response. @@ -15,7 +14,7 @@ background. ## How to use -As mentioned before, Esmerald uses the background tasks from Starlette and you can pass them in +As mentioned before, Esmerald uses the background tasks from Lilya and you can pass them in different ways: * Via [handlers](#via-handlers) @@ -102,8 +101,8 @@ The `.add_task()` receives as arguments: ## Technical information -The class `BackgroundTask` and `BackgroundTasks` come directly from `starlette.background`. This -means you can import directly from Starlette if you want. +The class `BackgroundTask` and `BackgroundTasks` come directly from `lilya.background`. This +means you can import directly from Lilya if you want. Esmerald imports those classes and adds some extra typing information but without affecting the overall functionality of the core. @@ -112,8 +111,8 @@ You can use `def` or `async def` functions when declaring those functionalities the `BackgroundTask` and Esmerald will know how to handle those for you. For more details about these objects, you can see in -[Starlette official docs for Background Tasks](https://www.starlette.io/background/). +[Lilya official docs for Background Tasks](https://www.lilya.dev/tasks/). ## API Reference -Learn more about the `BackgroundTask` by checking the [API Reference](http://localhost:8000/references/background/). +Learn more about the `BackgroundTask` by checking the [API Reference](./references/background.md). diff --git a/docs/configurations/staticfiles.md b/docs/configurations/staticfiles.md index f513cb0a..0401b172 100644 --- a/docs/configurations/staticfiles.md +++ b/docs/configurations/staticfiles.md @@ -4,7 +4,7 @@ StaticFilesConfig is simple set of configurations that when passed enables the b When a StaticFilesConfig object is passed to an application instance, it will enable the static files serving. !!! Check - StaticFiles are considereed an `app` and they are pure Starlette app so using Starlette StaticFiles + StaticFiles are considereed an `app` and they are pure Lilya app so using Lilya StaticFiles will also work with Esmerald. ## StaticFilesConfig and application diff --git a/docs/datastructures.md b/docs/datastructures.md index 7843a78d..3c6b4710 100644 --- a/docs/datastructures.md +++ b/docs/datastructures.md @@ -1,6 +1,6 @@ # Datastructures -Esmerald comes equipped with some datastructures. If you want to use the same as Starlette, feel free as they are +Esmerald comes equipped with some datastructures. If you want to use the same as Lilya, feel free as they are compatible and work out of the box. ## How to access them @@ -13,13 +13,13 @@ from esmerald.datastructures import Headers The available datastructures to be imported from `Esmerald` are as follow. -* **Starlette**: +* **Lilya**: * `URL` * `Address` * `FormData` * `Headers` * `MutableHeaders` - * `QueryParams` + * `QueryParam` * [UploadFile](./extras/upload-files.md) * `URLPath` * **Esmerald**: @@ -36,5 +36,5 @@ and see how to use them. All the datastructures can be and are used across the codebase and can be applied by you anywhere. !!! Note - As mentioned before, all the Starlette datastructures are 100% compatible with `Esmerald`. You can use whatever + As mentioned before, all the Lilya datastructures are 100% compatible with `Esmerald`. You can use whatever it suits you. diff --git a/docs/external.md b/docs/external.md index 49cf135d..375f03bd 100644 --- a/docs/external.md +++ b/docs/external.md @@ -10,9 +10,7 @@ working solutions built by great minds. ### Ariadne -Great framework with great documentation and great examples how to use it. Currently they also have an integration -with Starlette and that also means **Esmerald**. Their example is with Starlette but here we also provide an example -for Esmerald. +Great framework with great documentation and great examples how to use it. ```python {!> ../docs_src/external_packages/ariadne.py !} @@ -20,14 +18,6 @@ for Esmerald. The documentation can be found here. -### Ariadne version 0.16.1 - -!!! Warning - By the time of this writing, `Starlette` was in the version `0.21.0`. - Due to the nature of `Esmerald`, when installing `Ariadne` it will install a lower version and this will - conflict with **Esmerald**. After you install `ariadne` make sure you reinstall `Starlette>=0.21.0`. - Until a new version of the package comes out supporting the latest from Starlette, this is the workaround. - ## [Esmerald Sessions](https://esmerald-sessions.dymmond.com/) Manages sessions for Esmerald using Redis, Memcache or any custom backend. diff --git a/docs/lifespan-events.md b/docs/lifespan-events.md index e5c3b365..4d81cb41 100644 --- a/docs/lifespan-events.md +++ b/docs/lifespan-events.md @@ -14,34 +14,17 @@ These cycles cover the **whole** application. Currently Esmerald supports **on_startup**, **on_shutdown** and **lifespan**. -Esmerald being built on the top of Starlette, it inherited those behaviours, which means, it can be +Esmerald being built on the top of Lilya, it inherited those behaviours, which means, it can be easily assembled to work directly with these events 🤖. -!!! Deprecation Warning - After the release of Starlette 0.26.0 it was announced the deprecation of **on_startup** and - **on_shutdown** in favour of the newly **lifespan**. Esmerald supports lifespan since its - inception so this won't affect directly the framework. - ### Esmerald on_startup and on_shutdown -Since the annoucement of Starlette that `on_startup` and `on_shutdown` would be removed after the -the release 1.0 in favour of `lifespan`, Esmerald decided to follow through since it is 100% -compatible with it. - -Despite the deprecation of those fields, Esmerald has an underlying implementation that -**will allow** the `on_startup` and `on_shutdown` to still exist even after this deprecation from -Starlette. - -**Does this mean that Esmerald will have to maintain its own events?** No, despite allowing -`on_startup` and `on_shutdown` to exist in the same way it does now, the underlying implementation -changed. - If you pass an `on_startup` and an `on_shutdown` parameters intead of the `lifespan`, Esmerald will **automatically generate the async context manager** for you and pass it to the `lifespan` internally for you. -This way Esmerald assures 100% compatibility with Starlette and still maintains the same -"look and feel" as before. +This way Esmerald assures 100% compatibility with Lilya and still maintains the same +"look and feel" as before. **You can use on_startup/on_shutdown and lifespan but not both at the same time**. @@ -105,7 +88,7 @@ Let us see what does it mean in practical examples by changing the previous one This is quite something to unwrap here. What is actually happening? So, before you need to explicitly declare the `on_startup` and `on_shutdown` events in the -corresponding parameters in the Esmerald application but with the `lifespan` you do that in +corresponding parameters in the Esmerald application but with the `lifespan` you do that in **one place only**. The first part before the `yield` will be executed **before the application starts** and @@ -142,9 +125,9 @@ new `lifespan` async context manager directly to it. ## Curiosity about async context managers -This section is out of the scope of the lifespan and events of Esmerald and it is +This section is out of the scope of the lifespan and events of Esmerald and it is **for curiosity only**. Please see the [lifespan](#lifespan) section as in the case of Esmerald, -the way of **declaring is different** and an `app: Esmerald` parameter is **always required**. +the way of **declaring is different** and an `app: Esmerald` parameter is **always required**. ### General approach to async context managers diff --git a/docs/middleware/middleware.md b/docs/middleware/middleware.md index 75d19f8c..402a7768 100644 --- a/docs/middleware/middleware.md +++ b/docs/middleware/middleware.md @@ -5,17 +5,17 @@ them by using [protocols](../protocols.md). Inspired by other great frameworks, for the middleware protocol. Let's be honest, it is not that we can reinvent the wheel on something already working out of the box. -There are two ways of designing the middleware for Esmerald. [Starlette middleware](#starlette-middleware) and +There are two ways of designing the middleware for Esmerald. [Lilya middleware](#lilya-middleware) and [Esmerald protocols](#esmerald-protocols) as both work quite well together. -## Starlette middleware +## Lilya middleware -The Starlette middleware is the classic already available way of declaring the middleware within an **Esmerald** +The Lilya middleware is the classic already available way of declaring the middleware within an **Esmerald** application. !!! Tip - You can create a middleware like Starlette and add it into the application. To understand how to build them, - Starlette has some great documentation here. + You can create a middleware like Lilya and add it into the application. To understand how to build them, + Lilya has some great documentation here. ```python {!> ../docs_src/middleware/starlette_middleware.py !} @@ -28,7 +28,7 @@ and automatically enable the built-in middlewares. ## Esmerald protocols -Esmerald protocols are not too different from the [Starlette middleware](#starlette-middleware). In fact, +Esmerald protocols are not too different from the [Lilya middleware](#lilya-middleware). In fact, the name itself happens only because of the use of the python protocols which forces a certain structure to happen and since **Esmerald** likes configurations as much as possible, @@ -50,11 +50,11 @@ In the case of Esmerald configurations, a `config` parameter is declared and pas in the `__init__` but this is not enforced on a protocol level but on a subclass level, the middleware itself. Enforcing this protocol also aligns with writing -pure asgi middlewares. +pure asgi middlewares. !!! Note MiddlewareProtocol does not enforce `config` parameters but enforces the `app` parameter as this will make sure - it will also work with Starlette as well as used as standard. + it will also work with Lilya as well as used as standard. ### Quick sample @@ -91,11 +91,11 @@ To add middlewares to the application is very simple. and [route](./../routing/handlers.md#route) as well as [websocket](./../routing/handlers.md#websocket). We simply choose `Gateway` as it looks simpler to read and understand. -## Writing ASGI middlewares +## Writing ASGI middlewares -**Esmerald** since follows the ASGI practices and uses Starlette underneath a good way of understand what can be -done with middleware and how to write some of them, Starlette also goes through with a lot of -detail. +**Esmerald** since follows the ASGI practices and uses Lilya underneath a good way of understand what can be +done with middleware and how to write some of them, Lilya also goes through with a lot of +detail. ## BaseAuthMiddleware @@ -192,24 +192,24 @@ INFO: Application startup complete. ### Important If you need to specify parameters in your middleware then you will need to wrap it in a -`starlette.middleware.Middleware` object to do it so. See `GZipMiddleware` [example](#middleware-and-the-settings). +`lilya.middleware.DefineMiddleware` object to do it so. See `GZipMiddleware` [example](#middleware-and-the-settings). If no parameters are needed, then you can simply pass the middleware class directly and Esmerald will take care of the rest. ## Available middlewares -There are some available middlewares that are also available from Starlette. +There are some available middlewares that are also available from Lilya. * `CSRFMiddleware` - Handles with the CSRF and there is a [built-in](../configurations/csrf.md) how to enable. * `CORSMiddleware` - Handles with the CORS and there is a [built-in](../configurations/cors.md) how to enable. * `TrustedHostMiddleware` - Handles with the CORS if a given `allowed_hosts` is populated, the [built-in](../configurations/cors.md) explains how to use it. -* `GZipMiddleware` - Same middleware as the one from Starlette. +* `GZipMiddleware` - Same middleware as the one from Lilya. * `HTTPSRedirectMiddleware` - Middleware that handles HTTPS redirects for your application. Very useful to be used for production or production like environments. * `RequestSettingsMiddleware` - The middleware that exposes the application settings in the request. -* `SessionMiddleware` - Same middleware as the one from Starlette. +* `SessionMiddleware` - Same middleware as the one from Lilya. * `WSGIMiddleware` - Allows to connect WSGI applications and run them inside Esmerald. A [great example](../wsgi.md) how to use it is available. @@ -259,7 +259,7 @@ Adds signed cookie-based HTTP sessions. Session information is readable but not ### HTTPSRedirectMiddleware -Like Starlette, enforces that all incoming requests must either be https or wss. Any http os ws will be redirected to +Like Lilya, enforces that all incoming requests must either be https or wss. Any http os ws will be redirected to the secure schemes instead. ```python @@ -276,7 +276,7 @@ Enforces all requests to have a correct set `Host` header in order to protect ag ### GZipMiddleware -Like Starlette, it handles GZip responses for any request that includes "gzip" in the Accept-Encoding header. +Like Lilya, it handles GZip responses for any request that includes "gzip" in the Accept-Encoding header. ```python {!> ../docs_src/middleware/available/gzip.py !} @@ -293,10 +293,10 @@ in the [WSGI Frameworks](../wsgi.md) section. ### Other middlewares -You can build your own middlewares as explained above but also reuse middlewares directly for Starlette if you wish. +You can build your own middlewares as explained above but also reuse middlewares directly for Lilya if you wish. The middlewares are 100% compatible. -Although some of the middlewares might mention Starlette or other ASGI framework, they are 100% +Although some of the middlewares might mention Lilya or other ASGI framework, they are 100% compatible with Esmerald as well. #### RateLimitMiddleware @@ -319,10 +319,10 @@ This integration works using [EsmeraldTimming](https://github.com/dymmond/esmera ## Important points -1. Esmerald supports [Starlette middleware](#starlette-middleware), [MiddlewareProtocol](#esmerald-protocols). +1. Esmerald supports [Lilya middleware](#lilya-middleware), [MiddlewareProtocol](#esmerald-protocols). 2. A MiddlewareProtocol is simply an interface that enforces `__init__` and `async __call__` to be implemented. 3. `app` is required parameter from any class inheriting from the `MiddlewareProtocol`. -4. Pure ASGI Middleware +4. Pure ASGI Middleware is encouraged and the `MiddlewareProtocol` enforces that. 5. Middleware classes can be added to any [layer of the application](#quick-note) 6. All authentication middlewares must inherit from the BaseAuthMiddleware. diff --git a/docs/permissions.md b/docs/permissions.md index abc4f93c..8e28e251 100644 --- a/docs/permissions.md +++ b/docs/permissions.md @@ -4,9 +4,12 @@ Authentication and authorization are a must in every application. Managing those and also widely used but with **Esmerald** you have a clear separation of permissions although still allowing [inject](./dependencies.md) to happen as well. -Inspired by the same author of Starlette, Esmerald permissions are as simple as you want them to be and as complex +Inspired by the same author of Django Rest Framework, Esmerald permissions are as simple as you want them to be and as complex as you design. For all tastes. +!!! Note + This permission system is not the same as the [Lilya permission system](https://lilya.dev/permissions). + ## BasePermission and custom permissions If you are used to Django Rest Framework, the way **Esmerald** designed was very much within the same lines and with diff --git a/docs/references/background.md b/docs/references/background.md index db8f3a4d..5d389005 100644 --- a/docs/references/background.md +++ b/docs/references/background.md @@ -3,7 +3,7 @@ This is the reference for the `BackgroundTask` and `BackgroundTasks` objects where it contains all API informationhow to use it. -Like Starlette and any other Starlette based frameworks, in Esmerald you can define background tasks to run after the returning response. +Like Lilya, in Esmerald you can define background tasks to run after the returning response. This can be useful for those operations that need to happen after the request without blocking the client (the client doesn't have to wait to complete) from receiving that same response. diff --git a/docs/references/status-codes.md b/docs/references/status-codes.md index bede8af4..d2b5b2c1 100644 --- a/docs/references/status-codes.md +++ b/docs/references/status-codes.md @@ -6,10 +6,10 @@ You can import the `status` module from `esmerald`: from esmerald import status ``` -The `status` is provided by Starlette which means you can also: +The `status` is provided by Lilya which means you can also: ```python -from starlette import status +from lilya import status ``` By default, the Esmerald [handlers](https://esmerald.dev/routing/handlers/) take care of the diff --git a/docs/references/test-client.md b/docs/references/test-client.md index 2968d028..b2c7bcdc 100644 --- a/docs/references/test-client.md +++ b/docs/references/test-client.md @@ -1,6 +1,6 @@ # Test Client -Esmerald offers an extension of the Starlette `TestClient` called `EsmeraldTestClient` as well +Esmerald offers an extension of the Lilya `TestClient` called `EsmeraldTestClient` as well as a `create_client` that can be used for context testing. diff --git a/docs/release-notes.md b/docs/release-notes.md index d166667f..bad896a8 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -5,6 +5,47 @@ hide: # Release Notes +## 3.0.0-beta2 + +### Added + +- Allow the use `from lilya.middleware import Middleware` as alternative to `DefineMiddleware`, + +### Changed + +- Cleaned the `ServerErrorMiddleware` from the lilya import. + +## 3.0.0-beta1 + +!!! Warning + This is a major release and it will be under the the version `3` of Esmerald. + You should not be affected but in case you are, please report any issues + so we can correct it. + +### Added + +- Support for `Lilya` and drop `Starlette`. + +### Changed + +- `CSRFConfig` `cookie_secure` renamed to `secure`. +- `CSRFConfig` `httponly` renamed to `httponly`. +- `CSRFConfig` `cookie_samesite` renamed to `samesite`. +- `CSRFConfig` `cookie_domain` renamed to `domain`. +- `CSRFConfig` `cookie_secure` renamed to `secure`. +- Removed support for the `BasicMiddleware` as this can be imported from any other ASGI application. + +#### Internal + +In the past, `Middleware` was being used but with the introduction of Lilya, now is `DefineMiddleware` that +is applied. + +```python +from lilya.middleware import DefineMiddleware +``` + +- The `PlainTextResponse` was renamed to `PlainText`. + ## 2.7.4 ### Fixed @@ -368,7 +409,7 @@ lists as definition. [More details](https://esmerald.dev/responses/#lists). ### Changed - Updated pyproject.toml keywords. -- Updated to the latest [Starlette 0.28.0](https://www.starlette.io/release-notes/). +- Updated to the latest [Starlette 0.28.0](https://www.lilya.dev/release-notes/). - Exception handler logic refactored. ## 1.2.3 diff --git a/docs/requests.md b/docs/requests.md index 021dceaa..e0951a29 100644 --- a/docs/requests.md +++ b/docs/requests.md @@ -3,7 +3,7 @@ While browsing the documentation a lot of examples where used using the `Request` object from Esmerald. -Well, the `Request` is actually inherited from `Starlette` and some extas were added to tune it +Well, the `Request` is actually inherited from `Lilya` and some extas were added to tune it to the needs of the framework. Importing is as simple as: @@ -16,14 +16,14 @@ from esmerald import Request You can learn more about the `Request` by checking the [API Reference](./references/request.md). -You can also read more about the `request` in the [Official Starlette documentation](https://www.starlette.io/requests/). +You can also read more about the `request` in the [Official Lilya documentation](https://www.lilya.dev/requests/). ## The Request The `request` is what is used (generally) to get the data from the `path_parameters`, `cookies`, `headers` or even the `user` current logged in. -As **Esmerald** uses `Starlette` under the hood, using the `Request` from it, it won't cause any +As **Esmerald** uses `Lilya` under the hood, using the `Request` from it, it won't cause any damage but you won't be able to access the whole scope of what **Esmerald** can do for you as well as the unique way of handling the `.json()`. diff --git a/docs/responses.md b/docs/responses.md index 62b8c47c..3f5aadaa 100644 --- a/docs/responses.md +++ b/docs/responses.md @@ -2,7 +2,7 @@ Like every application, there are many different responses that can be used for different use cases and scenarios. -Esmerald having `Starlette` under the hood also means that all available responses from it simply just work. +Esmerald having `Lilya` under the hood also means that all available responses from it simply just work. You simply just need to decide which type of response your function will have and let `Esmerald` take care of the rest. @@ -73,10 +73,10 @@ Check out the [API Reference for JSON](./references/responses/json.md) for more #### JSONResponse -You can always use directly the `JSONResponse` from Starlette without using the Esmerald wrapper. +You can always use directly the `JSONResponse` from Lilya without using the Esmerald wrapper. ```python -from starlette.responses import JSONResponse as JSONResponse +from lilya.responses import JSONResponse as JSONResponse ``` or alternatively @@ -205,11 +205,11 @@ Check out the [API Reference for Stream](./references/responses/stream.md) for m ## Important notes [Template](#template), [Redirect](#redirect), [File](#file) and [Stream](#stream) are wrappers -around the Starlette `TemplateResponse`, `RedirectResponse`, `FileResponse` and `StreamResponse`. +around the Lilya `TemplateResponse`, `RedirectResponse`, `FileResponse` and `StreamResponse`. Those responses are also possible to be used directly without the need of using the wrapper. -The wrappers, like Starlette, also accept the classic parameters such as `headers` and `cookies`. +The wrappers, like Lilya, also accept the classic parameters such as `headers` and `cookies`. ## Response status codes diff --git a/docs/routing/handlers.md b/docs/routing/handlers.md index 8b549da7..88f4b588 100644 --- a/docs/routing/handlers.md +++ b/docs/routing/handlers.md @@ -149,7 +149,7 @@ needs usually to be constantly opened. More on websockets. !!! warning - Due to the nature of websockets, Esmerald does a direct implementation of the WebSocket from Starlette which also + Due to the nature of websockets, Esmerald does a direct implementation of the WebSocket from Lilya which also means no `sync` functions. ### WebSocket diff --git a/docs/routing/router.md b/docs/routing/router.md index eba9e81c..2b21df5b 100644 --- a/docs/routing/router.md +++ b/docs/routing/router.md @@ -135,7 +135,7 @@ requests (HTTP and Websockets). * **middleware** - A list of middleware to run for every request. The middlewares of a Include will be checked from top-down. * **interceptors** - A list of [interceptors](../interceptors.md). -or Starlette Middleware as they are both converted +or Lilya Middleware as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). * **dependencies** - A dictionary of string and [Inject](../dependencies.md) instances enable application level dependency injection. @@ -158,7 +158,7 @@ requests (HTTP and Websockets). * **interceptors** - A list of [interceptors](../interceptors.md). * **middleware** - A list of middleware to run for every request. The middlewares of a Include will be checked from top-down. -or Starlette Middleware as they are both converted +or Lilya Middleware as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). * **dependencies** - A dictionary of string and [Inject](../dependencies.md) instances enable application level dependency injection. @@ -183,7 +183,7 @@ requests (HTTP and Websockets). * **interceptors** - A list of [interceptors](../interceptors.md). * **middleware** - A list of middleware to run for every request. The middlewares of a Include will be checked from top-down. -or Starlette Middleware as they are both converted +or Lilya Middleware as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). * **dependencies** - A dictionary of string and [Inject](../dependencies.md) instances enable application level dependency injection. diff --git a/docs/routing/routes.md b/docs/routing/routes.md index b42e9dc4..4205b1e2 100644 --- a/docs/routing/routes.md +++ b/docs/routing/routes.md @@ -8,7 +8,7 @@ Using an enterprise application as example, the routing system surely will not components and packages and imported also inside complex design systems. Esmerald handles with those cases without any kind of issues at all. -Starlette routing system alone wasn't enough to serve all the complexities and cases for all sort of +Lilya routing system alone wasn't enough to serve all the complexities and cases for all sort of different APIs and systems so Esmerald created its own. ## Gateway @@ -33,7 +33,7 @@ All the parameters and defaults are available in the [Gateway Reference](../refe ## WebSocketGateway -Same principle as [Gateway](#gateway) with one particularity. Due to the nature of Starlette and websockets we +Same principle as [Gateway](#gateway) with one particularity. Due to the nature of Lilya and websockets we decided not to interfere (for now) with what already works and therefore the only supported websockets are `async`. ### WebSocketGateway and application @@ -53,10 +53,10 @@ All the parameters and defaults are available in the [WebSocketGateway Reference ## Include -Includes are unique to Esmerald, very similar to the `Mount` of Starlette but more powerful and with more control +Includes are unique to Esmerald, very similar to the `Include` of Lilya but more powerful and with more control and feature and allows: -1. Scalability without issues (thanks to Starlette). +1. Scalability without issues (thanks to Lilya). 2. Clean routing design. 3. Separation of concerns. 4. Separation of routes. @@ -142,7 +142,7 @@ When complexity increses and the level of routes increases as well, `Include` al `Include` supports as many nested routes with different paths and Gateways, WebSocketGateways and Includes as you desire to have. Once the application starts, the routes are assembled and it will not impact the performance, thanks -to Starlette. +to Lilya. Nested routes also allows all the functionalities on each level, from middleware, permissions and exception handlers to dependencies. @@ -204,13 +204,13 @@ The path is `/` for both `src.urls` and `accounts.v1.urls` and unique with their !!! Check Remember, the route paths are registered only once and there is no "override". First in, first registered. - This is feature came from Starlette and there is a reason why it is like this and we decided not to break it since + This is feature came from Lilya and there is a reason why it is like this and we decided not to break it since it was designed to be hierarchical, from the top to bottom. ## Routes priority The [application routes](#application-routes) in simple terms are simply prioritised. Since **Esmerald** uses -Starlette under the hood that also means that the incoming paths are matched agains each [Gateway](#gateway), +Lilya under the hood that also means that the incoming paths are matched agains each [Gateway](#gateway), [WebSocketGateway](#websocketgateway) and [Include](#include) in order. In cases where more than one, let's say Gateway could match an incoming path, you should ensure that more specifc @@ -249,8 +249,8 @@ Gateway('/customers/{customer_id}', handler=customer) By default this will capture characters up to the end of the path of the next '/' and also are joint to the path of a handler. In the example above, it will become `/customers/{customer_id}/example`. -Converters can be used to modify what is being captured. The current available converters are the same ones used -by Starlette as well. +Transformers can be used to modify what is being captured. The current available transformers are the same ones used +by Lilya as well. * `str` returns a string, and is the default. * `int` returns a Python integer. @@ -258,7 +258,7 @@ by Starlette as well. * `uuid` returns a Python `uuid.UUID` instance. * `path` returns the rest of the path, including any additional `/` characters. -As per standard, the converters are used by prefixing them with a colon: +As per standard, the transformers are used by prefixing them with a colon: ```python Gateway('/customers/{customer_id:int}', handler=customer) @@ -266,16 +266,16 @@ Gateway('/floating-point/{number:float}', handler=floating_point) Gateway('/uploaded/{rest_of_path:path}', handler=uploaded) ``` -### Custom converters +### Custom transformers -If a need for a different converter that is not defined or available, you can also create your own. Using the same -example as Starlette since it works with **Esmerald**. +If a need for a different transformer that is not defined or available, you can also create your own. Using the same +example as Lilya since it works with **Esmerald**. ```python {!> ../docs_src/routing/routes/converter_example.py !} ``` -With the custom converter created you can now use it. +With the custom transformer created you can now use it. ```python Gateway('/sells/{date:datetime}', handler=sell) diff --git a/docs/sponsorship.md b/docs/sponsorship.md index 2d2c7d00..3456f6a5 100644 --- a/docs/sponsorship.md +++ b/docs/sponsorship.md @@ -46,6 +46,8 @@ sponsor for Esmerald. ## ⚙️ Sponsor the tools that made Esmerald possible As you have seen in the documentation, Esmerald stands on the shoulders of giants, Starlette and Pydantic. +Although Esmerald is not longer on top of Starlette, for a very long time it was and they deserve all the help +they can get. You can also sponsor: diff --git a/docs/testclient.md b/docs/testclient.md index 7be6b4e7..fdc0946c 100644 --- a/docs/testclient.md +++ b/docs/testclient.md @@ -30,13 +30,13 @@ You can use any of the `httpx` standard API like authentication, session cookies {!> ../docs_src/testclient/example2.py !} ``` -And like Starlette, the same example to send files with `EsmeraldTestClient`. +And like Lilya, the same example to send files with `EsmeraldTestClient`. ```python {!> ../docs_src/testclient/example3.py !} ``` -`httpx` is a great library created by the same author of `Starlette` and `Django Rest Framework`. +`httpx` is a great library created by the same author of `Django Rest Framework`. !!! Info By default the EsmeraldTestClient raise any exceptions that occur in the application. diff --git a/docs/vendors.md b/docs/vendors.md index d3271b67..5a2bc51e 100644 --- a/docs/vendors.md +++ b/docs/vendors.md @@ -8,8 +8,8 @@ part of the initial success of Esmerald. Esmerald initially started a small idea that was also initially designed to run on the top of Starlite. Excelent framework out there doing amazing things but plans have changed and so has Esmerald in many ways. -We have opted to do the implementation of Esmerald on the top of Starlette instead has the plans for the coming years -will be heavily focused on that as well. +We have opted to do the implementation of Esmerald on the top of Starlette initially and then Lilya instead as the plans +for the coming years will be heavily focused on that as well. Since Esmerald got the inspiration from Starlite, a few patterns were similar to allow us to deliver with high quality, things like the previous Signature and Kwargs that later on (from version 0.3.X) became our own diff --git a/docs/wsgi.md b/docs/wsgi.md index fbeb98ab..801dffc2 100644 --- a/docs/wsgi.md +++ b/docs/wsgi.md @@ -1,8 +1,7 @@ # WSGI frameworks -Did you know because of the awesome work from [a2wsgi](https://github.com/abersheeran/a2wsgi) and from previous -amazing jobs from Starlette added to the simplicity of Esmerald you can integrate any wsgi -framework (Flask, Django...)? +Did you know because of the awesome work from [a2wsgi](https://github.com/abersheeran/a2wsgi) +added to the simplicity of Esmerald you can integrate any wsgi framework (Flask, Django...)? Yes, that's right, you can now smoothly move to Esmerald without rewriting your old applications from the scratch, actually, you can reuse them directly within Esmerald, even another Esmerald running inside another Esmerald, diff --git a/docs_src/_shared/databases_important_note.md b/docs_src/_shared/databases_important_note.md index 2de2496b..bbb7b746 100644 --- a/docs_src/_shared/databases_important_note.md +++ b/docs_src/_shared/databases_important_note.md @@ -1,9 +1,9 @@ ### Important note -In the examples you could see sometimes the `StarletteMiddleware` being used and in other you didn't. The reason behind +In the examples you could see sometimes the `LilyaMiddleware` being used and in other you didn't. The reason behind is very simple and also explained in the [middleware section](../../middleware/middleware.md#important). -If you need to specify parameters in your middleware then you will need to wrap it in a `starlette.middleware.Middleware` +If you need to specify parameters in your middleware then you will need to wrap it in a `lilya.middleware.DefineMiddleware` object to do it so. If no parameters are needed, then you can simply pass the middleware class directly and Esmerald will take care of the diff --git a/docs_src/application/app/permissions_and_middlewares.py b/docs_src/application/app/permissions_and_middlewares.py index cbfafa00..ccfc55b5 100644 --- a/docs_src/application/app/permissions_and_middlewares.py +++ b/docs_src/application/app/permissions_and_middlewares.py @@ -1,8 +1,4 @@ from pydantic import BaseModel, EmailStr -from saffier.exceptions import ObjectNotFound -from starlette.middleware import Middleware as StarletteMiddleware -from starlette.requests import HTTPConnection -from starlette.types import ASGIApp from esmerald import ( APIView, @@ -24,6 +20,10 @@ from esmerald.middleware.authentication import AuthResult, BaseAuthMiddleware from esmerald.permissions import IsAdminUser from esmerald.security.jwt.token import Token +from lilya._internal._connection import Connection +from lilya.middleware import DefineMiddleware as LilyaMiddleware +from lilya.types import ASGIApp +from saffier.exceptions import ObjectNotFound class JWTAuthMiddleware(BaseAuthMiddleware): @@ -38,7 +38,7 @@ async def retrieve_user(self, user_id) -> User: except ObjectNotFound: raise NotAuthorized() - async def authenticate(self, request: HTTPConnection) -> AuthResult: + async def authenticate(self, request: Connection) -> AuthResult: token = request.headers.get(self.config.api_key_header) if not token: @@ -115,5 +115,5 @@ async def websocket_endpoint(self, socket: WebSocket) -> None: ) ], permissions=[IsAdmin], - middleware=[StarletteMiddleware(JWTAuthMiddleware, config=jwt_config)], + middleware=[LilyaMiddleware(JWTAuthMiddleware, config=jwt_config)], ) diff --git a/docs_src/configurations/jwt/claims/middleware.py b/docs_src/configurations/jwt/claims/middleware.py index e8843311..b1266a12 100644 --- a/docs_src/configurations/jwt/claims/middleware.py +++ b/docs_src/configurations/jwt/claims/middleware.py @@ -1,17 +1,17 @@ from jose import JWSError, JWTError -from starlette.middleware import Middleware as StarletteMiddleware -from starlette.requests import HTTPConnection from esmerald.conf import settings from esmerald.contrib.auth.mongoz.middleware import JWTAuthMiddleware as EsmeraldMiddleware from esmerald.exceptions import AuthenticationError, NotAuthorized from esmerald.middleware.authentication import AuthResult from esmerald.security.jwt.token import Token -from esmerald.utils.module_loading import import_string +from lilya._internal._connection import Connection +from lilya._internal._module_loading import import_string +from lilya.middleware import DefineMiddleware as LilyaMiddleware class JWTAuthMiddleware(EsmeraldMiddleware): - def get_token(self, request: HTTPConnection) -> Token: + def get_token(self, request: Connection) -> Token: """ Gets the token from the headers. """ @@ -37,7 +37,7 @@ def get_token(self, request: HTTPConnection) -> Token: raise AuthenticationError(str(e)) from e return token - async def authenticate(self, request: HTTPConnection) -> AuthResult: + async def authenticate(self, request: Connection) -> AuthResult: """ Retrieves the header default of the config, validates and returns the AuthResult. @@ -57,7 +57,7 @@ async def authenticate(self, request: HTTPConnection) -> AuthResult: # Middleware responsible from user accesses. # This can be imported in any level of the application -AuthMiddleware = StarletteMiddleware( +AuthMiddleware = LilyaMiddleware( JWTAuthMiddleware, config=settings.jwt_config, user_model=import_string("accounts.models.User"), diff --git a/docs_src/configurations/jwt/example1.py b/docs_src/configurations/jwt/example1.py index f2966db3..e68091ec 100644 --- a/docs_src/configurations/jwt/example1.py +++ b/docs_src/configurations/jwt/example1.py @@ -1,14 +1,14 @@ from myapp.models import User -from starlette.middleware import Middleware as StarletteMiddleware from esmerald import Esmerald, settings from esmerald.config.jwt import JWTConfig -from esmerald.contrib.auth.tortoise.middleware import JWTAuthMiddleware +from esmerald.contrib.auth.edgy.middleware import JWTAuthMiddleware +from lilya.middleware import DefineMiddleware as LilyaMiddleware jwt_config = JWTConfig( signing_key=settings.secret_key, ) -auth_middleware = StarletteMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User) +auth_middleware = LilyaMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User) app = Esmerald(middleware=[auth_middleware]) diff --git a/docs_src/configurations/jwt/settings.py b/docs_src/configurations/jwt/settings.py index 5fe7c684..018cf5af 100644 --- a/docs_src/configurations/jwt/settings.py +++ b/docs_src/configurations/jwt/settings.py @@ -1,11 +1,10 @@ from typing import TYPE_CHECKING, List -from starlette.middleware import Middleware as StarletteMiddleware - from esmerald import EsmeraldAPISettings from esmerald.config.jwt import JWTConfig from esmerald.contrib.auth.saffier.middleware import JWTAuthMiddleware -from esmerald.utils.module_loading import import_string +from lilya._internal._module_loading import import_string +from lilya.middleware import DefineMiddleware as LilyaMiddleware if TYPE_CHECKING: from esmerald.types import Middleware @@ -25,7 +24,7 @@ def middleware(self) -> List["Middleware"]: Initial middlewares to be loaded on startup of the application. """ return [ - StarletteMiddleware( + LilyaMiddleware( JWTAuthMiddleware, config=self.jwt_config, user_model=import_string("myapp.models.User"), diff --git a/docs_src/databases/edgy/example/assemble.py b/docs_src/databases/edgy/example/assemble.py index dda2a40a..cd4ea978 100644 --- a/docs_src/databases/edgy/example/assemble.py +++ b/docs_src/databases/edgy/example/assemble.py @@ -6,11 +6,10 @@ import sys from pathlib import Path -from starlette.middleware import Middleware as StarletteMiddleware - from esmerald import Esmerald, Gateway, Include from esmerald.conf import settings from esmerald.contrib.auth.edgy.middleware import JWTAuthMiddleware +from lilya.middleware import DefineMiddleware as LilyaMiddleware def build_path(): @@ -40,9 +39,7 @@ def get_application(): Include( routes=[Gateway(handler=home)], middleware=[ - StarletteMiddleware( - JWTAuthMiddleware, config=settings.jwt_config, user_model=User - ) + LilyaMiddleware(JWTAuthMiddleware, config=settings.jwt_config, user_model=User) ], ), ], diff --git a/docs_src/databases/edgy/example/home.py b/docs_src/databases/edgy/example/home.py index 31e8eca2..d45ad1c4 100644 --- a/docs_src/databases/edgy/example/home.py +++ b/docs_src/databases/edgy/example/home.py @@ -5,7 +5,7 @@ async def home(request: Request) -> JSONResponse: """ Esmerald request has a `user` property that also - comes from its origins (Starlette). + comes from its origins (Lilya). When building an authentication middleware, it is recommended to inherit from the `BaseAuthMiddleware`. diff --git a/docs_src/databases/edgy/jwt/settings.py b/docs_src/databases/edgy/jwt/settings.py index c5ee2e9f..d482b371 100644 --- a/docs_src/databases/edgy/jwt/settings.py +++ b/docs_src/databases/edgy/jwt/settings.py @@ -1,11 +1,10 @@ from typing import TYPE_CHECKING, List -from starlette.middleware import Middleware as StarletteMiddleware - from esmerald import EsmeraldAPISettings from esmerald.config.jwt import JWTConfig from esmerald.contrib.auth.edgy.middleware import JWTAuthMiddleware -from esmerald.utils.module_loading import import_string +from lilya._internal._module_loading import import_string +from lilya.middleware import DefineMiddleware as LilyaMiddleware if TYPE_CHECKING: from esmerald.types import Middleware @@ -25,7 +24,7 @@ def middleware(self) -> List["Middleware"]: Initial middlewares to be loaded on startup of the application. """ return [ - StarletteMiddleware( + LilyaMiddleware( JWTAuthMiddleware, config=self.jwt_config, user_model=import_string("myapp.models.User"), diff --git a/docs_src/databases/edgy/middleware/example1.py b/docs_src/databases/edgy/middleware/example1.py index f19bf85a..e8912222 100644 --- a/docs_src/databases/edgy/middleware/example1.py +++ b/docs_src/databases/edgy/middleware/example1.py @@ -1,14 +1,13 @@ -from starlette.middleware import Middleware as StarletteMiddleware - from esmerald import Esmerald from esmerald.conf import settings from esmerald.config.jwt import JWTConfig from esmerald.contrib.auth.edgy.middleware import JWTAuthMiddleware -from esmerald.utils.module_loading import import_string +from lilya._internal._module_loading import import_string +from lilya.middleware import DefineMiddleware as LilyaMiddleware jwt_config = JWTConfig(signing_key=settings.secret_key, auth_header_types=["Bearer", "Token"]) -jwt_auth_middleware = StarletteMiddleware( +jwt_auth_middleware = LilyaMiddleware( JWTAuthMiddleware, config=jwt_config, user=import_string("myapp.models.User"), diff --git a/docs_src/databases/edgy/middleware/example2.py b/docs_src/databases/edgy/middleware/example2.py index 87373cf8..ae052fa9 100644 --- a/docs_src/databases/edgy/middleware/example2.py +++ b/docs_src/databases/edgy/middleware/example2.py @@ -1,10 +1,9 @@ -from starlette.types import ASGIApp - from esmerald import Esmerald from esmerald.conf import settings from esmerald.config.jwt import JWTConfig from esmerald.contrib.auth.edgy.middleware import JWTAuthMiddleware -from esmerald.utils.module_loading import import_string +from lilya._internal._module_loading import import_string +from lilya.types import ASGIApp class AppAuthMiddleware(JWTAuthMiddleware): diff --git a/docs_src/databases/edgy/middleware/example3.py b/docs_src/databases/edgy/middleware/example3.py index 8ad841a3..65b25d16 100644 --- a/docs_src/databases/edgy/middleware/example3.py +++ b/docs_src/databases/edgy/middleware/example3.py @@ -1,12 +1,11 @@ from typing import TYPE_CHECKING, List -from starlette.types import ASGIApp - from esmerald import EsmeraldAPISettings from esmerald.conf import settings from esmerald.config.jwt import JWTConfig from esmerald.contrib.auth.edgy.middleware import JWTAuthMiddleware -from esmerald.utils.module_loading import import_string +from lilya._internal._module_loading import import_string +from lilya.types import ASGIApp if TYPE_CHECKING: from esmerald.types import Middleware diff --git a/docs_src/databases/mongoz/example/assemble.py b/docs_src/databases/mongoz/example/assemble.py index a305120c..ff2d6426 100644 --- a/docs_src/databases/mongoz/example/assemble.py +++ b/docs_src/databases/mongoz/example/assemble.py @@ -6,11 +6,10 @@ import sys from pathlib import Path -from starlette.middleware import Middleware as StarletteMiddleware - from esmerald import Esmerald, Gateway, Include from esmerald.conf import settings from esmerald.contrib.auth.mongoz.middleware import JWTAuthMiddleware +from lilya.middleware import DefineMiddleware as LilyaMiddleware def build_path(): @@ -40,9 +39,7 @@ def get_application(): Include( routes=[Gateway(handler=home)], middleware=[ - StarletteMiddleware( - JWTAuthMiddleware, config=settings.jwt_config, user_model=User - ) + LilyaMiddleware(JWTAuthMiddleware, config=settings.jwt_config, user_model=User) ], ), ], diff --git a/docs_src/databases/mongoz/example/home.py b/docs_src/databases/mongoz/example/home.py index 31e8eca2..d45ad1c4 100644 --- a/docs_src/databases/mongoz/example/home.py +++ b/docs_src/databases/mongoz/example/home.py @@ -5,7 +5,7 @@ async def home(request: Request) -> JSONResponse: """ Esmerald request has a `user` property that also - comes from its origins (Starlette). + comes from its origins (Lilya). When building an authentication middleware, it is recommended to inherit from the `BaseAuthMiddleware`. diff --git a/docs_src/databases/mongoz/jwt/settings.py b/docs_src/databases/mongoz/jwt/settings.py index ecd8d3c7..5dd0cf01 100644 --- a/docs_src/databases/mongoz/jwt/settings.py +++ b/docs_src/databases/mongoz/jwt/settings.py @@ -1,11 +1,10 @@ from typing import TYPE_CHECKING, List -from starlette.middleware import Middleware as StarletteMiddleware - from esmerald import EsmeraldAPISettings from esmerald.config.jwt import JWTConfig from esmerald.contrib.auth.mongoz.middleware import JWTAuthMiddleware -from esmerald.utils.module_loading import import_string +from lilya._internal._module_loading import import_string +from lilya.middleware import DefineMiddleware as LilyaMiddleware if TYPE_CHECKING: from esmerald.types import Middleware @@ -25,7 +24,7 @@ def middleware(self) -> List["Middleware"]: Initial middlewares to be loaded on startup of the application. """ return [ - StarletteMiddleware( + LilyaMiddleware( JWTAuthMiddleware, config=self.jwt_config, user_model=import_string("myapp.models.User"), diff --git a/docs_src/databases/mongoz/middleware/example1.py b/docs_src/databases/mongoz/middleware/example1.py index 88bffd02..2ddb4b5d 100644 --- a/docs_src/databases/mongoz/middleware/example1.py +++ b/docs_src/databases/mongoz/middleware/example1.py @@ -1,14 +1,13 @@ -from starlette.middleware import Middleware as StarletteMiddleware - from esmerald import Esmerald from esmerald.conf import settings from esmerald.config.jwt import JWTConfig from esmerald.contrib.auth.mongoz.middleware import JWTAuthMiddleware -from esmerald.utils.module_loading import import_string +from lilya._internal._module_loading import import_string +from lilya.middleware import DefineMiddleware as LilyaMiddleware jwt_config = JWTConfig(signing_key=settings.secret_key, auth_header_types=["Bearer", "Token"]) -jwt_auth_middleware = StarletteMiddleware( +jwt_auth_middleware = LilyaMiddleware( JWTAuthMiddleware, config=jwt_config, user_model=import_string("myapp.models.User"), diff --git a/docs_src/databases/mongoz/middleware/example2.py b/docs_src/databases/mongoz/middleware/example2.py index 8b17ef28..497247e8 100644 --- a/docs_src/databases/mongoz/middleware/example2.py +++ b/docs_src/databases/mongoz/middleware/example2.py @@ -1,10 +1,9 @@ -from starlette.types import ASGIApp - from esmerald import Esmerald from esmerald.conf import settings from esmerald.config.jwt import JWTConfig from esmerald.contrib.auth.mongoz.middleware import JWTAuthMiddleware -from esmerald.utils.module_loading import import_string +from lilya._internal._module_loading import import_string +from lilya.types import ASGIApp class AppAuthMiddleware(JWTAuthMiddleware): diff --git a/docs_src/databases/mongoz/middleware/example3.py b/docs_src/databases/mongoz/middleware/example3.py index daa7af9f..5279fbad 100644 --- a/docs_src/databases/mongoz/middleware/example3.py +++ b/docs_src/databases/mongoz/middleware/example3.py @@ -1,12 +1,11 @@ from typing import TYPE_CHECKING, List -from starlette.types import ASGIApp - from esmerald import EsmeraldAPISettings from esmerald.conf import settings from esmerald.config.jwt import JWTConfig from esmerald.contrib.auth.mongoz.middleware import JWTAuthMiddleware -from esmerald.utils.module_loading import import_string +from lilya._internal._module_loading import import_string +from lilya.types import ASGIApp if TYPE_CHECKING: from esmerald.types import Middleware diff --git a/docs_src/databases/saffier/example/assemble.py b/docs_src/databases/saffier/example/assemble.py index 0b2baa78..0ce52572 100644 --- a/docs_src/databases/saffier/example/assemble.py +++ b/docs_src/databases/saffier/example/assemble.py @@ -6,11 +6,10 @@ import sys from pathlib import Path -from starlette.middleware import Middleware as StarletteMiddleware - from esmerald import Esmerald, Gateway, Include from esmerald.conf import settings from esmerald.contrib.auth.saffier.middleware import JWTAuthMiddleware +from lilya.middleware import DefineMiddleware as LilyaMiddleware def build_path(): @@ -40,9 +39,7 @@ def get_application(): Include( routes=[Gateway(handler=home)], middleware=[ - StarletteMiddleware( - JWTAuthMiddleware, config=settings.jwt_config, user_model=User - ) + LilyaMiddleware(JWTAuthMiddleware, config=settings.jwt_config, user_model=User) ], ), ], diff --git a/docs_src/databases/saffier/example/home.py b/docs_src/databases/saffier/example/home.py index 31e8eca2..d45ad1c4 100644 --- a/docs_src/databases/saffier/example/home.py +++ b/docs_src/databases/saffier/example/home.py @@ -5,7 +5,7 @@ async def home(request: Request) -> JSONResponse: """ Esmerald request has a `user` property that also - comes from its origins (Starlette). + comes from its origins (Lilya). When building an authentication middleware, it is recommended to inherit from the `BaseAuthMiddleware`. diff --git a/docs_src/databases/saffier/jwt/settings.py b/docs_src/databases/saffier/jwt/settings.py index 5fe7c684..018cf5af 100644 --- a/docs_src/databases/saffier/jwt/settings.py +++ b/docs_src/databases/saffier/jwt/settings.py @@ -1,11 +1,10 @@ from typing import TYPE_CHECKING, List -from starlette.middleware import Middleware as StarletteMiddleware - from esmerald import EsmeraldAPISettings from esmerald.config.jwt import JWTConfig from esmerald.contrib.auth.saffier.middleware import JWTAuthMiddleware -from esmerald.utils.module_loading import import_string +from lilya._internal._module_loading import import_string +from lilya.middleware import DefineMiddleware as LilyaMiddleware if TYPE_CHECKING: from esmerald.types import Middleware @@ -25,7 +24,7 @@ def middleware(self) -> List["Middleware"]: Initial middlewares to be loaded on startup of the application. """ return [ - StarletteMiddleware( + LilyaMiddleware( JWTAuthMiddleware, config=self.jwt_config, user_model=import_string("myapp.models.User"), diff --git a/docs_src/databases/saffier/middleware/example1.py b/docs_src/databases/saffier/middleware/example1.py index 1df9626a..71b777b1 100644 --- a/docs_src/databases/saffier/middleware/example1.py +++ b/docs_src/databases/saffier/middleware/example1.py @@ -1,14 +1,13 @@ -from starlette.middleware import Middleware as StarletteMiddleware - from esmerald import Esmerald from esmerald.conf import settings from esmerald.config.jwt import JWTConfig from esmerald.contrib.auth.saffier.middleware import JWTAuthMiddleware -from esmerald.utils.module_loading import import_string +from lilya._internal._module_loading import import_string +from lilya.middleware import DefineMiddleware as LilyaMiddleware jwt_config = JWTConfig(signing_key=settings.secret_key, auth_header_types=["Bearer", "Token"]) -jwt_auth_middleware = StarletteMiddleware( +jwt_auth_middleware = LilyaMiddleware( JWTAuthMiddleware, config=jwt_config, user=import_string("myapp.models.User"), diff --git a/docs_src/databases/saffier/middleware/example2.py b/docs_src/databases/saffier/middleware/example2.py index c16f5719..36db7bc9 100644 --- a/docs_src/databases/saffier/middleware/example2.py +++ b/docs_src/databases/saffier/middleware/example2.py @@ -1,10 +1,9 @@ -from starlette.types import ASGIApp - from esmerald import Esmerald from esmerald.conf import settings from esmerald.config.jwt import JWTConfig from esmerald.contrib.auth.saffier.middleware import JWTAuthMiddleware -from esmerald.utils.module_loading import import_string +from lilya._internal._module_loading import import_string +from lilya.types import ASGIApp class AppAuthMiddleware(JWTAuthMiddleware): diff --git a/docs_src/databases/saffier/middleware/example3.py b/docs_src/databases/saffier/middleware/example3.py index e4b230b0..b251a329 100644 --- a/docs_src/databases/saffier/middleware/example3.py +++ b/docs_src/databases/saffier/middleware/example3.py @@ -1,12 +1,11 @@ from typing import TYPE_CHECKING, List -from starlette.types import ASGIApp - from esmerald import EsmeraldAPISettings from esmerald.conf import settings from esmerald.config.jwt import JWTConfig from esmerald.contrib.auth.saffier.middleware import JWTAuthMiddleware -from esmerald.utils.module_loading import import_string +from lilya._internal._module_loading import import_string +from lilya.types import ASGIApp if TYPE_CHECKING: from esmerald.types import Middleware diff --git a/docs_src/exception_handlers/precedent.py b/docs_src/exception_handlers/precedent.py index 51cdddc1..fff03b6e 100644 --- a/docs_src/exception_handlers/precedent.py +++ b/docs_src/exception_handlers/precedent.py @@ -1,5 +1,4 @@ from pydantic.error_wrappers import ValidationError -from starlette import status from esmerald import ( Esmerald, @@ -10,6 +9,7 @@ ValidationErrorException, get, ) +from lilya import status async def validation_error_exception_handler( diff --git a/docs_src/exceptions/custom_exception.py b/docs_src/exceptions/custom_exception.py index e7677d56..b8d52381 100644 --- a/docs_src/exceptions/custom_exception.py +++ b/docs_src/exceptions/custom_exception.py @@ -1,9 +1,9 @@ from typing import Optional from pydantic import BaseModel -from starlette import status from esmerald import HTTPException, JSONResponse, Request, post +from lilya import status class PartialContentException(HTTPException): diff --git a/docs_src/exceptions/overriding.py b/docs_src/exceptions/overriding.py index 39cd8c41..4080cbad 100644 --- a/docs_src/exceptions/overriding.py +++ b/docs_src/exceptions/overriding.py @@ -1,7 +1,4 @@ from pydantic.error_wrappers import ValidationError -from starlette import status -from starlette.exceptions import HTTPException as StarletteHTTPException -from starlette.responses import Response as StarletteResponse from esmerald import ( HTTPException, @@ -13,14 +10,17 @@ ) from esmerald.applications import Esmerald from esmerald.enums import MediaType +from lilya import status +from lilya.exceptions import HTTPException as LilyaHTTPException +from lilya.responses import Response as LilyaResponse async def improperly_configured_exception_handler( request: Request, exc: ImproperlyConfigured -) -> StarletteResponse: +) -> LilyaResponse: status_code = ( exc.status_code - if isinstance(exc, StarletteHTTPException) + if isinstance(exc, LilyaHTTPException) else status.HTTP_500_INTERNAL_SERVER_ERROR ) if not status_code: @@ -29,7 +29,7 @@ async def improperly_configured_exception_handler( content = {"detail": exc.detail} if exc.extra: content.update({"extra": exc.extra}) - headers = exc.headers if isinstance(exc, (HTTPException, StarletteHTTPException)) else None + headers = exc.headers if isinstance(exc, (HTTPException, LilyaHTTPException)) else None return Response( media_type=MediaType.JSON, diff --git a/docs_src/interceptors/cookie_interceptor.py b/docs_src/interceptors/cookie_interceptor.py index aab5e57a..b39b42b6 100644 --- a/docs_src/interceptors/cookie_interceptor.py +++ b/docs_src/interceptors/cookie_interceptor.py @@ -1,8 +1,7 @@ -from starlette.types import Receive, Scope, Send - from esmerald import EsmeraldInterceptor from esmerald.exceptions import NotAuthorized from esmerald.requests import Request +from lilya.types import Receive, Scope, Send class CookieInterceptor(EsmeraldInterceptor): diff --git a/docs_src/interceptors/custom/logging.py b/docs_src/interceptors/custom/logging.py index 0fd51399..891203ca 100644 --- a/docs_src/interceptors/custom/logging.py +++ b/docs_src/interceptors/custom/logging.py @@ -1,5 +1,6 @@ from loguru import logger -from starlette.types import Receive, Scope, Send + +from lilya.types import Receive, Scope, Send class LoggingInterceptor: diff --git a/docs_src/interceptors/logging.py b/docs_src/interceptors/logging.py index 03e13baf..bce429b8 100644 --- a/docs_src/interceptors/logging.py +++ b/docs_src/interceptors/logging.py @@ -1,7 +1,7 @@ from loguru import logger -from starlette.types import Receive, Scope, Send from esmerald import EsmeraldInterceptor +from lilya.types import Receive, Scope, Send class LoggingInterceptor(EsmeraldInterceptor): diff --git a/docs_src/interceptors/request_interceptor.py b/docs_src/interceptors/request_interceptor.py index eba29f7f..645da1d6 100644 --- a/docs_src/interceptors/request_interceptor.py +++ b/docs_src/interceptors/request_interceptor.py @@ -1,7 +1,6 @@ -from starlette.types import Receive, Scope, Send - from esmerald import EsmeraldInterceptor from esmerald.requests import Request +from lilya.types import Receive, Scope, Send class RequestParamInterceptor(EsmeraldInterceptor): diff --git a/docs_src/middleware/auth_middleware_example.py b/docs_src/middleware/auth_middleware_example.py index a620f68b..e791c83f 100644 --- a/docs_src/middleware/auth_middleware_example.py +++ b/docs_src/middleware/auth_middleware_example.py @@ -1,12 +1,11 @@ -from saffier.exceptions import ObjectNotFound -from starlette.requests import HTTPConnection -from starlette.types import ASGIApp - from esmerald.config.jwt import JWTConfig from esmerald.contrib.auth.saffier.base_user import User from esmerald.exceptions import NotAuthorized from esmerald.middleware.authentication import AuthResult, BaseAuthMiddleware from esmerald.security.jwt.token import Token +from lilya._internal._connection import Connection +from lilya.types import ASGIApp +from saffier.exceptions import ObjectNotFound class JWTAuthMiddleware(BaseAuthMiddleware): @@ -21,7 +20,7 @@ async def retrieve_user(self, user_id) -> User: except ObjectNotFound: raise NotAuthorized() - async def authenticate(self, request: HTTPConnection) -> AuthResult: + async def authenticate(self, request: Connection) -> AuthResult: token = request.headers.get(self.config.api_key_header) if not token: diff --git a/docs_src/middleware/available/cors.py b/docs_src/middleware/available/cors.py index 3df2a8fa..8e1b27c9 100644 --- a/docs_src/middleware/available/cors.py +++ b/docs_src/middleware/available/cors.py @@ -1,13 +1,12 @@ -from starlette.middleware import Middleware as StarletteMiddleware - from esmerald import Esmerald, EsmeraldAPISettings from esmerald.config import CORSConfig from esmerald.middleware import CORSMiddleware +from lilya.middleware import DefineMiddleware as LilyaMiddleware routes = [...] # Option one -middleware = [StarletteMiddleware(CORSMiddleware, allow_origins=["*"])] +middleware = [LilyaMiddleware(CORSMiddleware, allow_origins=["*"])] app = Esmerald(routes=routes, middleware=middleware) diff --git a/docs_src/middleware/available/csrf.py b/docs_src/middleware/available/csrf.py index 2e80935c..df209b5b 100644 --- a/docs_src/middleware/available/csrf.py +++ b/docs_src/middleware/available/csrf.py @@ -1,13 +1,12 @@ -from starlette.middleware import Middleware as StarletteMiddleware - from esmerald import Esmerald, EsmeraldAPISettings from esmerald.config import CSRFConfig from esmerald.middleware import CSRFMiddleware +from lilya.middleware import DefineMiddleware as LilyaMiddleware routes = [...] # Option one -middleware = [StarletteMiddleware(CSRFMiddleware, secret="your-long-unique-secret")] +middleware = [LilyaMiddleware(CSRFMiddleware, secret="your-long-unique-secret")] app = Esmerald(routes=routes, middleware=middleware) diff --git a/docs_src/middleware/available/gzip.py b/docs_src/middleware/available/gzip.py index 966d70ee..c1fd48d9 100644 --- a/docs_src/middleware/available/gzip.py +++ b/docs_src/middleware/available/gzip.py @@ -1,10 +1,9 @@ -from starlette.middleware import Middleware as StarletteMiddleware - from esmerald import Esmerald from esmerald.middleware import GZipMiddleware +from lilya.middleware import DefineMiddleware as LilyaMiddleware routes = [...] -middleware = [StarletteMiddleware(GZipMiddleware, minimum_size=1000)] +middleware = [LilyaMiddleware(GZipMiddleware, minimum_size=1000)] app = Esmerald(routes=routes, middleware=middleware) diff --git a/docs_src/middleware/available/https.py b/docs_src/middleware/available/https.py index 32864991..ea10e507 100644 --- a/docs_src/middleware/available/https.py +++ b/docs_src/middleware/available/https.py @@ -1,15 +1,14 @@ from typing import List -from starlette.middleware import Middleware as StarletteMiddleware - from esmerald import Esmerald, EsmeraldAPISettings from esmerald.middleware import HTTPSRedirectMiddleware from esmerald.types import Middleware +from lilya.middleware import DefineMiddleware as LilyaMiddleware routes = [...] # Option one -middleware = [StarletteMiddleware(HTTPSRedirectMiddleware)] +middleware = [LilyaMiddleware(HTTPSRedirectMiddleware)] app = Esmerald(routes=routes, middleware=middleware) @@ -19,6 +18,6 @@ class AppSettings(EsmeraldAPISettings): @property def middleware(self) -> List["Middleware"]: - # There is no need to wrap in a StarletteMiddleware here. + # There is no need to wrap in a LilyaMiddleware here. # Esmerald automatically will do it once the application is up and running. return [HTTPSRedirectMiddleware] diff --git a/docs_src/middleware/available/request_settings_middleware.py b/docs_src/middleware/available/request_settings_middleware.py index 79e7b634..df1deb66 100644 --- a/docs_src/middleware/available/request_settings_middleware.py +++ b/docs_src/middleware/available/request_settings_middleware.py @@ -1,8 +1,7 @@ -from starlette.middleware import Middleware as StarletteMiddleware - from esmerald import Esmerald from esmerald.middleware import RequestSettingsMiddleware +from lilya.middleware import DefineMiddleware as LilyaMiddleware -middleware = [StarletteMiddleware(RequestSettingsMiddleware)] +middleware = [LilyaMiddleware(RequestSettingsMiddleware)] app = Esmerald(routes=[...], middleware=middleware) diff --git a/docs_src/middleware/available/sessions.py b/docs_src/middleware/available/sessions.py index 0f3a9766..eca50d7c 100644 --- a/docs_src/middleware/available/sessions.py +++ b/docs_src/middleware/available/sessions.py @@ -1,13 +1,12 @@ -from starlette.middleware import Middleware as StarletteMiddleware - from esmerald import Esmerald, EsmeraldAPISettings from esmerald.config import SessionConfig from esmerald.middleware import SessionMiddleware +from lilya.middleware import DefineMiddleware as LilyaMiddleware routes = [...] # Option one -middleware = [StarletteMiddleware(SessionMiddleware, secret_key=...)] +middleware = [LilyaMiddleware(SessionMiddleware, secret_key=...)] app = Esmerald(routes=routes, middleware=middleware) diff --git a/docs_src/middleware/available/trusted_hosts.py b/docs_src/middleware/available/trusted_hosts.py index d9309e86..23ab18b1 100644 --- a/docs_src/middleware/available/trusted_hosts.py +++ b/docs_src/middleware/available/trusted_hosts.py @@ -1,15 +1,14 @@ from typing import List -from starlette.middleware import Middleware as StarletteMiddleware - from esmerald import Esmerald, EsmeraldAPISettings from esmerald.middleware import TrustedHostMiddleware +from lilya.middleware import DefineMiddleware as LilyaMiddleware routes = [...] # Option one middleware = [ - StarletteMiddleware(TrustedHostMiddleware, allowed_hosts=["www.example.com", "*.example.com"]) + LilyaMiddleware(TrustedHostMiddleware, allowed_hosts=["www.example.com", "*.example.com"]) ] app = Esmerald(routes=routes, middleware=middleware) diff --git a/docs_src/middleware/settings.py b/docs_src/middleware/settings.py index a61099eb..2888fbde 100644 --- a/docs_src/middleware/settings.py +++ b/docs_src/middleware/settings.py @@ -1,10 +1,9 @@ from typing import List -from starlette.middleware import Middleware as StarletteMiddleware - from esmerald import EsmeraldAPISettings from esmerald.middleware import GZipMiddleware, HTTPSRedirectMiddleware from esmerald.types import Middleware +from lilya.middleware import DefineMiddleware as LilyaMiddleware class AppSettings(EsmeraldAPISettings): @@ -15,5 +14,5 @@ def middleware(self) -> List["Middleware"]: """ return [ HTTPSRedirectMiddleware, - StarletteMiddleware(GZipMiddleware, minimum_size=500, compresslevel=9), + LilyaMiddleware(GZipMiddleware, minimum_size=500, compresslevel=9), ] diff --git a/docs_src/middleware/starlette_middleware.py b/docs_src/middleware/starlette_middleware.py index 956a732c..c4f814ea 100644 --- a/docs_src/middleware/starlette_middleware.py +++ b/docs_src/middleware/starlette_middleware.py @@ -1,12 +1,11 @@ -from starlette.middleware import Middleware as StarletteMiddleware - from esmerald import Esmerald from esmerald.middleware import HTTPSRedirectMiddleware, TrustedHostMiddleware +from lilya.middleware import DefineMiddleware as LilyaMiddleware app = Esmerald( routes=[...], middleware=[ - StarletteMiddleware(TrustedHostMiddleware, allowed_hosts=["example.com", "*.example.com"]), - StarletteMiddleware(HTTPSRedirectMiddleware), + LilyaMiddleware(TrustedHostMiddleware, allowed_hosts=["example.com", "*.example.com"]), + LilyaMiddleware(HTTPSRedirectMiddleware), ], ) diff --git a/docs_src/routing/handlers/websocket.py b/docs_src/routing/handlers/websocket.py index 94fe8816..935cc07e 100644 --- a/docs_src/routing/handlers/websocket.py +++ b/docs_src/routing/handlers/websocket.py @@ -5,7 +5,7 @@ @websocket(path="/") async def websocket_endpoint_switch(socket: WebSocket) -> None: await socket.accept() - await socket.send_json({"URL": str(socket.url_for("websocket_endpoint"))}) + await socket.send_json({"URL": str(socket.path_for("websocket_endpoint"))}) await socket.close() diff --git a/docs_src/routing/routes/converter_example.py b/docs_src/routing/routes/converter_example.py index 1329ee08..c8410bae 100644 --- a/docs_src/routing/routes/converter_example.py +++ b/docs_src/routing/routes/converter_example.py @@ -1,9 +1,9 @@ from datetime import datetime -from starlette.convertors import Convertor, register_url_convertor +from lilya.transformers import Transformer, register_path_transformer -class DateTimeConvertor(Convertor): +class DateTimeConvertor(Transformer): regex = "[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(.[0-9]+)?" def convert(self, value: str) -> datetime: @@ -13,4 +13,4 @@ def to_string(self, value: datetime) -> str: return value.strftime("%Y-%m-%dT%H:%M:%S") -register_url_convertor("datetime", DateTimeConvertor()) +register_path_transformer("datetime", DateTimeConvertor()) diff --git a/docs_src/routing/routes/middleware.py b/docs_src/routing/routes/middleware.py index 59971260..d01f800e 100644 --- a/docs_src/routing/routes/middleware.py +++ b/docs_src/routing/routes/middleware.py @@ -1,5 +1,3 @@ -from starlette.middleware.base import BaseHTTPMiddleware - from esmerald import Esmerald, Gateway, MiddlewareProtocol, get from esmerald.types import ASGIApp @@ -15,17 +13,9 @@ def __init__(self, app: "ASGIApp") -> None: self.app = app -class BaseRequestLoggingMiddleware(BaseHTTPMiddleware): - async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -> Response: # type: ignore - return await call_next(request) - - @get(path="/home", middleware=[RequestLoggingMiddlewareProtocol]) async def homepage() -> dict: return {"page": "ok"} -app = Esmerald( - routes=[Gateway(handler=homepage, middleware=[ExampleMiddleware])], - middleware=[BaseRequestLoggingMiddleware], -) +app = Esmerald(routes=[Gateway(handler=homepage, middleware=[ExampleMiddleware])]) diff --git a/docs_src/settings/custom/base.py b/docs_src/settings/custom/base.py index 014da615..791c966d 100644 --- a/docs_src/settings/custom/base.py +++ b/docs_src/settings/custom/base.py @@ -2,8 +2,9 @@ from esmerald import EsmeraldAPISettings from esmerald.conf.enums import EnvironmentType -from esmerald.middleware.basic import BasicHTTPMiddleware +from esmerald.middleware.https import HTTPSRedirectMiddleware from esmerald.types import Middleware +from lilya.middleware import DefineMiddleware class AppSettings(EsmeraldAPISettings): @@ -15,4 +16,4 @@ class AppSettings(EsmeraldAPISettings): @property def middleware(self) -> list[Middleware]: - return [BasicHTTPMiddleware] + return [DefineMiddleware(HTTPSRedirectMiddleware)] diff --git a/docs_src/testclient/example1.py b/docs_src/testclient/example1.py index 89834359..8c014dc6 100644 --- a/docs_src/testclient/example1.py +++ b/docs_src/testclient/example1.py @@ -1,6 +1,5 @@ -from starlette.responses import HTMLResponse - from esmerald.testclient import EsmeraldTestClient +from lilya.responses import HTMLResponse async def app(scope, receive, send): diff --git a/docs_src/testclient/example2.py b/docs_src/testclient/example2.py index 1b03d7d5..e7124d76 100644 --- a/docs_src/testclient/example2.py +++ b/docs_src/testclient/example2.py @@ -1,6 +1,5 @@ -from starlette.responses import HTMLResponse - from esmerald.testclient import EsmeraldTestClient +from lilya.responses import HTMLResponse async def app(scope, receive, send): diff --git a/docs_src/testclient/example3.py b/docs_src/testclient/example3.py index ab5a95ac..a351628a 100644 --- a/docs_src/testclient/example3.py +++ b/docs_src/testclient/example3.py @@ -1,6 +1,5 @@ -from starlette.responses import HTMLResponse - from esmerald.testclient import EsmeraldTestClient +from lilya.responses import HTMLResponse async def app(scope, receive, send): diff --git a/docs_src/testclient/example4.py b/docs_src/testclient/example4.py index 4931f72d..3977b9a6 100644 --- a/docs_src/testclient/example4.py +++ b/docs_src/testclient/example4.py @@ -1,11 +1,11 @@ import pytest -from starlette.responses import Response from esmerald import Gateway, Include, Request, WebSocket, WebSocketGateway, get, websocket from esmerald.enums import MediaType from esmerald.permissions import AllowAny, DenyAll from esmerald.responses import JSONResponse from esmerald.testclient import create_client +from lilya.responses import Response @get(path="/", permissions=[DenyAll]) diff --git a/esmerald/__init__.py b/esmerald/__init__.py index f3c9f5d8..a3516056 100644 --- a/esmerald/__init__.py +++ b/esmerald/__init__.py @@ -1,7 +1,7 @@ -__version__ = "2.7.4" +__version__ = "3.0.0-beta2" -from starlette import status +from lilya import status from esmerald.conf import settings from esmerald.conf.global_settings import EsmeraldAPISettings diff --git a/esmerald/applications.py b/esmerald/applications.py index 3ed68ec5..f1c7330e 100644 --- a/esmerald/applications.py +++ b/esmerald/applications.py @@ -15,12 +15,12 @@ cast, ) +from lilya.apps import Lilya +from lilya.middleware import DefineMiddleware # noqa +from lilya.types import Lifespan, Receive, Scope, Send from openapi_schemas_pydantic.v3_1_0 import Contact, License, SecurityScheme from openapi_schemas_pydantic.v3_1_0.open_api import OpenAPI from pydantic import AnyUrl, ValidationError -from starlette.applications import Starlette -from starlette.middleware import Middleware as StarletteMiddleware # noqa -from starlette.types import Lifespan, Receive, Scope, Send from typing_extensions import Annotated, Doc from esmerald.conf import settings as esmerald_settings @@ -72,7 +72,7 @@ AppType = TypeVar("AppType", bound="Esmerald") -class Esmerald(Starlette): +class Esmerald(Lilya): """ `Esmerald` application object. The main entry-point for any application/API with Esmerald. @@ -189,7 +189,7 @@ def __init__( Boolean indicating if the application should return the debug tracebacks on server errors, in other words, if you want to have debug errors being displayed. - Read more about this in the official [Starlette documentation](https://www.starlette.io/applications/#instantiating-the-application). + Read more about this in the official [Lilya documentation](https://www.lilya.dev/applications/#applications). !!! Tip Do not use this in production. @@ -589,7 +589,7 @@ async def has_permission(self, request: "Request", apiview: "APIGateHandler"): ```python from loguru import logger - from starlette.types import Receive, Scope, Send + from lilya.types import Receive, Scope, Send from esmerald import Esmerald from esmerald import EsmeraldInterceptor @@ -1075,7 +1075,7 @@ async def another(request: Request) -> str: Optional[Sequence["Middleware"]], Doc( """ - A global sequence of Starlette middlewares or `esmerald.middlewares` that are + A global sequence of Lilya middlewares or `esmerald.middlewares` that are used by the application. Read more about the [Middleware](https://esmerald.dev/middleware/middleware/). @@ -1089,8 +1089,8 @@ async def another(request: Request) -> str: app = Esmerald( routes=[...], middleware=[ - StarletteMiddleware(TrustedHostMiddleware, allowed_hosts=["example.com", "*.example.com"]), - StarletteMiddleware(HTTPSRedirectMiddleware), + DefineMiddleware(TrustedHostMiddleware, allowed_hosts=["example.com", "*.example.com"]), + DefineMiddleware(HTTPSRedirectMiddleware), ], ) ``` @@ -1509,7 +1509,7 @@ def extend(self, config: PluggableConfig) -> None: elif isinstance(settings_module, EsmeraldAPISettings): self.settings_module = settings_module # type: ignore elif is_class_and_subclass(settings_module, EsmeraldAPISettings): - self.settings_module = settings_module() + self.settings_module = settings_module() # type: ignore assert lifespan is None or ( on_startup is None and on_shutdown is None @@ -1602,7 +1602,7 @@ def extend(self, config: PluggableConfig) -> None: This can be defined as the application state and not request state which means that it does not change each request. - Learn more in the [Starlette documentation](https://www.starlette.io/applications/#storing-state-on-the-app-instance). + Learn more in the [Lilya documentation](https://www.lilya.dev/applications/#storing-state-on-the-app-instance). """ ), ] = State() @@ -1656,12 +1656,16 @@ def load_settings_value( """ if not is_boolean: if not value: - return self.get_settings_value(self.settings_module, esmerald_settings, name) + return self.get_settings_value( + cast("EsmeraldAPISettings", self.settings_module), esmerald_settings, name + ) return value if value is not None: return value - return self.get_settings_value(self.settings_module, esmerald_settings, name) + return self.get_settings_value( + cast("EsmeraldAPISettings", self.settings_module), esmerald_settings, name + ) def create_webhooks_signature_model(self, webhooks: Sequence[gateways.WebhookGateway]) -> None: """ @@ -1921,7 +1925,7 @@ def add_route( Optional[List["Middleware"]], Doc( """ - A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Starlette Middleware](https://www.starlette.io/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). + A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Lilya Middleware](https://www.lilya.dev/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). """ ), ] = None, @@ -1929,7 +1933,7 @@ def add_route( Optional[str], Doc( """ - The name for the Gateway. The name can be reversed by `url_path_for()`. + The name for the Gateway. The name can be reversed by `path_for()`. """ ), ] = None, @@ -2028,7 +2032,7 @@ def add_websocket_route( Optional[str], Doc( """ - The name for the WebSocketGateway. The name can be reversed by `url_path_for()`. + The name for the WebSocketGateway. The name can be reversed by `path_for()`. """ ), ] = None, @@ -2068,7 +2072,7 @@ def add_websocket_route( Optional[List["Middleware"]], Doc( """ - A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Starlette Middleware](https://www.starlette.io/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). + A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Lilya Middleware](https://www.lilya.dev/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). """ ), ] = None, @@ -2248,7 +2252,7 @@ async def hello(self) -> str: dependencies=route.dependencies, exception_handlers=route.exception_handlers, name=route.name, - middleware=route.middleware, + middleware=cast("List[Middleware]", route.middleware), interceptors=route.interceptors, permissions=route.permissions, routes=cast("Sequence[Union[APIGateHandler, Include]]", route.routes), @@ -2321,7 +2325,7 @@ def build_routes_exception_handlers( exception_handlers = {} if isinstance(route, Include): - exception_handlers.update(route.exception_handlers) # type: ignore + exception_handlers.update(route.exception_handlers) app = getattr(route, "app", None) if app and isinstance(app, (Esmerald, ChildEsmerald)): return exception_handlers @@ -2332,13 +2336,13 @@ def build_routes_exception_handlers( ) if isinstance(route, (gateways.Gateway, gateways.WebSocketGateway)): - exception_handlers.update(route.exception_handlers) # type: ignore + exception_handlers.update(route.exception_handlers) if route.handler.exception_handlers: - exception_handlers.update(route.handler.exception_handlers) # type: ignore + exception_handlers.update(route.handler.exception_handlers) return exception_handlers - def build_user_middleware_stack(self) -> List["StarletteMiddleware"]: + def build_user_middleware_stack(self) -> List["DefineMiddleware"]: """ Configures all the passed settings and wraps inside an exception handler. @@ -2352,31 +2356,33 @@ def build_user_middleware_stack(self) -> List["StarletteMiddleware"]: if self.allowed_hosts: user_middleware.append( - StarletteMiddleware(TrustedHostMiddleware, allowed_hosts=self.allowed_hosts) + DefineMiddleware(TrustedHostMiddleware, allowed_hosts=self.allowed_hosts) ) if self.cors_config: user_middleware.append( - StarletteMiddleware(CORSMiddleware, **self.cors_config.model_dump()) + DefineMiddleware(CORSMiddleware, **self.cors_config.model_dump()) ) if self.csrf_config: - user_middleware.append(StarletteMiddleware(CSRFMiddleware, config=self.csrf_config)) + user_middleware.append( + DefineMiddleware(CSRFMiddleware, **self.csrf_config.model_dump()) + ) if self.session_config: user_middleware.append( - StarletteMiddleware(SessionMiddleware, **self.session_config.model_dump()) + DefineMiddleware(SessionMiddleware, **self.session_config.model_dump()) ) for middleware in self._middleware or []: - if isinstance(middleware, StarletteMiddleware): + if isinstance(middleware, DefineMiddleware): user_middleware.append(middleware) else: - user_middleware.append(StarletteMiddleware(middleware)) + user_middleware.append(DefineMiddleware(middleware)) return user_middleware def build_middleware_stack(self) -> "ASGIApp": """ Esmerald uses the [esmerald.protocols.MiddlewareProtocol] (interfaces) and therefore we - wrap the StarletteMiddleware in a slighly different manner. + wrap the DefineMiddleware in a slighly different manner. Overriding the default build_middleware_stack will allow to control the initial middlewares that are loaded by Esmerald. All of these values can be updated. @@ -2402,7 +2408,7 @@ def build_middleware_stack(self) -> "ASGIApp": middleware = ( [ - StarletteMiddleware( + DefineMiddleware( EsmeraldAPIExceptionMiddleware, exception_handlers=exception_handlers, error_handler=error_handler, @@ -2411,12 +2417,12 @@ def build_middleware_stack(self) -> "ASGIApp": ] + self.user_middleware + [ - StarletteMiddleware( + DefineMiddleware( ExceptionMiddleware, handlers=exception_handlers, debug=debug, ), - StarletteMiddleware(AsyncExitStackMiddleware, config=self.async_exit_config), + DefineMiddleware(AsyncExitStackMiddleware, config=self.async_exit_config), ] ) diff --git a/esmerald/backgound.py b/esmerald/backgound.py index 56fa35b1..efa1fbb7 100644 --- a/esmerald/backgound.py +++ b/esmerald/backgound.py @@ -1,17 +1,12 @@ -from typing import Any, Callable, List, Optional, TypeVar +from typing import Any, Callable, List, Optional -from starlette.background import ( - BackgroundTask as StarletteBackgroundTask, # noqa - BackgroundTasks as StarletteBackgroundTasks, # noqa -) -from starlette.responses import Response as StarletteResponse # noqa +from lilya.background import Task, Tasks from typing_extensions import ParamSpec P = ParamSpec("P") -R = TypeVar("R", bound=StarletteResponse) -class BackgroundTask(StarletteBackgroundTask): +class BackgroundTask(Task): """ `BackgroundTask` as a single instance can be easily achieved. @@ -48,7 +43,7 @@ def __init__(self, func: Callable[P, Any], *args: P.args, **kwargs: P.kwargs) -> super().__init__(func, *args, **kwargs) -class BackgroundTasks(StarletteBackgroundTasks): +class BackgroundTasks(Tasks): """ Alternatively, the `BackgroundTasks` can also be used to be passed in. diff --git a/esmerald/conf/__init__.py b/esmerald/conf/__init__.py index 2569f896..463994f2 100644 --- a/esmerald/conf/__init__.py +++ b/esmerald/conf/__init__.py @@ -1,8 +1,9 @@ import os from typing import TYPE_CHECKING, Any, Optional, Type +from lilya._internal._module_loading import import_string + from esmerald.utils.functional import LazyObject, empty -from esmerald.utils.module_loading import import_string if TYPE_CHECKING: from esmerald.conf.global_settings import EsmeraldAPISettings diff --git a/esmerald/conf/enums.py b/esmerald/conf/enums.py index 21482b4a..d9d4b17d 100644 --- a/esmerald/conf/enums.py +++ b/esmerald/conf/enums.py @@ -1,8 +1,17 @@ from enum import Enum -class EnvironmentType(str, Enum): - """An Enum for environment types.""" +class BaseEnum(str, Enum): + def __str__(self) -> str: + return self.value # type: ignore + + def __repr__(self) -> str: + return str(self) + + +class EnvironmentType(BaseEnum): + """ + An Enum for environments.""" DEVELOPMENT = "development" TESTING = "testing" diff --git a/esmerald/conf/global_settings.py b/esmerald/conf/global_settings.py index c8c3aac1..806dd4fc 100644 --- a/esmerald/conf/global_settings.py +++ b/esmerald/conf/global_settings.py @@ -1,10 +1,10 @@ from functools import cached_property from typing import TYPE_CHECKING, Any, Dict, List, Optional, Sequence, Union +from lilya.types import Lifespan from openapi_schemas_pydantic.v3_1_0 import Contact, License, SecurityScheme from pydantic import AnyUrl from pydantic_settings import BaseSettings, SettingsConfigDict -from starlette.types import Lifespan from typing_extensions import Annotated, Doc from esmerald import __version__ @@ -77,7 +77,7 @@ class AppSettings(EsmeraldAPISettings): Boolean indicating if the application should return the debug tracebacks on server errors, in other words, if you want to have debug errors being displayed. - Read more about this in the official [Starlette documentation](https://www.starlette.io/applications/#instantiating-the-application). + Read more about this in the official [Lilya documentation](https://www.lilya.dev/applications/#instantiating-the-application). !!! Tip Do not use this in production as `True`. @@ -971,7 +971,7 @@ def openapi_config(self) -> OpenAPIConfig: @property def middleware(self) -> Sequence[Middleware]: """ - A global sequence of Starlette middlewares or `esmerald.middlewares` that are + A global sequence of Lilya middlewares or `esmerald.middlewares` that are used by the application. Read more about the [Middleware](https://esmerald.dev/middleware/middleware/). @@ -984,7 +984,7 @@ def middleware(self) -> Sequence[Middleware]: ```python from esmerald import EsmeraldAPISettings from esmerald.middleware import HTTPSRedirectMiddleware, TrustedHostMiddleware - from starlette.middleware import Middleware as StarletteMiddleware + from lilya.middleware import Middleware as StarletteMiddleware class AppSettings(EsmeraldAPISettings): @@ -1137,7 +1137,7 @@ def interceptors(self) -> List[Interceptor]: ```python from loguru import logger - from starlette.types import Receive, Scope, Send + from lilya.types import Receive, Scope, Send from esmerald import Esmerald, EsmeraldInterceptor, EsmeraldAPISettings diff --git a/esmerald/config/csrf.py b/esmerald/config/csrf.py index b4b1e1ee..cc25f69b 100644 --- a/esmerald/config/csrf.py +++ b/esmerald/config/csrf.py @@ -65,7 +65,7 @@ class CSRFConfig(BaseModel): """ ), ] = "X-CSRFToken" - cookie_secure: Annotated[ + secure: Annotated[ bool, Doc( """ @@ -73,7 +73,7 @@ class CSRFConfig(BaseModel): """ ), ] = False - cookie_httponly: Annotated[ + httponly: Annotated[ bool, Doc( """ @@ -81,7 +81,7 @@ class CSRFConfig(BaseModel): """ ), ] = False - cookie_samesite: Annotated[ + samesite: Annotated[ Literal["lax", "strict", "none"], Doc( """ @@ -89,7 +89,7 @@ class CSRFConfig(BaseModel): """ ), ] = "lax" - cookie_domain: Annotated[ + domain: Annotated[ Optional[str], Doc( """ diff --git a/esmerald/config/static_files.py b/esmerald/config/static_files.py index 90bb3bc2..611c5211 100644 --- a/esmerald/config/static_files.py +++ b/esmerald/config/static_files.py @@ -1,13 +1,12 @@ from pathlib import Path from typing import Any, Dict, List, Optional, Tuple, Union +from lilya._internal._path import clean_path +from lilya.staticfiles import StaticFiles +from lilya.types import ASGIApp from pydantic import BaseModel, DirectoryPath, constr, field_validator -from starlette.staticfiles import StaticFiles -from starlette.types import ASGIApp from typing_extensions import Annotated, Doc -from esmerald.utils.url import clean_path - class StaticFilesConfig(BaseModel): """ diff --git a/esmerald/context.py b/esmerald/context.py index f6f4400b..0372e328 100644 --- a/esmerald/context.py +++ b/esmerald/context.py @@ -2,7 +2,7 @@ import warnings from typing import TYPE_CHECKING, Any, Dict, Union -from starlette.datastructures import URL +from lilya.datastructures import URL from typing_extensions import Annotated, Doc if TYPE_CHECKING: @@ -133,7 +133,7 @@ def get_context_data(self, **kwargs: Any) -> Dict[Any, Any]: } return context_data - def url_for( + def path_for( self, name: Annotated[ str, @@ -162,5 +162,5 @@ def url_for( ), ], ) -> Any: - url: URL = self.request.url_for(name, **path_params) + url: URL = self.request.path_for(name, **path_params) return str(url) diff --git a/esmerald/contrib/auth/common/middleware.py b/esmerald/contrib/auth/common/middleware.py index 4116d879..e08a217e 100644 --- a/esmerald/contrib/auth/common/middleware.py +++ b/esmerald/contrib/auth/common/middleware.py @@ -1,8 +1,8 @@ from typing import TypeVar from jose import JWSError, JWTError -from starlette.requests import HTTPConnection -from starlette.types import ASGIApp +from lilya._internal._connection import Connection +from lilya.types import ASGIApp from esmerald.config.jwt import JWTConfig from esmerald.exceptions import AuthenticationError, NotAuthorized @@ -58,7 +58,7 @@ def __init__(self, app: "ASGIApp"): self.config = config self.user_model = user_model - async def authenticate(self, request: HTTPConnection) -> AuthResult: + async def authenticate(self, request: Connection) -> AuthResult: """ Retrieves the header default of the config and validates against the decoding. @@ -81,7 +81,7 @@ async def authenticate(self, request: HTTPConnection) -> AuthResult: token=auth_token, key=self.config.signing_key, algorithms=[self.config.algorithm], - ) # type: ignore + ) except (JWSError, JWTError) as e: raise AuthenticationError(str(e)) from e diff --git a/esmerald/contrib/auth/edgy/middleware.py b/esmerald/contrib/auth/edgy/middleware.py index ae14588d..7edbf5e7 100644 --- a/esmerald/contrib/auth/edgy/middleware.py +++ b/esmerald/contrib/auth/edgy/middleware.py @@ -1,7 +1,7 @@ from typing import Any, TypeVar from edgy import ObjectNotFound -from starlette.types import ASGIApp +from lilya.types import ASGIApp from esmerald.config.jwt import JWTConfig from esmerald.contrib.auth.common.middleware import CommonJWTAuthMiddleware diff --git a/esmerald/contrib/auth/hashers.py b/esmerald/contrib/auth/hashers.py index 8bab7529..484586ea 100644 --- a/esmerald/contrib/auth/hashers.py +++ b/esmerald/contrib/auth/hashers.py @@ -4,12 +4,12 @@ import warnings from typing import Any, Callable, Dict, Optional, Sequence, Union +from lilya._internal._module_loading import import_string from passlib.context import CryptContext from esmerald.conf import settings from esmerald.exceptions import ImproperlyConfigured from esmerald.utils.crypto import get_random_string as _get_random_string -from esmerald.utils.module_loading import import_string from .constants import ( RANDOM_STRING_CHARS, diff --git a/esmerald/contrib/auth/mongoz/middleware.py b/esmerald/contrib/auth/mongoz/middleware.py index 12bbf911..fd6fff38 100644 --- a/esmerald/contrib/auth/mongoz/middleware.py +++ b/esmerald/contrib/auth/mongoz/middleware.py @@ -1,8 +1,8 @@ from typing import Any, TypeVar import bson +from lilya.types import ASGIApp from mongoz import DocumentNotFound -from starlette.types import ASGIApp from esmerald.config.jwt import JWTConfig from esmerald.contrib.auth.common.middleware import CommonJWTAuthMiddleware diff --git a/esmerald/contrib/auth/saffier/middleware.py b/esmerald/contrib/auth/saffier/middleware.py index 3bcf2221..dce2f627 100644 --- a/esmerald/contrib/auth/saffier/middleware.py +++ b/esmerald/contrib/auth/saffier/middleware.py @@ -1,7 +1,7 @@ from typing import Any, TypeVar +from lilya.types import ASGIApp from saffier.exceptions import DoesNotFound -from starlette.types import ASGIApp from esmerald.config.jwt import JWTConfig from esmerald.contrib.auth.common.middleware import CommonJWTAuthMiddleware diff --git a/esmerald/core/di/provider.py b/esmerald/core/di/provider.py index a137cbab..6279c9b0 100644 --- a/esmerald/core/di/provider.py +++ b/esmerald/core/di/provider.py @@ -4,8 +4,9 @@ from typing import Any, Callable, Tuple, cast +from lilya._internal._module_loading import import_string + from esmerald.exceptions import ImproperlyConfigured -from esmerald.utils.module_loading import import_string def _lookup(klass: Any, comp: Any, import_path: Any) -> Any: # pragma: no cover diff --git a/esmerald/core/directives/base.py b/esmerald/core/directives/base.py index 1b1db29f..972e0adc 100644 --- a/esmerald/core/directives/base.py +++ b/esmerald/core/directives/base.py @@ -1,20 +1,13 @@ -import argparse -import os -import sys -from abc import ABC, abstractmethod -from typing import Any, Type +from lilya.cli.base import BaseDirective as LilyaBaseDirective import esmerald -from esmerald.core.directives.exceptions import DirectiveError -from esmerald.core.directives.parsers import DirectiveParser from esmerald.core.terminal.print import Print from esmerald.parsers import ArbitraryExtraBaseModel -from esmerald.utils.helpers import is_async_callable printer = Print() -class BaseDirective(ArbitraryExtraBaseModel, ABC): +class BaseDirective(ArbitraryExtraBaseModel, LilyaBaseDirective): """The base class from which all directrives derive""" help: str = "" @@ -24,52 +17,3 @@ def get_version(self) -> str: Returns the current version of Esmerald. """ return esmerald.__version__ - - def add_arguments(self, parser: Type["argparse.ArgumentParser"]) -> Any: - """ - Entrypoint for directives and custom arguments - """ - ... - - def create_parser(self, name: str, subdirective: str, **kwargs: Any) -> DirectiveParser: - parser = DirectiveParser( - prog="{} {}".format(os.path.basename(name), subdirective), - description=self.help, - **kwargs, - ) - self.add_arguments(parser) # type: ignore - return parser - - async def execute_from_command(self, argv: Any, program_name: str, position: int = 5) -> None: - """ - Executes dynamically the directive from the command line and passing the parameters - """ - parser = self.create_parser(program_name, argv[position]) - - options = parser.parse_args(argv[position + 1 :]) - cmd_options = vars(options) - - # Move positional args out of options to mimic legacy optparse - args = cmd_options.pop("args", ()) - try: - await self.run(*args, **cmd_options) - except DirectiveError as e: - printer.write_error("{}: {}".format(e.__class__.__name__, e)) - sys.exit(e.returncode) - - async def run(self, *args: Any, **options: Any) -> Any: - """ - Executes the handle() - """ - if not is_async_callable(self.handle): - output = self.handle(*args, **options) - else: - output = await self.handle(*args, **options) - if output: - printer.write_info(output) - return output - - @abstractmethod - def handle(self, *args: Any, **options: Any) -> Any: - """The logic of the directive. Subclasses must implement this method""" - raise NotImplementedError("subclasses of BaseDirective must provide a handle() method.") diff --git a/esmerald/core/directives/exceptions.py b/esmerald/core/directives/exceptions.py index da89d860..96029ac3 100644 --- a/esmerald/core/directives/exceptions.py +++ b/esmerald/core/directives/exceptions.py @@ -1,16 +1,13 @@ -from typing import TYPE_CHECKING +from typing import Any from esmerald.exceptions import EsmeraldAPIException -if TYPE_CHECKING: - from pydantic.typing import DictAny - class DirectiveError(EsmeraldAPIException): """ Exception indicating a problem while executing a directive. """ - def __init__(self, detail: str, returncode: int = 1, **kwargs: "DictAny"): + def __init__(self, detail: str, returncode: int = 1, **kwargs: Any): self.returncode = returncode super().__init__(detail=detail, **kwargs) diff --git a/esmerald/core/directives/operations/run.py b/esmerald/core/directives/operations/run.py index 1ffb49b6..565917cf 100644 --- a/esmerald/core/directives/operations/run.py +++ b/esmerald/core/directives/operations/run.py @@ -4,14 +4,14 @@ from typing import TYPE_CHECKING, Any, Optional, TypeVar, Union import click -from starlette.types import Lifespan +from lilya.compat import run_sync +from lilya.types import Lifespan from esmerald.core.directives.constants import APP_PARAMETER, ESMERALD_DISCOVER_APP from esmerald.core.directives.env import DirectiveEnv from esmerald.core.directives.utils import fetch_directive from esmerald.core.terminal.print import Print from esmerald.routing.events import generate_lifespan_events -from esmerald.utils.sync import run_sync if TYPE_CHECKING: from esmerald.applications import ChildEsmerald, Esmerald diff --git a/esmerald/core/directives/operations/shell/base.py b/esmerald/core/directives/operations/shell/base.py index e8bf08da..f27cf6e0 100644 --- a/esmerald/core/directives/operations/shell/base.py +++ b/esmerald/core/directives/operations/shell/base.py @@ -1,14 +1,11 @@ import select import sys -from typing import Any, Callable, Optional, Sequence import click -import nest_asyncio +from lilya.cli.directives.operations.shell.base import handle_lifespan_events, run_shell +from lilya.compat import run_sync from esmerald.core.directives.env import DirectiveEnv -from esmerald.core.directives.operations.shell.enums import ShellOption -from esmerald.routing.events import AyncLifespanContextManager -from esmerald.utils.sync import run_sync @click.option( @@ -40,36 +37,3 @@ def shell(env: DirectiveEnv, kernel: bool) -> None: ) run_sync(run_shell(env.app, lifespan, kernel)) # type: ignore return None - - -async def run_shell(app: Any, lifespan: Any, kernel: str) -> None: - """Executes the database shell connection""" - - async with lifespan(app): - if kernel == ShellOption.IPYTHON: - from esmerald.core.directives.operations.shell.ipython import get_ipython - - ipython_shell = get_ipython(app=app) - nest_asyncio.apply() - ipython_shell() - else: - from esmerald.core.directives.operations.shell.ptpython import get_ptpython - - ptpython = get_ptpython(app=app) - nest_asyncio.apply() - ptpython() - - -def handle_lifespan_events( - on_startup: Optional[Sequence[Callable]] = None, - on_shutdown: Optional[Sequence[Callable]] = None, - lifespan: Optional[Any] = None, -) -> Any: - """Handles with the lifespan events in the new Starlette format of lifespan. - This adds a mask that keeps the old `on_startup` and `on_shutdown` events variable - declaration for legacy and comprehension purposes and build the async context manager - for the lifespan. - """ - if lifespan: - return lifespan - return AyncLifespanContextManager(on_startup=on_startup, on_shutdown=on_shutdown) diff --git a/esmerald/core/directives/operations/shell/utils.py b/esmerald/core/directives/operations/shell/utils.py index 5640a37a..367af977 100644 --- a/esmerald/core/directives/operations/shell/utils.py +++ b/esmerald/core/directives/operations/shell/utils.py @@ -3,10 +3,10 @@ from typing import Any, Dict import pydantic +from lilya._internal._module_loading import import_string import esmerald from esmerald.core.terminal import OutputColour, Print -from esmerald.utils.module_loading import import_string printer = Print() diff --git a/esmerald/core/directives/operations/show_urls.py b/esmerald/core/directives/operations/show_urls.py index acdb1c49..20433811 100644 --- a/esmerald/core/directives/operations/show_urls.py +++ b/esmerald/core/directives/operations/show_urls.py @@ -4,6 +4,7 @@ from typing import TYPE_CHECKING, Any, Optional, Union import click +from lilya._internal._path import clean_path from rich.console import Console from rich.table import Table @@ -13,10 +14,9 @@ from esmerald.core.terminal import OutputColour, Print, Terminal from esmerald.enums import HttpMethod from esmerald.routing.apis.base import View -from esmerald.utils.url import clean_path if TYPE_CHECKING: - from starlette.routing import BaseRoute + from lilya.routing import BasePath from esmerald.applications import ChildEsmerald, Esmerald from esmerald.routing.router import Router @@ -82,7 +82,7 @@ def get_routes_table(app: Optional[Union["Esmerald", "ChildEsmerald"]], table: T table.add_column("HTTP Methods", style=OutputColour.RED, vertical="middle") def parse_routes( - app: Optional[Union["Esmerald", "ChildEsmerald", "Router", "BaseRoute"]], + app: Optional[Union["Esmerald", "ChildEsmerald", "Router", "BasePath"]], table: Table, route: Optional[Any] = None, prefix: Optional[str] = "", diff --git a/esmerald/core/directives/parsers.py b/esmerald/core/directives/parsers.py index f412fb69..3432d3e2 100644 --- a/esmerald/core/directives/parsers.py +++ b/esmerald/core/directives/parsers.py @@ -1,9 +1 @@ -from argparse import ArgumentParser - - -class DirectiveParser(ArgumentParser): - """ - Customized ArgumentParser class to improve some error messages and prevent SystemExit. - """ - - ... +from lilya.cli.parsers import DirectiveParser as DirectiveParser diff --git a/esmerald/datastructures/__init__.py b/esmerald/datastructures/__init__.py index 840735dc..79c906a9 100644 --- a/esmerald/datastructures/__init__.py +++ b/esmerald/datastructures/__init__.py @@ -3,9 +3,8 @@ Address, Cookie, FormData, - Headers, - MutableHeaders, - QueryParams, + Header, + QueryParam, ResponseContainer, ResponseHeader, Secret, @@ -24,10 +23,9 @@ "Cookie", "File", "FormData", - "Headers", + "Header", "JSON", - "MutableHeaders", - "QueryParams", + "QueryParam", "Redirect", "ResponseContainer", "ResponseHeader", diff --git a/esmerald/datastructures/base.py b/esmerald/datastructures/base.py index 512ab8c4..5ac76145 100644 --- a/esmerald/datastructures/base.py +++ b/esmerald/datastructures/base.py @@ -1,5 +1,4 @@ from abc import ABC, abstractmethod -from copy import copy from http.cookies import SimpleCookie from typing import ( TYPE_CHECKING, @@ -16,6 +15,18 @@ cast, ) +from lilya._internal._message import Address as Address # noqa: F401 +from lilya.datastructures import ( + URL as URL, # noqa: F401 + DataUpload as LilyaUploadFile, # noqa + FormData as FormData, # noqa: F401 + Header as Header, # noqa: F401 + QueryParam as QueryParam, # noqa: F401 + Secret as Secret, # noqa + State as State, # noqa: F401 + URLPath as URLPath, # noqa: F401 +) +from lilya.responses import Response as LilyaResponse # noqa from pydantic import BaseModel, ConfigDict, field_validator # noqa from pydantic._internal._schema_generation_shared import ( GetJsonSchemaHandler as GetJsonSchemaHandler, @@ -26,31 +37,18 @@ PlainValidatorFunctionSchema, with_info_plain_validator_function as general_plain_validator_function, ) -from starlette.datastructures import ( - URL as URL, # noqa: F401 - Address as Address, # noqa: F401 - FormData as FormData, # noqa: F401 - Headers as Headers, # noqa: F401 - MutableHeaders as MutableHeaders, # noqa - QueryParams as QueryParams, # noqa: F401 - Secret as StarletteSecret, # noqa - State as StarletteStateClass, # noqa: F401 - UploadFile as StarletteUploadFile, # noqa - URLPath as URLPath, # noqa: F401 -) -from starlette.responses import Response as StarletteResponse # noqa from typing_extensions import Literal from esmerald.backgound import BackgroundTask, BackgroundTasks # noqa from esmerald.enums import MediaType -R = TypeVar("R", bound=StarletteResponse) +R = TypeVar("R", bound=LilyaResponse) if TYPE_CHECKING: # pragma: no cover from esmerald.applications import Esmerald -class UploadFile(StarletteUploadFile): # pragma: no cover +class UploadFile(LilyaUploadFile): # pragma: no cover """ Adding pydantic specific functionalitty for parsing. """ @@ -61,13 +59,13 @@ def __get_validators__(cls: Type["UploadFile"]) -> Iterable[Callable[..., Any]]: @classmethod def validate(cls: Type["UploadFile"], v: Any) -> Any: - if not isinstance(v, StarletteUploadFile): + if not isinstance(v, LilyaUploadFile): raise ValueError(f"Expected UploadFile, got: {type(v)}") return v @classmethod def _validate(cls, __input_value: Any, _: Any) -> "UploadFile": - if not isinstance(__input_value, StarletteUploadFile): + if not isinstance(__input_value, LilyaUploadFile): raise ValueError(f"Expected UploadFile, got: {type(__input_value)}") return cast(UploadFile, __input_value) @@ -84,33 +82,6 @@ def __get_pydantic_core_schema__( return cast(PlainValidatorFunctionSchema, general_plain_validator_function(cls._validate)) -class Secret(StarletteSecret): # pragma: no cover - def __len__(self) -> int: - return len(self._value) - - -class State(StarletteStateClass): # pragma: no cover - state: Dict[str, Any] - - def __copy__(self) -> "State": - return self.__class__(copy(self._state)) - - def __len__(self) -> int: - return len(self._state) - - def __getattr__(self, key: str) -> Any: - try: - return self._state[key] - except KeyError as e: - raise AttributeError(f"State has no key '{key}'") from e - - def __getitem__(self, key: str) -> Any: - return self._state[key] - - def copy(self) -> "State": - return copy(self) - - class Cookie(BaseModel): key: str value: Optional[str] = None diff --git a/esmerald/datastructures/file.py b/esmerald/datastructures/file.py index 11ac199d..8b44c380 100644 --- a/esmerald/datastructures/file.py +++ b/esmerald/datastructures/file.py @@ -1,8 +1,8 @@ import os from typing import TYPE_CHECKING, Any, Dict, Optional, Type, Union, cast -from pydantic import FilePath, field_validator, model_validator # noqa -from starlette.responses import FileResponse # noqa +from lilya.responses import FileResponse # noqa +from pydantic import FilePath, model_validator # noqa from typing_extensions import Annotated, Doc from esmerald.datastructures.base import ResponseContainer diff --git a/esmerald/datastructures/redirect.py b/esmerald/datastructures/redirect.py index e27089a6..16ce10a9 100644 --- a/esmerald/datastructures/redirect.py +++ b/esmerald/datastructures/redirect.py @@ -1,6 +1,6 @@ from typing import TYPE_CHECKING, Any, Dict, Type, Union -from starlette.responses import RedirectResponse # noqa +from lilya.responses import RedirectResponse # noqa from typing_extensions import Annotated, Doc from esmerald.datastructures.base import ResponseContainer # noqa diff --git a/esmerald/datastructures/stream.py b/esmerald/datastructures/stream.py index f9f61bb3..486ca4a1 100644 --- a/esmerald/datastructures/stream.py +++ b/esmerald/datastructures/stream.py @@ -13,7 +13,7 @@ Union, ) -from starlette.responses import StreamingResponse # noqa +from lilya.responses import StreamingResponse # noqa from typing_extensions import Annotated, Doc from esmerald.datastructures.base import ResponseContainer # noqa diff --git a/esmerald/enums.py b/esmerald/enums.py index d103b277..b6bc20d7 100644 --- a/esmerald/enums.py +++ b/esmerald/enums.py @@ -1,7 +1,15 @@ from enum import Enum -class HttpMethod(str, Enum): +class StrEnum(str, Enum): + def __str__(self) -> str: + return self.value # type: ignore + + def __repr__(self) -> str: + return str(self) + + +class HttpMethod(StrEnum, Enum): GET = "GET" POST = "POST" PUT = "PUT" @@ -12,7 +20,7 @@ class HttpMethod(str, Enum): TRACE = "TRACE" -class MediaType(str, Enum): +class MediaType(StrEnum, Enum): JSON = "application/json" HTML = "text/html" TEXT = "text/plain" @@ -22,23 +30,23 @@ class MediaType(str, Enum): OCTET = "application/octet-stream" -class OpenAPIMediaType(str, Enum): +class OpenAPIMediaType(StrEnum, Enum): OPENAPI_YAML = "application/vnd.oai.openapi" OPENAPI_JSON = "application/vnd.oai.openapi+json" -class EncodingType(str, Enum): +class EncodingType(StrEnum, Enum): JSON = "application/json" MULTI_PART = "multipart/form-data" URL_ENCODED = "application/x-www-form-urlencoded" -class ScopeType(str, Enum): +class ScopeType(StrEnum, Enum): HTTP = "http" WEBSOCKET = "websocket" -class ParamType(str, Enum): +class ParamType(StrEnum, Enum): PATH = "path" QUERY = "query" COOKIE = "cookie" diff --git a/esmerald/exception_handlers.py b/esmerald/exception_handlers.py index bce4edf0..edc32474 100644 --- a/esmerald/exception_handlers.py +++ b/esmerald/exception_handlers.py @@ -1,11 +1,11 @@ from typing import Union +from lilya import status +from lilya.exceptions import HTTPException as LilyaHTTPException +from lilya.requests import Request +from lilya.responses import Response as LilyaResponse from orjson import loads from pydantic import ValidationError -from starlette import status -from starlette.exceptions import HTTPException as StarletteHTTPException -from starlette.requests import Request -from starlette.responses import Response as StarletteResponse from esmerald.enums import MediaType from esmerald.exceptions import ExceptionErrorMap, HTTPException, ImproperlyConfigured @@ -13,10 +13,10 @@ async def http_exception_handler( - request: Request, exc: Union[HTTPException, StarletteHTTPException] + request: Request, exc: Union[HTTPException, LilyaHTTPException] ) -> Union[JSONResponse, Response]: # pragma: no cover """ - Default exception handler for StarletteHTTPException and Esmerald HTTPException. + Default exception handler for LilyaHTTPException and Esmerald HTTPException. """ extra = getattr(exc, "extra", None) headers = getattr(exc, "headers", None) @@ -65,13 +65,13 @@ async def http_error_handler( async def improperly_configured_exception_handler( request: Request, exc: ImproperlyConfigured -) -> StarletteResponse: # pragma: no cover +) -> LilyaResponse: # pragma: no cover """ When an ImproperlyConfiguredException is raised. """ status_code = ( exc.status_code - if isinstance(exc, StarletteHTTPException) + if isinstance(exc, LilyaHTTPException) else status.HTTP_500_INTERNAL_SERVER_ERROR ) if not status_code: @@ -80,7 +80,7 @@ async def improperly_configured_exception_handler( content = {"detail": exc.detail} if exc.extra: content.update({"extra": exc.extra}) # type: ignore[dict-item] - headers = exc.headers if isinstance(exc, (HTTPException, StarletteHTTPException)) else None + headers = exc.headers if isinstance(exc, (HTTPException, LilyaHTTPException)) else None return Response( media_type=MediaType.JSON, diff --git a/esmerald/exceptions.py b/esmerald/exceptions.py index 5962cab5..b20f8d2a 100644 --- a/esmerald/exceptions.py +++ b/esmerald/exceptions.py @@ -1,12 +1,13 @@ from http import HTTPStatus from typing import Any, Dict, Optional, Type, Union -from pydantic import BaseModel, create_model -from starlette import status -from starlette.exceptions import ( - HTTPException as StarletteHTTPException, - WebSocketException as StarletteWebSocketException, +from lilya import status +from lilya.exceptions import ( + HTTPException as LilyaHTTPException, + ImproperlyConfigured as ImproperlyConfigured, + WebSocketException as LilyaWebSocketException, ) +from pydantic import BaseModel, create_model from typing_extensions import Annotated, Doc RequestErrorModel: Type[BaseModel] = create_model("Request") @@ -27,7 +28,7 @@ def __str__(self) -> str: return "".join(self.args).strip() -class HTTPException(StarletteHTTPException, EsmeraldAPIException): +class HTTPException(LilyaHTTPException, EsmeraldAPIException): """ Base of all `Esmerald` execeptions. @@ -104,9 +105,6 @@ class EsmeraldError(RuntimeError): ... -class ImproperlyConfigured(HTTPException, ValueError): ... - - class ImproperlyMiddlewareConfigured(ImproperlyConfigured): ... @@ -158,7 +156,7 @@ class MissingDependency(EsmeraldAPIException, ImportError): ... class OpenAPIException(ImproperlyConfigured): ... -class WebSocketException(StarletteWebSocketException): ... +class WebSocketException(LilyaWebSocketException): ... class AuthenticationError(HTTPException): diff --git a/esmerald/interceptors/interceptor.py b/esmerald/interceptors/interceptor.py index 26f5e683..f011c205 100644 --- a/esmerald/interceptors/interceptor.py +++ b/esmerald/interceptors/interceptor.py @@ -1,10 +1,8 @@ from abc import ABC -from typing import TYPE_CHECKING -from esmerald.protocols.interceptor import InterceptorProtocol +from lilya.types import Receive, Scope, Send -if TYPE_CHECKING: # pragma: no cover - from starlette.types import Receive, Scope, Send +from esmerald.protocols.interceptor import InterceptorProtocol class EsmeraldInterceptor(ABC, InterceptorProtocol): @@ -21,7 +19,7 @@ class EsmeraldInterceptor(ABC, InterceptorProtocol): from esmerald import Esmerald, Gateway, JSONResponse, get from loguru import logger - from starlette.types import Receive, Scope, Send + from lilya.types import Receive, Scope, Send class LoggingInterceptor(EsmeraldInterceptor): @@ -47,7 +45,7 @@ async def intercept(self, scope: "Scope", receive: "Receive", send: "Send") -> N ```python from loguru import logger - from starlette.types import Receive, Scope, Send + from lilya.types import Receive, Scope, Send class LoggingInterceptor(EsmeraldInterceptor): diff --git a/esmerald/middleware/__init__.py b/esmerald/middleware/__init__.py index 81fd21ae..cfdba106 100644 --- a/esmerald/middleware/__init__.py +++ b/esmerald/middleware/__init__.py @@ -1,6 +1,5 @@ from .asyncexitstack import AsyncExitStackMiddleware from .authentication import BaseAuthMiddleware -from .basic import BasicHTTPMiddleware from .cors import CORSMiddleware from .csrf import CSRFMiddleware from .gzip import GZipMiddleware @@ -12,7 +11,6 @@ __all__ = [ "AsyncExitStackMiddleware", "BaseAuthMiddleware", - "BasicHTTPMiddleware", "CORSMiddleware", "CSRFMiddleware", "GZipMiddleware", diff --git a/esmerald/middleware/_exception_handlers.py b/esmerald/middleware/_exception_handlers.py index 9b8df699..21143b96 100644 --- a/esmerald/middleware/_exception_handlers.py +++ b/esmerald/middleware/_exception_handlers.py @@ -1,25 +1,25 @@ import inspect import typing -from starlette._exception_handler import ( +from lilya._internal._exception_handlers import ( ExceptionHandlers, StatusHandlers, _lookup_exception_handler, ) -from starlette._utils import is_async_callable -from starlette.concurrency import run_in_threadpool -from starlette.exceptions import HTTPException -from starlette.requests import Request -from starlette.responses import Response -from starlette.types import ASGIApp, Message, Receive, Scope, Send -from starlette.websockets import WebSocket +from lilya.compat import is_async_callable +from lilya.concurrency import run_in_threadpool +from lilya.exceptions import HTTPException +from lilya.requests import Request +from lilya.responses import Response +from lilya.types import ASGIApp, Message, Receive, Scope, Send +from lilya.websockets import WebSocket def wrap_app_handling_exceptions(app: ASGIApp, conn: typing.Union[Request, WebSocket]) -> ASGIApp: exception_handlers: ExceptionHandlers status_handlers: StatusHandlers try: - exception_handlers, status_handlers = conn.scope["starlette.exception_handlers"] + exception_handlers, status_handlers = conn.scope["lilya.exception_handlers"] except KeyError: # pragma: no cover exception_handlers, status_handlers = {}, {} @@ -67,6 +67,6 @@ async def sender(message: Message) -> None: else: await handler(conn, exc) else: - await run_in_threadpool(handler, conn, exc) # type: ignore + await run_in_threadpool(handler, conn, exc) return wrapped_app diff --git a/esmerald/middleware/asyncexitstack.py b/esmerald/middleware/asyncexitstack.py index 8a508f48..e0ccdb84 100644 --- a/esmerald/middleware/asyncexitstack.py +++ b/esmerald/middleware/asyncexitstack.py @@ -1,7 +1,7 @@ from contextlib import AsyncExitStack from typing import Optional -from starlette.types import ASGIApp, Receive, Scope, Send +from lilya.types import ASGIApp, Receive, Scope, Send from esmerald.config import AsyncExitConfig from esmerald.protocols.middleware import MiddlewareProtocol diff --git a/esmerald/middleware/authentication.py b/esmerald/middleware/authentication.py index d50183ef..f870f9cf 100644 --- a/esmerald/middleware/authentication.py +++ b/esmerald/middleware/authentication.py @@ -1,8 +1,8 @@ from abc import ABC, abstractmethod from typing import Any, Set -from starlette.requests import HTTPConnection -from starlette.types import ASGIApp, Receive, Scope, Send +from lilya._internal._connection import Connection +from lilya.types import ASGIApp, Receive, Scope, Send from typing_extensions import Annotated, Doc from esmerald.enums import ScopeType @@ -29,7 +29,7 @@ class BaseAuthMiddleware(ABC, MiddlewareProtocol): # pragma: no cover It is not mandatory to use it and you are free to implement your. - Esmerald being based on Starlette, also offers a simple but powerful + Esmerald being based on Lilya, also offers a simple but powerful interface for handling `authentication` and [permissions](https://esmerald.dev/permissions/). Once you have installed the `AuthenticationMiddleware` and implemented the @@ -70,12 +70,12 @@ async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: await self.app(scope, receive, send) return - auth_result = await self.authenticate(HTTPConnection(scope)) + auth_result = await self.authenticate(Connection(scope)) scope["user"] = auth_result.user await self.app(scope, receive, send) @abstractmethod - async def authenticate(self, request: HTTPConnection) -> AuthResult: + async def authenticate(self, request: Connection) -> AuthResult: """ The abstract method that needs to be implemented for any authentication middleware. """ diff --git a/esmerald/middleware/basic.py b/esmerald/middleware/basic.py deleted file mode 100644 index bc198892..00000000 --- a/esmerald/middleware/basic.py +++ /dev/null @@ -1,22 +0,0 @@ -from typing import TypeVar - -from starlette.middleware.base import ( - BaseHTTPMiddleware, # noqa - RequestResponseEndpoint as RequestResponseEndpoint, # noqa -) -from starlette.requests import Request as StarletteRequest -from starlette.responses import Response - -from esmerald.requests import Request - -Req = TypeVar("Req", Request, StarletteRequest) - - -class BasicHTTPMiddleware(BaseHTTPMiddleware): - """ - BaseHTTPMiddleware of all Esmerald applications. - """ - - async def dispatch(self, request: Req, call_next: RequestResponseEndpoint) -> Response: - response = await call_next(request) - return response diff --git a/esmerald/middleware/cors.py b/esmerald/middleware/cors.py index d8176ede..ebf72cac 100644 --- a/esmerald/middleware/cors.py +++ b/esmerald/middleware/cors.py @@ -1,3 +1,3 @@ -from starlette.middleware.cors import CORSMiddleware # noqa +from lilya.middleware.cors import CORSMiddleware # noqa __all__ = ["CORSMiddleware"] diff --git a/esmerald/middleware/csrf.py b/esmerald/middleware/csrf.py index 263e6a7a..fd7e520a 100644 --- a/esmerald/middleware/csrf.py +++ b/esmerald/middleware/csrf.py @@ -1,149 +1,3 @@ -""" -The MIT License (MIT) +from lilya.middleware.csrf import CSRFMiddleware # noqa -Copyright (c) 2021, 2022 Starlite-API - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -""" - -import hashlib -import hmac -import secrets -from typing import TYPE_CHECKING, Optional - -from starlette.datastructures import MutableHeaders -from starlette.types import ASGIApp, Message, Receive, Scope, Send - -from esmerald.datastructures import Cookie -from esmerald.enums import ScopeType -from esmerald.exceptions import PermissionDenied -from esmerald.protocols.middleware import MiddlewareProtocol -from esmerald.requests import Request - -if TYPE_CHECKING: # pragma: no cover - from esmerald.config import CSRFConfig - -CSRF_SECRET_BYTES = 32 -CSRF_SECRET_LENGTH = CSRF_SECRET_BYTES * 2 - - -class CSRFMiddleware(MiddlewareProtocol): - def __init__( - self, - app: "ASGIApp", - config: "CSRFConfig", - ): - """CSRF Middleware class. - - This Middleware protects against attacks by setting a CSRF cookie with a token and verifying it in request headers. - - Args: - app: The 'next' ASGI app to call. - config: The CSRFConfig instance. - """ - super().__init__(app) - self.app = app - self.config = config - - async def __call__(self, scope: "Scope", receive: "Receive", send: "Send") -> None: - if scope["type"] != ScopeType.HTTP: - await self.app(scope, receive, send) - return - - request = Request(scope=scope) - csrf_cookie = request.cookies.get(self.config.cookie_name) - current_token = request.headers.get(self.config.header_name) - - if request.method in self.config.safe_methods: - await self.app(scope, receive, self.get_send_wrapper(send, csrf_cookie)) - elif self._csrf_tokens_match(current_token, csrf_cookie): - await self.app(scope, receive, send) - else: - raise PermissionDenied(detail="CSRF token verification failed.") - - def get_send_wrapper(self, send: "Send", csrf_cookie: Optional[str]) -> "Send": - async def send_wrapper(message: "Message") -> None: - """Send function that wraps the original send to inject a - cookie. - - Args: - message: An ASGI 'Message' - - Returns: - None - """ - if csrf_cookie is None and message["type"] == "http.response.start": - message.setdefault("headers", []) - self._set_cookie_if_needed(message) - await send(message) - - return send_wrapper - - def _set_cookie_if_needed(self, message: "Message") -> None: - headers = MutableHeaders(scope=message) - if "set-cookie" not in headers: - cookie = Cookie( - key=self.config.cookie_name, - value=self._generate_csrf_token(), - path=self.config.cookie_path, - secure=self.config.cookie_secure, - httponly=self.config.cookie_httponly, - samesite=self.config.cookie_samesite, - domain=self.config.cookie_domain, - ) - headers.append("set-cookie", cookie.to_header(header="")) - - def _generate_csrf_hash(self, token: str) -> str: - """Generate an HMAC that signs the CSRF token.""" - return hmac.new(self.config.secret.encode(), token.encode(), hashlib.sha256).hexdigest() - - def _generate_csrf_token(self) -> str: - """Generate a CSRF token that includes a randomly generated string - signed by an HMAC.""" - token = secrets.token_hex(CSRF_SECRET_BYTES) - token_hash = self._generate_csrf_hash(token) - return token + token_hash - - def _decode_csrf_token(self, token: str) -> Optional[str]: - """Decode a CSRF token and validate its HMAC.""" - if len(token) < CSRF_SECRET_LENGTH + 1: - return None - - token_secret = token[:CSRF_SECRET_LENGTH] - existing_hash = token[CSRF_SECRET_LENGTH:] - expected_hash = self._generate_csrf_hash(token_secret) - if not secrets.compare_digest(existing_hash, expected_hash): - return None - - return token_secret - - def _csrf_tokens_match( - self, request_csrf_token: Optional[str], cookie_csrf_token: Optional[str] - ) -> bool: - """Takes the CSRF tokens from the request and the cookie and verifies - both are valid and identical.""" - if not (request_csrf_token and cookie_csrf_token): - return False - - decoded_request_token = self._decode_csrf_token(request_csrf_token) - decoded_cookie_token = self._decode_csrf_token(cookie_csrf_token) - if decoded_request_token is None or decoded_cookie_token is None: - return False - - return secrets.compare_digest(decoded_request_token, decoded_cookie_token) +__all__ = ["CSRFMiddleware"] diff --git a/esmerald/middleware/errors.py b/esmerald/middleware/errors.py index 0715856d..3bcb0b94 100644 --- a/esmerald/middleware/errors.py +++ b/esmerald/middleware/errors.py @@ -1,38 +1 @@ -import traceback -from typing import TypeVar - -from starlette.middleware.errors import ServerErrorMiddleware as StarletteServerErrorMiddleware -from starlette.requests import Request as StarletteRequest -from starlette.responses import HTMLResponse, PlainTextResponse, Response - -from esmerald.requests import Request as _Request - -Request = TypeVar("Request", _Request, StarletteRequest) - - -class ServerErrorMiddleware(StarletteServerErrorMiddleware): # pragma: no cover - """ - Handles returning 500 responses when a server error occurs. - - If 'debug' is set, then traceback responses will be returned, - otherwise the designated 'handler' will be called. - - This middleware class should generally be used to wrap *everything* - else up, so that unhandled exceptions anywhere in the stack - always result in an appropriate 500 response. - """ - - def generate_plain_text(self, exc: Exception) -> str: - return "".join(traceback.format_exception(type(exc), exc, exc.__traceback__)) - - def debug_response(self, request: Request, exc: Exception) -> Response: - accept = request.headers.get("accept", "") - - if "text/html" in accept: - content = self.generate_html(exc) - return HTMLResponse(content, status_code=500) - content = self.generate_plain_text(exc) - return PlainTextResponse(content, status_code=500) - - def error_response(self, request: Request, exc: Exception) -> Response: - return PlainTextResponse("Internal Server Error", status_code=500) +from lilya.middleware.server_error import ServerErrorMiddleware as ServerErrorMiddleware # noqa diff --git a/esmerald/middleware/exceptions.py b/esmerald/middleware/exceptions.py index c211f1f7..b375cd6d 100644 --- a/esmerald/middleware/exceptions.py +++ b/esmerald/middleware/exceptions.py @@ -1,25 +1,25 @@ from inspect import getmro from typing import Any, Callable, Dict, List, Mapping, Optional, Type, Union, cast +from lilya import status +from lilya.exceptions import HTTPException as LilyaException +from lilya.middleware.exceptions import ExceptionMiddleware as LilyaExceptionMiddleware +from lilya.responses import Response as LilyaResponse +from lilya.types import ASGIApp, Receive, Scope, Send from pydantic import BaseModel -from starlette import status -from starlette.exceptions import HTTPException as StarletteHTTPException -from starlette.middleware.errors import ServerErrorMiddleware -from starlette.middleware.exceptions import ExceptionMiddleware as StarletteExceptionMiddleware -from starlette.responses import Response as StarletteResponse -from starlette.types import ASGIApp, Receive, Scope, Send from esmerald.enums import MediaType, ScopeType from esmerald.exception_handlers import http_exception_handler from esmerald.exceptions import HTTPException, WebSocketException from esmerald.middleware._exception_handlers import wrap_app_handling_exceptions +from esmerald.middleware.errors import ServerErrorMiddleware from esmerald.requests import Request from esmerald.responses import Response from esmerald.types import ExceptionHandler, ExceptionHandlerMap from esmerald.websockets import WebSocket -class ExceptionMiddleware(StarletteExceptionMiddleware): +class ExceptionMiddleware(LilyaExceptionMiddleware): """ Reimplementation of the Exception Middleware. """ @@ -35,7 +35,7 @@ def __init__( self._status_handlers: Dict[int, Callable] = {} self._exception_handlers: Dict[Type[Exception], Callable] = { HTTPException: http_exception_handler, - StarletteHTTPException: http_exception_handler, + LilyaException: http_exception_handler, WebSocketException: self.websocket_exception, } if handlers is not None: @@ -49,7 +49,7 @@ async def __call__( await self.app(scope, receive, send) return - scope["starlette.exception_handlers"] = ( + scope["lilya.exception_handlers"] = ( self._exception_handlers, self._status_handlers, ) @@ -99,7 +99,7 @@ async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: if isinstance(ex, WebSocketException): code = ex.code reason = ex.detail - elif isinstance(ex, StarletteHTTPException): + elif isinstance(ex, LilyaException): code = ex.status_code + 4000 reason = ex.detail else: @@ -109,13 +109,11 @@ async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: event = {"type": "websocket.close", "code": code, "reason": reason} await send(event) - def default_http_exception_handler( - self, request: Request, exc: Exception - ) -> "StarletteResponse": + def default_http_exception_handler(self, request: Request, exc: Exception) -> "LilyaResponse": """Default handler for exceptions subclassed from HTTPException.""" status_code = ( exc.status_code - if isinstance(exc, StarletteHTTPException) + if isinstance(exc, LilyaException) else status.HTTP_500_INTERNAL_SERVER_ERROR ) if status_code == status.HTTP_500_INTERNAL_SERVER_ERROR and self.debug: @@ -124,7 +122,7 @@ def default_http_exception_handler( return self.create_exception_response(exc) def create_exception_response(self, exc: Exception) -> Response: - if isinstance(exc, (HTTPException, StarletteHTTPException)): + if isinstance(exc, (HTTPException, LilyaException)): content = ResponseContent(detail=exc.detail, status_code=exc.status_code) if isinstance(exc, HTTPException): extra = exc.extra.get("extra", {}) @@ -136,9 +134,7 @@ def create_exception_response(self, exc: Exception) -> Response: media_type=MediaType.JSON, content=content.model_dump(exclude_none=True), status_code=content.status_code, - headers=( - exc.headers if isinstance(exc, (HTTPException, StarletteHTTPException)) else None - ), + headers=(exc.headers if isinstance(exc, (HTTPException, LilyaException)) else None), ) def get_exception_handler( diff --git a/esmerald/middleware/gzip.py b/esmerald/middleware/gzip.py index 0d52a264..f24ea753 100644 --- a/esmerald/middleware/gzip.py +++ b/esmerald/middleware/gzip.py @@ -1,3 +1,3 @@ -from starlette.middleware.gzip import GZipMiddleware # noqa +from lilya.middleware.compression import GZipMiddleware # noqa __all__ = ["GZipMiddleware"] diff --git a/esmerald/middleware/https.py b/esmerald/middleware/https.py index efa3b5a7..acaac125 100644 --- a/esmerald/middleware/https.py +++ b/esmerald/middleware/https.py @@ -1,3 +1,3 @@ -from starlette.middleware.httpsredirect import HTTPSRedirectMiddleware +from lilya.middleware.httpsredirect import HTTPSRedirectMiddleware __all__ = ["HTTPSRedirectMiddleware"] diff --git a/esmerald/middleware/sessions.py b/esmerald/middleware/sessions.py index 34eccb63..9a8b40c6 100644 --- a/esmerald/middleware/sessions.py +++ b/esmerald/middleware/sessions.py @@ -1 +1 @@ -from starlette.middleware.sessions import SessionMiddleware as SessionMiddleware # noqa +from lilya.middleware.sessions import SessionMiddleware as SessionMiddleware # noqa diff --git a/esmerald/middleware/settings_middleware.py b/esmerald/middleware/settings_middleware.py index 9099142c..d53c8386 100644 --- a/esmerald/middleware/settings_middleware.py +++ b/esmerald/middleware/settings_middleware.py @@ -1,4 +1,4 @@ -from starlette.types import ASGIApp, Receive, Scope, Send +from lilya.types import ASGIApp, Receive, Scope, Send from esmerald.conf import settings as esmerald_settings from esmerald.protocols.middleware import MiddlewareProtocol diff --git a/esmerald/middleware/trustedhost.py b/esmerald/middleware/trustedhost.py index e78afb88..c767edfe 100644 --- a/esmerald/middleware/trustedhost.py +++ b/esmerald/middleware/trustedhost.py @@ -1,3 +1,3 @@ -from starlette.middleware.trustedhost import TrustedHostMiddleware # noqa +from lilya.middleware.trustedhost import TrustedHostMiddleware # noqa __all__ = ["TrustedHostMiddleware"] diff --git a/esmerald/openapi/docs.py b/esmerald/openapi/docs.py index e57690a9..5d89bc4f 100644 --- a/esmerald/openapi/docs.py +++ b/esmerald/openapi/docs.py @@ -1,7 +1,7 @@ import json from typing import Any, Dict, Optional -from starlette.responses import HTMLResponse +from lilya.responses import HTMLResponse swagger_ui_default_parameters = { "dom_id": "#swagger-ui", diff --git a/esmerald/openapi/openapi.py b/esmerald/openapi/openapi.py index 98e04c05..e279e8b7 100644 --- a/esmerald/openapi/openapi.py +++ b/esmerald/openapi/openapi.py @@ -4,13 +4,14 @@ import warnings from typing import Any, Dict, List, Optional, Sequence, Set, Tuple, Union, cast +from lilya._internal._path import clean_path +from lilya.middleware import DefineMiddleware +from lilya.routing import BasePath +from lilya.status import HTTP_422_UNPROCESSABLE_ENTITY from orjson import loads from pydantic import AnyUrl from pydantic.fields import FieldInfo from pydantic.json_schema import GenerateJsonSchema, JsonSchemaValue -from starlette.middleware import Middleware -from starlette.routing import BaseRoute -from starlette.status import HTTP_422_UNPROCESSABLE_ENTITY from typing_extensions import Literal from esmerald.enums import MediaType @@ -39,7 +40,6 @@ from esmerald.typing import Undefined from esmerald.utils.constants import DATA, PAYLOAD from esmerald.utils.helpers import is_class_and_subclass -from esmerald.utils.url import clean_path def get_flat_params(route: Union[router.HTTPHandler, Any]) -> List[Any]: @@ -76,7 +76,7 @@ def get_openapi_security_schemes(schemes: Any) -> Tuple[dict, list]: def get_fields_from_routes( - routes: Sequence[BaseRoute], request_fields: Optional[List[FieldInfo]] = None + routes: Sequence[BasePath], request_fields: Optional[List[FieldInfo]] = None ) -> List[FieldInfo]: """Extracts the fields from the given routes of Esmerald""" body_fields: List[FieldInfo] = [] @@ -132,9 +132,9 @@ def get_openapi_operation( operation_id = route.operation_id if operation_id in operation_ids: message = ( - f"Duplicate Operation ID {operation_id} for function " + f"{route.endpoint.__name__}" + f"Duplicate Operation ID {operation_id} for function " + f"{route.handler.__name__}" ) - file_name = getattr(route.endpoint, "__globals__", {}).get("__file__") + file_name = getattr(route.handler, "__globals__", {}).get("__file__") if file_name: message += f" at {file_name}" warnings.warn(message, stacklevel=1) @@ -399,7 +399,7 @@ def is_middleware_app(route: router.Include) -> bool: """ from esmerald import MiddlewareProtocol - return bool(isinstance(route.app, (Middleware, MiddlewareProtocol))) + return bool(isinstance(route.app, (DefineMiddleware, MiddlewareProtocol))) def get_openapi( @@ -410,13 +410,13 @@ def get_openapi( openapi_version: str = "3.1.0", summary: Optional[str] = None, description: Optional[str] = None, - routes: Sequence[BaseRoute], + routes: Sequence[BasePath], tags: Optional[List[str]] = None, servers: Optional[List[Dict[str, Union[str, Any]]]] = None, terms_of_service: Optional[Union[str, AnyUrl]] = None, contact: Optional[Contact] = None, license: Optional[License] = None, - webhooks: Optional[Sequence[BaseRoute]] = None, + webhooks: Optional[Sequence[BasePath]] = None, ) -> Dict[str, Any]: # pragma: no cover """ Builds the whole OpenAPI route structure and object @@ -457,7 +457,7 @@ def get_openapi( # Iterate through the routes def iterate_routes( app: Any, - routes: Sequence[BaseRoute], + routes: Sequence[BasePath], definitions: Any = None, components: Any = None, prefix: Optional[str] = "", diff --git a/esmerald/parsers.py b/esmerald/parsers.py index 273d8880..698d9c07 100644 --- a/esmerald/parsers.py +++ b/esmerald/parsers.py @@ -2,15 +2,15 @@ from json import JSONDecodeError, loads from typing import TYPE_CHECKING, Any, Dict, List, get_args, get_origin +from lilya.datastructures import DataUpload as LilyaUploadFile from pydantic import BaseModel, ConfigDict from pydantic.fields import FieldInfo -from starlette.datastructures import UploadFile as StarletteUploadFile from esmerald.datastructures import UploadFile from esmerald.enums import EncodingType if TYPE_CHECKING: # pragma: no cover - from starlette.datastructures import FormData + from lilya.datastructures import FormData class HashableBaseModel(BaseModel): # pragma: no cover @@ -84,7 +84,7 @@ def parse_form_data( """ values_dict: Dict[str, Any] = {} for key, value in form_data.multi_items(): - if not isinstance(value, StarletteUploadFile): + if not isinstance(value, LilyaUploadFile): with suppress(JSONDecodeError): value = loads(value) value_in_dict = values_dict.get(key) @@ -99,13 +99,13 @@ def parse_form_data( if get_origin(field.annotation) is list: values = list(values_dict.values()) return flatten(values=values) - if field.annotation in (StarletteUploadFile, UploadFile) and values_dict: + if field.annotation in (LilyaUploadFile, UploadFile) and values_dict: return list(values_dict.values())[0] # Check the arguments if there is any MULTI_PART in a possible Union with UploadFile # and a None (Optional). for arg in get_args(field.annotation): - if issubclass(arg, (StarletteUploadFile, UploadFile)) and values_dict: + if issubclass(arg, (LilyaUploadFile, UploadFile)) and values_dict: return list(values_dict.values())[0] return values_dict if values_dict else None diff --git a/esmerald/permissions/base.py b/esmerald/permissions/base.py index cb5b50a9..518845cb 100644 --- a/esmerald/permissions/base.py +++ b/esmerald/permissions/base.py @@ -212,7 +212,7 @@ def is_user_authenticated(self, request: "Request") -> bool: This method must be overridden by subclasses. Args: - request: A Starlette 'HTTPConnection' instance. + request: A Lilya 'Connection' instance. Returns: bool: True or False @@ -225,7 +225,7 @@ def is_user_staff(self, request: "Request") -> bool: This method must be overridden by subclasses. Args: - request: A Starlette 'HTTPConnection' instance. + request: A Lilya 'Connection' instance. Returns: bool: True or False @@ -278,7 +278,7 @@ def has_permission( ) -> bool: """ Args: - request: A Starlette 'HTTPConnection' instance. + request: A Lilya 'Connection' instance. apiview: A Esmerald 'APIController' instance or a `APIGateHandler` instance. Returns: @@ -300,7 +300,7 @@ def has_permission( ) -> bool: """ Args: - request: A Starlette 'HTTPConnection' instance. + request: A Lilya 'Connection' instance. apiview: A Esmerald 'APIController' instance or a `APIGateHandler` instance. Returns: @@ -322,7 +322,7 @@ def has_permission( ) -> bool: """ Args: - request: A Starlette 'HTTPConnection' instance. + request: A Lilya 'Connection' instance. apiview: A Esmerald 'APIController' instance or a `APIGateHandler` instance. Returns: diff --git a/esmerald/protocols/extension.py b/esmerald/protocols/extension.py index 2d15aeb9..3e7604b9 100644 --- a/esmerald/protocols/extension.py +++ b/esmerald/protocols/extension.py @@ -1,6 +1,6 @@ from typing import Any, Dict, Optional -from starlette.types import ASGIApp +from lilya.types import ASGIApp from typing_extensions import Protocol, runtime_checkable diff --git a/esmerald/protocols/interceptor.py b/esmerald/protocols/interceptor.py index a2bde661..6022a1b9 100644 --- a/esmerald/protocols/interceptor.py +++ b/esmerald/protocols/interceptor.py @@ -1,6 +1,6 @@ from typing import TypeVar -from starlette.types import Receive, Scope, Send +from lilya.types import Receive, Scope, Send from typing_extensions import Protocol, runtime_checkable T = TypeVar("T") @@ -10,9 +10,9 @@ class InterceptorProtocol(Protocol): # pragma: no cover """ Generic object serving the base for interception of messages, - before reaching the endpoint. This is inspired by the AOP (Aspect Oriented Programming). + before reaching the handler. This is inspired by the AOP (Aspect Oriented Programming). - The interceptor is handled between the call and the API endpoint itself and acts on it. + The interceptor is handled between the call and the API handler itself and acts on it. An interceptor could be anything from logging to rerouting or even input sanitizing. """ diff --git a/esmerald/protocols/middleware.py b/esmerald/protocols/middleware.py index 864d1be1..be5002fa 100644 --- a/esmerald/protocols/middleware.py +++ b/esmerald/protocols/middleware.py @@ -1,6 +1,6 @@ from typing import Any -from starlette.types import ASGIApp, Receive, Scope, Send +from lilya.types import ASGIApp, Receive, Scope, Send from typing_extensions import Protocol, runtime_checkable diff --git a/esmerald/protocols/template.py b/esmerald/protocols/template.py index c9ecb4ec..177c5235 100644 --- a/esmerald/protocols/template.py +++ b/esmerald/protocols/template.py @@ -6,7 +6,7 @@ @runtime_checkable class TemplateProtocol(Protocol): # pragma: no cover - def render(self, **context: Optional[Dict[str, Any]]) -> str: ... + def make_response(self, **context: Optional[Dict[str, Any]]) -> str: ... TP = TypeVar("TP", bound=TemplateProtocol, covariant=True) diff --git a/esmerald/requests.py b/esmerald/requests.py index 62c96c4a..51a78a35 100644 --- a/esmerald/requests.py +++ b/esmerald/requests.py @@ -1,16 +1,15 @@ -# from json import loads from typing import TYPE_CHECKING, Any, cast -from orjson import loads -from starlette.datastructures import URL # noqa -from starlette.requests import ( # noqa +from lilya._internal._connection import Connection as Connection # noqa: F401 +from lilya.datastructures import URL # noqa +from lilya.requests import ( ClientDisconnect as ClientDisconnect, # noqa - HTTPConnection as HTTPConnection, # noqa: F401 - Request as StarletteRequest, # noqa: F401 + Request as LilyaRequest, # noqa: F401 empty_receive, empty_send, ) -from starlette.types import Receive, Scope, Send +from lilya.types import Receive, Scope, Send +from orjson import loads from esmerald.typing import Void @@ -20,7 +19,7 @@ from esmerald.types import HTTPMethod -class Request(StarletteRequest): +class Request(LilyaRequest): def __init__( self, scope: "Scope", @@ -67,6 +66,6 @@ async def json(self) -> Any: self._json = loads(body) return self._json - def url_for(self, __name: str, **path_params: Any) -> Any: - url: URL = super().url_for(__name, **path_params) + def path_for(self, __name: str, **path_params: Any) -> Any: + url: URL = super().path_for(__name, **path_params) return str(url) diff --git a/esmerald/responses/__init__.py b/esmerald/responses/__init__.py index fc852468..dfa72de5 100644 --- a/esmerald/responses/__init__.py +++ b/esmerald/responses/__init__.py @@ -1,21 +1,24 @@ from .base import ( + Error, FileResponse, HTMLResponse, JSONResponse, - PlainTextResponse, + LilyaResponse, + PlainText, Response, - StarletteResponse, StreamingResponse, ) from .template import TemplateResponse __all__ = [ + "Error", "FileResponse", "HTMLResponse", "JSONResponse", - "PlainTextResponse", + "PlainText", + "PlainText", "Response", - "StarletteResponse", + "LilyaResponse", "StreamingResponse", "TemplateResponse", ] diff --git a/esmerald/responses/base.py b/esmerald/responses/base.py index 02df992e..dfa0f2de 100644 --- a/esmerald/responses/base.py +++ b/esmerald/responses/base.py @@ -3,23 +3,27 @@ from typing import TYPE_CHECKING, Any, Dict, Generic, NoReturn, Optional, TypeVar, Union, cast import msgspec -from orjson import OPT_OMIT_MICROSECONDS, OPT_SERIALIZE_NUMPY, dumps -from pydantic import BaseModel -from starlette import status -from starlette.responses import ( +from lilya import status +from lilya.responses import ( + Error as Error, FileResponse as FileResponse, # noqa HTMLResponse as HTMLResponse, # noqa JSONResponse as JSONResponse, # noqa - PlainTextResponse as PlainTextResponse, # noqa + Ok as Ok, + PlainText as PlainText, # noqa RedirectResponse as RedirectResponse, # noqa - Response as StarletteResponse, # noqa + Response as LilyaResponse, # noqa StreamingResponse as StreamingResponse, # noqa ) +from orjson import OPT_OMIT_MICROSECONDS, OPT_SERIALIZE_NUMPY, dumps +from pydantic import BaseModel from typing_extensions import Annotated, Doc from esmerald.enums import MediaType from esmerald.exceptions import ImproperlyConfigured +PlainTextResponse = PlainText + if TYPE_CHECKING: # pragma: no cover from esmerald.backgound import BackgroundTask, BackgroundTasks from esmerald.types import ResponseCookies @@ -27,7 +31,7 @@ T = TypeVar("T") -class Response(StarletteResponse, Generic[T]): +class Response(LilyaResponse, Generic[T]): """ Default `Response` object from Esmerald where it can be as the return annotation of a [handler](https://esmerald.dev/routing/handlers/). @@ -153,7 +157,7 @@ def transform(value: Any) -> Dict[str, Any]: return msgspec.structs.asdict(value) raise TypeError("unsupported type") # pragma: no cover - def render(self, content: Any) -> bytes: + def make_response(self, content: Any) -> Union[bytes, str]: try: if ( content is None @@ -171,6 +175,6 @@ def render(self, content: Any) -> bytes: default=self.transform, option=OPT_SERIALIZE_NUMPY | OPT_OMIT_MICROSECONDS, ) - return super().render(content) + return super().make_response(content) except (AttributeError, ValueError, TypeError) as e: # pragma: no cover raise ImproperlyConfigured("Unable to serialize response content") from e diff --git a/esmerald/responses/encoders.py b/esmerald/responses/encoders.py index 4fbd1b0e..1cbdcd5c 100644 --- a/esmerald/responses/encoders.py +++ b/esmerald/responses/encoders.py @@ -1,6 +1,6 @@ from typing import Any -from starlette.responses import JSONResponse as JSONResponse +from lilya.responses import JSONResponse as JSONResponse from esmerald.responses.json import BaseJSONResponse @@ -23,7 +23,7 @@ class ORJSONResponse(BaseJSONResponse): In the same way the JSONResponse is used, so is the `ORJSONResponse`. """ - def render(self, content: Any) -> bytes: + def make_response(self, content: Any) -> bytes: assert orjson is not None, "You must install the encoders or orjson to use ORJSONResponse" return orjson.dumps( content, @@ -39,6 +39,6 @@ class UJSONResponse(BaseJSONResponse): In the same way the JSONResponse is used, so is the `UJSONResponse`. """ - def render(self, content: Any) -> bytes: + def make_response(self, content: Any) -> bytes: assert ujson is not None, "You must install the encoders or ujson to use UJSONResponse" return ujson.dumps(content, ensure_ascii=False).encode("utf-8") diff --git a/esmerald/responses/template.py b/esmerald/responses/template.py index a1d075f9..c59a6b8f 100644 --- a/esmerald/responses/template.py +++ b/esmerald/responses/template.py @@ -2,7 +2,7 @@ from pathlib import PurePath from typing import TYPE_CHECKING, Any, Dict, Optional, Union -from starlette.types import Receive, Scope, Send +from lilya.types import Receive, Scope, Send from esmerald.enums import MediaType from esmerald.responses.base import Response diff --git a/esmerald/routing/apis/_mixins.py b/esmerald/routing/apis/_mixins.py index 0bbfe729..8e67b781 100644 --- a/esmerald/routing/apis/_mixins.py +++ b/esmerald/routing/apis/_mixins.py @@ -141,13 +141,15 @@ def is_signature_valid( (HTTPHandler, WebSocketHandler, WebhookHandler), ): if ( # type: ignore - not method.signature.return_annotation - or method.signature.return_annotation is None + not method.handler_signature.return_annotation + or method.handler_signature.return_annotation is None ): return True - if not is_class_and_subclass(method.signature.return_annotation, signature_type): + if not is_class_and_subclass( + method.handler_signature.return_annotation, signature_type + ): raise ImproperlyConfigured( - f"{cls.__name__} must return type lists, got {type(method.signature.return_annotation)} instead." + f"{cls.__name__} must return type lists, got {type(method.handler_signature.return_annotation)} instead." ) return True diff --git a/esmerald/routing/apis/base.py b/esmerald/routing/apis/base.py index f384b5d7..a13239c8 100644 --- a/esmerald/routing/apis/base.py +++ b/esmerald/routing/apis/base.py @@ -1,12 +1,11 @@ from copy import copy from typing import TYPE_CHECKING, Dict, List, Optional, Sequence, Tuple, Union, cast -from starlette.routing import compile_path -from starlette.types import Receive, Scope, Send +from lilya._internal._path import clean_path +from lilya.routing import compile_path +from lilya.types import Receive, Scope, Send from typing_extensions import Annotated, Doc -from esmerald.utils.url import clean_path - if TYPE_CHECKING: # pragma: no cover from openapi_schemas_pydantic.v3_1_0.security_scheme import SecurityScheme @@ -113,7 +112,7 @@ class CustomView(View): Optional[List["Middleware"]], Doc( """ - A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Starlette Middleware](https://www.starlette.io/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). + A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Lilya Middleware](https://www.lilya.dev/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). """ ), ] @@ -256,7 +255,7 @@ def __init__(self, parent: Union["Gateway", "WebSocketGateway"]) -> None: setattr(self, key, None) self.path = clean_path(self.path or "/") - self.path_regex, self.path_format, self.param_convertors = compile_path(self.path) + self.path_regex, self.path_format, self.param_convertors, _ = compile_path(self.path) self.parent = parent self.route_map: Dict[str, Tuple["HTTPHandler", "TransformerModel"]] = {} self.operation_id: Optional[str] = None @@ -311,7 +310,7 @@ def get_route_handlers( self.get_route_middleware(route_handler) if self.exception_handlers: - route_handler.exception_handlers = self.get_exception_handlers(route_handler) + route_handler.exception_handlers = self.get_exception_handlers(route_handler) # type: ignore if self.tags or []: # pragma: no cover for tag in reversed(self.tags): route_handler.tags.insert(0, tag) diff --git a/esmerald/routing/base.py b/esmerald/routing/base.py index f3443a43..4b563439 100644 --- a/esmerald/routing/base.py +++ b/esmerald/routing/base.py @@ -20,14 +20,11 @@ ) from uuid import UUID -from starlette.convertors import CONVERTOR_TYPES -from starlette.requests import HTTPConnection -from starlette.responses import Response as StarletteResponse -from starlette.routing import ( - Mount as Mount, # noqa - compile_path, -) -from starlette.types import Receive, Scope, Send +from lilya._internal._connection import Connection +from lilya.responses import Response as LilyaResponse +from lilya.routing import compile_path +from lilya.transformers import TRANSFORMER_TYPES +from lilya.types import Receive, Scope, Send from typing_extensions import TypedDict from esmerald.backgound import BackgroundTask, BackgroundTasks @@ -78,7 +75,7 @@ "path": Path, } -CONV2TYPE = {conv: typ for typ, conv in CONVERTOR_TYPES.items()} +CONV2TYPE = {conv: typ for typ, conv in TRANSFORMER_TYPES.items()} T = TypeVar("T", bound="BaseHandlerMixin") @@ -93,10 +90,10 @@ class PathParameterSchema(TypedDict): class OpenAPIDefinitionMixin: # pragma: no cover def parse_path(self, path: str) -> List[Union[str, PathParameterSchema]]: """ - Using the Starlette CONVERTORS and the application registered convertors, + Using the Lilya TRANSFORMERS and the application registered convertors, transforms the path into a PathParameterSchema used for the OpenAPI definition. """ - _, path, variables = compile_path(path) + _, path, variables, _ = compile_path(path) parsed_components: List[Union[str, PathParameterSchema]] = [] @@ -167,7 +164,7 @@ def response_container_handler( async def response_content( data: ResponseContainer, app: Type["Esmerald"], **kwargs: Dict[str, Any] - ) -> StarletteResponse: + ) -> LilyaResponse: _headers = {**self.get_headers(headers), **data.headers} _cookies = self.get_cookies(data.cookies, cookies) response: Response = data.to_response( @@ -189,7 +186,7 @@ def response_handler( status_code: Optional[int] = None, media_type: Optional[str] = MediaType.TEXT, ) -> "AsyncAnyCallable": - async def response_content(data: Response, **kwargs: Dict[str, Any]) -> StarletteResponse: + async def response_content(data: Response, **kwargs: Dict[str, Any]) -> LilyaResponse: _cookies = self.get_cookies(data.cookies, cookies) _headers = { **self.get_headers(headers), @@ -220,7 +217,7 @@ def json_response_handler( ) -> "AsyncAnyCallable": """Creates a handler function for Esmerald JSON responses""" - async def response_content(data: Response, **kwargs: Dict[str, Any]) -> StarletteResponse: + async def response_content(data: Response, **kwargs: Dict[str, Any]) -> LilyaResponse: _cookies = self.get_cookies(cookies, []) _headers = { **self.get_headers(headers), @@ -244,11 +241,9 @@ def starlette_response_handler( cookies: "ResponseCookies", headers: Optional["ResponseHeaders"] = None, ) -> "AsyncAnyCallable": - """Creates an handler for Starlette Responses.""" + """Creates an handler for Lilya Responses.""" - async def response_content( - data: StarletteResponse, **kwargs: Dict[str, Any] - ) -> StarletteResponse: + async def response_content(data: LilyaResponse, **kwargs: Dict[str, Any]) -> LilyaResponse: _cookies = self.get_cookies(cookies, []) _headers = { **self.get_headers(headers), @@ -273,7 +268,8 @@ def _handler( response_class: Any, status_code: int, ) -> "AsyncAnyCallable": - async def response_content(data: Any, **kwargs: Dict[str, Any]) -> StarletteResponse: + async def response_content(data: Any, **kwargs: Dict[str, Any]) -> LilyaResponse: + data = await self.get_response_data(data=data) _cookies = self.get_cookies(cookies, []) if isinstance(data, JSONResponse): @@ -301,7 +297,7 @@ async def get_response_for_request( request: Request, route: "HTTPHandler", parameter_model: "TransformerModel", - ) -> "StarletteResponse": + ) -> "LilyaResponse": """ Get response for the given request using the specified route and parameter model. @@ -312,7 +308,7 @@ async def get_response_for_request( parameter_model (TransformerModel): The parameter model for handling request parameters. Returns: - StarletteResponse: The response generated for the request. + LilyaResponse: The response generated for the request. """ response = await self.call_handler_function( scope=scope, @@ -320,7 +316,7 @@ async def get_response_for_request( route=route, parameter_model=parameter_model, ) - return cast("StarletteResponse", response) + return cast("LilyaResponse", response) async def call_handler_function( self, @@ -403,7 +399,7 @@ async def _get_response_data( return await fn() return fn() - def get_response_handler(self) -> Callable[[Any], Awaitable[StarletteResponse]]: + def get_response_handler(self) -> Callable[[Any], Awaitable[LilyaResponse]]: """ Checks and validates the type of return response and maps to the corresponding handler with the given parameters. @@ -417,7 +413,7 @@ def get_response_handler(self) -> Callable[[Any], Awaitable[StarletteResponse]]: headers = self.get_response_headers() cookies = self.get_response_cookies() - if is_class_and_subclass(self.signature.return_annotation, ResponseContainer): + if is_class_and_subclass(self.handler_signature.return_annotation, ResponseContainer): handler = self.response_container_handler( cookies=cookies, media_type=self.media_type, @@ -425,20 +421,20 @@ def get_response_handler(self) -> Callable[[Any], Awaitable[StarletteResponse]]: headers=headers, ) elif is_class_and_subclass( - self.signature.return_annotation, + self.handler_signature.return_annotation, JSONResponse, ): handler = self.json_response_handler( status_code=self.status_code, cookies=cookies, headers=headers ) - elif is_class_and_subclass(self.signature.return_annotation, Response): + elif is_class_and_subclass(self.handler_signature.return_annotation, Response): handler = self.response_handler( cookies=cookies, status_code=self.status_code, media_type=self.media_type, headers=headers, ) - elif is_class_and_subclass(self.signature.return_annotation, StarletteResponse): + elif is_class_and_subclass(self.handler_signature.return_annotation, LilyaResponse): handler = self.starlette_response_handler( cookies=cookies, headers=headers, @@ -455,7 +451,7 @@ def get_response_handler(self) -> Callable[[Any], Awaitable[StarletteResponse]]: self._response_handler = handler return cast( - "Callable[[Any], Awaitable[StarletteResponse]]", + "Callable[[Any], Awaitable[LilyaResponse]]", self._response_handler, ) @@ -466,7 +462,7 @@ class BaseHandlerMixin(BaseSignature, BaseResponseHandler, OpenAPIDefinitionMixi """ @property - def signature(self) -> Signature: + def handler_signature(self) -> Signature: """The Signature of 'self.fn'.""" return Signature.from_callable(cast("AnyCallable", self.fn)) @@ -608,7 +604,7 @@ async def get_response_data(self, data: Any) -> Any: # pragma: no cover data = await data return data - async def allow_connection(self, connection: "HTTPConnection") -> None: # pragma: no cover + async def allow_connection(self, connection: "Connection") -> None: # pragma: no cover """ Validates the connection. diff --git a/esmerald/routing/events.py b/esmerald/routing/events.py index b39a3f4d..60277612 100644 --- a/esmerald/routing/events.py +++ b/esmerald/routing/events.py @@ -1,7 +1,7 @@ from typing import TYPE_CHECKING, Any, Optional, Sequence, TypeVar -from starlette._utils import is_async_callable -from starlette.types import Lifespan, Receive, Scope, Send +from lilya.compat import is_async_callable +from lilya.types import Lifespan, Receive, Scope, Send if TYPE_CHECKING: # pragma: no cover from esmerald.types import LifeSpanHandler @@ -15,10 +15,6 @@ class AyncLifespanContextManager: # pragma: no cover Manages and handles the on_startup and on_shutdown events in an Esmerald way. - This is not the same as the on_startup and on_shutdown - from Starlette. Those are now deprecated and will be removed - in the version 1.0 of Starlette. - This aims to provide a similar functionality but by generating a lifespan event based on the values from the on_startup and on_shutdown lists. @@ -39,7 +35,7 @@ async def __aenter__(self) -> None: """Runs the functions on startup""" for handler in self.on_startup: if is_async_callable(handler): - await handler() + await handler() # type: ignore else: handler() @@ -47,7 +43,7 @@ async def __aexit__(self, scope: Scope, receive: Receive, send: Send, **kwargs: """Runs the functions on shutdown""" for handler in self.on_shutdown: if is_async_callable(handler): - await handler() + await handler() # type: ignore else: handler() @@ -57,7 +53,7 @@ def handle_lifespan_events( on_shutdown: Optional[Sequence["LifeSpanHandler"]] = None, lifespan: Optional[Lifespan[Any]] = None, ) -> Any: # pragma: no cover - """Handles with the lifespan events in the new Starlette format of lifespan. + """Handles with the lifespan events in the new Lilya format of lifespan. This adds a mask that keeps the old `on_startup` and `on_shutdown` events variable declaration for legacy and comprehension purposes and build the async context manager for the lifespan. diff --git a/esmerald/routing/gateways.py b/esmerald/routing/gateways.py index 9e161f59..e0b32500 100644 --- a/esmerald/routing/gateways.py +++ b/esmerald/routing/gateways.py @@ -1,20 +1,17 @@ import re from typing import TYPE_CHECKING, Any, Callable, List, Optional, Sequence, Union, cast -from starlette.middleware import Middleware as StarletteMiddleware -from starlette.routing import ( - Route as StarletteRoute, - WebSocketRoute as StarletteWebSocketRoute, - compile_path, -) -from starlette.types import Receive, Scope, Send +from lilya._internal._path import clean_path +from lilya._utils import is_class_and_subclass +from lilya.middleware import DefineMiddleware +from lilya.routing import Path as LilyaPath, WebSocketPath as LilyaWebSocketPath, compile_path +from lilya.types import Receive, Scope, Send from typing_extensions import Annotated, Doc from esmerald.routing.apis.base import View from esmerald.routing.base import BaseInterceptorMixin from esmerald.typing import Void, VoidType -from esmerald.utils.helpers import clean_string, is_class_and_subclass -from esmerald.utils.url import clean_path +from esmerald.utils.helpers import clean_string if TYPE_CHECKING: # pragma: no cover from openapi_schemas_pydantic.v3_1_0.security_scheme import SecurityScheme @@ -36,15 +33,16 @@ def handle_middleware( if not is_class_and_subclass(handler, View) and not isinstance(handler, View): base_middleware += handler.middleware or [] - for middleware in base_middleware or []: - if isinstance(middleware, StarletteMiddleware): - _middleware.append(middleware) - else: - _middleware.append(StarletteMiddleware(middleware)) # type: ignore + + for middleware in base_middleware or []: + if isinstance(middleware, DefineMiddleware): + _middleware.append(middleware) + else: + _middleware.append(DefineMiddleware(middleware)) # type: ignore return _middleware -class Gateway(StarletteRoute, BaseInterceptorMixin, BaseMiddleware): +class Gateway(LilyaPath, BaseInterceptorMixin, BaseMiddleware): """ `Gateway` object class used by Esmerald routes. @@ -120,7 +118,7 @@ def __init__( Optional[str], Doc( """ - The name for the Gateway. The name can be reversed by `url_path_for()`. + The name for the Gateway. The name can be reversed by `path_for()`. """ ), ] = None, @@ -154,7 +152,7 @@ def __init__( Optional[List["Middleware"]], Doc( """ - A list of middleware to run for every request. The middlewares of a Gateway will be checked from top-down or [Starlette Middleware](https://www.starlette.io/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). + A list of middleware to run for every request. The middlewares of a Gateway will be checked from top-down or [Lilya Middleware](https://www.lilya.dev/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). """ ), ] = None, @@ -256,24 +254,24 @@ def __init__( ) super().__init__( path=self.path, - endpoint=cast(Callable, handler), + handler=cast(Callable, handler), include_in_schema=include_in_schema, name=name, - methods=cast("List[str]", self.methods), + methods=self.methods, middleware=self._middleware, + exception_handlers=exception_handlers, ) """ A "bridge" to a handler and router mapping functionality. - Since the default Starlette Route endpoint does not understand the Esmerald handlers, + Since the default Lilya Route handler does not understand the Esmerald handlers, the Gateway bridges both functionalities and adds an extra "flair" to be compliant with both class based views and decorated function views. """ self._interceptors: Union[List["Interceptor"], "VoidType"] = Void self.name = name - self.handler = handler + self.handler = cast("Callable", handler) self.dependencies = dependencies or {} self.interceptors: Sequence["Interceptor"] = interceptors or [] - self.permissions: Sequence["Permission"] = permissions or [] - self.exception_handlers = exception_handlers or {} + self.permissions: Sequence["Permission"] = permissions or [] # type: ignore self.response_class = None self.response_cookies = None self.response_headers = None @@ -281,11 +279,9 @@ def __init__( self.parent = parent self.security = security self.tags = tags or [] - ( - handler.path_regex, - handler.path_format, - handler.param_convertors, - ) = compile_path(self.path) + (handler.path_regex, handler.path_format, handler.param_convertors, _) = compile_path( + self.path + ) if not is_class_and_subclass(self.handler, View) and not isinstance(self.handler, View): self.handler.name = self.name @@ -293,7 +289,7 @@ def __init__( if not handler.operation_id: handler.operation_id = self.generate_operation_id() - async def handle(self, scope: "Scope", receive: "Receive", send: "Send") -> None: + async def handle_dispatch(self, scope: "Scope", receive: "Receive", send: "Send") -> None: """ Handles the interception of messages and calls from the API. if self._middleware: @@ -311,10 +307,10 @@ def generate_operation_id(self) -> str: assert self.handler.methods operation_id = f"{operation_id}_{methods[0].lower()}" - return operation_id + return cast(str, operation_id) -class WebSocketGateway(StarletteWebSocketRoute, BaseInterceptorMixin, BaseMiddleware): +class WebSocketGateway(LilyaWebSocketPath, BaseInterceptorMixin, BaseMiddleware): """ `WebSocketGateway` object class used by Esmerald routes. @@ -393,7 +389,7 @@ def __init__( Optional[str], Doc( """ - The name for the Gateway. The name can be reversed by `url_path_for()`. + The name for the Gateway. The name can be reversed by `path_for()`. """ ), ] = None, @@ -419,7 +415,7 @@ def __init__( Optional[List["Middleware"]], Doc( """ - A list of middleware to run for every request. The middlewares of a Gateway will be checked from top-down or [Starlette Middleware](https://www.starlette.io/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). + A list of middleware to run for every request. The middlewares of a Gateway will be checked from top-down or [Lilya Middleware](https://www.lilya.dev/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). """ ), ] = None, @@ -482,30 +478,28 @@ def __init__( super().__init__( path=self.path, - endpoint=cast("Callable", handler), + handler=cast("Callable", handler), name=name, middleware=self._middleware, + exception_handlers=exception_handlers, ) """ A "bridge" to a handler and router mapping functionality. - Since the default Starlette Route endpoint does not understand the Esmerald handlers, + Since the default Lilya Route handler does not understand the Esmerald handlers, the Gateway bridges both functionalities and adds an extra "flair" to be compliant with both class based views and decorated function views. """ self._interceptors: Union[List["Interceptor"], "VoidType"] = Void - self.handler = handler + self.handler = cast("Callable", handler) self.dependencies = dependencies or {} self.interceptors = interceptors or [] - self.permissions = permissions or [] - self.exception_handlers = exception_handlers or {} + self.permissions = permissions or [] # type: ignore self.include_in_schema = False self.parent = parent - ( - handler.path_regex, - handler.path_format, - handler.param_convertors, - ) = compile_path(self.path) + (handler.path_regex, handler.path_format, handler.param_convertors, _) = compile_path( + self.path + ) - async def handle(self, scope: "Scope", receive: "Receive", send: "Send") -> None: + async def handle_dispatch(self, scope: "Scope", receive: "Receive", send: "Send") -> None: """ Handles the interception of messages and calls from the API. if self._middleware: @@ -514,7 +508,7 @@ async def handle(self, scope: "Scope", receive: "Receive", send: "Send") -> None await self.app(scope, receive, send) -class WebhookGateway(StarletteRoute, BaseInterceptorMixin): +class WebhookGateway(LilyaPath, BaseInterceptorMixin): """ `WebhookGateway` object class used by Esmerald routes. @@ -633,15 +627,14 @@ def __init__( else: name = clean_string(handler.__class__.__name__) - self.endpoint = cast("Callable", handler) + self.handler = cast("Callable", handler) self.include_in_schema = include_in_schema self._interceptors: Union[List["Interceptor"], "VoidType"] = Void self.name = name - self.handler = handler self.dependencies: Any = {} self.interceptors: Sequence["Interceptor"] = [] - self.permissions: Sequence["Permission"] = [] + self.permissions: Sequence["Permission"] = [] # type: ignore self.middleware: Any = [] self.exception_handlers: Any = {} self.response_class = None @@ -651,11 +644,9 @@ def __init__( self.parent = parent self.security = security self.tags = tags or [] - ( - handler.path_regex, - handler.path_format, - handler.param_convertors, - ) = compile_path(self.path) + (handler.path_regex, handler.path_format, handler.param_convertors, _) = compile_path( + self.path + ) if not is_class_and_subclass(self.handler, View) and not isinstance(self.handler, View): self.handler.name = self.name @@ -673,4 +664,4 @@ def generate_operation_id(self) -> str: assert self.handler.methods operation_id = f"{operation_id}_{methods[0].lower()}" - return operation_id + return cast(str, operation_id) diff --git a/esmerald/routing/handlers.py b/esmerald/routing/handlers.py index ba487e44..5e81065d 100644 --- a/esmerald/routing/handlers.py +++ b/esmerald/routing/handlers.py @@ -1,6 +1,6 @@ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Sequence, Union, cast -from starlette import status +from lilya import status from typing_extensions import Annotated, Doc from esmerald.enums import HttpMethod, MediaType @@ -94,7 +94,7 @@ async def get_joiners() -> None: An integer indicating the status code of the handler. This can be achieved by passing directly the value or - by using the `esmerald.status` or even the `starlette.status`. + by using the `esmerald.status` or even the `lilya.status`. """ ), ] = status.HTTP_200_OK, @@ -159,7 +159,7 @@ async def get_joiners() -> None: Optional[List["Middleware"]], Doc( """ - A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Starlette Middleware](https://www.starlette.io/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). + A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Lilya Middleware](https://www.lilya.dev/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). """ ), ] = None, @@ -421,7 +421,7 @@ def wrapper(func: Any) -> HTTPHandler: responses=responses, ) handler.fn = func - handler.endpoint = func + handler.handler = func handler.__type__ = HttpMethod.GET.value handler.validate_handler() return handler @@ -464,7 +464,7 @@ def head( An integer indicating the status code of the handler. This can be achieved by passing directly the value or - by using the `esmerald.status` or even the `starlette.status`. + by using the `esmerald.status` or even the `lilya.status`. """ ), ] = status.HTTP_200_OK, @@ -529,7 +529,7 @@ def head( Optional[List["Middleware"]], Doc( """ - A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Starlette Middleware](https://www.starlette.io/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). + A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Lilya Middleware](https://www.lilya.dev/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). """ ), ] = None, @@ -690,7 +690,7 @@ def wrapper(func: Any) -> HTTPHandler: responses=responses, ) handler.fn = func - handler.endpoint = func + handler.handler = func handler.__type__ = HttpMethod.HEAD.value handler.validate_handler() return handler @@ -771,7 +771,7 @@ async def create_joiners() -> None: An integer indicating the status code of the handler. This can be achieved by passing directly the value or - by using the `esmerald.status` or even the `starlette.status`. + by using the `esmerald.status` or even the `lilya.status`. """ ), ] = status.HTTP_201_CREATED, @@ -836,7 +836,7 @@ async def create_joiners() -> None: Optional[List["Middleware"]], Doc( """ - A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Starlette Middleware](https://www.starlette.io/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). + A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Lilya Middleware](https://www.lilya.dev/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). """ ), ] = None, @@ -1098,7 +1098,7 @@ def wrapper(func: Any) -> HTTPHandler: responses=responses, ) handler.fn = func - handler.endpoint = func + handler.handler = func handler.__type__ = HttpMethod.POST.value handler.validate_handler() return handler @@ -1174,7 +1174,7 @@ async def update_joiners() -> None: An integer indicating the status code of the handler. This can be achieved by passing directly the value or - by using the `esmerald.status` or even the `starlette.status`. + by using the `esmerald.status` or even the `lilya.status`. """ ), ] = status.HTTP_200_OK, @@ -1239,7 +1239,7 @@ async def update_joiners() -> None: Optional[List["Middleware"]], Doc( """ - A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Starlette Middleware](https://www.starlette.io/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). + A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Lilya Middleware](https://www.lilya.dev/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). """ ), ] = None, @@ -1501,7 +1501,7 @@ def wrapper(func: Any) -> HTTPHandler: responses=responses, ) handler.fn = func - handler.endpoint = func + handler.handler = func handler.__type__ = HttpMethod.PUT.value handler.validate_handler() return handler @@ -1577,7 +1577,7 @@ async def update_partial_joiners() -> None: An integer indicating the status code of the handler. This can be achieved by passing directly the value or - by using the `esmerald.status` or even the `starlette.status`. + by using the `esmerald.status` or even the `lilya.status`. """ ), ] = status.HTTP_200_OK, @@ -1642,7 +1642,7 @@ async def update_partial_joiners() -> None: Optional[List["Middleware"]], Doc( """ - A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Starlette Middleware](https://www.starlette.io/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). + A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Lilya Middleware](https://www.lilya.dev/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). """ ), ] = None, @@ -1904,7 +1904,7 @@ def wrapper(func: Any) -> HTTPHandler: responses=responses, ) handler.fn = func - handler.endpoint = func + handler.handler = func handler.__type__ = HttpMethod.PATCH.value handler.validate_handler() return handler @@ -1980,7 +1980,7 @@ async def delete_joiners() -> None: An integer indicating the status code of the handler. This can be achieved by passing directly the value or - by using the `esmerald.status` or even the `starlette.status`. + by using the `esmerald.status` or even the `lilya.status`. """ ), ] = status.HTTP_204_NO_CONTENT, @@ -2045,7 +2045,7 @@ async def delete_joiners() -> None: Optional[List["Middleware"]], Doc( """ - A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Starlette Middleware](https://www.starlette.io/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). + A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Lilya Middleware](https://www.lilya.dev/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). """ ), ] = None, @@ -2307,7 +2307,7 @@ def wrapper(func: Any) -> HTTPHandler: responses=responses, ) handler.fn = func - handler.endpoint = func + handler.handler = func handler.__type__ = HttpMethod.DELETE.value handler.validate_handler() return handler @@ -2350,7 +2350,7 @@ def options( An integer indicating the status code of the handler. This can be achieved by passing directly the value or - by using the `esmerald.status` or even the `starlette.status`. + by using the `esmerald.status` or even the `lilya.status`. """ ), ] = status.HTTP_200_OK, @@ -2415,7 +2415,7 @@ def options( Optional[List["Middleware"]], Doc( """ - A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Starlette Middleware](https://www.starlette.io/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). + A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Lilya Middleware](https://www.lilya.dev/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). """ ), ] = None, @@ -2576,7 +2576,7 @@ def wrapper(func: Any) -> HTTPHandler: responses=responses, ) handler.fn = func - handler.endpoint = func + handler.handler = func handler.__type__ = HttpMethod.OPTIONS.value handler.validate_handler() return handler @@ -2619,7 +2619,7 @@ def trace( An integer indicating the status code of the handler. This can be achieved by passing directly the value or - by using the `esmerald.status` or even the `starlette.status`. + by using the `esmerald.status` or even the `lilya.status`. """ ), ] = status.HTTP_200_OK, @@ -2684,7 +2684,7 @@ def trace( Optional[List["Middleware"]], Doc( """ - A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Starlette Middleware](https://www.starlette.io/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). + A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Lilya Middleware](https://www.lilya.dev/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). """ ), ] = None, @@ -2846,7 +2846,7 @@ def wrapper(func: Any) -> HTTPHandler: responses=responses, ) handler.fn = func - handler.endpoint = func + handler.handler = func handler.__type__ = HttpMethod.TRACE.value handler.validate_handler() return handler @@ -2941,7 +2941,7 @@ async def operate_joiners() -> None: An integer indicating the status code of the handler. This can be achieved by passing directly the value or - by using the `esmerald.status` or even the `starlette.status`. + by using the `esmerald.status` or even the `lilya.status`. """ ), ] = status.HTTP_200_OK, @@ -3006,7 +3006,7 @@ async def operate_joiners() -> None: Optional[List["Middleware"]], Doc( """ - A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Starlette Middleware](https://www.starlette.io/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). + A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Lilya Middleware](https://www.lilya.dev/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). """ ), ] = None, @@ -3284,7 +3284,7 @@ def wrapper(func: Any) -> HTTPHandler: ) handler.fn = func - handler.endpoint = func + handler.handler = func handler.__type__ = HttpMethod.OPTIONS.value handler.validate_handler() return handler @@ -3336,7 +3336,7 @@ def websocket( Optional[List["Middleware"]], Doc( """ - A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Starlette Middleware](https://www.starlette.io/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). + A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Lilya Middleware](https://www.lilya.dev/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). """ ), ] = None, @@ -3358,7 +3358,7 @@ def wrapper(func: Any) -> WebSocketHandler: middleware=middleware, ) handler.fn = func - handler.endpoint = func + handler.handler = func handler.validate_websocket_handler_function() return handler diff --git a/esmerald/routing/router.py b/esmerald/routing/router.py index 38cbb62e..f1ea3803 100644 --- a/esmerald/routing/router.py +++ b/esmerald/routing/router.py @@ -22,22 +22,24 @@ cast, ) -from starlette import status -from starlette.datastructures import URLPath -from starlette.middleware import Middleware as StarletteMiddleware -from starlette.requests import HTTPConnection -from starlette.responses import JSONResponse, Response as StarletteResponse -from starlette.routing import ( - BaseRoute as StarletteBaseRoute, +from lilya import status +from lilya._internal._connection import Connection +from lilya._internal._module_loading import import_string +from lilya._internal._path import clean_path +from lilya.datastructures import URLPath +from lilya.middleware import DefineMiddleware +from lilya.responses import JSONResponse, Response as LilyaResponse +from lilya.routing import ( + BasePath as LilyaBasePath, Host, - Mount, + Include as LilyaInclude, NoMatchFound, - Route as StarletteRoute, - Router as StarletteRouter, - WebSocketRoute as StarletteWebSocketRoute, + Path as LilyaPath, + Router as LilyaRouter, + WebSocketPath as LilyaWebSocketPath, compile_path, ) -from starlette.types import ASGIApp, Lifespan, Receive, Scope, Send +from lilya.types import ASGIApp, Lifespan, Receive, Scope, Send from typing_extensions import Annotated, Doc from esmerald.conf import settings @@ -67,8 +69,6 @@ from esmerald.typing import Void, VoidType from esmerald.utils.constants import DATA, PAYLOAD, REDIRECT_STATUS_CODES, REQUEST, SOCKET from esmerald.utils.helpers import is_async_callable, is_class_and_subclass -from esmerald.utils.module_loading import import_string -from esmerald.utils.url import clean_path from esmerald.websockets import WebSocket, WebSocketClose if TYPE_CHECKING: # pragma: no cover @@ -93,7 +93,7 @@ from esmerald.typing import AnyCallable -class BaseRouter(StarletteRouter): +class BaseRouter(LilyaRouter): __slots__ = ( "redirect_slashes", "default", @@ -113,6 +113,10 @@ class BaseRouter(StarletteRouter): "on_startup", "on_shutdown", "root_path", + "path", + "_app", + "esmerald_lifespan", + "routing", ) def __init__( @@ -316,7 +320,7 @@ async def another(request: Request) -> str: Optional[str], Doc( """ - The name for the Gateway. The name can be reversed by `url_path_for()`. + The name for the Gateway. The name can be reversed by `path_for()`. """ ), ] = None, @@ -356,7 +360,7 @@ async def another(request: Request) -> str: Optional[List[Middleware]], Doc( """ - A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Starlette Middleware](https://www.starlette.io/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). + A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Lilya Middleware](https://www.lilya.dev/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). """ ), ] = None, @@ -490,8 +494,8 @@ async def another(request: Request) -> str: Include, Gateway, WebSocketGateway, - StarletteBaseRoute, - Mount, + LilyaBasePath, + LilyaBasePath, Host, Router, ), @@ -521,7 +525,7 @@ async def another(request: Request) -> str: self.dependencies = dependencies or {} self.exception_handlers = exception_handlers or {} self.interceptors: Sequence[Interceptor] = interceptors or [] - self.permissions: Sequence[Permission] = permissions or [] + self.permissions: Sequence[Permission] = permissions or [] # type: ignore self.routes: Any = routes or [] self.middleware = middleware or [] self.tags = tags or [] @@ -558,7 +562,7 @@ async def not_found( """Esmerald version of a not found handler when a resource is called and cannot be dealt with properly. - Esmerald overrides the original Starlette not_found to return always + Esmerald overrides the original Lilya not_found to return always JSONReponse. For plain ASGI apps, just returns the response. @@ -574,17 +578,17 @@ async def not_found( response = JSONResponse({"detail": "Not Found"}, status_code=status.HTTP_404_NOT_FOUND) await response(scope, receive, send) - def url_path_for(self, name: str, **path_params: Any) -> URLPath: + def path_for(self, name: str, **path_params: Any) -> URLPath: for route in self.routes or []: try: - return cast("URLPath", route.url_path_for(name, **path_params)) + return cast("URLPath", route.path_for(name, **path_params)) except NoMatchFound: ... if isinstance(route, (Gateway, WebSocketGateway)): handler = cast("Union[HTTPHandler, WebSocketHandler]", route.handler) try: - return handler.url_path_for(name, **path_params) + return handler.path_for(name, **path_params) except NoMatchFound: ... @@ -732,7 +736,7 @@ async def hello(self) -> str: """ routes = [] if not value.handler.parent: # pragma: no cover - value.handler(parent=self) # type: ignore + value.handler(parent=self) route_handlers: List[Union[HTTPHandler, WebSocketHandler]] = value.handler.get_route_handlers() # type: ignore for route_handler in route_handlers: @@ -811,7 +815,7 @@ def add_route( Optional[List[Middleware]], Doc( """ - A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Starlette Middleware](https://www.starlette.io/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). + A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Lilya Middleware](https://www.lilya.dev/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). """ ), ] = None, @@ -819,7 +823,7 @@ def add_route( Optional[str], Doc( """ - The name for the Gateway. The name can be reversed by `url_path_for()`. + The name for the Gateway. The name can be reversed by `path_for()`. """ ), ] = None, @@ -904,7 +908,7 @@ def add_websocket_route( Optional[str], Doc( """ - The name for the Gateway. The name can be reversed by `url_path_for()`. + The name for the Gateway. The name can be reversed by `path_for()`. """ ), ] = None, @@ -944,7 +948,7 @@ def add_websocket_route( Optional[List[Middleware]], Doc( """ - A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Starlette Middleware](https://www.starlette.io/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). + A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Lilya Middleware](https://www.lilya.dev/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). """ ), ] = None, @@ -993,7 +997,7 @@ async def websocket_route(socket: WebSocket) -> None: self.routes.append(websocket_gateway) -class HTTPHandler(BaseInterceptorMixin, FieldInfoMixin, StarletteRoute): +class HTTPHandler(BaseInterceptorMixin, FieldInfoMixin, LilyaPath): __slots__ = ( "path", "_interceptors", @@ -1028,7 +1032,7 @@ class HTTPHandler(BaseInterceptorMixin, FieldInfoMixin, StarletteRoute): def __init__( self, path: Optional[str] = None, - endpoint: Callable[..., Any] = None, + handler: Callable[..., Any] = None, *, methods: Optional[Sequence[str]] = None, status_code: Optional[int] = None, @@ -1058,18 +1062,21 @@ def __init__( """ if not path: path = "/" - super().__init__(path=path, endpoint=endpoint, include_in_schema=include_in_schema) + super().__init__( + path=path, + handler=handler, + include_in_schema=include_in_schema, + exception_handlers=exception_handlers, + ) self._permissions: Union[List[Permission], VoidType] = Void self._dependencies: Dependencies = {} - self._response_handler: Union[Callable[[Any], Awaitable[StarletteResponse]], VoidType] = ( - Void - ) + self._response_handler: Union[Callable[[Any], Awaitable[LilyaResponse]], VoidType] = Void self.parent: ParentType = None self.path = path - self.endpoint = endpoint + self.handler = handler self.summary = summary self.tags = tags or [] self.include_in_schema = include_in_schema @@ -1088,16 +1095,15 @@ def __init__( if not isinstance(method, str): raise ImproperlyConfigured(f"`{method}` in `methods` must be a string.") - self.methods: Set[str] = {HttpMethod[method].value for method in methods} + self.methods: Set[str] = {HttpMethod[method].value for method in methods} # type: ignore if isinstance(status_code, IntEnum): # pragma: no cover status_code = int(status_code) self.status_code = status_code - self.exception_handlers = exception_handlers or {} self.dependencies: Dependencies = dependencies or {} - self.description = description or inspect.cleandoc(self.endpoint.__doc__ or "") - self.permissions = list(permissions) if permissions else [] + self.description = description or inspect.cleandoc(self.handler.__doc__ or "") + self.permissions = list(permissions) if permissions else [] # type: ignore self.interceptors: Sequence[Interceptor] = [] self.middleware = list(middleware) if middleware else [] self.description = self.description.split("\f")[0] @@ -1115,7 +1121,7 @@ def __init__( self.fn: Optional[AnyCallable] = None self.route_map: Dict[str, Tuple[HTTPHandler, TransformerModel]] = {} - self.path_regex, self.path_format, self.param_convertors = compile_path(path) + self.path_regex, self.path_format, self.param_convertors, _ = compile_path(path) self._middleware: List[Middleware] = [] self._interceptors: Union[List[Interceptor], VoidType] = Void self.__type__: Union[str, None] = None @@ -1124,7 +1130,7 @@ def __init__( self.validate_responses(responses=self.responses) async def __call__(self, scope: Scope, receive: Receive, send: Send) -> Any: - await self.handle(scope=scope, receive=receive, send=send) + await self.handle_dispatch(scope=scope, receive=receive, send=send) def validate_responses(self, responses: Dict[int, OpenAPIResponse]) -> None: """ @@ -1160,7 +1166,7 @@ async def allowed_methods( @property def allow_header(self) -> Mapping[str, str]: """ - Default allow header to be injected in the Response and Starlette Response type + Default allow header to be injected in the Response and Lilya Response type handlers. """ return {"allow": str(self.methods)} @@ -1199,7 +1205,7 @@ def get_response_cookies(self) -> ResponseCookies: filtered_cookies.append(cookie) return filtered_cookies - async def handle(self, scope: "Scope", receive: "Receive", send: "Send") -> None: + async def handle_dispatch(self, scope: "Scope", receive: "Receive", send: "Send") -> None: """ ASGIapp that authorizes the connection and then awaits the handler function. """ @@ -1213,7 +1219,7 @@ async def handle(self, scope: "Scope", receive: "Receive", send: "Send") -> None route_handler, parameter_model = self.route_map[scope["method"]] if self.get_permissions(): - connection = HTTPConnection(scope=scope, receive=receive) + connection = Connection(scope=scope, receive=receive) await self.allow_connection(connection) response = await self.get_response_for_request( @@ -1263,7 +1269,7 @@ def validate_annotations(self) -> None: # pragma: no cover """ Validate annotations of the handlers. """ - return_annotation = self.signature.return_annotation + return_annotation = self.handler_signature.return_annotation if return_annotation is Signature.empty: raise ImproperlyConfigured( @@ -1296,15 +1302,15 @@ def validate_reserved_kwargs(self) -> None: # pragma: no cover """ Validates if special words are in the signature. """ - if DATA in self.signature.parameters and "GET" in self.methods: + if DATA in self.handler_signature.parameters and "GET" in self.methods: raise ImproperlyConfigured("'data' argument is unsupported for 'GET' request handlers") - if PAYLOAD in self.signature.parameters and "GET" in self.methods: + if PAYLOAD in self.handler_signature.parameters and "GET" in self.methods: raise ImproperlyConfigured( "'payload' argument is unsupported for 'GET' request handlers" ) - if SOCKET in self.signature.parameters: + if SOCKET in self.handler_signature.parameters: raise ImproperlyConfigured("The 'socket' argument is not supported with http handlers") def validate_handler(self) -> None: @@ -1312,12 +1318,12 @@ def validate_handler(self) -> None: self.validate_annotations() self.validate_reserved_kwargs() - async def to_response(self, app: "Esmerald", data: Any) -> StarletteResponse: + async def to_response(self, app: "Esmerald", data: Any) -> LilyaResponse: response_handler = self.get_response_handler() return await response_handler(app=app, data=data) # type: ignore[call-arg] -class WebhookHandler(HTTPHandler, FieldInfoMixin, StarletteRoute): +class WebhookHandler(HTTPHandler, FieldInfoMixin, LilyaPath): """ Base for a webhook handler. """ @@ -1353,7 +1359,7 @@ class WebhookHandler(HTTPHandler, FieldInfoMixin, StarletteRoute): def __init__( self, path: Optional[str] = None, - endpoint: Callable[..., Any] = None, + handler: Callable[..., Any] = None, *, methods: Optional[Sequence[str]] = None, status_code: Optional[int] = None, @@ -1383,7 +1389,7 @@ def __init__( _path = "/" # pragma: no cover super().__init__( path=_path, - endpoint=endpoint, + handler=handler, methods=methods, summary=summary, description=description, @@ -1410,7 +1416,7 @@ def __init__( self.path = path -class WebSocketHandler(BaseInterceptorMixin, StarletteWebSocketRoute): +class WebSocketHandler(BaseInterceptorMixin, LilyaWebSocketPath): """ Websocket handler object representation. """ @@ -1428,7 +1434,7 @@ def __init__( self, path: Optional[str] = None, *, - endpoint: Callable[..., Any] = None, + handler: Callable[..., Any] = None, dependencies: Optional[Dependencies] = None, exception_handlers: Optional[ExceptionHandlerMap] = None, permissions: Optional[List[Permission]] = None, @@ -1436,19 +1442,16 @@ def __init__( ): if not path: path = "/" - super().__init__(path=path, endpoint=endpoint) + super().__init__(path=path, handler=handler, exception_handlers=exception_handlers) self._permissions: Union[List[Permission], VoidType] = Void self._dependencies: Dependencies = {} - self._response_handler: Union[Callable[[Any], Awaitable[StarletteResponse]], VoidType] = ( - Void - ) + self._response_handler: Union[Callable[[Any], Awaitable[LilyaResponse]], VoidType] = Void self._interceptors: Union[List[Interceptor], VoidType] = Void self.interceptors: Sequence[Interceptor] = [] - self.endpoint = endpoint + self.handler = handler self.parent: ParentType = None self.dependencies = dependencies - self.exception_handlers = exception_handlers - self.permissions = permissions + self.permissions = permissions # type: ignore self.middleware = middleware self.signature_model: Optional[Type[SignatureModel]] = None self.websocket_parameter_model: Optional[TransformerModel] = None @@ -1489,7 +1492,7 @@ def validate_websocket_handler_function(self) -> None: # pragma: no cover "Functions decorated with 'asgi, get, patch, put, post and delete' must be async functions." ) - async def handle(self, scope: "Scope", receive: "Receive", send: "Send") -> None: + async def handle_dispatch(self, scope: "Scope", receive: "Receive", send: "Send") -> None: """The handle of a websocket""" if self.get_interceptors(): await self.intercept(scope, receive, send) @@ -1526,7 +1529,7 @@ async def get_kwargs(self, websocket: WebSocket) -> Any: return signature_model.parse_values_for_connection(connection=websocket, **kwargs) -class Include(Mount): +class Include(LilyaInclude): """ `Include` object class that allows scalability and modularity to happen with elegance. @@ -1638,7 +1641,7 @@ async def home(request: Request) -> dict: Optional[str], Doc( """ - The name for the Gateway. The name can be reversed by `url_path_for()`. + The name for the Gateway. The name can be reversed by `path_for()`. """ ), ] = None, @@ -1807,7 +1810,7 @@ async def another(request: Request) -> str: Optional[List[Middleware]], Doc( """ - A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Starlette Middleware](https://www.starlette.io/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). + A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Lilya Middleware](https://www.lilya.dev/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). """ ), ] = None, @@ -1879,35 +1882,16 @@ async def another(request: Request) -> str: if namespace: routes = include(namespace, pattern) - self.name = name - self.namespace = namespace - self.pattern = pattern - self.include_in_schema = include_in_schema - self.dependencies = dependencies or {} - self.interceptors: Sequence[Interceptor] = interceptors or [] - self.permissions: Sequence[Permission] = permissions or [] - self.middleware = middleware or [] - self.exception_handlers = exception_handlers or {} - self.deprecated = deprecated - self.response_class = None - self.response_cookies = None - self.response_headers = None - self.parent = parent - self.security = security or [] - self.tags = tags or [] - - if routes: - routes = self.resolve_route_path_handler(routes) - # Add the middleware to the include + self.middleware = middleware or [] include_middleware: Sequence[Middleware] = [] for _middleware in self.middleware: - if isinstance(_middleware, StarletteMiddleware): # pragma: no cover + if isinstance(_middleware, DefineMiddleware): # pragma: no cover include_middleware.append(_middleware) else: include_middleware.append( # type: ignore - StarletteMiddleware(cast("Type[StarletteMiddleware]", _middleware)) + DefineMiddleware(cast("Type[DefineMiddleware]", _middleware)) ) if isinstance(app, str): @@ -1915,12 +1899,31 @@ async def another(request: Request) -> str: self.app = self.resolve_app_parent(app=app) + self.dependencies = dependencies or {} + self.interceptors: Sequence[Interceptor] = interceptors or [] + self.permissions: Sequence[Permission] = permissions or [] # type: ignore + self.response_class = None + self.response_cookies = None + self.response_headers = None + self.parent = parent + self.security = security or [] + self.tags = tags or [] + + if namespace: + routes = include(namespace, pattern) + + if routes: + routes = self.resolve_route_path_handler(routes) + super().__init__( - self.path, + path=self.path, app=self.app, routes=routes, name=name, middleware=include_middleware, + exception_handlers=exception_handlers, + deprecated=deprecated, + include_in_schema=include_in_schema, ) def resolve_app_parent(self, app: Optional[Any]) -> Optional[Any]: @@ -1937,21 +1940,21 @@ def resolve_route_path_handler( self, routes: Sequence[Union[APIGateHandler, Include]] ) -> List[Union[Gateway, WebSocketGateway, Include]]: """ - Make sure the paths are properly configured from the handler endpoint. - The handler can be a Starlette function, an View or a HTTPHandler. + Make sure the paths are properly configured from the handler handler. + The handler can be a Lilya function, an View or a HTTPHandler. - Mount() has a limitation of not allowing nested Mount(). + LilyaBasePath() has a limitation of not allowing nested LilyaBasePath(). Example: route_patterns = [ - Mount(path='/my path', routes=[ - Mount(path='/another mount') + LilyaBasePath(path='/my path', routes=[ + LilyaBasePath(path='/another mount') ]) ] - Include() extends the Mount and adds extras on the top. Allowing nested + Include() extends the LilyaBasePath and adds extras on the top. Allowing nested Include() also allows importing in different ways. Via qualified namespace or via list() but not both. @@ -1995,17 +1998,17 @@ def resolve_route_path_handler( continue if isinstance(route.handler, (HTTPHandler, WebSocketHandler)): - route.handler.parent = route + route.handler.parent = route # type: ignore routing.append(route) continue if is_class_and_subclass(route.handler, View) or isinstance(route.handler, View): if not route.handler.parent: - route.handler = route.handler(parent=self) # type: ignore + route.handler = route.handler(parent=self) - route_handlers: List[ - Union[HTTPHandler, WebSocketHandler] - ] = route.handler.get_route_handlers() # type: ignore[union-attr] + route_handlers: List[Union[HTTPHandler, WebSocketHandler]] = ( + route.handler.get_route_handlers() + ) for route_handler in route_handlers: gateway = ( diff --git a/esmerald/routing/webhooks/handlers.py b/esmerald/routing/webhooks/handlers.py index ecf140dc..0019e659 100644 --- a/esmerald/routing/webhooks/handlers.py +++ b/esmerald/routing/webhooks/handlers.py @@ -1,6 +1,6 @@ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Sequence, Union, cast -from starlette import status +from lilya import status from typing_extensions import Annotated, Doc from esmerald.enums import HttpMethod, MediaType @@ -94,7 +94,7 @@ async def get_joiners() -> None: An integer indicating the status code of the handler. This can be achieved by passing directly the value or - by using the `esmerald.status` or even the `starlette.status`. + by using the `esmerald.status` or even the `lilya.status`. """ ), ] = status.HTTP_200_OK, @@ -159,7 +159,7 @@ async def get_joiners() -> None: Optional[List["Middleware"]], Doc( """ - A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Starlette Middleware](https://www.starlette.io/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). + A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Lilya Middleware](https://www.lilya.dev/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). """ ), ] = None, @@ -421,7 +421,7 @@ def wrapper(func: Any) -> WebhookHandler: responses=responses, ) handler.fn = func - handler.endpoint = func + handler.handler = func handler.__type__ = HttpMethod.GET.value handler.validate_handler() return handler @@ -464,7 +464,7 @@ def whead( An integer indicating the status code of the handler. This can be achieved by passing directly the value or - by using the `esmerald.status` or even the `starlette.status`. + by using the `esmerald.status` or even the `lilya.status`. """ ), ] = status.HTTP_200_OK, @@ -529,7 +529,7 @@ def whead( Optional[List["Middleware"]], Doc( """ - A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Starlette Middleware](https://www.starlette.io/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). + A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Lilya Middleware](https://www.lilya.dev/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). """ ), ] = None, @@ -690,7 +690,7 @@ def wrapper(func: Any) -> WebhookHandler: responses=responses, ) handler.fn = func - handler.endpoint = func + handler.handler = func handler.__type__ = HttpMethod.HEAD.value handler.validate_handler() return handler @@ -771,7 +771,7 @@ async def create_joiners() -> None: An integer indicating the status code of the handler. This can be achieved by passing directly the value or - by using the `esmerald.status` or even the `starlette.status`. + by using the `esmerald.status` or even the `lilya.status`. """ ), ] = status.HTTP_201_CREATED, @@ -836,7 +836,7 @@ async def create_joiners() -> None: Optional[List["Middleware"]], Doc( """ - A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Starlette Middleware](https://www.starlette.io/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). + A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Lilya Middleware](https://www.lilya.dev/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). """ ), ] = None, @@ -1098,7 +1098,7 @@ def wrapper(func: Any) -> WebhookHandler: responses=responses, ) handler.fn = func - handler.endpoint = func + handler.handler = func handler.__type__ = HttpMethod.POST.value handler.validate_handler() return handler @@ -1174,7 +1174,7 @@ async def update_joiners() -> None: An integer indicating the status code of the handler. This can be achieved by passing directly the value or - by using the `esmerald.status` or even the `starlette.status`. + by using the `esmerald.status` or even the `lilya.status`. """ ), ] = status.HTTP_200_OK, @@ -1239,7 +1239,7 @@ async def update_joiners() -> None: Optional[List["Middleware"]], Doc( """ - A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Starlette Middleware](https://www.starlette.io/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). + A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Lilya Middleware](https://www.lilya.dev/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). """ ), ] = None, @@ -1501,7 +1501,7 @@ def wrapper(func: Any) -> WebhookHandler: responses=responses, ) handler.fn = func - handler.endpoint = func + handler.handler = func handler.__type__ = HttpMethod.PUT.value handler.validate_handler() return handler @@ -1577,7 +1577,7 @@ async def update_partial_joiners() -> None: An integer indicating the status code of the handler. This can be achieved by passing directly the value or - by using the `esmerald.status` or even the `starlette.status`. + by using the `esmerald.status` or even the `lilya.status`. """ ), ] = status.HTTP_200_OK, @@ -1642,7 +1642,7 @@ async def update_partial_joiners() -> None: Optional[List["Middleware"]], Doc( """ - A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Starlette Middleware](https://www.starlette.io/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). + A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Lilya Middleware](https://www.lilya.dev/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). """ ), ] = None, @@ -1904,7 +1904,7 @@ def wrapper(func: Any) -> WebhookHandler: responses=responses, ) handler.fn = func - handler.endpoint = func + handler.handler = func handler.__type__ = HttpMethod.PATCH.value handler.validate_handler() return handler @@ -1980,7 +1980,7 @@ async def delete_joiners() -> None: An integer indicating the status code of the handler. This can be achieved by passing directly the value or - by using the `esmerald.status` or even the `starlette.status`. + by using the `esmerald.status` or even the `lilya.status`. """ ), ] = status.HTTP_204_NO_CONTENT, @@ -2045,7 +2045,7 @@ async def delete_joiners() -> None: Optional[List["Middleware"]], Doc( """ - A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Starlette Middleware](https://www.starlette.io/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). + A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Lilya Middleware](https://www.lilya.dev/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). """ ), ] = None, @@ -2307,7 +2307,7 @@ def wrapper(func: Any) -> WebhookHandler: responses=responses, ) handler.fn = func - handler.endpoint = func + handler.handler = func handler.__type__ = HttpMethod.DELETE.value handler.validate_handler() return handler @@ -2350,7 +2350,7 @@ def whoptions( An integer indicating the status code of the handler. This can be achieved by passing directly the value or - by using the `esmerald.status` or even the `starlette.status`. + by using the `esmerald.status` or even the `lilya.status`. """ ), ] = status.HTTP_200_OK, @@ -2415,7 +2415,7 @@ def whoptions( Optional[List["Middleware"]], Doc( """ - A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Starlette Middleware](https://www.starlette.io/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). + A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Lilya Middleware](https://www.lilya.dev/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). """ ), ] = None, @@ -2576,7 +2576,7 @@ def wrapper(func: Any) -> WebhookHandler: responses=responses, ) handler.fn = func - handler.endpoint = func + handler.handler = func handler.__type__ = HttpMethod.OPTIONS.value handler.validate_handler() return handler @@ -2619,7 +2619,7 @@ def whtrace( An integer indicating the status code of the handler. This can be achieved by passing directly the value or - by using the `esmerald.status` or even the `starlette.status`. + by using the `esmerald.status` or even the `lilya.status`. """ ), ] = status.HTTP_200_OK, @@ -2684,7 +2684,7 @@ def whtrace( Optional[List["Middleware"]], Doc( """ - A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Starlette Middleware](https://www.starlette.io/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). + A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Lilya Middleware](https://www.lilya.dev/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). """ ), ] = None, @@ -2846,7 +2846,7 @@ def wrapper(func: Any) -> WebhookHandler: responses=responses, ) handler.fn = func - handler.endpoint = func + handler.handler = func handler.__type__ = HttpMethod.TRACE.value handler.validate_handler() return handler @@ -2941,7 +2941,7 @@ async def operate_joiners() -> None: An integer indicating the status code of the handler. This can be achieved by passing directly the value or - by using the `esmerald.status` or even the `starlette.status`. + by using the `esmerald.status` or even the `lilya.status`. """ ), ] = status.HTTP_200_OK, @@ -3006,7 +3006,7 @@ async def operate_joiners() -> None: Optional[List["Middleware"]], Doc( """ - A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Starlette Middleware](https://www.starlette.io/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). + A list of middleware to run for every request. The middlewares of an Include will be checked from top-down or [Lilya Middleware](https://www.lilya.dev/middleware/) as they are both converted internally. Read more about [Python Protocols](https://peps.python.org/pep-0544/). """ ), ] = None, @@ -3284,7 +3284,7 @@ def wrapper(func: Any) -> WebhookHandler: ) handler.fn = func - handler.endpoint = func + handler.handler = func handler.__type__ = HttpMethod.OPTIONS.value handler.validate_handler() return handler diff --git a/esmerald/staticfiles.py b/esmerald/staticfiles.py index 299015d4..96d9088a 100644 --- a/esmerald/staticfiles.py +++ b/esmerald/staticfiles.py @@ -1 +1 @@ -from starlette.staticfiles import StaticFiles as StaticFiles # noqa +from lilya.staticfiles import StaticFiles as StaticFiles # noqa diff --git a/esmerald/template/jinja.py b/esmerald/template/jinja.py index d4f128c3..8842b7d8 100644 --- a/esmerald/template/jinja.py +++ b/esmerald/template/jinja.py @@ -30,7 +30,7 @@ jinja2 = None -class JinjaTemplateEngine(TemplateEngineProtocol[JinjaTemplate]): +class JinjaTemplateEngine(TemplateEngineProtocol[JinjaTemplate]): # type: ignore def __init__( self, directory: Union["DirectoryPath", List["DirectoryPath"]], **env_options: Any ) -> None: @@ -43,7 +43,7 @@ def _create_environment( @pass_context def url_for(context: dict, name: str, **path_params: Any) -> Any: request = context["request"] - return request.url_for(name, **path_params) + return request.path_for(name, **path_params) loader = FileSystemLoader(directory) env_options.setdefault("loader", loader) diff --git a/esmerald/testclient.py b/esmerald/testclient.py index d3199be8..57943230 100644 --- a/esmerald/testclient.py +++ b/esmerald/testclient.py @@ -12,9 +12,9 @@ ) from httpx._client import CookieTypes +from lilya.testclient import TestClient # noqa from openapi_schemas_pydantic.v3_1_0 import Contact, License, SecurityScheme from pydantic import AnyUrl -from starlette.testclient import TestClient # noqa from esmerald.applications import Esmerald from esmerald.utils.crypto import get_random_secret_key diff --git a/esmerald/transformers/utils.py b/esmerald/transformers/utils.py index a639ccca..957f2759 100644 --- a/esmerald/transformers/utils.py +++ b/esmerald/transformers/utils.py @@ -1,7 +1,7 @@ from typing import TYPE_CHECKING, Any, Dict, List, NamedTuple, Set, Tuple, Type, Union, cast +from lilya.datastructures import URL from pydantic.fields import FieldInfo -from starlette.datastructures import URL from esmerald.enums import ParamType, ScopeType from esmerald.exceptions import ImproperlyConfigured, ValidationErrorException diff --git a/esmerald/types.py b/esmerald/types.py index b36e79ca..48736b03 100644 --- a/esmerald/types.py +++ b/esmerald/types.py @@ -13,9 +13,9 @@ get_args, ) -from starlette.middleware import Middleware as StarletteMiddleware # noqa -from starlette.responses import Response as StarletteResponse # noqa -from starlette.types import ASGIApp # noqa +from lilya.middleware import DefineMiddleware # noqa +from lilya.responses import Response as LilyaResponse # noqa +from lilya.types import ASGIApp # noqa from typing_extensions import Literal from esmerald.backgound import BackgroundTask, BackgroundTasks @@ -70,7 +70,7 @@ HTTPMethod = Literal["GET", "POST", "DELETE", "PATCH", "PUT", "HEAD"] HTTPMethods = get_args(HTTPMethod) -Middleware = StarletteMiddleware +Middleware = DefineMiddleware ResponseType = Type[Response] diff --git a/esmerald/utils/constants.py b/esmerald/utils/constants.py index 3b07cd6b..ec15acad 100644 --- a/esmerald/utils/constants.py +++ b/esmerald/utils/constants.py @@ -1,4 +1,4 @@ -from starlette import status +from lilya import status from esmerald.enums import HttpMethod diff --git a/esmerald/utils/crypto.py b/esmerald/utils/crypto.py index e10aba21..47eb86d3 100644 --- a/esmerald/utils/crypto.py +++ b/esmerald/utils/crypto.py @@ -1,22 +1,3 @@ -import secrets -from typing import Optional +from lilya.crypto import get_random_secret_key, get_random_string - -def get_random_string( - length: int = 12, - allowed_chars: str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", -) -> str: - """ - Returns a securely generated random string. - The default length of 12 with the a-z, A-Z, 0-9 character set returns - a 71-bit value. log_2((26+26+10)^12) =~ 71 bits - """ - return "".join(secrets.choice(allowed_chars) for _ in range(length)) - - -def get_random_secret_key(length: Optional[int] = 50) -> str: - """ - Return a 50 character random string usable as a SECRET_KEY setting value. - """ - chars = "abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)" - return get_random_string(length, chars) +__all__ = ["get_random_string", "get_random_secret_key"] diff --git a/esmerald/utils/helpers.py b/esmerald/utils/helpers.py index e842c0ba..908c43db 100644 --- a/esmerald/utils/helpers.py +++ b/esmerald/utils/helpers.py @@ -4,7 +4,7 @@ from typing import Any, Union import slugify -from starlette._utils import is_async_callable as is_async_callable +from lilya.compat import is_async_callable as is_async_callable from typing_extensions import get_args, get_origin from esmerald.datastructures.msgspec import Struct diff --git a/esmerald/utils/module_loading.py b/esmerald/utils/module_loading.py index cd2444b1..38e4905d 100644 --- a/esmerald/utils/module_loading.py +++ b/esmerald/utils/module_loading.py @@ -1,22 +1 @@ -from importlib import import_module -from typing import Any - - -def import_string(dotted_path: str) -> Any: - """ - Import a dotted module path and return the attribute/class designated by the - last name in the path. Raise ImportError if the import failed. - """ - try: - module_path, class_name = dotted_path.rsplit(".", 1) - except ValueError as err: - raise ImportError("%s doesn't look like a module path" % dotted_path) from err - - module = import_module(module_path) - - try: - return getattr(module, class_name) - except AttributeError as err: - raise ImportError( - 'Module "%s" does not define a "%s" attribute/class' % (module_path, class_name) - ) from err +from lilya._internal._module_loading import import_string as import_string diff --git a/esmerald/utils/sync.py b/esmerald/utils/sync.py index da456844..be063248 100644 --- a/esmerald/utils/sync.py +++ b/esmerald/utils/sync.py @@ -1,8 +1,5 @@ -import asyncio -from concurrent import futures -from concurrent.futures import Future from functools import partial -from typing import Any, Awaitable, Callable, Generic, TypeVar +from typing import Awaitable, Callable, Generic, TypeVar, cast from anyio import to_thread from typing_extensions import ParamSpec @@ -19,21 +16,9 @@ class AsyncCallable(Generic[P, T]): def __init__(self, fn: Callable[P, T]): self.fn: Callable[P, Awaitable[T]] if is_async_callable(fn): - self.fn = fn + self.fn = cast(Callable, fn) else: self.fn = partial(to_thread.run_sync, fn) async def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T: return await self.fn(*args, **kwargs) - - -def run_sync(async_function: Awaitable) -> Any: - """ - Runs the queries in sync mode - """ - try: - return asyncio.run(async_function) # type: ignore - except RuntimeError: - with futures.ThreadPoolExecutor(max_workers=1) as executor: - future: Future = executor.submit(asyncio.run, async_function) # type: ignore - return future.result() diff --git a/esmerald/websockets.py b/esmerald/websockets.py index 483eb66f..170ffc1c 100644 --- a/esmerald/websockets.py +++ b/esmerald/websockets.py @@ -1,15 +1,15 @@ from typing import Optional -from starlette.requests import HTTPConnection as HTTPConnection # noqa: F401 -from starlette.websockets import ( +from lilya._internal._connection import Connection as Connection # noqa: F401 +from lilya.websockets import ( WebSocket as WebSocket, # noqa WebSocketClose as WebSocketClose, # noqa - WebSocketDisconnect as StarletteWebSocketDisconnect, # noqa + WebSocketDisconnect as LilyaWebSocketDisconnect, # noqa WebSocketState as WebSocketState, # noqa ) -class WebSocketDisconnect(StarletteWebSocketDisconnect): +class WebSocketDisconnect(LilyaWebSocketDisconnect): """Esmerald WebSocketDisconnect""" def __init__(self, code: int = 1000, reason: Optional[str] = None) -> None: diff --git a/mkdocs.yml b/mkdocs.yml index 8b767c46..3488a852 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -57,7 +57,7 @@ plugins: - griffe_typingdoc show_root_heading: true show_if_no_docstring: true - preload_modules: [httpx, starlette, a2wsgi] + preload_modules: [httpx, lilya, a2wsgi] inherited_members: true members_order: source separate_signature: true diff --git a/pyproject.toml b/pyproject.toml index f8331808..ad97a08c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,6 +44,7 @@ dependencies = [ "click>=8.1.4,<9.0.0", "itsdangerous>=2.1.2,<3.0.0", "jinja2>=3.1.2,<4.0.0", + "lilya>=0.2.3", "loguru>=0.7.0,<0.8.0", "pydantic>=2.5.3,<3.0.0", "pydantic-settings>=2.0.0,<3.0.0", @@ -53,7 +54,6 @@ dependencies = [ "msgspec>=0.18.5,<1.0.0", "nest_asyncio>=1.5.8,<2.0.0", "rich>=13.3.1,<14.0.0", - "starlette>=0.36.3,<1", ] keywords = [ "api", @@ -62,6 +62,7 @@ keywords = [ "asgi", "pydantic", "starlette", + "lilya", "framework", "websocket", "openapi", @@ -109,9 +110,9 @@ test = [ "passlib==1.7.4", "polyfactory>=2.5.0,<3.0.0", "python-jose>=3.3.0,<4", - "saffier[postgres]>=0.18.0", + "saffier[postgres]>=1.3.7", "edgy[postgres]>=0.4.0", - "mongoz>=0.4.1", + "mongoz>=0.6.0", "requests>=2.28.2,<3.0.0", "ujson>=5.7.0,<6", @@ -164,7 +165,7 @@ combine-as-imports = true [tool.isort] profile = "black" -known_third_party = ["esmerald", "pydantic", "starlette"] +known_third_party = ["esmerald", "pydantic", "msgspec", "lilya"] [tool.mypy] strict = true diff --git a/scripts/enforce b/scripts/write similarity index 100% rename from scripts/enforce rename to scripts/write diff --git a/tests/app_settings/test_settings.py b/tests/app_settings/test_settings.py index b0797586..9b3b8891 100644 --- a/tests/app_settings/test_settings.py +++ b/tests/app_settings/test_settings.py @@ -2,8 +2,8 @@ from typing import Any, Dict, List import pytest +from lilya.middleware import DefineMiddleware from pydantic import BaseModel -from starlette.middleware import Middleware as StarletteMiddleware from esmerald import ( ChildEsmerald, @@ -93,7 +93,7 @@ def test_settings_global(test_client_factory): with create_client( app_name="my app", routes=[Gateway(handler=_request_settings), Gateway(handler=_app_settings)], - middleware=[StarletteMiddleware(RequestSettingsMiddleware)], + middleware=[DefineMiddleware(RequestSettingsMiddleware)], ) as client: request_settings = client.get("/request-settings") app_settings = client.get("/app-settings") @@ -107,7 +107,7 @@ def test_settings_global(test_client_factory): def test_settings_global_without_parameters(test_client_factory): with create_client( routes=[Gateway(handler=_request_settings), Gateway(handler=_app_settings)], - middleware=[StarletteMiddleware(RequestSettingsMiddleware)], + middleware=[DefineMiddleware(RequestSettingsMiddleware)], ) as client: request_settings = client.get("/request-settings") app_settings = client.get("/app-settings") @@ -195,7 +195,7 @@ async def _app_settings(request: Request) -> Dict[Any, Any]: child = ChildEsmerald( routes=[Gateway(handler=_app_settings)], settings_module=ChildSettings, - middleware=[StarletteMiddleware(RequestSettingsMiddleware)], + middleware=[DefineMiddleware(RequestSettingsMiddleware)], ) with create_client(routes=[Include("/child", app=child)]) as client: @@ -232,7 +232,7 @@ async def _app_settings(request: Request) -> Dict[Any, Any]: child = ChildEsmerald( routes=[Gateway(handler=_app_settings)], settings_module=ChildSettings, - middleware=[StarletteMiddleware(RequestSettingsMiddleware)], + middleware=[DefineMiddleware(RequestSettingsMiddleware)], csrf_config=CSRFConfig(secret=secret), ) @@ -267,7 +267,7 @@ async def _app_settings(request: Request) -> Dict[Any, Any]: child = ChildEsmerald( routes=[Gateway(handler=_app_settings)], settings_module=NestedChildSettings, - middleware=[StarletteMiddleware(RequestSettingsMiddleware)], + middleware=[DefineMiddleware(RequestSettingsMiddleware)], ) nested_child = ChildEsmerald(routes=[Include(app=child)], settings_module=ChildSettings) diff --git a/tests/applications/test_applications.py b/tests/applications/test_applications.py index 1522d56f..e577b76b 100644 --- a/tests/applications/test_applications.py +++ b/tests/applications/test_applications.py @@ -3,15 +3,15 @@ import anyio import pytest -from starlette import status -from starlette.middleware import Middleware -from starlette.routing import Host +from lilya import status +from lilya.middleware import DefineMiddleware +from lilya.routing import Host from esmerald import Request from esmerald.applications import Esmerald from esmerald.exceptions import HTTPException, ImproperlyConfigured, WebSocketException from esmerald.middleware import TrustedHostMiddleware -from esmerald.responses import JSONResponse, PlainTextResponse +from esmerald.responses import JSONResponse, PlainText from esmerald.routing.gateways import Gateway, WebSocketGateway from esmerald.routing.handlers import get, head, options, route, websocket from esmerald.routing.router import Include, Router @@ -32,29 +32,29 @@ async def http_exception(request, exc): @route(methods=["GET", "HEAD", "TRACE"]) -def func_homepage(request: Request) -> PlainTextResponse: - return PlainTextResponse("Hello, world!") +def func_homepage(request: Request) -> PlainText: + return PlainText("Hello, world!") @get() -async def async_homepage(request: Request) -> PlainTextResponse: - return PlainTextResponse("Hello, world!") +async def async_homepage(request: Request) -> PlainText: + return PlainText("Hello, world!") @get() -def all_users_page(request: Request) -> PlainTextResponse: - return PlainTextResponse("Hello, everyone!") +def all_users_page(request: Request) -> PlainText: + return PlainText("Hello, everyone!") @get() -def user_page(request: Request) -> PlainTextResponse: +def user_page(request: Request) -> PlainText: username = request.path_params["username"] - return PlainTextResponse(f"Hello, {username}!") + return PlainText(f"Hello, {username}!") @get() -def custom_subdomain(request: Request) -> PlainTextResponse: - return PlainTextResponse("Subdomain: " + request.path_params["subdomain"]) +def custom_subdomain(request: Request) -> PlainText: + return PlainText("Subdomain: " + request.path_params["subdomain"]) @get() @@ -63,13 +63,13 @@ def runtime_error(request: Request) -> None: @head() -def head_func(request: Request) -> PlainTextResponse: - return PlainTextResponse("Hello, world!") +def head_func(request: Request) -> PlainText: + return PlainText("Hello, world!") @options() -def head_options(request: Request) -> PlainTextResponse: - return PlainTextResponse("Hello, world!") +def head_options(request: Request) -> PlainText: + return PlainText("Hello, world!") @websocket() @@ -119,7 +119,9 @@ def custom_ws_exception_handler(websocket: WebSocket, exc: CustomWSException): CustomWSException: custom_ws_exception_handler, } -middleware = [Middleware(TrustedHostMiddleware, allowed_hosts=["testserver", "*.example.org"])] +middleware = [ + DefineMiddleware(TrustedHostMiddleware, allowed_hosts=["testserver", "*.example.org"]) +] app = Esmerald( routes=[ @@ -146,7 +148,7 @@ def client(test_client_factory): def test_url_path_for(): - assert app.url_path_for("func_homepage") == "/func" + assert app.path_for("func_homepage") == "/func" def test_func_route(client): @@ -210,7 +212,7 @@ def test_websocket_route(client): def test_400(client): response = client.get("/404") assert response.status_code == 404 - assert response.json() == {"detail": "The resource cannot be found."} + assert response.json() == {"detail": "Not Found"} def test_405(client): @@ -289,8 +291,8 @@ async def homepage(request: Request) -> None: def test_app_add_route(test_client_factory): @get() - async def homepage(request: Request) -> PlainTextResponse: - return PlainTextResponse("Hello, World!") + async def homepage(request: Request) -> PlainText: + return PlainText("Hello, World!") app = Esmerald( routes=[ @@ -307,8 +309,8 @@ async def homepage(request: Request) -> PlainTextResponse: @pytest.mark.parametrize("path", ["/stuff", "/test", "/a-root-path", "/new-root-path"]) def test_app_add_route_with_root_path(test_client_factory, path): @get() - async def homepage(request: Request) -> PlainTextResponse: - return PlainTextResponse("Hello, World!") + async def homepage(request: Request) -> PlainText: + return PlainText("Hello, World!") app = Esmerald( routes=[ @@ -391,20 +393,11 @@ async def lifespan(app): assert cleanup_complete -deprecated_lifespan = pytest.mark.filterwarnings( - r"ignore" - r":(async )?generator function lifespans are deprecated, use an " - r"@contextlib\.asynccontextmanager function instead" - r":DeprecationWarning" - r":starlette.routing" -) - - -@deprecated_lifespan def test_app_async_gen_lifespan(test_client_factory): startup_complete = False cleanup_complete = False + @asynccontextmanager async def lifespan(app): nonlocal startup_complete, cleanup_complete startup_complete = True @@ -422,28 +415,6 @@ async def lifespan(app): assert cleanup_complete -@deprecated_lifespan -def test_app_sync_gen_lifespan(test_client_factory): - startup_complete = False - cleanup_complete = False - - def lifespan(app): - nonlocal startup_complete, cleanup_complete - startup_complete = True - yield - cleanup_complete = True - - app = Esmerald(lifespan=lifespan) - - assert not startup_complete - assert not cleanup_complete - with test_client_factory(app): - assert startup_complete - assert not cleanup_complete - assert startup_complete - assert cleanup_complete - - def test_raise_improperly_configured_on_route_function(test_client_factory): with pytest.raises(ImproperlyConfigured): app = Esmerald(routes=[]) diff --git a/tests/applications/test_events.py b/tests/applications/test_events.py index 5ba1eb8f..8d9d01dc 100644 --- a/tests/applications/test_events.py +++ b/tests/applications/test_events.py @@ -1,7 +1,5 @@ from contextlib import asynccontextmanager -import pytest - from esmerald import Esmerald @@ -103,20 +101,11 @@ async def lifespan(app): assert cleanup_complete -deprecated_lifespan = pytest.mark.filterwarnings( - r"ignore" - r":(async )?generator function lifespans are deprecated, use an " - r"@contextlib\.asynccontextmanager function instead" - r":DeprecationWarning" - r":starlette.routing" -) - - -@deprecated_lifespan def test_app_async_gen_lifespan(test_client_factory): startup_complete = False cleanup_complete = False + @asynccontextmanager async def lifespan(app): nonlocal startup_complete, cleanup_complete startup_complete = True @@ -132,25 +121,3 @@ async def lifespan(app): assert not cleanup_complete assert startup_complete assert cleanup_complete - - -@deprecated_lifespan -def test_app_sync_gen_lifespan(test_client_factory): - startup_complete = False - cleanup_complete = False - - def lifespan(app): - nonlocal startup_complete, cleanup_complete - startup_complete = True - yield - cleanup_complete = True - - app = Esmerald(lifespan=lifespan) - - assert not startup_complete - assert not cleanup_complete - with test_client_factory(app): - assert startup_complete - assert not cleanup_complete - assert startup_complete - assert cleanup_complete diff --git a/tests/databases/edgy/test_middleware_data.py b/tests/databases/edgy/test_middleware_data.py index cf93c26c..8e158d62 100644 --- a/tests/databases/edgy/test_middleware_data.py +++ b/tests/databases/edgy/test_middleware_data.py @@ -8,8 +8,8 @@ from anyio import from_thread, sleep, to_thread from edgy import ObjectNotFound from httpx import AsyncClient +from lilya.middleware import DefineMiddleware as LilyaMiddleware from pydantic import BaseModel -from starlette.middleware import Middleware as StarletteMiddleware from esmerald import Esmerald, Gateway, Include, JSONResponse, Request, get, post, status from esmerald.conf import settings @@ -180,7 +180,7 @@ def app(): Include( routes=[Gateway(handler=home)], middleware=[ - StarletteMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User) + LilyaMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User) ], ), ], @@ -198,7 +198,7 @@ async def async_client(app) -> AsyncGenerator: async def test_cannot_access_endpoint_without_header(test_client_factory, async_client): with create_client( routes=[Gateway(handler=home)], - middleware=[StarletteMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User)], + middleware=[LilyaMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User)], ) as client: response = client.get("/") @@ -211,7 +211,7 @@ async def test_cannot_access_endpoint_with_invalid_header(test_client_factory, a with create_client( routes=[Gateway(handler=home)], - middleware=[StarletteMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User)], + middleware=[LilyaMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User)], ) as client: response = client.get("/", headers={jwt_config.authorization_header: token}) @@ -224,7 +224,7 @@ async def test_cannot_access_endpoint_with_invalid_token(test_client_factory, as with create_client( routes=[Gateway(handler=home)], - middleware=[StarletteMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User)], + middleware=[LilyaMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User)], raise_server_exceptions=False, ): response = await async_client.get( diff --git a/tests/databases/edgy/test_middleware_payload.py b/tests/databases/edgy/test_middleware_payload.py index 4df375fa..01a54958 100644 --- a/tests/databases/edgy/test_middleware_payload.py +++ b/tests/databases/edgy/test_middleware_payload.py @@ -8,8 +8,8 @@ from anyio import from_thread, sleep, to_thread from edgy import ObjectNotFound from httpx import AsyncClient +from lilya.middleware import DefineMiddleware as LilyaMiddleware from pydantic import BaseModel -from starlette.middleware import Middleware as StarletteMiddleware from esmerald import Esmerald, Gateway, Include, JSONResponse, Request, get, post, status from esmerald.conf import settings @@ -180,7 +180,7 @@ def app(): Include( routes=[Gateway(handler=home)], middleware=[ - StarletteMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User) + LilyaMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User) ], ), ], @@ -198,7 +198,7 @@ async def async_client(app) -> AsyncGenerator: async def test_cannot_access_endpoint_without_header(test_client_factory, async_client): with create_client( routes=[Gateway(handler=home)], - middleware=[StarletteMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User)], + middleware=[LilyaMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User)], ) as client: response = client.get("/") @@ -211,7 +211,7 @@ async def test_cannot_access_endpoint_with_invalid_header(test_client_factory, a with create_client( routes=[Gateway(handler=home)], - middleware=[StarletteMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User)], + middleware=[LilyaMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User)], ) as client: response = client.get("/", headers={jwt_config.authorization_header: token}) @@ -224,7 +224,7 @@ async def test_cannot_access_endpoint_with_invalid_token(test_client_factory, as with create_client( routes=[Gateway(handler=home)], - middleware=[StarletteMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User)], + middleware=[LilyaMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User)], raise_server_exceptions=False, ): response = await async_client.get( diff --git a/tests/databases/mongoz/test_middleware_data.py b/tests/databases/mongoz/test_middleware_data.py index b99f8f83..1db92739 100644 --- a/tests/databases/mongoz/test_middleware_data.py +++ b/tests/databases/mongoz/test_middleware_data.py @@ -7,9 +7,9 @@ import pytest from anyio import from_thread, sleep, to_thread from httpx import AsyncClient +from lilya.middleware import DefineMiddleware as LilyaMiddleware from mongoz import DocumentNotFound from pydantic import BaseModel -from starlette.middleware import Middleware as StarletteMiddleware from esmerald import Esmerald, Gateway, Include, JSONResponse, Request, get, post, status from esmerald.conf import settings @@ -163,7 +163,7 @@ def app(): Include( routes=[Gateway(handler=home)], middleware=[ - StarletteMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User) + LilyaMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User) ], ), ], @@ -181,7 +181,7 @@ async def async_client(app) -> AsyncGenerator: async def test_cannot_access_endpoint_without_header(test_client_factory, async_client): with create_client( routes=[Gateway(handler=home)], - middleware=[StarletteMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User)], + middleware=[LilyaMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User)], ) as client: response = client.get("/") @@ -194,7 +194,7 @@ async def test_cannot_access_endpoint_with_invalid_header(test_client_factory, a with create_client( routes=[Gateway(handler=home)], - middleware=[StarletteMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User)], + middleware=[LilyaMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User)], ) as client: response = client.get("/", headers={jwt_config.authorization_header: token}) @@ -207,7 +207,7 @@ async def test_cannot_access_endpoint_with_invalid_token(test_client_factory, as with create_client( routes=[Gateway(handler=home)], - middleware=[StarletteMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User)], + middleware=[LilyaMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User)], raise_server_exceptions=False, ): response = await async_client.get( diff --git a/tests/databases/mongoz/test_middleware_payload_on_gateway.py b/tests/databases/mongoz/test_middleware_payload_on_gateway.py index 8a67b33e..eb896a1f 100644 --- a/tests/databases/mongoz/test_middleware_payload_on_gateway.py +++ b/tests/databases/mongoz/test_middleware_payload_on_gateway.py @@ -7,9 +7,9 @@ import pytest from anyio import from_thread, sleep, to_thread from httpx import AsyncClient +from lilya.middleware import DefineMiddleware as LilyaMiddleware from mongoz import DocumentNotFound from pydantic import BaseModel -from starlette.middleware import Middleware as StarletteMiddleware from esmerald import Esmerald, Gateway, Include, JSONResponse, Request, get, post, status from esmerald.conf import settings @@ -165,9 +165,7 @@ def app(): Gateway( handler=home, middleware=[ - StarletteMiddleware( - JWTAuthMiddleware, config=jwt_config, user_model=User - ) + LilyaMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User) ], ) ], @@ -187,7 +185,7 @@ async def async_client(app) -> AsyncGenerator: async def test_cannot_access_endpoint_without_header(test_client_factory, async_client): with create_client( routes=[Gateway(handler=home)], - middleware=[StarletteMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User)], + middleware=[LilyaMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User)], ) as client: response = client.get("/") @@ -200,7 +198,7 @@ async def test_cannot_access_endpoint_with_invalid_header(test_client_factory, a with create_client( routes=[Gateway(handler=home)], - middleware=[StarletteMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User)], + middleware=[LilyaMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User)], ) as client: response = client.get("/", headers={jwt_config.authorization_header: token}) @@ -213,7 +211,7 @@ async def test_cannot_access_endpoint_with_invalid_token(test_client_factory, as with create_client( routes=[Gateway(handler=home)], - middleware=[StarletteMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User)], + middleware=[LilyaMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User)], raise_server_exceptions=False, ): response = await async_client.get( diff --git a/tests/databases/mongoz/test_middleware_payload_on_handler.py b/tests/databases/mongoz/test_middleware_payload_on_handler.py index a860d398..45cb86ad 100644 --- a/tests/databases/mongoz/test_middleware_payload_on_handler.py +++ b/tests/databases/mongoz/test_middleware_payload_on_handler.py @@ -7,9 +7,9 @@ import pytest from anyio import from_thread, sleep, to_thread from httpx import AsyncClient +from lilya.middleware import DefineMiddleware as LilyaMiddleware from mongoz import DocumentNotFound from pydantic import BaseModel -from starlette.middleware import Middleware as StarletteMiddleware from esmerald import Esmerald, Gateway, Include, JSONResponse, Request, get, post, status from esmerald.conf import settings @@ -126,7 +126,7 @@ class CreateUser(BaseModel): @get( - middleware=[StarletteMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User)], + middleware=[LilyaMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User)], ) async def home(request: Request) -> dict: return {"message": f"hello {request.user.email}"} @@ -184,7 +184,7 @@ async def async_client(app) -> AsyncGenerator: async def test_cannot_access_endpoint_without_header(test_client_factory, async_client): with create_client( routes=[Gateway(handler=home)], - middleware=[StarletteMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User)], + middleware=[LilyaMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User)], ) as client: response = client.get("/") @@ -197,7 +197,7 @@ async def test_cannot_access_endpoint_with_invalid_header(test_client_factory, a with create_client( routes=[Gateway(handler=home)], - middleware=[StarletteMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User)], + middleware=[LilyaMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User)], ) as client: response = client.get("/", headers={jwt_config.authorization_header: token}) @@ -210,7 +210,7 @@ async def test_cannot_access_endpoint_with_invalid_token(test_client_factory, as with create_client( routes=[Gateway(handler=home)], - middleware=[StarletteMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User)], + middleware=[LilyaMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User)], raise_server_exceptions=False, ): response = await async_client.get( diff --git a/tests/databases/saffier/test_middleware_data.py b/tests/databases/saffier/test_middleware_data.py index 2374ace4..60fdac67 100644 --- a/tests/databases/saffier/test_middleware_data.py +++ b/tests/databases/saffier/test_middleware_data.py @@ -7,9 +7,9 @@ import pytest from anyio import from_thread, sleep, to_thread from httpx import AsyncClient +from lilya.middleware import DefineMiddleware as LilyaMiddleware from pydantic import BaseModel from saffier.exceptions import DoesNotFound -from starlette.middleware import Middleware as StarletteMiddleware from esmerald import Esmerald, Gateway, Include, JSONResponse, Request, get, post, status from esmerald.conf import settings @@ -180,7 +180,7 @@ def app(): Include( routes=[Gateway(handler=home)], middleware=[ - StarletteMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User) + LilyaMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User) ], ), ], @@ -198,7 +198,7 @@ async def async_client(app) -> AsyncGenerator: async def test_cannot_access_endpoint_without_header(test_client_factory, async_client): with create_client( routes=[Gateway(handler=home)], - middleware=[StarletteMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User)], + middleware=[LilyaMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User)], ) as client: response = client.get("/") @@ -211,7 +211,7 @@ async def test_cannot_access_endpoint_with_invalid_header(test_client_factory, a with create_client( routes=[Gateway(handler=home)], - middleware=[StarletteMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User)], + middleware=[LilyaMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User)], ) as client: response = client.get("/", headers={jwt_config.authorization_header: token}) @@ -224,7 +224,7 @@ async def test_cannot_access_endpoint_with_invalid_token(test_client_factory, as with create_client( routes=[Gateway(handler=home)], - middleware=[StarletteMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User)], + middleware=[LilyaMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User)], raise_server_exceptions=False, ): response = await async_client.get( diff --git a/tests/databases/saffier/test_middleware_payload.py b/tests/databases/saffier/test_middleware_payload.py index 1317b8de..d8df05fa 100644 --- a/tests/databases/saffier/test_middleware_payload.py +++ b/tests/databases/saffier/test_middleware_payload.py @@ -7,9 +7,9 @@ import pytest from anyio import from_thread, sleep, to_thread from httpx import AsyncClient +from lilya.middleware import DefineMiddleware as LilyaMiddleware from pydantic import BaseModel from saffier.exceptions import DoesNotFound -from starlette.middleware import Middleware as StarletteMiddleware from esmerald import Esmerald, Gateway, Include, JSONResponse, Request, get, post, status from esmerald.conf import settings @@ -180,7 +180,7 @@ def app(): Include( routes=[Gateway(handler=home)], middleware=[ - StarletteMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User) + LilyaMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User) ], ), ], @@ -198,7 +198,7 @@ async def async_client(app) -> AsyncGenerator: async def test_cannot_access_endpoint_without_header(test_client_factory, async_client): with create_client( routes=[Gateway(handler=home)], - middleware=[StarletteMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User)], + middleware=[LilyaMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User)], ) as client: response = client.get("/") @@ -211,7 +211,7 @@ async def test_cannot_access_endpoint_with_invalid_header(test_client_factory, a with create_client( routes=[Gateway(handler=home)], - middleware=[StarletteMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User)], + middleware=[LilyaMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User)], ) as client: response = client.get("/", headers={jwt_config.authorization_header: token}) @@ -224,7 +224,7 @@ async def test_cannot_access_endpoint_with_invalid_token(test_client_factory, as with create_client( routes=[Gateway(handler=home)], - middleware=[StarletteMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User)], + middleware=[LilyaMiddleware(JWTAuthMiddleware, config=jwt_config, user_model=User)], raise_server_exceptions=False, ): response = await async_client.get( diff --git a/tests/dependencies/test_http_handler_dependency_injection.py b/tests/dependencies/test_http_handler_dependency_injection.py index c06cf77d..2d307c64 100644 --- a/tests/dependencies/test_http_handler_dependency_injection.py +++ b/tests/dependencies/test_http_handler_dependency_injection.py @@ -1,7 +1,7 @@ from asyncio import sleep from typing import Any, Dict -from starlette.status import HTTP_200_OK, HTTP_400_BAD_REQUEST +from lilya.status import HTTP_200_OK, HTTP_400_BAD_REQUEST from esmerald.applications import ChildEsmerald from esmerald.injector import Inject diff --git a/tests/dependencies/test_injection_of_generic_models.py b/tests/dependencies/test_injection_of_generic_models.py index 0f1bb7c3..e981d17a 100644 --- a/tests/dependencies/test_injection_of_generic_models.py +++ b/tests/dependencies/test_injection_of_generic_models.py @@ -1,7 +1,7 @@ from typing import Generic, Optional, Type, TypeVar +from lilya.status import HTTP_200_OK from pydantic import BaseModel -from starlette.status import HTTP_200_OK from esmerald.injector import Inject from esmerald.routing.gateways import Gateway diff --git a/tests/dependencies/test_inter_dependencies.py b/tests/dependencies/test_inter_dependencies.py index f3afd986..a9086da6 100644 --- a/tests/dependencies/test_inter_dependencies.py +++ b/tests/dependencies/test_inter_dependencies.py @@ -1,6 +1,6 @@ from random import randint -from starlette.status import HTTP_200_OK +from lilya.status import HTTP_200_OK from esmerald.enums import MediaType from esmerald.injector import Inject diff --git a/tests/dependencies/test_websocket_handler_dependency_injection.py b/tests/dependencies/test_websocket_handler_dependency_injection.py index 705a8152..206a9892 100644 --- a/tests/dependencies/test_websocket_handler_dependency_injection.py +++ b/tests/dependencies/test_websocket_handler_dependency_injection.py @@ -2,7 +2,7 @@ from typing import Any, Dict import pytest -from starlette.websockets import WebSocketDisconnect +from lilya.websockets import WebSocketDisconnect from esmerald.injector import Inject from esmerald.routing.apis.views import APIView diff --git a/tests/exception_handlers/test_exception_handlers.py b/tests/exception_handlers/test_exception_handlers.py index b8e9489f..f293acc4 100644 --- a/tests/exception_handlers/test_exception_handlers.py +++ b/tests/exception_handlers/test_exception_handlers.py @@ -1,7 +1,7 @@ from typing import Type import pytest -from starlette.status import HTTP_400_BAD_REQUEST +from lilya.status import HTTP_400_BAD_REQUEST from esmerald.applications import ChildEsmerald from esmerald.enums import MediaType @@ -40,7 +40,7 @@ def create_named_handler( ) -> "ExceptionHandlerMap": def handler(req: Request, exc: Exception) -> Response: assert isinstance(exc, expected_exception) - assert isinstance(req, Request) + caller["name"] = caller_name return Response( media_type=MediaType.JSON, @@ -95,7 +95,7 @@ def create_named_handler( ) -> "ExceptionHandlerMap": def handler(req: Request, exc: Exception) -> Response: assert isinstance(exc, expected_exception) - assert isinstance(req, Request) + caller["name"] = caller_name return Response( media_type=MediaType.JSON, @@ -152,7 +152,7 @@ def create_named_handler( ) -> "ExceptionHandlerMap": def handler(req: Request, exc: Exception) -> Response: assert isinstance(exc, expected_exception) - assert isinstance(req, Request) + caller["name"] = caller_name return Response( media_type=MediaType.JSON, @@ -263,8 +263,9 @@ def create_named_handler( ) -> "ExceptionHandlerMap": def handler(req: Request, exc: Exception) -> Response: assert isinstance(exc, expected_exception) - assert isinstance(req, Request) + caller["name"] = caller_name + print(caller_name) return Response( media_type=MediaType.JSON, content={}, @@ -327,7 +328,7 @@ def create_named_handler( ) -> "ExceptionHandlerMap": def handler(req: Request, exc: Exception) -> Response: assert isinstance(exc, expected_exception) - assert isinstance(req, Request) + caller["name"] = caller_name return Response( media_type=MediaType.JSON, @@ -398,7 +399,7 @@ def create_named_handler( ) -> "ExceptionHandlerMap": def handler(req: Request, exc: Exception) -> Response: assert isinstance(exc, expected_exception) - assert isinstance(req, Request) + caller["name"] = caller_name return Response( media_type=MediaType.JSON, diff --git a/tests/handlers/test_defaults.py b/tests/handlers/test_defaults.py index 1442bebf..44e9fa46 100644 --- a/tests/handlers/test_defaults.py +++ b/tests/handlers/test_defaults.py @@ -1,7 +1,7 @@ from typing import Any import pytest -from starlette.status import HTTP_200_OK, HTTP_201_CREATED, HTTP_204_NO_CONTENT +from lilya.status import HTTP_200_OK, HTTP_201_CREATED, HTTP_204_NO_CONTENT from esmerald.enums import HttpMethod from esmerald.exceptions import ImproperlyConfigured diff --git a/tests/handlers/test_handlers.py b/tests/handlers/test_handlers.py deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/handlers/test_http_and_route.py b/tests/handlers/test_http_and_route.py index f9470ebe..78884915 100644 --- a/tests/handlers/test_http_and_route.py +++ b/tests/handlers/test_http_and_route.py @@ -1,7 +1,7 @@ from typing import Any import pytest -from starlette.status import HTTP_200_OK, HTTP_201_CREATED, HTTP_204_NO_CONTENT +from lilya.status import HTTP_200_OK, HTTP_201_CREATED, HTTP_204_NO_CONTENT from esmerald import Include, route, status from esmerald.enums import HttpMethod diff --git a/tests/handlers/test_http_defaults.py b/tests/handlers/test_http_defaults.py index 1442bebf..44e9fa46 100644 --- a/tests/handlers/test_http_defaults.py +++ b/tests/handlers/test_http_defaults.py @@ -1,7 +1,7 @@ from typing import Any import pytest -from starlette.status import HTTP_200_OK, HTTP_201_CREATED, HTTP_204_NO_CONTENT +from lilya.status import HTTP_200_OK, HTTP_201_CREATED, HTTP_204_NO_CONTENT from esmerald.enums import HttpMethod from esmerald.exceptions import ImproperlyConfigured diff --git a/tests/handlers/test_to_response_data.py b/tests/handlers/test_to_response_data.py index a4d04beb..29d83d02 100644 --- a/tests/handlers/test_to_response_data.py +++ b/tests/handlers/test_to_response_data.py @@ -5,18 +5,18 @@ from typing import Any, AsyncGenerator, AsyncIterator, Generator, Iterator import pytest -from pydantic import ValidationError -from starlette import status -from starlette.responses import ( +from lilya import status +from lilya.responses import ( FileResponse, HTMLResponse, JSONResponse, - PlainTextResponse, + PlainText, RedirectResponse, - Response as StarletteResponse, + Response as LilyaResponse, StreamingResponse, ) -from starlette.status import HTTP_200_OK, HTTP_308_PERMANENT_REDIRECT +from lilya.status import HTTP_200_OK, HTTP_308_PERMANENT_REDIRECT +from pydantic import ValidationError from esmerald.backgound import BackgroundTask from esmerald.datastructures import Cookie, File, Redirect, ResponseHeader, Stream, Template @@ -137,8 +137,8 @@ def test_function() -> Response: "expected_response", [ Response(status_code=HTTP_200_OK, content=b"abc", media_type=MediaType.TEXT), - StarletteResponse(status_code=HTTP_200_OK, content=b"abc"), - PlainTextResponse(content="abc"), + LilyaResponse(status_code=HTTP_200_OK, content=b"abc"), + PlainText(content="abc"), HTMLResponse(content="
Response: ], ) async def test_to_response_returning_redirect_starlette_response( - expected_response: StarletteResponse, + expected_response: LilyaResponse, ) -> None: @get(path="/test") - def test_function() -> StarletteResponse: + def test_function() -> LilyaResponse: return expected_response with create_client(test_function) as client: route = client.app.routes[0] # type: ignore response = await route.to_response(data=route.fn(), app=None) # type: ignore - assert isinstance(response, StarletteResponse) + assert isinstance(response, LilyaResponse) assert response is expected_response @@ -192,8 +192,8 @@ def test_function() -> Redirect: cookies = response.headers.getlist("set-cookie") assert len(cookies) == 2 - assert cookies[0] == "redirect-cookie=xyz; Path=/; SameSite=lax" - assert cookies[1] == "general-cookie=xxx; Path=/; SameSite=lax" + assert cookies[0] == b"redirect-cookie=xyz; Path=/; SameSite=lax" + assert cookies[1] == b"general-cookie=xxx; Path=/; SameSite=lax" assert response.background == background_task @@ -258,8 +258,8 @@ def test_function() -> File: cookies = response.headers.getlist("set-cookie") assert len(cookies) == 3 - assert cookies[0] == "file-cookie=xyz; Path=/; SameSite=lax" - assert cookies[1] == "general-cookie=xxx; Path=/; SameSite=lax" + assert cookies[0] == b"file-cookie=xyz; Path=/; SameSite=lax" + assert cookies[1] == b"general-cookie=xxx; Path=/; SameSite=lax" assert response.background == background_task @@ -309,8 +309,8 @@ def test_function() -> Stream: cookies = response.headers.getlist("set-cookie") assert len(cookies) == 3 - assert cookies[0] == "streaming-cookie=xyz; Path=/; SameSite=lax" - assert cookies[1] == "general-cookie=xxx; Path=/; SameSite=lax" + assert cookies[0] == b"streaming-cookie=xyz; Path=/; SameSite=lax" + assert cookies[1] == b"general-cookie=xxx; Path=/; SameSite=lax" assert response.background == background_task else: with pytest.raises(ValidationError): @@ -348,6 +348,6 @@ def test_function() -> Template: cookies = response.headers.getlist("set-cookie") assert len(cookies) == 2 - assert cookies[0] == "template-cookie=xyz; Path=/; SameSite=lax" - assert cookies[1] == "general-cookie=xxx; Path=/; SameSite=lax" + assert cookies[0] == b"template-cookie=xyz; Path=/; SameSite=lax" + assert cookies[1] == b"general-cookie=xxx; Path=/; SameSite=lax" assert response.background == background_task diff --git a/tests/interceptors/test_interceptors.py b/tests/interceptors/test_interceptors.py index 336bdccb..a8286812 100644 --- a/tests/interceptors/test_interceptors.py +++ b/tests/interceptors/test_interceptors.py @@ -1,6 +1,6 @@ +from lilya.types import Receive, Scope, Send from loguru import logger from pydantic import BaseModel -from starlette.types import Receive, Scope, Send from esmerald import ChildEsmerald, Gateway, Include, JSONResponse, Request, post from esmerald.exceptions import NotAuthorized diff --git a/tests/interceptors/test_interceptors_payload.py b/tests/interceptors/test_interceptors_payload.py index fa66fd0c..6c5ffa24 100644 --- a/tests/interceptors/test_interceptors_payload.py +++ b/tests/interceptors/test_interceptors_payload.py @@ -1,6 +1,6 @@ +from lilya.types import Receive, Scope, Send from loguru import logger from pydantic import BaseModel -from starlette.types import Receive, Scope, Send from esmerald import ChildEsmerald, Gateway, Include, JSONResponse, Request, post from esmerald.exceptions import NotAuthorized diff --git a/tests/middleware/complex/test_complex.py b/tests/middleware/complex/test_complex.py index 2e7c28e6..35b29e9b 100644 --- a/tests/middleware/complex/test_complex.py +++ b/tests/middleware/complex/test_complex.py @@ -1,5 +1,5 @@ import pytest -from starlette.types import ASGIApp +from lilya.types import ASGIApp from esmerald import Gateway, Include, Request from esmerald.conf import settings diff --git a/tests/middleware/complex/test_with_other.py b/tests/middleware/complex/test_with_other.py index 8c6316ab..609978c0 100644 --- a/tests/middleware/complex/test_with_other.py +++ b/tests/middleware/complex/test_with_other.py @@ -4,13 +4,13 @@ import pytest from edgy.exceptions import ObjectNotFound from jose.exceptions import JWTError -from starlette.types import ASGIApp +from lilya.types import ASGIApp from esmerald import APIView, Gateway, HTTPException, Request, Response, get, settings, status from esmerald.contrib.auth.edgy.base_user import AbstractUser from esmerald.exceptions import NotAuthorized from esmerald.middleware.authentication import AuthResult, BaseAuthMiddleware -from esmerald.requests import HTTPConnection +from esmerald.requests import Connection from esmerald.security.jwt.token import Token from esmerald.testclient import create_client @@ -71,7 +71,7 @@ async def retrieve_user(self, user_id) -> User: except ObjectNotFound: raise NotAuthorized() from None - async def authenticate(self, request: HTTPConnection) -> AuthResult: + async def authenticate(self, request: Connection) -> AuthResult: try: # token_raw = request.headers.get(self.config.authorization_header, None) # token = token_raw.split(" ")[1] if token_raw else None diff --git a/tests/middleware/test_basic.py b/tests/middleware/test_basic.py deleted file mode 100644 index fbc7150d..00000000 --- a/tests/middleware/test_basic.py +++ /dev/null @@ -1,15 +0,0 @@ -from esmerald import Gateway, get -from esmerald.middleware.basic import BasicHTTPMiddleware -from esmerald.testclient import create_client - - -@get("/home") -async def home() -> str: - return "home" - - -def test_basic_middleware(test_client_factory): - with create_client(routes=[Gateway(handler=home)], middleware=[BasicHTTPMiddleware]) as client: - response = client.get("/home") - - assert response.status_code == 200 diff --git a/tests/middleware/test_cors.py b/tests/middleware/test_cors.py index 1f4943fc..ddef12a6 100644 --- a/tests/middleware/test_cors.py +++ b/tests/middleware/test_cors.py @@ -1,20 +1,20 @@ -from starlette.middleware import Middleware +from lilya.middleware import DefineMiddleware from esmerald import CORSConfig, Gateway, Request, get, route from esmerald.applications import Esmerald from esmerald.middleware.cors import CORSMiddleware -from esmerald.responses import PlainTextResponse +from esmerald.responses import PlainText def test_cors_allow_all(test_client_factory): @get() - def homepage(request: Request) -> PlainTextResponse: - return PlainTextResponse("Homepage", status_code=200) + def homepage(request: Request) -> PlainText: + return PlainText("Homepage", status_code=200) app = Esmerald( routes=[Gateway("/", handler=homepage)], middleware=[ - Middleware( + DefineMiddleware( CORSMiddleware, allow_origins=["*"], allow_headers=["*"], @@ -68,13 +68,13 @@ def homepage(request: Request) -> PlainTextResponse: def test_cors_allow_all_except_credentials(test_client_factory): @get() - def homepage(request: Request) -> PlainTextResponse: - return PlainTextResponse("Homepage", status_code=200) + def homepage(request: Request) -> PlainText: + return PlainText("Homepage", status_code=200) app = Esmerald( routes=[Gateway("/", handler=homepage)], middleware=[ - Middleware( + DefineMiddleware( CORSMiddleware, allow_origins=["*"], allow_headers=["*"], @@ -118,8 +118,8 @@ def homepage(request: Request) -> PlainTextResponse: def test_cors_allow_specific_origin_with_config(test_client_factory): @get() - def homepage(request: Request) -> PlainTextResponse: - return PlainTextResponse("Homepage", status_code=200) + def homepage(request: Request) -> PlainText: + return PlainText("Homepage", status_code=200) cors_config = CORSConfig( allow_origins=["https://example.org"], @@ -161,13 +161,13 @@ def homepage(request: Request) -> PlainTextResponse: def test_cors_allow_specific_origin(test_client_factory): @get() - def homepage(request: Request) -> PlainTextResponse: - return PlainTextResponse("Homepage", status_code=200) + def homepage(request: Request) -> PlainText: + return PlainText("Homepage", status_code=200) app = Esmerald( routes=[Gateway("/", handler=homepage)], middleware=[ - Middleware( + DefineMiddleware( CORSMiddleware, allow_origins=["https://example.org"], allow_headers=["X-Example", "Content-Type"], @@ -215,7 +215,7 @@ def homepage(request: Request) -> None: app = Esmerald( routes=[Gateway("/", handler=homepage)], middleware=[ - Middleware( + DefineMiddleware( CORSMiddleware, allow_origins=["https://example.org"], allow_headers=["X-Example"], @@ -236,8 +236,6 @@ def homepage(request: Request) -> None: assert response.text == "Disallowed CORS origin, method, headers" assert "access-control-allow-origin" not in response.headers - # Bug specific test, https://github.com/encode/starlette/pull/1199 - # Test preflight response text with multiple disallowed headers headers = { "Origin": "https://example.org", "Access-Control-Request-Method": "GET", @@ -271,8 +269,6 @@ def homepage(request: Request) -> None: assert response.text == "Disallowed CORS origin, headers" assert "access-control-allow-origin" not in response.headers - # Bug specific test, https://github.com/encode/starlette/pull/1199 - # Test preflight response text with multiple disallowed headers headers = { "Origin": "https://example.org", "Access-Control-Request-Method": "GET", @@ -292,7 +288,7 @@ def homepage(request: Request) -> None: app = Esmerald( routes=[Gateway("/", handler=homepage)], middleware=[ - Middleware( + DefineMiddleware( CORSMiddleware, allow_origins=["*"], allow_methods=["POST"], @@ -356,7 +352,7 @@ def homepage(request: Request) -> None: app = Esmerald( routes=[Gateway("/", handler=homepage)], - middleware=[Middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"])], + middleware=[DefineMiddleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"])], ) client = test_client_factory(app) @@ -395,8 +391,8 @@ def homepage(request: Request) -> None: def test_cors_allow_all_methods(test_client_factory): @route(methods=["delete", "get", "head", "options", "patch", "post", "put"]) - def homepage(request: Request) -> PlainTextResponse: - return PlainTextResponse("Homepage", status_code=200) + def homepage(request: Request) -> PlainText: + return PlainText("Homepage", status_code=200) app = Esmerald( routes=[ @@ -405,7 +401,7 @@ def homepage(request: Request) -> PlainTextResponse: handler=homepage, ) ], - middleware=[Middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"])], + middleware=[DefineMiddleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"])], ) client = test_client_factory(app) @@ -422,13 +418,13 @@ def homepage(request: Request) -> PlainTextResponse: def test_cors_allow_origin_regex(test_client_factory): @get() - def homepage(request: Request) -> PlainTextResponse: - return PlainTextResponse("Homepage", status_code=200) + def homepage(request: Request) -> PlainText: + return PlainText("Homepage", status_code=200) app = Esmerald( routes=[Gateway("/", handler=homepage)], middleware=[ - Middleware( + DefineMiddleware( CORSMiddleware, allow_headers=["X-Example", "Content-Type"], allow_origin_regex="https://.*", @@ -493,13 +489,13 @@ def homepage(request: Request) -> PlainTextResponse: def test_cors_allow_origin_regex_fullmatch(test_client_factory): @get() - def homepage(request: Request) -> PlainTextResponse: - return PlainTextResponse("Homepage", status_code=200) + def homepage(request: Request) -> PlainText: + return PlainText("Homepage", status_code=200) app = Esmerald( routes=[Gateway("/", handler=homepage)], middleware=[ - Middleware( + DefineMiddleware( CORSMiddleware, allow_headers=["X-Example", "Content-Type"], allow_origin_regex=r"https://.*\.example.org", @@ -527,12 +523,12 @@ def homepage(request: Request) -> PlainTextResponse: def test_cors_credentialed_requests_return_specific_origin(test_client_factory): @get() - def homepage(request: Request) -> PlainTextResponse: - return PlainTextResponse("Homepage", status_code=200) + def homepage(request: Request) -> PlainText: + return PlainText("Homepage", status_code=200) app = Esmerald( routes=[Gateway("/", handler=homepage)], - middleware=[Middleware(CORSMiddleware, allow_origins=["*"])], + middleware=[DefineMiddleware(CORSMiddleware, allow_origins=["*"])], ) client = test_client_factory(app) @@ -547,8 +543,8 @@ def homepage(request: Request) -> PlainTextResponse: def test_cors_credentialed_requests_return_specific_origin_config(test_client_factory): @get() - def homepage(request: Request) -> PlainTextResponse: - return PlainTextResponse("Homepage", status_code=200) + def homepage(request: Request) -> PlainText: + return PlainText("Homepage", status_code=200) cors = CORSConfig(allow_origins=["*"]) app = Esmerald(routes=[Gateway("/", handler=homepage)], cors_config=cors) @@ -565,12 +561,12 @@ def homepage(request: Request) -> PlainTextResponse: def test_cors_vary_header_defaults_to_origin(test_client_factory): @get() - def homepage(request: Request) -> PlainTextResponse: - return PlainTextResponse("Homepage", status_code=200) + def homepage(request: Request) -> PlainText: + return PlainText("Homepage", status_code=200) app = Esmerald( routes=[Gateway("/", handler=homepage)], - middleware=[Middleware(CORSMiddleware, allow_origins=["https://example.org"])], + middleware=[DefineMiddleware(CORSMiddleware, allow_origins=["https://example.org"])], ) headers = {"Origin": "https://example.org"} @@ -584,8 +580,8 @@ def homepage(request: Request) -> PlainTextResponse: def test_cors_vary_header_defaults_to_origin_config(test_client_factory): @get() - def homepage(request: Request) -> PlainTextResponse: - return PlainTextResponse("Homepage", status_code=200) + def homepage(request: Request) -> PlainText: + return PlainText("Homepage", status_code=200) cors = CORSConfig(allow_origins=["https://example.org"]) app = Esmerald(routes=[Gateway("/", handler=homepage)], cors_config=cors) @@ -601,12 +597,12 @@ def homepage(request: Request) -> PlainTextResponse: def test_cors_vary_header_is_not_set_for_non_credentialed_request(test_client_factory): @get() - def homepage(request: Request) -> PlainTextResponse: - return PlainTextResponse("Homepage", status_code=200, headers={"Vary": "Accept-Encoding"}) + def homepage(request: Request) -> PlainText: + return PlainText("Homepage", status_code=200, headers={"Vary": "Accept-Encoding"}) app = Esmerald( routes=[Gateway("/", handler=homepage)], - middleware=[Middleware(CORSMiddleware, allow_origins=["*"])], + middleware=[DefineMiddleware(CORSMiddleware, allow_origins=["*"])], ) client = test_client_factory(app) @@ -619,8 +615,8 @@ def test_cors_vary_header_is_not_set_for_non_credentialed_request_config( test_client_factory, ): @get() - def homepage(request: Request) -> PlainTextResponse: - return PlainTextResponse("Homepage", status_code=200, headers={"Vary": "Accept-Encoding"}) + def homepage(request: Request) -> PlainText: + return PlainText("Homepage", status_code=200, headers={"Vary": "Accept-Encoding"}) cors = CORSConfig(allow_origins=["*"]) app = Esmerald(routes=[Gateway("/", handler=homepage)], cors_config=cors) @@ -633,12 +629,12 @@ def homepage(request: Request) -> PlainTextResponse: def test_cors_vary_header_is_properly_set_for_credentialed_request(test_client_factory): @get() - def homepage(request: Request) -> PlainTextResponse: - return PlainTextResponse("Homepage", status_code=200, headers={"Vary": "Accept-Encoding"}) + def homepage(request: Request) -> PlainText: + return PlainText("Homepage", status_code=200, headers={"Vary": "Accept-Encoding"}) app = Esmerald( routes=[Gateway("/", handler=homepage)], - middleware=[Middleware(CORSMiddleware, allow_origins=["*"])], + middleware=[DefineMiddleware(CORSMiddleware, allow_origins=["*"])], ) client = test_client_factory(app) @@ -651,14 +647,14 @@ def test_cors_vary_header_is_properly_set_when_allow_origins_is_not_wildcard( test_client_factory, ): @get() - def homepage(request: Request) -> PlainTextResponse: - return PlainTextResponse("Homepage", status_code=200, headers={"Vary": "Accept-Encoding"}) + def homepage(request: Request) -> PlainText: + return PlainText("Homepage", status_code=200, headers={"Vary": "Accept-Encoding"}) app = Esmerald( routes=[ Gateway("/", handler=homepage), ], - middleware=[Middleware(CORSMiddleware, allow_origins=["https://example.org"])], + middleware=[DefineMiddleware(CORSMiddleware, allow_origins=["https://example.org"])], ) client = test_client_factory(app) @@ -671,8 +667,8 @@ def test_cors_vary_header_is_properly_set_when_allow_origins_is_not_wildcard_con test_client_factory, ): @get() - def homepage(request: Request) -> PlainTextResponse: - return PlainTextResponse("Homepage", status_code=200, headers={"Vary": "Accept-Encoding"}) + def homepage(request: Request) -> PlainText: + return PlainText("Homepage", status_code=200, headers={"Vary": "Accept-Encoding"}) cors = CORSConfig(allow_origins=["https://example.org"]) app = Esmerald( @@ -692,15 +688,15 @@ def test_cors_allowed_origin_does_not_leak_between_credentialed_requests( test_client_factory, ): @get() - def homepage(request: Request) -> PlainTextResponse: - return PlainTextResponse("Homepage", status_code=200) + def homepage(request: Request) -> PlainText: + return PlainText("Homepage", status_code=200) app = Esmerald( routes=[ Gateway("/", handler=homepage), ], middleware=[ - Middleware( + DefineMiddleware( CORSMiddleware, allow_origins=["*"], allow_headers=["*"], @@ -727,8 +723,8 @@ def test_cors_allowed_origin_does_not_leak_between_credentialed_requests_config( test_client_factory, ): @get() - def homepage(request: Request) -> PlainTextResponse: - return PlainTextResponse("Homepage", status_code=200) + def homepage(request: Request) -> PlainText: + return PlainText("Homepage", status_code=200) cors = CORSConfig( allow_origins=["*"], diff --git a/tests/middleware/test_csrf.py b/tests/middleware/test_csrf.py index 4d50f152..a5d03ea3 100644 --- a/tests/middleware/test_csrf.py +++ b/tests/middleware/test_csrf.py @@ -1,8 +1,8 @@ from typing import Optional import pytest -from starlette import status -from starlette.status import HTTP_200_OK, HTTP_201_CREATED +from lilya import status +from lilya.status import HTTP_200_OK, HTTP_201_CREATED from esmerald.config import CSRFConfig from esmerald.routing.gateways import Gateway, WebSocketGateway diff --git a/tests/middleware/test_exception_handler_middleware.py b/tests/middleware/test_exception_handler_middleware.py index fd3e3309..5693fc31 100644 --- a/tests/middleware/test_exception_handler_middleware.py +++ b/tests/middleware/test_exception_handler_middleware.py @@ -1,8 +1,8 @@ import json from typing import Any -from starlette.exceptions import HTTPException as StarletteHTTPException -from starlette.status import HTTP_500_INTERNAL_SERVER_ERROR +from lilya.exceptions import HTTPException as LilyaException +from lilya.status import HTTP_500_INTERNAL_SERVER_ERROR from esmerald.exceptions import HTTPException from esmerald.middleware.exceptions import EsmeraldAPIExceptionMiddleware @@ -70,9 +70,7 @@ def test_default_handle_esmerald_http_exception_extra_list() -> None: def test_default_handle_starlette_http_exception_handling() -> None: response = middleware.default_http_exception_handler( Request(scope={"type": "http", "method": "GET"}), # type: ignore - StarletteHTTPException( - detail="esmerald exception", status_code=HTTP_500_INTERNAL_SERVER_ERROR - ), + LilyaException(detail="esmerald exception", status_code=HTTP_500_INTERNAL_SERVER_ERROR), ) assert response.status_code == HTTP_500_INTERNAL_SERVER_ERROR assert json.loads(response.body) == { diff --git a/tests/middleware/test_http_redirect.py b/tests/middleware/test_http_redirect.py index 575afcf6..4f304859 100644 --- a/tests/middleware/test_http_redirect.py +++ b/tests/middleware/test_http_redirect.py @@ -1,19 +1,19 @@ -from starlette.middleware import Middleware +from lilya.middleware import DefineMiddleware from esmerald import Gateway, Request, get from esmerald.applications import Esmerald from esmerald.middleware.https import HTTPSRedirectMiddleware -from esmerald.responses import PlainTextResponse +from esmerald.responses import PlainText def test_https_redirect_middleware(test_client_factory): @get() - def homepage(request: Request) -> PlainTextResponse: - return PlainTextResponse("OK", status_code=200) + def homepage(request: Request) -> PlainText: + return PlainText("OK", status_code=200) app = Esmerald( routes=[Gateway("/", handler=homepage)], - middleware=[Middleware(HTTPSRedirectMiddleware)], + middleware=[DefineMiddleware(HTTPSRedirectMiddleware)], ) client = test_client_factory(app, base_url="https://testserver") diff --git a/tests/middleware/test_middleware_handling_data.py b/tests/middleware/test_middleware_handling_data.py index 79f7bc42..5414a3b8 100644 --- a/tests/middleware/test_middleware_handling_data.py +++ b/tests/middleware/test_middleware_handling_data.py @@ -1,23 +1,14 @@ import logging -from typing import Any, Awaitable, Callable, List, Type, cast from _pytest.logging import LogCaptureFixture +from lilya.types import ASGIApp, Receive, Scope, Send from pydantic import BaseModel -from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint -from starlette.middleware.cors import CORSMiddleware -from starlette.middleware.trustedhost import TrustedHostMiddleware -from starlette.types import ASGIApp, Receive, Scope, Send -from esmerald.applications import ChildEsmerald -from esmerald.config import CORSConfig from esmerald.enums import ScopeType from esmerald.protocols.middleware import MiddlewareProtocol from esmerald.requests import Request -from esmerald.responses import Response -from esmerald.routing.apis.views import APIView from esmerald.routing.gateways import Gateway from esmerald.routing.handlers import get, post -from esmerald.routing.router import Include from esmerald.testclient import create_client logger = logging.getLogger(__name__) @@ -36,23 +27,6 @@ async def __call__(self, scope: "Scope", receive: "Receive", send: "Send") -> No await self.app(scope, receive, send) -class BaseMiddlewareRequestLoggingMiddleware(BaseHTTPMiddleware): # pragma: no cover - async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -> Response: # type: ignore - logging.getLogger(__name__).info("%s - %s", request.method, request.url) - return await call_next(request) # type: ignore - - -class MiddlewareWithArgsAndKwargs(BaseHTTPMiddleware): # pragma: no cover - def __init__(self, arg: int = 0, *, app: Any, kwarg: str) -> None: - super().__init__(app) - self.arg = arg - self.kwarg = kwarg - - async def dispatch( # type: ignore - self, request: Request, call_next: Callable[[Request], Awaitable[Response]] - ) -> Response: ... - - @get(path="/") def handler() -> None: """ """ @@ -69,56 +43,6 @@ def post_handler(data: JSONRequest) -> JSONRequest: return data -def test_setting_cors_middleware() -> None: - cors_config = CORSConfig() - assert cors_config.allow_credentials is False - assert cors_config.allow_headers == ["*"] - assert cors_config.allow_methods == ["*"] - assert cors_config.allow_origins == ["*"] - assert cors_config.allow_origin_regex is None - assert cors_config.max_age == 600 - assert cors_config.expose_headers == [] - - with create_client( - routes=[Gateway(path="/", handler=handler)], cors_config=cors_config - ) as client: - unpacked_middleware = [] - _ids = [] - cur = client.app.router - - while hasattr(cur, "app"): - unpacked_middleware.extend(cur._app.user_middleware) - _ids.append(id(cur._app.user_middleware)) - cur = cast("Any", cur._app) # type: ignore - - assert len(unpacked_middleware) == 2 - cors_middleware = cast("Any", unpacked_middleware[1]) - - assert isinstance(cors_middleware.cls, type(CORSMiddleware)) - assert cors_middleware.kwargs["allow_headers"] == ["*"] - assert cors_middleware.kwargs["allow_methods"] == ["*"] - assert cors_middleware.kwargs["allow_origins"] == cors_config.allow_origins - assert cors_middleware.kwargs["allow_origins"] == cors_config.allow_origins - - -def test_trusted_hosts_middleware() -> None: - client = create_client(routes=[Gateway(path="/", handler=handler)], allowed_hosts=["*"]) - unpacked_middleware = [] - _ids = [] - - cur = client.app.router - - while hasattr(cur, "app"): - unpacked_middleware.extend(cur._app.user_middleware) - _ids.append(id(cur._app.user_middleware)) - cur = cast("Any", cur._app) # type: ignore - - assert len(unpacked_middleware) == 1 - trusted_hosts_middleware = cast("Any", unpacked_middleware[0]) - assert isinstance(trusted_hosts_middleware.cls, type(TrustedHostMiddleware)) - assert trusted_hosts_middleware.kwargs["allowed_hosts"] == ["*"] - - def test_request_body_logging_middleware(caplog: "LogCaptureFixture") -> None: with caplog.at_level(logging.INFO): client = create_client( @@ -128,357 +52,3 @@ def test_request_body_logging_middleware(caplog: "LogCaptureFixture") -> None: response = client.post("/", json={"name": "moishe zuchmir", "age": 40, "programmer": True}) assert response.status_code == 201 assert "test logging" in caplog.text - - -def test_middleware_call_order() -> None: - """Test that middlewares are called in the order they have been passed.""" - - results: List[int] = [] - - def create_test_middleware(middleware_id: int) -> "Type[MiddlewareProtocol]": - class TestMiddleware(MiddlewareProtocol): - def __init__(self, app: "ASGIApp") -> None: - self.app = app - - async def __call__(self, scope: "Scope", receive: "Receive", send: "Send") -> None: - results.append(middleware_id) - await self.app(scope, receive, send) - - return TestMiddleware - - class MyController(APIView): - path = "/controller" - middleware = [create_test_middleware(4), create_test_middleware(5)] - - @get( - "/handler", - middleware=[create_test_middleware(6), create_test_middleware(7)], - ) - def my_handler(self) -> None: - """ """ - - with create_client( - routes=[ - Gateway( - path="/router", - handler=MyController, - middleware=[create_test_middleware(2), create_test_middleware(3)], - ) - ], - middleware=[ - create_test_middleware(0), - create_test_middleware(1), - ], - ) as client: - client.get("/router/controller/handler") - - assert results == [0, 1, 2, 3, 4, 5, 6, 7] - - -def test_middleware_call_order_with_include() -> None: - """Test that middlewares are called in the order they have been passed with include.""" - - results: List[int] = [] - - def create_test_middleware(middleware_id: int) -> "Type[MiddlewareProtocol]": - class TestMiddleware(MiddlewareProtocol): - def __init__(self, app: "ASGIApp") -> None: - self.app = app - - async def __call__(self, scope: "Scope", receive: "Receive", send: "Send") -> None: - results.append(middleware_id) - await self.app(scope, receive, send) - - return TestMiddleware - - class MyController(APIView): - path = "/controller" - middleware = [create_test_middleware(4), create_test_middleware(5)] - - @get( - "/handler", - middleware=[create_test_middleware(6), create_test_middleware(7)], - ) - def my_handler(self) -> None: - """ """ - - with create_client( - routes=[ - Include( - routes=[Gateway(path="/", handler=MyController)], - middleware=[create_test_middleware(2), create_test_middleware(3)], - ), - ], - middleware=[ - create_test_middleware(0), - create_test_middleware(1), - ], - ) as client: - client.get("/controller/handler") - assert results == [0, 1, 2, 3, 4, 5, 6, 7] - - -def test_middleware_call_order_with_nested_include() -> None: - """Test that middlewares are called in the order they have been passed with nested include.""" - - results: List[int] = [] - - def create_test_middleware(middleware_id: int) -> "Type[MiddlewareProtocol]": - class TestMiddleware(MiddlewareProtocol): - def __init__(self, app: "ASGIApp") -> None: - self.app = app - - async def __call__(self, scope: "Scope", receive: "Receive", send: "Send") -> None: - results.append(middleware_id) - await self.app(scope, receive, send) - - return TestMiddleware - - class MyController(APIView): - path = "/controller" - middleware = [create_test_middleware(4), create_test_middleware(5)] - - @get( - "/handler", - middleware=[create_test_middleware(6), create_test_middleware(7)], - ) - def my_handler(self) -> None: - """ """ - - with create_client( - routes=[ - Include( - path="/", - routes=[ - Include( - path="/", - routes=[Gateway(path="/", handler=MyController)], - middleware=[ - create_test_middleware(2), - create_test_middleware(3), - ], - ), - ], - ), - ], - middleware=[ - create_test_middleware(0), - create_test_middleware(1), - ], - ) as client: - client.get("/controller/handler") - - assert results == [0, 1, 2, 3, 4, 5, 6, 7] - - -def test_middleware_call_order_with_heavy_nested_include() -> None: - """Test that middlewares are called in the order they have been passed with heavy nested includes.""" - - results: List[int] = [] - - def create_test_middleware(middleware_id: int) -> "Type[MiddlewareProtocol]": - class TestMiddleware(MiddlewareProtocol): - def __init__(self, app: "ASGIApp") -> None: - self.app = app - - async def __call__(self, scope: "Scope", receive: "Receive", send: "Send") -> None: - results.append(middleware_id) - await self.app(scope, receive, send) - - return TestMiddleware - - class MyController(APIView): - path = "/controller" - middleware = [create_test_middleware(4), create_test_middleware(5)] - - @get( - "/handler", - middleware=[create_test_middleware(6), create_test_middleware(7)], - ) - def my_handler(self) -> None: - """ """ - - with create_client( - routes=[ - Include( - path="/", - routes=[ - Include( - path="/", - routes=[ - Include( - path="/", - routes=[ - Include( - path="/", - routes=[ - Include( - path="/", - routes=[ - Include( - path="/", - routes=[ - Include( - path="/", - routes=[ - Include( - path="/", - routes=[ - Include( - path="/", - routes=[ - Include( - path="/", - routes=[ - Gateway( - path="/", - handler=MyController, - ) - ], - middleware=[ - create_test_middleware( - 2 - ), - create_test_middleware( - 3 - ), - ], - ), - ], - ) - ], - ) - ], - ) - ], - ) - ], - ) - ], - ) - ], - ) - ], - ) - ], - ), - ], - middleware=[ - create_test_middleware(0), - create_test_middleware(1), - ], - ) as client: - client.get("/controller/handler") - - assert results == [0, 1, 2, 3, 4, 5, 6, 7] - - -def test_middleware_call_order_with_child_esmerald() -> None: - """Test that middlewares are called in the order they have been passed with include.""" - - results: List[int] = [] - - def create_test_middleware(middleware_id: int) -> "Type[MiddlewareProtocol]": - class TestMiddleware(MiddlewareProtocol): - def __init__(self, app: "ASGIApp") -> None: - self.app = app - - async def __call__(self, scope: "Scope", receive: "Receive", send: "Send") -> None: - results.append(middleware_id) - await self.app(scope, receive, send) - - return TestMiddleware - - class MyController(APIView): - path = "/controller" - middleware = [create_test_middleware(4), create_test_middleware(5)] - - @get( - "/handler", - middleware=[create_test_middleware(6), create_test_middleware(7)], - ) - def my_handler(self) -> None: - """ """ - - child_esmerald = ChildEsmerald(routes=[Gateway(path="/", handler=MyController)]) - - with create_client( - routes=[ - Include( - path="/child", - routes=[ - Include( - path="/esmerald", - routes=[Include(routes=[Include(app=child_esmerald)])], - ) - ], - middleware=[create_test_middleware(2), create_test_middleware(3)], - ), - ], - middleware=[ - create_test_middleware(0), - create_test_middleware(1), - ], - ) as client: - client.get("/child/esmerald/controller/handler") - - assert results == [0, 1, 2, 3, 4, 5, 6, 7] - - -def test_middleware_call_order_with_child_esmerald_as_parent() -> None: - """Test that middlewares are called in the order they have been passed with include.""" - - results: List[int] = [] - - def create_test_middleware(middleware_id: int) -> "Type[MiddlewareProtocol]": - class TestMiddleware(MiddlewareProtocol): - def __init__(self, app: "ASGIApp") -> None: - self.app = app - - async def __call__(self, scope: "Scope", receive: "Receive", send: "Send") -> None: - results.append(middleware_id) - await self.app(scope, receive, send) - - return TestMiddleware - - class MyController(APIView): - path = "/controller" - middleware = [create_test_middleware(4), create_test_middleware(5)] - - @get( - "/handler", - middleware=[create_test_middleware(6), create_test_middleware(7)], - ) - def my_handler(self) -> None: - """ """ - - child_esmerald = ChildEsmerald(routes=[Gateway(path="/", handler=MyController)]) - - with create_client( - routes=[ - Include( - path="/child", - routes=[ - Include( - path="/esmerald", - routes=[ - Include( - routes=[Include(app=child_esmerald)], - middleware=[ - create_test_middleware(2), - create_test_middleware(3), - ], - ) - ], - ) - ], - ), - ], - middleware=[ - create_test_middleware(0), - create_test_middleware(1), - ], - ) as client: - client.get("/child/esmerald/controller/handler") - - assert results == [0, 1, 2, 3, 4, 5, 6, 7] diff --git a/tests/middleware/test_middleware_handling_payload.py b/tests/middleware/test_middleware_handling_payload.py index de9e2d98..50e74e1f 100644 --- a/tests/middleware/test_middleware_handling_payload.py +++ b/tests/middleware/test_middleware_handling_payload.py @@ -1,8 +1,8 @@ import logging from _pytest.logging import LogCaptureFixture +from lilya.types import ASGIApp, Receive, Scope, Send from pydantic import BaseModel -from starlette.types import ASGIApp, Receive, Scope, Send from esmerald.enums import ScopeType from esmerald.protocols.middleware import MiddlewareProtocol diff --git a/tests/middleware/test_session_middleware.py b/tests/middleware/test_session_middleware.py index e87148f6..4ed543aa 100644 --- a/tests/middleware/test_session_middleware.py +++ b/tests/middleware/test_session_middleware.py @@ -2,8 +2,8 @@ import re import pytest +from lilya.responses import JSONResponse from pydantic import ValidationError -from starlette.responses import JSONResponse from esmerald.config import SessionConfig from esmerald.datastructures import Secret diff --git a/tests/middleware/test_settings_middleware.py b/tests/middleware/test_settings_middleware.py index 0381a0bc..b4d97ccb 100644 --- a/tests/middleware/test_settings_middleware.py +++ b/tests/middleware/test_settings_middleware.py @@ -1,4 +1,4 @@ -from starlette.middleware import Middleware as StarletteMiddleware +from lilya.middleware import DefineMiddleware from esmerald import Gateway, JSONResponse, Request, get, settings, status from esmerald.middleware import RequestSettingsMiddleware @@ -25,7 +25,7 @@ def test_request_settings_default(test_client_factory): """ with create_client( routes=[Gateway(handler=home)], - middleware=[StarletteMiddleware(RequestSettingsMiddleware)], + middleware=[DefineMiddleware(RequestSettingsMiddleware)], ) as client: response = client.get("/") diff --git a/tests/middleware/test_trusted_host.py b/tests/middleware/test_trusted_host.py index 3fbb1fe3..038e2d2a 100644 --- a/tests/middleware/test_trusted_host.py +++ b/tests/middleware/test_trusted_host.py @@ -1,15 +1,15 @@ -from starlette.middleware import Middleware +from lilya.middleware import DefineMiddleware from esmerald import Gateway, Request, get from esmerald.applications import Esmerald from esmerald.middleware.trustedhost import TrustedHostMiddleware -from esmerald.responses import PlainTextResponse +from esmerald.responses import PlainText def test_trusted_host_middleware_settings(test_client_factory): @get() - def homepage(request: Request) -> PlainTextResponse: - return PlainTextResponse("OK", status_code=200) + def homepage(request: Request) -> PlainText: + return PlainText("OK", status_code=200) app = Esmerald( routes=[Gateway("/", handler=homepage)], @@ -31,13 +31,13 @@ def homepage(request: Request) -> PlainTextResponse: def test_trusted_host_middleware(test_client_factory): @get() - def homepage(request: Request) -> PlainTextResponse: - return PlainTextResponse("OK", status_code=200) + def homepage(request: Request) -> PlainText: + return PlainText("OK", status_code=200) app = Esmerald( routes=[Gateway("/", handler=homepage)], middleware=[ - Middleware(TrustedHostMiddleware, allowed_hosts=["testserver", "*.testserver"]) + DefineMiddleware(TrustedHostMiddleware, allowed_hosts=["testserver", "*.testserver"]) ], ) @@ -62,12 +62,12 @@ def test_default_allowed_hosts(): def test_www_redirect(test_client_factory): @get() - def homepage(request: Request) -> PlainTextResponse: - return PlainTextResponse("OK", status_code=200) + def homepage(request: Request) -> PlainText: + return PlainText("OK", status_code=200) app = Esmerald( routes=[Gateway("/", handler=homepage)], - middleware=[Middleware(TrustedHostMiddleware, allowed_hosts=["www.example.com"])], + middleware=[DefineMiddleware(TrustedHostMiddleware, allowed_hosts=["www.example.com"])], ) client = test_client_factory(app, base_url="https://example.com") diff --git a/tests/msgspec/test_msgspec.py b/tests/msgspec/test_msgspec.py index 58f9e3c4..aa437e9e 100644 --- a/tests/msgspec/test_msgspec.py +++ b/tests/msgspec/test_msgspec.py @@ -1,8 +1,8 @@ from typing import Union import msgspec +from lilya import status from pydantic import BaseModel -from starlette import status from typing_extensions import Annotated from esmerald.datastructures.msgspec import Struct diff --git a/tests/openapi/test_openapi_include_middleware.py b/tests/openapi/test_openapi_include_middleware.py index 9af9dbcd..04bcced1 100644 --- a/tests/openapi/test_openapi_include_middleware.py +++ b/tests/openapi/test_openapi_include_middleware.py @@ -1,7 +1,7 @@ from typing import Any, Dict, Union +from lilya.types import ASGIApp, Receive, Scope, Send from pydantic import BaseModel -from starlette.types import ASGIApp, Receive, Scope, Send from esmerald import JSON, Gateway, Include, MiddlewareProtocol, get from esmerald.testclient import create_client diff --git a/tests/permissions/test_permissions.py b/tests/permissions/test_permissions.py index 434f825e..f2060ec1 100644 --- a/tests/permissions/test_permissions.py +++ b/tests/permissions/test_permissions.py @@ -1,8 +1,8 @@ from typing import TYPE_CHECKING import pytest -from starlette.status import HTTP_200_OK, HTTP_403_FORBIDDEN -from starlette.websockets import WebSocketDisconnect +from lilya.status import HTTP_200_OK, HTTP_403_FORBIDDEN +from lilya.websockets import WebSocketDisconnect from esmerald.applications import ChildEsmerald from esmerald.permissions import AllowAny, BasePermission, DenyAll diff --git a/tests/permissions/test_permissions_async.py b/tests/permissions/test_permissions_async.py index bdf0cc7d..ff7d6633 100644 --- a/tests/permissions/test_permissions_async.py +++ b/tests/permissions/test_permissions_async.py @@ -1,8 +1,8 @@ from typing import TYPE_CHECKING import pytest -from starlette.status import HTTP_200_OK, HTTP_403_FORBIDDEN -from starlette.websockets import WebSocketDisconnect +from lilya.status import HTTP_200_OK, HTTP_403_FORBIDDEN +from lilya.websockets import WebSocketDisconnect from esmerald.applications import ChildEsmerald from esmerald.permissions import AllowAny, BasePermission, DenyAll diff --git a/tests/requests/test_base_http_starlette.py b/tests/requests/test_base_http_lilya.py similarity index 97% rename from tests/requests/test_base_http_starlette.py rename to tests/requests/test_base_http_lilya.py index 60236d4d..139ec8e7 100644 --- a/tests/requests/test_base_http_starlette.py +++ b/tests/requests/test_base_http_lilya.py @@ -1,20 +1,16 @@ -"""The tests in this file were adapted from: - -https://github.com/encode/starlette/blob/master/tests/test_requests.py. -""" - from typing import Any, Optional import anyio import pytest -from starlette.datastructures import Address, State -from starlette.status import HTTP_200_OK -from starlette.types import Receive, Send +from lilya._internal._message import Address +from lilya.datastructures import State +from lilya.status import HTTP_200_OK +from lilya.types import Receive, Send from esmerald.enums import MediaType from esmerald.exceptions import InternalServerError from esmerald.requests import ClientDisconnect, Request, empty_send -from esmerald.responses import JSONResponse, PlainTextResponse, Response +from esmerald.responses import JSONResponse, PlainText, Response from esmerald.testclient import EsmeraldTestClient @@ -224,7 +220,7 @@ async def app(scope, receive, send): request = Request(scope, receive) path = request.scope["path"] raw_path = request.scope["raw_path"] - response = PlainTextResponse(f"{path}, {raw_path}") + response = PlainText(f"{path}, {raw_path}") await response(scope, receive, send) client = EsmeraldTestClient(app) diff --git a/tests/requests/test_base_websocket_starlette.py b/tests/requests/test_base_websocket_starlette.py index 52730465..06e1ac90 100644 --- a/tests/requests/test_base_websocket_starlette.py +++ b/tests/requests/test_base_websocket_starlette.py @@ -1,15 +1,15 @@ """The tests in this file were adapted from: -https://github.com/encode/starlette/blob/master/tests/test_websockets.py +https://github.com/encode/lilya/blob/master/tests/test_websockets.py """ import sys import anyio import pytest -from starlette import status -from starlette.types import Receive, Scope, Send -from starlette.websockets import WebSocketDisconnect, WebSocketState +from lilya import status +from lilya.types import Receive, Scope, Send +from lilya.websockets import WebSocketDisconnect, WebSocketState from esmerald.testclient import EsmeraldTestClient from esmerald.websockets import WebSocket diff --git a/tests/requests/test_request.py b/tests/requests/test_request.py index 0f73bfae..a07f7b46 100644 --- a/tests/requests/test_request.py +++ b/tests/requests/test_request.py @@ -39,7 +39,7 @@ def proxy() -> None: @get(path="/test") def root(request: Request) -> dict: - return {"url": request.url_for("proxy")} + return {"url": request.path_for("proxy")} with create_client( routes=[ diff --git a/tests/requests/test_state_from_middleware.py b/tests/requests/test_state_from_middleware.py index c56ae67c..19d4b92d 100644 --- a/tests/requests/test_state_from_middleware.py +++ b/tests/requests/test_state_from_middleware.py @@ -1,6 +1,6 @@ from typing import Dict -from starlette.types import ASGIApp, Receive, Scope, Send +from lilya.types import ASGIApp, Receive, Scope, Send from esmerald.protocols.middleware import MiddlewareProtocol from esmerald.requests import Request diff --git a/tests/requests/test_websocket.py b/tests/requests/test_websocket.py index 2c22b9fa..85404553 100644 --- a/tests/requests/test_websocket.py +++ b/tests/requests/test_websocket.py @@ -1,7 +1,7 @@ from typing import Any import pytest -from starlette.datastructures import Headers +from lilya.datastructures import Header from typing_extensions import Literal from esmerald.routing.gateways import WebSocketGateway @@ -32,7 +32,7 @@ async def websocket_handler(socket: WebSocket) -> None: [ [(b"test", b"hello-world")], {"test": "hello-world"}, - Headers(headers={"test": "hello-world"}), + Header({"test": "hello-world"}), ], ) def test_accept_set_headers(headers: Any) -> None: diff --git a/tests/routing/test_include_errors.py b/tests/routing/test_include_errors.py index 759adf9a..ac7be15a 100644 --- a/tests/routing/test_include_errors.py +++ b/tests/routing/test_include_errors.py @@ -28,7 +28,7 @@ def test_raise_error_namespace(arg): @pytest.mark.parametrize("arg", [gateway, 2, get]) def test_raise_error_pattern(arg): with pytest.raises(ImproperlyConfigured): - Include(pattern=arg) + Include(pattern=arg, routes=[gateway]) def test_raise_error_pattern_and_routes(): diff --git a/tests/routing/test_routing.py b/tests/routing/test_routing.py index bf1637d5..e44975d3 100644 --- a/tests/routing/test_routing.py +++ b/tests/routing/test_routing.py @@ -3,10 +3,10 @@ from dataclasses import dataclass import pytest +from lilya.responses import JSONResponse, PlainText, Response as LilyaResponse +from lilya.routing import Host, NoMatchFound +from lilya.websockets import WebSocket, WebSocketDisconnect from pydantic.dataclasses import dataclass as pydantic_dataclass -from starlette.responses import JSONResponse, PlainTextResponse, Response as StarletteResponse -from starlette.routing import Host, NoMatchFound -from starlette.websockets import WebSocket, WebSocketDisconnect from esmerald.applications import Esmerald from esmerald.enums import MediaType @@ -32,47 +32,47 @@ async def allow_access(request: Request) -> JSONResponse: @get(path="/", media_type=MediaType.TEXT, status_code=200) -async def homepage(request: Request) -> StarletteResponse: - return StarletteResponse("Hello, world") +async def homepage(request: Request) -> LilyaResponse: + return LilyaResponse("Hello, world") @get(path="/", media_type=MediaType.TEXT, status_code=200) -async def users(request: Request) -> StarletteResponse: - return StarletteResponse("All users") +async def users(request: Request) -> LilyaResponse: + return LilyaResponse("All users") @get(path="/", media_type=MediaType.TEXT, status_code=200) -async def user(request: Request, username: str) -> StarletteResponse: +async def user(request: Request, username: str) -> LilyaResponse: content = "User " + username - return StarletteResponse(content) + return LilyaResponse(content) @get(path="/me", media_type=MediaType.TEXT, status_code=200) -async def user_me(request: Request) -> StarletteResponse: +async def user_me(request: Request) -> LilyaResponse: content = "User fixed me" - return StarletteResponse(content) + return LilyaResponse(content) @put(path="/", media_type=MediaType.TEXT, status_code=200) -async def disable_user(request: Request) -> StarletteResponse: +async def disable_user(request: Request) -> LilyaResponse: content = "User " + request.path_params["username"] + " disabled" - return StarletteResponse(content) + return LilyaResponse(content) @get(path="/", media_type=MediaType.TEXT, status_code=200) -async def user_no_match(request: Request) -> StarletteResponse: # pragma: no cover +async def user_no_match(request: Request) -> LilyaResponse: # pragma: no cover content = "User fixed no match" - return StarletteResponse(content) + return LilyaResponse(content) @get(path="/", media_type=MediaType.TEXT, status_code=200) -async def func_homepage(request: Request) -> StarletteResponse: - return StarletteResponse("Hello, world!") +async def func_homepage(request: Request) -> LilyaResponse: + return LilyaResponse("Hello, world!") @post(path="/", media_type=MediaType.TEXT, status_code=200) -async def contact(request: Request) -> StarletteResponse: - return StarletteResponse("Hello, POST!") +async def contact(request: Request) -> LilyaResponse: + return LilyaResponse("Hello, POST!") @get(path="/", status_code=200) @@ -165,9 +165,9 @@ class TestMyAPIView(APIView): async def esmerald(self, param: str, name: str) -> UJSONResponse: return UJSONResponse({"param": param, "name": name}) - @get(path="/name/{endpoint}") - async def name(self, param: str, name: str, endpoint: str) -> UJSONResponse: - return UJSONResponse({"param": param, "name": name, "endpoint": endpoint}) + @get(path="/name/{handler}") + async def name(self, param: str, name: str, handler: str) -> UJSONResponse: + return UJSONResponse({"param": param, "name": name, "handler": handler}) @websocket(path="/socket") async def websocket_endpoint_include(self, socket: WebSocket, param: str, name: str) -> None: @@ -236,7 +236,7 @@ async def websocket_endpoint_include(self, socket: WebSocket, param: str, name: ), Include( "/static", - app=StarletteResponse("xxxxx", media_type=MediaType.PNG, status_code=200), + app=LilyaResponse("xxxxx", media_type=MediaType.PNG, status_code=200), ), Gateway("/func", handler=func_homepage), Gateway("/func", handler=contact), @@ -283,7 +283,7 @@ def test_router(test_client_factory): response = client.get("/foo") assert response.status_code == 404 - assert response.json()["detail"] == "The resource cannot be found." + assert response.json()["detail"] == "Not Found" response = client.get("/users") assert response.status_code == 200 @@ -382,7 +382,7 @@ def test_router_apiview(test_client_factory): assert response.json() == { "name": "test", "param": "param", - "endpoint": "endpot", + "handler": "endpot", } @@ -427,32 +427,32 @@ def test_route_converters(test_client_factory): response = client.get("/int/5") assert response.status_code == 200 assert response.json() == {"int": 5} - assert app.url_path_for("int-convertor", param=5) == "/int/5" + assert app.path_for("int-convertor", param=5) == "/int/5" # Test path with parentheses response = client.get("/path-with-parentheses(7)") assert response.status_code == 200 assert response.json() == {"int": 7} - assert app.url_path_for("path-with-parentheses", param=7) == "/path-with-parentheses(7)" + assert app.path_for("path-with-parentheses", param=7) == "/path-with-parentheses(7)" # Test float conversion response = client.get("/float/25.5") assert response.status_code == 200 assert response.json() == {"float": 25.5} - assert app.url_path_for("float-convertor", param=25.5) == "/float/25.5" + assert app.path_for("float-convertor", param=25.5) == "/float/25.5" # Test path conversion response = client.get("/path/some/example") assert response.status_code == 200 assert response.json() == {"path": "some/example"} - assert app.url_path_for("path-convertor", param="some/example") == "/path/some/example" + assert app.path_for("path-convertor", param="some/example") == "/path/some/example" # Test UUID conversion response = client.get("/uuid/ec38df32-ceda-4cfa-9b4a-1aeb94ad551a") assert response.status_code == 200 assert response.json() == {"uuid": "ec38df32-ceda-4cfa-9b4a-1aeb94ad551a"} assert ( - app.url_path_for( + app.path_for( "uuid-convertor", param=uuid.UUID("ec38df32-ceda-4cfa-9b4a-1aeb94ad551a"), ) @@ -464,21 +464,21 @@ def test_url_path_for(test_client_factory): with create_client(routes=routes) as client: app = client.app - assert app.url_path_for("homepage") == "/" - assert app.url_path_for("user", username="esmerald") == "/users/esmerald" - assert app.url_path_for("websocket_endpoint") == "/ws" + assert app.path_for("homepage") == "/" + assert app.path_for("user", username="esmerald") == "/users/esmerald" + assert app.path_for("websocket_endpoint") == "/ws" with pytest.raises(NoMatchFound, match='No route exists for name "broken" and params "".'): - assert app.url_path_for("broken") + assert app.path_for("broken") with pytest.raises( NoMatchFound, match='No route exists for name "broken" and params "key, key2".', ): - assert app.url_path_for("broken", key="value", key2="value2") + assert app.path_for("broken", key="value", key2="value2") with pytest.raises(AssertionError): - app.url_path_for("user", username="fluid/esmerald") + app.path_for("user", username="fluid/esmerald") with pytest.raises(AssertionError): - app.url_path_for("user", username="") + app.path_for("user", username="") def test_url_for(test_client_factory): @@ -486,31 +486,27 @@ def test_url_for(test_client_factory): app = client.app assert ( - app.url_path_for("homepage").make_absolute_url(base_url="https://example.org") + app.path_for("homepage").make_absolute_url(base_url="https://example.org") == "https://example.org/" ) assert ( - app.url_path_for("homepage").make_absolute_url( - base_url="https://example.org/root_path/" - ) + app.path_for("homepage").make_absolute_url(base_url="https://example.org/root_path/") == "https://example.org/root_path/" ) assert ( - app.url_path_for("user", username="tomchristie").make_absolute_url( + app.path_for("user", username="tomchristie").make_absolute_url( base_url="https://example.org" ) == "https://example.org/users/tomchristie" ) assert ( - app.url_path_for("user", username="tomchristie").make_absolute_url( + app.path_for("user", username="tomchristie").make_absolute_url( base_url="https://example.org/root_path/" ) == "https://example.org/root_path/users/tomchristie" ) assert ( - app.url_path_for("websocket_endpoint").make_absolute_url( - base_url="https://example.org" - ) + app.path_for("websocket_endpoint").make_absolute_url(base_url="https://example.org") == "wss://example.org/ws" ) @@ -542,14 +538,14 @@ def test_router_add_websocket_route(test_client_factory): @get(path="/", media_type=MediaType.TEXT) async def http_endpoint(request: Request) -> Response: - url = request.url_for("http_endpoint") + url = request.path_for("http_endpoint") return Response(f"URL: {url}") @websocket(path="/") async def websocket_endpoint_switch(socket: WebSocket) -> None: await socket.accept() - await socket.send_json({"URL": str(socket.url_for("websocket_endpoint"))}) + await socket.send_json({"URL": str(socket.path_for("websocket_endpoint"))}) await socket.close() @@ -573,12 +569,12 @@ def test_protocol_switch(test_client_factory): pass # pragma: nocover -ok = PlainTextResponse("OK") +ok = PlainText("OK") @get(path="/") -async def get_ok() -> PlainTextResponse: - return PlainTextResponse("OK") # pragma: no cover +async def get_ok() -> PlainText: + return PlainText("OK") # pragma: no cover def test_include_urls(test_client_factory): @@ -593,12 +589,12 @@ def test_include_urls(test_client_factory): def test_reverse_include_urls(): included = Include("/users", app=ok, name="users") - assert included.url_path_for("users", path="/a") == "/users/a" + assert included.path_for("users", path="/a") == "/users/a" users = [Gateway("/{username}", handler=get_ok, name="user")] included = Include("/{subpath}/users", routes=users, name="users") - assert included.url_path_for("users:user", subpath="test", username="tom") == "/test/users/tom" - assert included.url_path_for("users", subpath="test", path="/tom") == "/test/users/tom" + assert included.path_for("users:user", subpath="test", username="tom") == "/test/users/tom" + assert included.path_for("users", subpath="test", path="/tom") == "/test/users/tom" def test_include_at_root(test_client_factory): @@ -676,19 +672,19 @@ def test_host_reverse_urls(test_client_factory): with create_client(routes=mixed_hosts_app) as client: app = client.app assert ( - app.url_path_for("homepage").make_absolute_url("https://whatever") + app.path_for("homepage").make_absolute_url("https://whatever") == "https://www.example.org/" ) assert ( - app.url_path_for("users").make_absolute_url("https://whatever") + app.path_for("users").make_absolute_url("https://whatever") == "https://www.example.org/users" ) assert ( - app.url_path_for("api:users").make_absolute_url("https://whatever") + app.path_for("api:users").make_absolute_url("https://whatever") == "https://api.example.org/users" ) assert ( - app.url_path_for("port:homepage").make_absolute_url("https://whatever") + app.path_for("port:homepage").make_absolute_url("https://whatever") == "https://port.example.org:3600/" ) @@ -714,7 +710,7 @@ def test_subdomain_routing(test_client_factory): def test_subdomain_reverse_urls(test_client_factory): client = test_client_factory(subdomain_router, base_url="https://esmerald.example.org/") assert ( - client.app.url_path_for( + client.app.path_for( "subdomains", subdomain="esmerald", path="/homepage" ).make_absolute_url("https://whatever") == "https://esmerald.example.org/homepage" @@ -725,8 +721,8 @@ def test_subdomain_reverse_urls(test_client_factory): async def echo_urls(request: Request) -> UJSONResponse: return UJSONResponse( { - "index": request.url_for("index"), - "submount": request.url_for("include:submount"), + "index": request.path_for("index"), + "submount": request.path_for("include:submount"), } ) @@ -773,13 +769,13 @@ async def stub_app(scope, receive, send): @get("/") -async def url_ok() -> PlainTextResponse: - return PlainTextResponse("Hello, World!") +async def url_ok() -> PlainText: + return PlainText("Hello, World!") def test_url_for_with_double_mount(test_client_factory): app = Esmerald(routes=double_mount_routes) - url = app.url_path_for("include:static", path="123") + url = app.path_for("include:static", path="123") assert url == "/include/static/123" @@ -798,7 +794,7 @@ def test_standalone_route_does_not_match(test_client_factory): with create_client(routes=routes) as client: response = client.get("/invalid") assert response.status_code == 404 - assert response.json()["detail"] == "The resource cannot be found." + assert response.json()["detail"] == "Not Found" @websocket(path="/") @@ -831,8 +827,8 @@ def test_lifespan_async(test_client_factory): shutdown_complete = False @get("/") - async def hello_world(request: Request) -> PlainTextResponse: - return PlainTextResponse("hello, world") + async def hello_world(request: Request) -> PlainText: + return PlainText("hello, world") async def run_startup() -> None: nonlocal startup_complete @@ -865,7 +861,7 @@ async def lifespan(app): app = Router( lifespan=lifespan, - routes=[Include("/", PlainTextResponse("hello, esmerald"))], + routes=[Include("/", PlainText("hello, esmerald"))], ) async def no_state_wrapper(scope, receive, send): @@ -884,8 +880,8 @@ def test_lifespan_sync(test_client_factory): shutdown_complete = False @get("/") - def hello_world(request: Request) -> PlainTextResponse: - return PlainTextResponse("hello, world") + def hello_world(request: Request) -> PlainText: + return PlainText("hello, world") def run_startup(): nonlocal startup_complete @@ -949,13 +945,13 @@ def run_shutdown(): def test_duplicated_param_names(): with pytest.raises( ValueError, - match="Duplicated param name id at path /{id}/{id}", + match="Duplicated param name id in the path /{id}/{id}", ): Gateway("/{id}/{id}", handler=user) with pytest.raises( ValueError, - match="Duplicated param names id, name at path /{id}/{name}/{id}/{name}", + match="Duplicated param names id, name in the path /{id}/{name}/{id}/{name}", ): Gateway("/{id}/{name}/{id}/{name}", handler=user) diff --git a/tests/test_apiviews.py b/tests/test_apiviews.py index d92721eb..ea4bdd5e 100644 --- a/tests/test_apiviews.py +++ b/tests/test_apiviews.py @@ -1,8 +1,8 @@ from typing import Any, Type, Union import pytest +from lilya.status import HTTP_200_OK, HTTP_201_CREATED, HTTP_204_NO_CONTENT from pydantic import BaseModel -from starlette.status import HTTP_200_OK, HTTP_201_CREATED, HTTP_204_NO_CONTENT from esmerald.enums import HttpMethod, MediaType from esmerald.responses import Response diff --git a/tests/test_child_esmerald.py b/tests/test_child_esmerald.py index 9fff12ae..6c3b0c37 100644 --- a/tests/test_child_esmerald.py +++ b/tests/test_child_esmerald.py @@ -1,4 +1,4 @@ -from starlette import status +from lilya import status from esmerald.applications import ChildEsmerald from esmerald.routing.gateways import Gateway diff --git a/tests/test_injects.py b/tests/test_injects.py index 2ee1cd7b..8e2a6b6c 100644 --- a/tests/test_injects.py +++ b/tests/test_injects.py @@ -1,7 +1,7 @@ from typing import Any, Dict, List, Optional import pytest -from starlette.status import HTTP_200_OK, HTTP_500_INTERNAL_SERVER_ERROR +from lilya.status import HTTP_200_OK, HTTP_500_INTERNAL_SERVER_ERROR from esmerald.applications import Esmerald from esmerald.exceptions import ImproperlyConfigured diff --git a/tests/test_responses.py b/tests/test_responses.py index 46de0c31..4eb6e977 100644 --- a/tests/test_responses.py +++ b/tests/test_responses.py @@ -1,4 +1,4 @@ -from starlette import status +from lilya import status from esmerald.responses.encoders import ORJSONResponse, UJSONResponse from esmerald.routing.gateways import Gateway diff --git a/tests/test_router.py b/tests/test_router.py index a8216444..61880bb6 100644 --- a/tests/test_router.py +++ b/tests/test_router.py @@ -1,7 +1,7 @@ from unittest import mock import pytest -from starlette import status +from lilya import status from esmerald import ChildEsmerald from esmerald.exceptions import ImproperlyConfigured diff --git a/tests/test_routes.py b/tests/test_routes.py index 7419aaff..c96c6936 100644 --- a/tests/test_routes.py +++ b/tests/test_routes.py @@ -1,5 +1,5 @@ import pytest -from starlette import status +from lilya import status from esmerald.exceptions import ImproperlyConfigured from esmerald.routing.apis.views import APIView diff --git a/tests/test_static_files.py b/tests/test_static_files.py index 9baf1f8b..f79f50b1 100644 --- a/tests/test_static_files.py +++ b/tests/test_static_files.py @@ -4,12 +4,8 @@ import pytest from pydantic import ValidationError -from starlette.middleware import Middleware -from starlette.middleware.base import BaseHTTPMiddleware from esmerald.config import StaticFilesConfig -from esmerald.requests import Request -from esmerald.routing.router import Include from esmerald.testclient import create_client @@ -49,30 +45,6 @@ def test_staticfiles_with_pathlib(tmpdir, test_client_factory): assert response.text == "" -def test_staticfiles_head_with_middleware(tmpdir, test_client_factory): - """ - see https://github.com/encode/starlette/pull/935 - """ - path = os.path.join(tmpdir, "example.txt") - with open(path, "w") as file: - file.write("x" * 100) - - async def does_nothing_middleware(request: Request, call_next): - response = await call_next(request) - return response - - static_files_config = StaticFilesConfig(path="/", directory=tmpdir) - routes = [Include("/static", app=static_files_config.to_app(), name="static")] - middleware = [Middleware(BaseHTTPMiddleware, dispatch=does_nothing_middleware)] - - with create_client( - routes=routes, static_files_config=static_files_config, middleware=middleware - ) as client: - response = client.head("/static/example.txt") - assert response.status_code == 200 - assert response.headers.get("content-length") == "100" - - def test_staticfiles_html(tmpdir: Any) -> None: path = tmpdir.join("index.html") path.write("content") diff --git a/tests/utils/test_module_loading.py b/tests/utils/test_module_loading.py index 5dee286d..346867c1 100644 --- a/tests/utils/test_module_loading.py +++ b/tests/utils/test_module_loading.py @@ -1,7 +1,7 @@ import pytest +from lilya._internal._module_loading import import_string from esmerald import EsmeraldAPISettings -from esmerald.utils.module_loading import import_string def test_import_error_module_loading(): diff --git a/tests/utils/test_sync.py b/tests/utils/test_sync.py index 7d2433fa..3caea40d 100644 --- a/tests/utils/test_sync.py +++ b/tests/utils/test_sync.py @@ -1,5 +1,5 @@ from esmerald.utils.helpers import is_async_callable -from esmerald.utils.sync import AsyncCallable, run_sync +from esmerald.utils.sync import AsyncCallable async def process() -> None: @@ -20,9 +20,3 @@ def test_async_callable_transform(): async_callable = AsyncCallable(another_process) assert is_async_callable(async_callable.fn) - - -def test_execsync(): - wrapper = run_sync(process()) - - assert is_async_callable(wrapper) is False diff --git a/tests/utils/test_url.py b/tests/utils/test_url.py index 5eb9c3b1..d1424e38 100644 --- a/tests/utils/test_url.py +++ b/tests/utils/test_url.py @@ -1,4 +1,4 @@ -from esmerald.utils.url import clean_path, join_paths +from lilya._internal._path import clean_path, join_paths def test_clean_path():