Перейти к содержанию

Ravyn

Ravyn

🚀 Performance, type safety, and elegance. A next-generation async Python framework for APIs, microservices, and web applications. 🚀

Test Suite Package version Supported Python versions


Документация: https://ravyn.dev 📚

Исходный код: https://github.com/dymmond/ravyn


Ravyn — это современный, мощный, гибкий и высокопроизводительный веб-фреймворк, созданный для построения не только API, но и полноценных масштабируемых приложений — от самых малых до уровня крупных компаний.

Ravyn разрабатывался для Python 3.9+ и использует стандартные подсказки типов (type hints) Python, основан на широко известном Lilya и Pydantic/msgspec.

Success

Официально поддерживается только последняя выпущенная версия.

Мотивация

Существуют отличные фреймворки такие, как FastAPI, Flama, Flask, Django и другие, решающие большинство повседневных задач для 99% приложений, но оставляющие тот 1%, который обычно связан со структурой и бизнес-логикой, без особых решений.

Ravyn черпает вдохновение в этих фреймворках и обладает всеми их известными возможностями, но также учитывает потребности бизнеса. Например, Starlite вдохновил на создание трансформеров и моделей Signature, что помогло интеграции с Pydantic. FastAPI вдохновил дизайн API, Django — систему разрешений, Flask — простоту, NestJS — контроллеры и многое другое.

Для качественной работы всегда требуется как драйвер, так и источник вдохновения.

Требования

  • Python 3.9+

Ravyn не был бы возможен без следующих двух компонентов:

Установка

$ pip install ravyn

Для работы в продакшене также потребуется ASGI сервер, рекомендуем Palfrey, но выбор остается за вами.

$ pip install palfrey

Поддержка встроенного планировщика::

$ pip install ravyn[schedulers]

Поддержка JWT, используемого внутри Ravyn::

$ pip install ravyn[jwt]

Для использования клиента тестирования Ravyn::

$ pip install ravyn[test]

Для использования оболочки Ravyn::

Подробнее здесь по теме в документации.

$ pip install ravyn[ipython] # default shell
$ pip install ravyn[ptpython] # ptpython shell

Начало проекта с использованием директив

Warning

Директивы рассчитаны на опытных пользователей, которые уже знакомы с Ravyn (или Python в целом), или если использование директив не вызывает затруднений. Если пока не чувствуете уверенности, продолжайте изучать документацию и знакомиться с Ravyn.

Чтобы начать Ravyn проект с простой предложенной структурой, выполните:

ravyn createproject <YOUR-PROJECT-NAME> --simple

Это создаст каркас проекта с некоторыми предопределенными файлами для простого запуска приложения Ravyn.

Также будет создан файл для тестов с использованием RavynTestClient, так что выполните:

$ pip install ravyn[test]

Эту часть можно пропустить, если не хотите использовать RavynTestClient.

Подробная информация об этой директиве и примерах ее использования.

Warning

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

Основные функции

  • Быстрый и эффективный: Благодаря Lilya и Pydantic/msgpec.
  • Быстрое развитие: Простота дизайна значительно сокращает время разработки.
  • Интуитивно понятный: Если знакомы с другими фреймворками, работать с Ravyn не составит труда.
  • Простота: Создан с учетом удобства и легкости в изучении.
  • Компактный: Благодаря встроенной поддержке ООП нет необходимости дублировать код. Поддержка SOLID.
  • Готовый к работе: Приложение запускается с готовым к продакшену кодом.
  • ООП и функциональный стиль: Проектируйте API любым удобным способом, поддержка ООП и функционального стиля.
  • Асинхронный и синхронный: Поддерживает как синхронный, так и асинхронный режимы.
  • Middleware: Применяйте middleware на уровне приложения или API.
  • Обработчики исключений: Применяйте обработчики на любом уровне.
  • Permissions: Применяйте правила и permissions для каждого API.
  • Interceptors: Перехватывайте запросы и добавляйте логику перед обработкой.
  • Плагины: Создавайте плагины для Ravyn и интегрируйте их в любое приложение, или опубликуйте свой пакет.
  • DAO и AsyncDAO: Избегайте вызовов базы данных напрямую из API, используйте бизнес-объекты.
  • Поддержка ORM: Поддержка [Edgy][_orm].
  • Поддержка ODM: Поддержка Mongoz.
  • Controller: Контроллеры в виде классов.
  • JSON сериализация/десериализация: Поддержка UJSON и ORJSON.
  • Lifespan: Поддержка lifespan Lilya.
  • Внедрение зависимостей: Как в любом хорошем фреймворке.
  • Планировщик: Поддержка задач в фоне.
  • Настройки: Поддержка системы настроек для чистоты кода.
  • msgspec — поддержка msgspec.

Отношение к Lilya и другим фреймворкам

Ravyn использует Lilya. Это решение обусловлено высокой производительностью и отсутствием проблем с маршрутизацией.

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

Быстрый старт

Пример как быстро начать работу с Ravyn. Для быстрого старта используйте palfrey.

#!/usr/bin/env python
import palfrey

from ravyn import Ravyn, Gateway, JSONResponse, Request, get


@get()
def welcome() -> JSONResponse:
    return JSONResponse({"message": "Welcome to Ravyn"})


@get()
def user(user: str) -> JSONResponse:
    return JSONResponse({"message": f"Welcome to Ravyn, {user}"})


@get()
def user_in_request(request: Request) -> JSONResponse:
    user = request.path_params["user"]
    return JSONResponse({"message": f"Welcome to Ravyn, {user}"})


app = Ravyn(
    routes=[
        Gateway("/ravyn", handler=welcome),
        Gateway("/ravyn/{user}", handler=user),
        Gateway("/ravyn/in-request/{user}", handler=user_in_request),
    ]
)

if __name__ == "__main__":
    palfrey.run(app, port=8000)

Затем вы можете получить доступ к endpoints.

Использование Ravyn в качестве декоратора

Чтобы быстро начать работу с Ravyn, вы также можете использовать его как декоратор. Вот как это сделать на примере с palfrey.

#!/usr/bin/env python
import palfrey

from ravyn import Ravyn, Gateway, JSONResponse, Request, get

app = Ravyn()


@app.get("/ravyn")
def welcome() -> JSONResponse:
    return JSONResponse({"message": "Welcome to Ravyn"})


@app.get("/ravyn/{user}")
def user(user: str) -> JSONResponse:
    return JSONResponse({"message": f"Welcome to Ravyn, {user}"})


@app.get("/ravyn/in-request/{user}")
def user_in_request(request: Request) -> JSONResponse:
    user = request.path_params["user"]
    return JSONResponse({"message": f"Welcome to Ravyn, {user}"})


if __name__ == "__main__":
    palfrey.run(app, port=8000)

Настройки

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

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

Пример классического подхода:

from example import ApplicationObjectExample

# ExampleObject — это экземпляр другого приложения,
# и он служит только в качестве примера

app = ApplicationObjectExample(setting_one=..., setting_two=..., setting_three=...)

Вдохновленный замечательным Django и используя pydantic, Ravyn предоставляет объект по умолчанию, готовый к использованию сразу «из коробки».

Ravyn:

from ravyn import Ravyn

app = Ravyn()

И это все! Все настройки по умолчанию загружаются автоматически! Почему? Потому что приложение ищет переменную окружения RAVYN_SETTINGS_MODULE для запуска, и если она не найдена, используются глобальные настройки приложения. Это просто, но можно ли переопределить их внутри объекта? Да, конечно.

from ravyn import Ravyn

app = Ravyn(app_name='My App', title='My title')

То же самое, что и классический подход.

Давайте поговорим о модуле настроек Ravyn.

Модуль настроек Ravyn

При запуске приложения система ищет переменную окружения RAVYN_SETTINGS_MODULE. Если переменная не указана, система по умолчанию использует настройки RavynSettings и запускается.

Пользовательские настройки

В наше время важно разделять настройки по окружениям и стандартных настроек Ravyn будет недостаточно для любого приложения.

Настройки соответствуют стандарту pydantic и, следовательно, совместимы с Ravyn. Система предоставляет несколько значений по умолчанию, которые можно использовать сразу, хотя это необязательно. Окружение по умолчанию — production.

from ravyn import RavynSettings
from ravyn.conf.enums import EnvironmentType


class Development(RavynSettings):
    app_name: str = 'My app in dev'
    environment: str = EnvironmentType.DEVELOPMENT

Загрузка настроек в ваше приложение Ravyn:

Предположим, ваше приложение Ravyn находится в файле src/app.py.

RAVYN_SETTINGS_MODULE='myapp.settings.Development' python -m src.app.py
$env:RAVYN_SETTINGS_MODULE="myapp.settings.Development"; python -m src.app.py

Gateway, WebSocketGateway и Include

Lilya предлагает классы Path для простых назначений путей, но это также очень ограничивает, если у вас есть что-то более сложное. Ravyn расширяет эту функциональность и добавляет немного 'стиля', улучшая её с помощью Gateway, WebSocketGateway и Include.

Эти специальные объекты позволяют происходить всей магии Ravyn.

Для классического, прямого подхода в одном файле:

src/app.py
from ravyn import Ravyn, 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 = Ravyn(routes=[
    Gateway(handler=home),
    Gateway(handler=another),
    WebSocketGateway(handler=world_socket),
])

Дизайн маршрутов

Хороший дизайн всегда приветствуется и Ravyn позволяет создавать сложные маршруты на любом уровне.

Обработчики (контроллеры)

src/myapp/accounts/controllers.py
from pydantic import BaseModel

from ravyn import (
    Controller,
    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(Controller):
    @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)

myapp/accounts/urls.py
from ravyn 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.

src/urls.py
from ravyn import Include

route_patterns = [Include(namespace="myapp.accounts.urls", pattern="my_urls")]
src/myapp/urls.py
from myapp.accounts.urls import route_patterns

from ravyn import Include

route_patterns = [Include(routes=route_patterns)]

Если path не указан, по умолчанию используется /.

Using a different pattern

src/myapp/accounts/urls.py
from ravyn 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),
]
src/myapp/urls.py
from ravyn import Include

route_patterns = [Include(namespace="myapp.accounts.urls", pattern="my_urls")]

Include и Ravyn

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

Пример:

src/urls.py
from ravyn import Include

route_patterns = [Include(namespace="myapp.accounts.urls", pattern="my_urls")]
src/app.py
from ravyn import Ravyn, Include

app = Ravyn(routes=[Include(namespace="src.urls")])

Запуск приложения

Как уже упоминалось, мы рекомендуем использовать palfrey в производственной среде, но это не обязательно.

Использование palfrey:

palfrey src:app --reload

INFO:     Palfrey 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.

Запуск приложения с пользовательскими настройками

Использование palfrey:

RAVYN_SETTINGS_MODULE=myapp.AppSettings palfrey src:app --reload

INFO:     Palfrey 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:RAVYN_SETTINGS_MODULE="myapp.AppSettings"; palfrey src:app --reload

INFO:     Palfrey 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

Ravyn также имеет встроенную документацию OpenAPI.

Ravyn автоматически запускает документацию OpenAPI, внедряя настройки OpenAPIConfig по умолчанию и предоставляет вам элементы Swagger, ReDoc и Stoplight "из коробки".

Чтобы получить доступ к OpenAPI, просто запустите вашу локальную разработку и перейдите по адресу:

  • Swagger - /docs/swagger.
  • Redoc - /docs/redoc.
  • Stoplight Elements - /docs/elements.
  • Rapidoc - /docs/rapidoc.

В этой документации есть более подробная информация о том, как настроить OpenAPIConfig здесь.

Также представлено хорошее объяснение о том, как использовать OpenAPIResponse.

Заметки

Это всего лишь очень общее демонстрационное описание того, как быстро начать и что может предложить Ravyn. Существует множество других возможностей, которые вы можете использовать с Ravyn. Наслаждайтесь! 😊

Спонсоры

В настоящее время у Ravyn нет спонсоров, но вы можете финансово помочь и поддержать автора через GitHub sponsors и стать Особенным или Легендой.