Esmerald¶
🚀 Масштабируемость, производительность, легкость в изучении и написании кода, подходит для любого приложения. 🚀
Документация: https://esmerald.dev 📚
Исходный код: https://github.com/dymmond/esmerald
Esmerald — это современный, мощный, гибкий и высокопроизводительный веб-фреймворк, созданный для построения не только API, но и полноценных масштабируемых приложений — от самых малых до уровня крупных компаний.
Esmerald разрабатывался для Python 3.9+ и использует стандартные подсказки типов (type hints) Python, основан на широко известном Lilya и Pydantic/msgspec.
Success
Официально поддерживается только последняя выпущенная версия.
Мотивация¶
Существуют отличные фреймворки такие, как FastAPI, Flama, Flask, Django и другие, решающие большинство повседневных задач для 99% приложений, но оставляющие тот 1%, который обычно связан со структурой и бизнес-логикой, без особых решений.
Esmerald черпает вдохновение в этих фреймворках и обладает всеми их известными возможностями, но также учитывает потребности бизнеса. Например, Starlite вдохновил на создание трансформеров и моделей Signature, что помогло интеграции с Pydantic. FastAPI вдохновил дизайн API, Django — систему разрешений, Flask — простоту, NestJS — контроллеры и многое другое.
Для качественной работы всегда требуется как драйвер, так и источник вдохновения.
Требования¶
- Python 3.9+
Esmerald не был бы возможен без следующих двух компонентов:
Установка¶
$ pip install esmerald
Для работы в продакшене также потребуется ASGI сервер, рекомендуем Uvicorn, но выбор остается за вами.
$ pip install uvicorn
Поддержка встроенного планировщика::
$ pip install esmerald[schedulers]
Поддержка JWT, используемого внутри Esmerald::
$ pip install esmerald[jwt]
Для использования клиента тестирования Esmerald::
$ pip install esmerald[test]
Для использования оболочки Esmerald::
Подробнее здесь по теме в документации.
$ pip install esmerald[ipython] # default shell
$ pip install esmerald[ptpython] # ptpython shell
Начало проекта с использованием директив¶
Warning
Директивы рассчитаны на опытных пользователей, которые уже знакомы с Esmerald (или Python в целом), или если использование директив не вызывает затруднений. Если пока не чувствуете уверенности, продолжайте изучать документацию и знакомиться с Esmerald.
Чтобы начать Esmerald проект с простой предложенной структурой, выполните:
esmerald createproject <YOUR-PROJECT-NAME> --simple
Это создаст каркас проекта с некоторыми предопределенными файлами для простого запуска приложения Esmerald.
Также будет создан файл для тестов с использованием EsmeraldTestClient, так что выполните:
$ pip install esmerald[test]
Эту часть можно пропустить, если не хотите использовать EsmeraldTestClient.
Подробная информация об этой директиве и примерах ее использования.
Warning
Запуск этой директивы создает только каркас проекта, и для его запуска потребуются дополнительные данные. Этот каркас лишь предоставляет структуру файлов для начала работы, но не является обязательным.
Основные функции¶
- Быстрый и эффективный: Благодаря Lilya и Pydantic/msgpec.
- Быстрое развитие: Простота дизайна значительно сокращает время разработки.
- Интуитивно понятный: Если знакомы с другими фреймворками, работать с Esmerald не составит труда.
- Простота: Создан с учетом удобства и легкости в изучении.
- Компактный: Благодаря встроенной поддержке ООП нет необходимости дублировать код. Поддержка SOLID.
- Готовый к работе: Приложение запускается с готовым к продакшену кодом.
- ООП и функциональный стиль: Проектируйте API любым удобным способом, поддержка ООП и функционального стиля.
- Асинхронный и синхронный: Поддерживает как синхронный, так и асинхронный режимы.
- Middleware: Применяйте middleware на уровне приложения или API.
- Обработчики исключений: Применяйте обработчики на любом уровне.
- Permissions: Применяйте правила и permissions для каждого API.
- Interceptors: Перехватывайте запросы и добавляйте логику перед обработкой.
- Плагины: Создавайте плагины для Esmerald и интегрируйте их в любое приложение, или опубликуйте свой пакет.
- DAO и AsyncDAO: Избегайте вызовов базы данных напрямую из API, используйте бизнес-объекты.
- Поддержка ORM: Поддержка [Edgy][_orm].
- Поддержка ODM: Поддержка Mongoz.
- APIView: Контроллеры в виде классов.
- JSON сериализация/десериализация: Поддержка UJSON и ORJSON.
- Lifespan: Поддержка lifespan Lilya.
- Внедрение зависимостей: Как в любом хорошем фреймворке.
- Планировщик: Поддержка задач в фоне.
- Настройки: Поддержка системы настроек для чистоты кода.
- msgspec — поддержка
msgspec
.
Отношение к Lilya и другим фреймворкам¶
Esmerald использует Lilya. Это решение обусловлено высокой производительностью и отсутствием проблем с маршрутизацией.
Esmerald поощряет стандартные практики и подходы к дизайну, что позволяет использовать его как для малых, так и для крупных приложений, не испытывая проблем с масштабируемостью.
Быстрый старт¶
Пример как быстро начать работу с Esmerald.
Для быстрого старта используйте uvicorn
.
#!/usr/bin/env python
import uvicorn
from esmerald import Esmerald, Gateway, JSONResponse, Request, get
@get()
def welcome() -> JSONResponse:
return JSONResponse({"message": "Welcome to Esmerald"})
@get()
def user(user: str) -> JSONResponse:
return JSONResponse({"message": f"Welcome to Esmerald, {user}"})
@get()
def user_in_request(request: Request) -> JSONResponse:
user = request.path_params["user"]
return JSONResponse({"message": f"Welcome to Esmerald, {user}"})
app = Esmerald(
routes=[
Gateway("/esmerald", handler=welcome),
Gateway("/esmerald/{user}", handler=user),
Gateway("/esmerald/in-request/{user}", handler=user_in_request),
]
)
if __name__ == "__main__":
uvicorn.run(app, port=8000)
Затем вы можете получить доступ к endpoints.
Использование Esmerald в качестве декоратора¶
Чтобы быстро начать работу с Esmerald, вы также можете использовать его как декоратор. Вот как это
сделать на примере с uvicorn
.
#!/usr/bin/env python
import uvicorn
from esmerald import Esmerald, Gateway, JSONResponse, Request, get
app = Esmerald()
@app.get("/esmerald")
def welcome() -> JSONResponse:
return JSONResponse({"message": "Welcome to Esmerald"})
@app.get("/esmerald/{user}")
def user(user: str) -> JSONResponse:
return JSONResponse({"message": f"Welcome to Esmerald, {user}"})
@app.get("/esmerald/in-request/{user}")
def user_in_request(request: Request) -> JSONResponse:
user = request.path_params["user"]
return JSONResponse({"message": f"Welcome to Esmerald, {user}"})
if __name__ == "__main__":
uvicorn.run(app, port=8000)
Настройки¶
Как и в любом другом фреймворке, при запуске приложения множество настроек можно или необходимо передать главному объекту, что иногда выглядит сложно и неудобно для поддержки и восприятия.
Esmerald изначально учитывает настройки. Набор параметров по умолчанию можно изменить, используя собственный модуль настроек, но при этом вы также можете использовать классический подход, передавая все параметры непосредственно при создании экземпляра Esmerald.
Пример классического подхода:
from example import ApplicationObjectExample
# ExampleObject — это экземпляр другого приложения,
# и он служит только в качестве примера
app = ApplicationObjectExample(setting_one=..., setting_two=..., setting_three=...)
Вдохновленный замечательным Django и используя pydantic, Esmerald предоставляет объект по умолчанию, готовый к использованию сразу «из коробки».
Esmerald:
from esmerald import Esmerald
app = Esmerald()
И это все! Все настройки по умолчанию загружаются автоматически! Почему?
Потому что приложение ищет переменную окружения ESMERALD_SETTINGS_MODULE
для запуска,
и если она не найдена, используются глобальные настройки приложения. Это просто, но можно ли
переопределить их внутри объекта? Да, конечно.
from esmerald import Esmerald
app = Esmerald(app_name='My App', title='My title')
То же самое, что и классический подход.
Давайте поговорим о модуле настроек Esmerald.
Модуль настроек Esmerald¶
При запуске приложения система ищет переменную окружения
ESMERALD_SETTINGS_MODULE
. Если переменная не указана, система по умолчанию использует настройки
EsmeraldSettings
и запускается.
Пользовательские настройки¶
В наше время важно разделять настройки по окружениям и стандартных настроек Esmerald будет недостаточно для любого приложения.
Настройки соответствуют стандарту pydantic и, следовательно, совместимы с Esmerald. Система предоставляет несколько значений по умолчанию, которые можно использовать сразу, хотя это необязательно. Окружение по умолчанию — production.
from esmerald import EsmeraldSettings
from esmerald.conf.enums import EnvironmentType
class Development(EsmeraldSettings):
app_name: str = 'My app in dev'
environment: str = EnvironmentType.DEVELOPMENT
Загрузка настроек в ваше приложение Esmerald:
Предположим, ваше приложение Esmerald находится в файле src/app.py
.
ESMERALD_SETTINGS_MODULE='myapp.settings.Development' python -m src.app.py
$env:ESMERALD_SETTINGS_MODULE="myapp.settings.Development"; python -m src.app.py
Gateway, WebSocketGateway и Include¶
Lilya предлагает классы Path
для простых назначений путей, но это также очень ограничивает,
если у вас есть что-то более сложное. Esmerald расширяет эту функциональность и добавляет немного
'стиля', улучшая её с помощью Gateway,
WebSocketGateway и Include.
Эти специальные объекты позволяют происходить всей магии Esmerald.
Для классического, прямого подхода в одном файле:
from esmerald import Esmerald, Gateway, JSONResponse, Request, Websocket, WebSocketGateway, get, status
@get(status_code=status.HTTP_200_OK)
async def home() -> JSONResponse:
return JSONResponse({
"detail": "Hello world"
})
@get()
async def another(request: Request) -> dict:
return {
"detail": "Another world!"
}
@websocket(path="/{path_param:str}")
async def world_socket(socket: Websocket) -> None:
await socket.accept()
msg = await socket.receive_json()
assert msg
assert socket
await socket.close()
app = Esmerald(routes=[
Gateway(handler=home),
Gateway(handler=another),
WebSocketGateway(handler=world_socket),
])
Дизайн маршрутов¶
Хороший дизайн всегда приветствуется и Esmerald позволяет создавать сложные маршруты на любом уровне.
Обработчики (контроллеры)¶
from pydantic import BaseModel
from esmerald import (
APIView,
JSONResponse,
Request,
Response,
WebSocket,
get,
post,
put,
status,
websocket,
)
class Product(BaseModel):
name: str
sku: str
price: float
@put("/product/{product_id}")
def update_product(product_id: int, data: Product) -> dict:
return {"product_id": product_id, "product_name": data.name}
@get(status_code=status.HTTP_200_OK)
async def home() -> JSONResponse:
return JSONResponse({"detail": "Hello world"})
@get()
async def another(request: Request) -> dict:
return {"detail": "Another world!"}
@websocket(path="/{path_param:str}")
async def world_socket(socket: WebSocket) -> None:
await socket.accept()
msg = await socket.receive_json()
assert msg
assert socket
await socket.close()
class World(APIView):
@get(path="/{url}")
async def home(self, request: Request, url: str) -> Response:
return Response(f"URL: {url}")
@post(path="/{url}", status_code=status.HTTP_201_CREATED)
async def mars(self, request: Request, url: str) -> JSONResponse: ...
@websocket(path="/{path_param:str}")
async def pluto(self, socket: WebSocket) -> None:
await socket.accept()
msg = await socket.receive_json()
assert msg
assert socket
await socket.close()
Если path
не указан, по умолчанию используется /
.
Gateways (urls)¶
from esmerald import Gateway, WebSocketGateway
from .controllers import home, another, world_socket, World
route_patterns = [
Gateway(handler=home),
Gateway(handler=another),
Gateway(handler=World),
WebSocketGateway(handler=world_socket),
]
Если path
не указан, по умолчанию используется /
.
Include¶
Это специальный объект, который позволяет импортировать
любой маршрут из любого места в приложении.
Include
принимает импорт через namespace
или через список routes
, но не оба одновременно.
При использовании namespace
Include
будет искать список объектов по умолчанию route_patterns
в
импортированном пространстве имен, если не указано другое.
Note
Шаблон (route_patterns) работает только в том случае, если импорт выполнен через namespace
, а не через routes
.
from esmerald import Include
route_patterns = [Include(namespace="myapp.accounts.urls", pattern="my_urls")]
from myapp.accounts.urls import route_patterns
from esmerald import Include
route_patterns = [Include(routes=route_patterns)]
Если path
не указан, по умолчанию используется /
.
Using a different pattern¶
from esmerald import Gateway, WebSocketGateway
from .controllers import World, another, home, world_socket
my_urls = [
Gateway(handler=update_product),
Gateway(handler=home),
Gateway(handler=another),
Gateway(handler=World),
WebSocketGateway(handler=world_socket),
]
from esmerald import Include
route_patterns = [Include(namespace="myapp.accounts.urls", pattern="my_urls")]
Include и Esmerald¶
Include
может быть очень полезен, особенно когда цель — избежать множества импортов и огромного списка объектов,
которые нужно передать в один единственный объект. Это может быть особенно полезно для создания экземпляра Esmerald.
Пример:
from esmerald import Include
route_patterns = [Include(namespace="myapp.accounts.urls", pattern="my_urls")]
from esmerald import Esmerald, Include
app = Esmerald(routes=[Include(namespace="src.urls")])
Запуск приложения¶
Как уже упоминалось, мы рекомендуем использовать uvicorn в производственной среде, но это не обязательно.
Использование uvicorn:
uvicorn src:app --reload
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [28720]
INFO: Started server process [28722]
INFO: Waiting for application startup.
INFO: Application startup complete.
Запуск приложения с пользовательскими настройками¶
Использование uvicorn:
ESMERALD_SETTINGS_MODULE=myapp.AppSettings uvicorn src:app --reload
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [28720]
INFO: Started server process [28722]
INFO: Waiting for application startup.
INFO: Application startup complete.
$env:ESMERALD_SETTINGS_MODULE="myapp.AppSettings"; uvicorn src:app --reload
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [28720]
INFO: Started server process [28722]
INFO: Waiting for application startup.
INFO: Application startup complete.
Документация OpenAPI¶
Esmerald также имеет встроенную документацию OpenAPI.
Esmerald автоматически запускает документацию OpenAPI, внедряя настройки OpenAPIConfig по умолчанию и предоставляет вам элементы Swagger, ReDoc и Stoplight "из коробки".
Чтобы получить доступ к OpenAPI, просто запустите вашу локальную разработку и перейдите по адресу:
- Swagger -
/docs/swagger
. - Redoc -
/docs/redoc
. - Stoplight Elements -
/docs/elements
. - Rapidoc -
/docs/rapidoc
.
В этой документации есть более подробная информация о том, как настроить OpenAPIConfig здесь.
Также представлено хорошее объяснение о том, как использовать OpenAPIResponse.
Заметки¶
Это всего лишь очень общее демонстрационное описание того, как быстро начать и что может предложить Esmerald. Существует множество других возможностей, которые вы можете использовать с Esmerald. Наслаждайтесь! 😊
Спонсоры¶
В настоящее время у Esmerald нет спонсоров, но вы можете финансово помочь и поддержать автора через GitHub sponsors и стать Особенным или Легендой.