Skip to content

Commit

Permalink
Merge branch 'feat/oauth' of github.com:dymmond/esmerald into feat/oauth
Browse files Browse the repository at this point in the history
  • Loading branch information
tarsil committed Dec 9, 2024
2 parents ef8b75d + c3be28b commit 2b3d043
Show file tree
Hide file tree
Showing 39 changed files with 381 additions and 101 deletions.
4 changes: 2 additions & 2 deletions docs/en/docs/background-tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ There are also two ways of passing via handlers.

### Using a single instance

This is probably the most common use case where you simply need to execute one bacground task upon
This is probably the most common use case where you simply need to execute one background task upon
receiving the request, for example, sending an email notification.

```python hl_lines="18"
Expand Down Expand Up @@ -73,7 +73,7 @@ needed.

### Using the add_task

Another way of adding multiple tasks is by using the `add_tasks` function provided by the
Another way of adding multiple tasks is by using the `add_task` function provided by the
`BackgroundTasks` object.

```python hl_lines="28-32"
Expand Down
12 changes: 12 additions & 0 deletions docs/en/docs/configurations/staticfiles.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,15 @@ but also via settings.
```

This will make sure you keep the settings clean, separated and without a bloated **Esmerald** instance.

## Multiple directories and multiple pathes

Imagine, for example, you have multiple directories you would like to access including a `node_modules/` one.
This is possible do do it by passing multiple `StaticFilesConfig` configurations and shown below:

```python
{!> ../../../docs_src/configurations/staticfiles/example_multiple.py!}
```

!!! Note
The first path match is used and there is currently no fallthrough in case no file is found, so the order is very important.
4 changes: 2 additions & 2 deletions docs/en/docs/configurations/template.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
TemplateConfig is a simple set of configurations that when passed enables the template engine.

!!! info
Currently Esmerald supports `Jinja2` and `Mako`.
Currently Esmerald supports `Jinja2` or `mako` (deprecated). Mako will be removed in the release 3.6.0 of Esmerald.

It is important to understand that you don't need to use the provided `JinjaTemplateEngine` or `MakoTemplateEngine`
from Esmerald within the `TemplateConfig`.
Expand All @@ -20,7 +20,7 @@ You are free to build your own and pass it to the `TemplateConfig`. This way you
You will notice the name of the parameters in the `TemplateConfig` match maority of the jinja2 implementation.

!!! Warning
The `Mako` engine has a limited integration within Esmerald. This will change in the future.
The `mako` engine integration, due to its own limitations, will be not supported by Esmerald from version 3.6.0.

## TemplateConfig and application

Expand Down
7 changes: 5 additions & 2 deletions docs/en/docs/directives/discovery.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,17 @@ When no `--app` or no `ESMERALD_DEFAULT_APP` environment variable is provided, E
* **main.py**
* **app.py**
* **application.py**
* **asgi.py**

!!! Warning
**If none of these files are found**, Esmerald will look **at the first children nodes, only**,
and repeats the same process. If no files are found then throws an `EnvironmentError`
exception.

* Once one of those files is found, Esmerald will analise the type of objects contained in the
module and will check if any of them is a valid `Esmerald` type and return it.
* Once one of those files is found, Esmerald will check the module for a truthy declaration with name `app` or `application`.

* If Esmerald doesn't find the declarations or they are not truthy, Esmerald will analise the type of objects contained in the
module and will check if any of them is a valid `Esmerald` type and return it.

* If Esmerald understand that none of those objects are type `Esmerald` (or subclasses), it will
do one last attempt and try to find specific function declarations:
Expand Down
15 changes: 15 additions & 0 deletions docs/en/docs/release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,21 @@ hide:

# Release Notes

## Unreleased

### Fixed

- Fix cli detection of wrapped esmerald instances or different ASGI servers.
- Allow passing multiple `StaticFilesConfig` configurations in a tuple.

## 3.5.1

### Changed

- Use assigned encoders at requests for json_encoder.
- Allow overwriting the `LILYA_ENCODER_TYPES` for different encoder sets or tests.
- Use more orjson for encoding requests.

## 3.5.0

### Added
Expand Down
17 changes: 17 additions & 0 deletions docs/ru/docs/about.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# О проекте

Esmerald появился в 2022 году из идеи двух разработчиков, стремящихся создать уникальное и легкое в
установке и использовании решение.

Никогда не задумывалось создать что-то, чтобы "соперничать" или "украсть рынок" у кого-либо.
Напротив, эти два разработчика разработали Esmerald как дополнение к текущему экосистеме Python.

## Вдохновение

В самом начале документации упоминаются замечательные фреймворки, которые вдохновили Esmerald,
но мы уверенны, что тот, кто заложил основу для всей этой прекрасной техники, был бесспорно Django.

## Что ждёт нас в будущем

С уверенностью можно говорить, что с ростом Esmerald и при помощи сообщества перед нами яркое
будущее, и новые функции могут и будут добавляться в соответствии с потребностями экосистемы.
97 changes: 97 additions & 0 deletions docs/ru/docs/background-tasks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# Background Tasks

Как и в Lilya, в Esmerald вы можете определять фоновые задачи для выполнения **после** возвращения ответа.

Это может быть полезно для тех операций, которые должны происходить после запроса, не блокируя клиента (клиенту не нужно
ждать завершения) для получения того же ответа.

Примеры:

* Регистрация пользователя в системе и отправка электронного письма с подтверждением регистрации.
* Обработка файла, которая может занять "некоторое время". Просто верните HTTP 202 и обработайте файл в фоновом режиме.

## Как использовать

Как уже упоминалось, Esmerald использует фоновые задачи из Lilya, и вы можете передавать их различными способами:

* Через [обработчики](#через-handlers)
* Через [ответ](#через-response)

## Через handlers

Это означает передачу фоновой задачи через get, put, post, delete или route.

Существует также два способа передачи через обработчики.

### Использование одного экземпляра

Это, вероятно, наиболее распространенный случай, когда вам нужно выполнить одну фоновую задачу при получении запроса,
например, отправка уведомления по электронной почте.

```python hl_lines="18"
{!> ../../../docs_src/background_tasks/via_handlers.py !}
```

Этот пример довольно простой, но он иллюстрирует, как вы могли бы использовать обработчики
для передачи фоновой задачи.

### Использование списка

Конечно, есть также ситуации, когда нужно выполнить более одной фоновой задачи.

```python hl_lines="27-32"
{!> ../../../docs_src/background_tasks/via_list.py !}
```

## Через response

Добавление задач через response, вероятно, будет тем способом, который вы будете использовать чаще всего,
иногда вам понадобится какая-то конкретная информация, которая доступна только внутри вашего представления.

Это достигается аналогичным образом, как и в [handlers](#через-handlers).

### Использование одного экземпляра

Таким же образом, как вы создавали одну фоновую задачу для handlers, в response это работает аналогично.

```python hl_lines="22-26"
{!> ../../../docs_src/background_tasks/response/via_handlers.py !}
```

### Использование списка

То же самое происходит при выполнении более одной фоновой задачи и когда требуется более одной операции.

```python hl_lines="30-39"
{!> ../../../docs_src/background_tasks/response/via_list.py !}
```

### Использование add_task

Другой способ добавления нескольких задач - использование функции `add_task`, предоставляемой объектом `BackgroundTasks`.

```python hl_lines="28-32"
{!> ../../../docs_src/background_tasks/response/add_tasks.py !}
```

Функция `.add_task()` принимает в качестве аргументов:

* Функцию задачи, которая должна быть выполнена в фоновом режиме (send_email_notification и write_in_file).
* Любую последовательность аргументов (*args), которые должны быть переданы функции задачи (email, message).
* Любые именованные аргументы (**kwargs), которые должны быть переданы функции задачи.

## Техническая информация

Классы `BackgroundTask` и `BackgroundTasks` приходят напрямую из `lilya.background`. Это означает, что вы можете импортировать
их напрямую из Lilya, если хотите.

Esmerald импортирует эти классы и добавляет некоторую дополнительную информацию о типах, но не влияет на общую функциональность ядра.

Вы можете использовать функции `def` или `async def` при объявлении этих функций для передачи в `BackgroundTask`,
Esmerald будет знать, как обработать их.

Для получения дополнительной информации об этих объектах вы можете ознакомиться с [официальной документацией Lilya по фоновым задачам](https://www.lilya.dev/tasks/).

## Справочник API

Узнайте больше о `BackgroundTask`, ознакомившись со [Справочником API](./references/background.md).
13 changes: 13 additions & 0 deletions docs_src/configurations/staticfiles/example_multiple.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from pathlib import Path

from esmerald import Esmerald, StaticFilesConfig

static_files_config = StaticFilesConfig(
path="/static", packages=["mypackage"], directory=Path("static")
)

static_files_node_modules_config = StaticFilesConfig(
path="/static/node_modules", directory=Path("node_modules")
)

app = Esmerald(static_files_config=[static_files_node_modules_config, static_files_config])
2 changes: 1 addition & 1 deletion esmerald/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = "3.5.0"
__version__ = "3.5.1"


from lilya import status
Expand Down
33 changes: 23 additions & 10 deletions esmerald/applications.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import warnings
from collections.abc import Callable, Iterable, Sequence
from datetime import timezone as dtimezone
from functools import cached_property
from inspect import isclass
from typing import (
TYPE_CHECKING,
Any,
Callable,
Dict,
List,
Optional,
Sequence,
Type,
TypeVar,
Union,
Expand Down Expand Up @@ -720,13 +720,18 @@ def is_valid(number: int) -> bool:
),
] = None,
static_files_config: Annotated[
Optional["StaticFilesConfig"],
Union[
"StaticFilesConfig",
list["StaticFilesConfig"],
tuple["StaticFilesConfig", ...],
None,
],
Doc(
"""
An instance of [StaticFilesConfig](https://esmerald.dev/configurations/staticfiles/).
This configuration is used to enable and serve static files via
Esmerald application.
Esmerald application. You can pass also multiple objects via a list or tuple.
**Example**
Expand Down Expand Up @@ -1024,7 +1029,7 @@ async def another(request: Request) -> str:
),
] = None,
encoders: Annotated[
Sequence[Optional[Encoder]],
Optional[Sequence[Union[Encoder, type[Encoder]]]],
Doc(
"""
A `list` of encoders to be used by the application once it
Expand Down Expand Up @@ -1608,7 +1613,15 @@ def extend(self, config: PluggableConfig) -> None:
] = State()
self.async_exit_config = esmerald_settings.async_exit_config

self.encoders = self.load_settings_value("encoders", encoders) or []
self.encoders = list(
cast(
Iterable[Union[Encoder]],
(
encoder if isclass(encoder) else encoder
for encoder in self.load_settings_value("encoders", encoders) or []
),
)
)
self._register_application_encoders()

if self.enable_scheduler:
Expand Down Expand Up @@ -1662,8 +1675,8 @@ def _register_application_encoders(self) -> None:
This way, the support still remains but using the Lilya Encoders.
"""
self.register_encoder(cast(Encoder[Any], PydanticEncoder))
self.register_encoder(cast(Encoder[Any], MsgSpecEncoder))
self.register_encoder(cast(Encoder, PydanticEncoder))
self.register_encoder(cast(Encoder, MsgSpecEncoder))

for encoder in self.encoders:
self.register_encoder(encoder)
Expand All @@ -1675,7 +1688,7 @@ def _configure(self) -> None:
if self.static_files_config:
for config in (
self.static_files_config
if isinstance(self.static_files_config, list)
if isinstance(self.static_files_config, (list, tuple))
else [self.static_files_config]
):
static_route = Include(path=config.path, app=config.to_app())
Expand Down Expand Up @@ -2611,7 +2624,7 @@ def on_event(self, event_type: str) -> Callable: # pragma: nocover
def add_event_handler(self, event_type: str, func: Callable) -> None: # pragma: no cover
self.router.add_event_handler(event_type, func)

def register_encoder(self, encoder: Encoder[Any]) -> None:
def register_encoder(self, encoder: Encoder) -> None:
"""
Registers a Encoder into the list of predefined encoders of the system.
"""
Expand Down
6 changes: 4 additions & 2 deletions esmerald/conf/global_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -869,13 +869,15 @@ def template_config(self) -> Optional["TemplateConfig"]:
class AppSettings(EsmeraldAPISettings):
@property
def template_config(self) -> TemplateConfig:
TemplateConfig(directory='templates', engine=MakoTemplateEngine)
TemplateConfig(directory='templates')
```
"""
return None

@property
def static_files_config(self) -> Optional[StaticFilesConfig]:
def static_files_config(
self,
) -> Union[StaticFilesConfig, list[StaticFilesConfig], tuple[StaticFilesConfig, ...], None]:
"""
An instance of [StaticFilesConfig](https://esmerald.dev/configurations/staticfiles/).
Expand Down
3 changes: 3 additions & 0 deletions esmerald/config/static_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ class StaticFilesConfig(BaseModel):
)
app = Esmerald(static_files_config=static_files_config)
# or multiple in descending priority
app = Esmerald(static_files_config=[static_files_config1, static_files_config2, ...])
```
"""

Expand Down
3 changes: 2 additions & 1 deletion esmerald/core/directives/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
HELP_PARAMETER = "--help"
EXCLUDED_DIRECTIVES = ["createproject", "createapp", "createdeployment"]
IGNORE_DIRECTIVES = ["directives"]
DISCOVERY_FILES = ["application.py", "app.py", "main.py"]
DISCOVERY_FILES = ["application.py", "app.py", "main.py", "asgi.py"]
DISCOVERY_FUNCTIONS = ["get_application", "get_app"]
DISCOVERY_ATTRS = ["application", "app"]
TREAT_AS_PROJECT_DIRECTIVE = ["deployment"]
8 changes: 8 additions & 0 deletions esmerald/core/directives/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from esmerald import ChildEsmerald, Esmerald
from esmerald.core.directives.constants import (
DISCOVERY_ATTRS,
DISCOVERY_FILES,
DISCOVERY_FUNCTIONS,
ESMERALD_DISCOVER_APP,
Expand Down Expand Up @@ -92,6 +93,13 @@ def _find_app_in_folder(self, path: Path, cwd: Path) -> typing.Union[Scaffold, N
# Load file from module
module = import_module(dotted_path)

# FIrst check some attrs
for attr in DISCOVERY_ATTRS:
value = getattr(module, attr, None)
if value:
app_path = f"{dotted_path}:{attr}"
return Scaffold(app=value, path=app_path)

# Iterates through the elements of the module.
for attr, value in module.__dict__.items():
if isinstance(value, Esmerald):
Expand Down
Loading

0 comments on commit 2b3d043

Please sign in to comment.