Metadata-Version: 2.4
Name: arvel
Version: 0.53.0
Summary: A Laravel-grade web framework for Python — async-first, type-safe, modular.
Author: arvel
License-Expression: MIT
License-File: LICENSE
Keywords: asgi,async,framework,laravel,web
Requires-Python: >=3.14
Requires-Dist: anyio>=4.14.1
Requires-Dist: cashews>=7.5.0
Requires-Dist: cryptography>=49.0.0
Requires-Dist: fsspec>=2026.6.0
Requires-Dist: httpx>=0.28.1
Requires-Dist: inflection>=0.5.1
Requires-Dist: itsdangerous>=2.2.0
Requires-Dist: msgspec>=0.21.1
Requires-Dist: pwdlib[argon2,bcrypt]>=0.3.0
Requires-Dist: python-dotenv>=1.2.2
Requires-Dist: python-slugify>=8.0.4
Requires-Dist: python-ulid>=3.1.0
Requires-Dist: structlog>=26.1.0
Requires-Dist: typer>=0.26.8
Requires-Dist: whenever>=0.10.0
Provides-Extra: 2fa
Requires-Dist: pyotp>=2.10.0; extra == '2fa'
Provides-Extra: all
Requires-Dist: adlfs>=2026.5.0; extra == 'all'
Requires-Dist: aiosmtplib>=5.1.2; extra == 'all'
Requires-Dist: aiosqlite>=0.22.1; extra == 'all'
Requires-Dist: alembic>=1.18.5; extra == 'all'
Requires-Dist: apprise>=1.11.0; extra == 'all'
Requires-Dist: asyncmy>=0.2.11; extra == 'all'
Requires-Dist: asyncpg>=0.31.0; extra == 'all'
Requires-Dist: authlib>=1.7.2; extra == 'all'
Requires-Dist: av>=17.1.0; extra == 'all'
Requires-Dist: babel>=2.18.0; extra == 'all'
Requires-Dist: gcsfs>=2026.6.0; extra == 'all'
Requires-Dist: granian[reload]>=2.7.7; extra == 'all'
Requires-Dist: httpx-oauth>=0.17.0; extra == 'all'
Requires-Dist: ipython>=9.15.0; extra == 'all'
Requires-Dist: jinja2>=3.1.6; extra == 'all'
Requires-Dist: litestar>=2.24.0; extra == 'all'
Requires-Dist: markdown-it-py>=4.2.0; extra == 'all'
Requires-Dist: meilisearch>=0.41.1; extra == 'all'
Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.42.1; extra == 'all'
Requires-Dist: opentelemetry-exporter-prometheus>=0.50b0; extra == 'all'
Requires-Dist: opentelemetry-sdk>=1.43.0; extra == 'all'
Requires-Dist: pgvector>=0.4.2; extra == 'all'
Requires-Dist: pillow>=12.2.0; extra == 'all'
Requires-Dist: prometheus-client>=0.20.0; extra == 'all'
Requires-Dist: pyjwt>=2.13.0; extra == 'all'
Requires-Dist: pyjwt[crypto]>=2.13.0; extra == 'all'
Requires-Dist: pyotp>=2.10.0; extra == 'all'
Requires-Dist: redis>=8.0.1; extra == 'all'
Requires-Dist: s3fs>=2026.6.0; extra == 'all'
Requires-Dist: sentry-sdk>=2.63.0; extra == 'all'
Requires-Dist: sqlalchemy[asyncio]>=2.0.51; extra == 'all'
Requires-Dist: supabase>=2.31.0; extra == 'all'
Requires-Dist: taskiq-aio-pika>=0.6.0; extra == 'all'
Requires-Dist: taskiq-redis>=1.2.3; extra == 'all'
Requires-Dist: taskiq>=0.12.4; extra == 'all'
Requires-Dist: uvicorn>=0.49.0; extra == 'all'
Provides-Extra: azure
Requires-Dist: adlfs>=2026.5.0; extra == 'azure'
Provides-Extra: console
Requires-Dist: ipython>=9.15.0; extra == 'console'
Provides-Extra: dev
Requires-Dist: bandit>=1.9.4; extra == 'dev'
Requires-Dist: coverage>=7.14.3; extra == 'dev'
Requires-Dist: faker>=40.23.0; extra == 'dev'
Requires-Dist: hypothesis>=6.155.7; extra == 'dev'
Requires-Dist: import-linter>=2.12; extra == 'dev'
Requires-Dist: ipython>=9.15.0; extra == 'dev'
Requires-Dist: moto>=5.2.2; extra == 'dev'
Requires-Dist: mypy>=2.1.0; extra == 'dev'
Requires-Dist: pip-audit>=2.10.1; extra == 'dev'
Requires-Dist: polyfactory>=3.3.0; extra == 'dev'
Requires-Dist: pre-commit>=4.6.0; extra == 'dev'
Requires-Dist: pyright>=1.1.411; extra == 'dev'
Requires-Dist: pytest-asyncio>=1.4.0; extra == 'dev'
Requires-Dist: pytest-xdist>=3.8.0; extra == 'dev'
Requires-Dist: pytest>=9.1.1; extra == 'dev'
Requires-Dist: ruff>=0.15.20; extra == 'dev'
Provides-Extra: docs
Requires-Dist: zensical>=0.0.46; extra == 'docs'
Provides-Extra: gcs
Requires-Dist: gcsfs>=2026.6.0; extra == 'gcs'
Provides-Extra: http
Requires-Dist: litestar>=2.24.0; extra == 'http'
Provides-Extra: i18n
Requires-Dist: babel>=2.18.0; extra == 'i18n'
Provides-Extra: image
Requires-Dist: pillow>=12.2.0; extra == 'image'
Provides-Extra: jwt
Requires-Dist: pyjwt>=2.13.0; extra == 'jwt'
Provides-Extra: mail
Requires-Dist: aiosmtplib>=5.1.2; extra == 'mail'
Requires-Dist: markdown-it-py>=4.2.0; extra == 'mail'
Provides-Extra: media
Requires-Dist: pillow>=12.2.0; extra == 'media'
Provides-Extra: mysql
Requires-Dist: alembic>=1.18.5; extra == 'mysql'
Requires-Dist: asyncmy>=0.2.11; extra == 'mysql'
Requires-Dist: sqlalchemy[asyncio]>=2.0.51; extra == 'mysql'
Provides-Extra: notifications
Requires-Dist: apprise>=1.11.0; extra == 'notifications'
Provides-Extra: oauth
Requires-Dist: authlib>=1.7.2; extra == 'oauth'
Requires-Dist: httpx-oauth>=0.17.0; extra == 'oauth'
Provides-Extra: oidc
Requires-Dist: pyjwt[crypto]>=2.13.0; extra == 'oidc'
Provides-Extra: postgres
Requires-Dist: alembic>=1.18.5; extra == 'postgres'
Requires-Dist: asyncpg>=0.31.0; extra == 'postgres'
Requires-Dist: sqlalchemy[asyncio]>=2.0.51; extra == 'postgres'
Provides-Extra: queue
Requires-Dist: taskiq>=0.12.4; extra == 'queue'
Provides-Extra: queue-amqp
Requires-Dist: taskiq-aio-pika>=0.6.0; extra == 'queue-amqp'
Requires-Dist: taskiq>=0.12.4; extra == 'queue-amqp'
Provides-Extra: queue-redis
Requires-Dist: taskiq-redis>=1.2.3; extra == 'queue-redis'
Requires-Dist: taskiq>=0.12.4; extra == 'queue-redis'
Provides-Extra: redis
Requires-Dist: redis>=8.0.1; extra == 'redis'
Provides-Extra: s3
Requires-Dist: s3fs>=2026.6.0; extra == 's3'
Provides-Extra: search
Requires-Dist: meilisearch>=0.41.1; extra == 'search'
Provides-Extra: server
Requires-Dist: granian[reload]>=2.7.7; extra == 'server'
Requires-Dist: uvicorn>=0.49.0; extra == 'server'
Provides-Extra: sqlite
Requires-Dist: aiosqlite>=0.22.1; extra == 'sqlite'
Requires-Dist: alembic>=1.18.5; extra == 'sqlite'
Requires-Dist: sqlalchemy[asyncio]>=2.0.51; extra == 'sqlite'
Provides-Extra: standard
Requires-Dist: aiosmtplib>=5.1.2; extra == 'standard'
Requires-Dist: alembic>=1.18.5; extra == 'standard'
Requires-Dist: asyncpg>=0.31.0; extra == 'standard'
Requires-Dist: granian[reload]>=2.7.7; extra == 'standard'
Requires-Dist: ipython>=9.15.0; extra == 'standard'
Requires-Dist: jinja2>=3.1.6; extra == 'standard'
Requires-Dist: litestar>=2.24.0; extra == 'standard'
Requires-Dist: markdown-it-py>=4.2.0; extra == 'standard'
Requires-Dist: pillow>=12.2.0; extra == 'standard'
Requires-Dist: pyjwt>=2.13.0; extra == 'standard'
Requires-Dist: redis>=8.0.1; extra == 'standard'
Requires-Dist: sqlalchemy[asyncio]>=2.0.51; extra == 'standard'
Requires-Dist: taskiq-redis>=1.2.3; extra == 'standard'
Requires-Dist: taskiq>=0.12.4; extra == 'standard'
Requires-Dist: uvicorn>=0.49.0; extra == 'standard'
Provides-Extra: supabase
Requires-Dist: supabase>=2.31.0; extra == 'supabase'
Provides-Extra: telemetry
Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.42.1; extra == 'telemetry'
Requires-Dist: opentelemetry-exporter-prometheus>=0.50b0; extra == 'telemetry'
Requires-Dist: opentelemetry-sdk>=1.43.0; extra == 'telemetry'
Requires-Dist: prometheus-client>=0.20.0; extra == 'telemetry'
Requires-Dist: sentry-sdk>=2.63.0; extra == 'telemetry'
Provides-Extra: test-integration
Requires-Dist: testcontainers>=4.14.2; extra == 'test-integration'
Provides-Extra: vector
Requires-Dist: pgvector>=0.4.2; extra == 'vector'
Provides-Extra: video
Requires-Dist: av>=17.1.0; extra == 'video'
Provides-Extra: view
Requires-Dist: jinja2>=3.1.6; extra == 'view'
Description-Content-Type: text/markdown

<div align="center">

<picture>
  <source media="(prefers-color-scheme: dark)" srcset="docs/assets/arvel-mark-dark.svg">
  <img src="docs/assets/arvel-mark.svg" alt="arvel" width="96" height="96">
</picture>

# arvel

**The batteries-included, async-first web framework for Python** — type-safe and modular.

Expressive facades, an Active-Record ORM, a real queue, auth, mail, caching, views and a
powerful CLI — one coherent, fully-typed, `async`/`await`-native toolkit. You get a productive,
high-level developer experience without giving up Python's type system or performance.

</div>

---

## Install

```bash
uv add arvel                 # light core — no heavy dependencies
uv add 'arvel[standard]'     # the common set: http, db, queue, cache, view, mail, image
uv add 'arvel[all]'          # everything
```

Requires **Python 3.14+**. Capabilities are opt-in [extras](#extras) — you install only what you use,
and `import arvel` stays light because engines are imported lazily.

## Quickstart

```bash
arvel new blog            # scaffold a new app
cd blog && uv sync
source .venv/bin/activate # activate the virtualenv
arvel serve --reload      # http://127.0.0.1:8000
```

`arvel new` gives you a runnable project: an ASGI entrypoint, a `bootstrap/app.py` factory, web/api
route groups, config files, a `User` model with migrations and a factory, and a test suite.

## A taste

**Routing** — define routes with the `Route` facade; web (stateful) and api (JSON) groups:

```python
from arvel import Route, Schema

async def show(request):
    return {"hello": request.path_param("name")}

Route.get("/hello/{name}", show, name="hello")

class CreatePost(Schema):      # typed request/response → automatic OpenAPI at /schema
    title: str

async def store(request, data: CreatePost) -> CreatePost:
    return data

Route.post("/posts", store, name="posts.store")
```

**ORM** — an Active-Record model on SQLAlchemy Core:

```python
from arvel import Model

class Post(Model):
    __fillable__ = ["title", "body"]
    __casts__    = {"published": "bool", "meta": "json"}

post = await Post.create(title="Hello", body="…")
posts = await Post.where("published", True).order_by("created_at", "desc").get()
await Post.with_("author").get()          # eager-load relations — no N+1
```

**Validation** — concise rules, returning only the validated data:

```python
from arvel import Validator

data = Validator(request_body, {
    "email": "required|email",
    "age":   "nullable|integer|min:18",
}).validate()
```

**Queues, authorization, mail, cache** — the facades you'd expect, all `async`:

```python
from arvel import Job, Gate, Mail, Cache

await SendWelcome.dispatch(user_id=42)            # background job
await SendWelcome.dispatch_after(600, user_id=42) # …or run it in 10 minutes
if await Gate.allows("update", post): ...         # authorization
await Mail.to(user).send(WelcomeMail())           # mailables
await Cache.remember("stats", 300, compute_stats) # cache-aside
```

More: notifications, events & listeners, task scheduling, file storage (local/S3/GCS/Azure),
localization, server-rendered views, and a rich `Str`/`Arr`/`Collection` helper set.

## Why arvel

- **Batteries included, async-first.** Routing, ORM, queue, cache, auth, mail, views, CLI — one
  coherent DX, built `async`/`await`-native from the ground up.
- **Type-safe.** Strict typing across the public API, so your editor and CI catch mistakes before
  runtime — no stub-chasing.
- **Lightweight & modular.** One package, opt-in extras, lazy imports. You pay only for what you use,
  and the CLI stays fast.
- **Convention over configuration.** Sensible defaults, expressive facades, and a familiar project
  layout — scaffold and ship without wiring boilerplate.

## The four gates

Engineering guarantees enforced in CI from the first commit:

- **G1 — boundaries.** `import-linter` keeps modules honest: kernel isolation, a layered DAG, and no
  heavy import at module load.
- **G2 — startup.** `import arvel` pulls **zero** heavy libraries; the CLI stays snappy.
- **G3 — types.** Strict `mypy` **and** `pyright` on every public API.
- **G4 — stack fidelity.** Each capability is built on its mandated engine (Litestar, SQLAlchemy
  Core, whenever, Typer, …) — verified by a per-module test.

## Extras

| Extra | Adds |
|-------|------|
| `http`, `server` | Litestar routing · the granian/uvicorn dev server |
| `sqlite`, `postgres`, `mysql` | SQLAlchemy + the matching async driver + Alembic |
| `queue`, `queue-redis`, `queue-amqp` | taskiq jobs · Redis broker · RabbitMQ/AMQP broker |
| `redis` | cashews caching (Redis backend) |
| `jwt`, `oauth`, `2fa` | JWT tokens · OAuth providers · TOTP two-factor (auth is core) |
| `mail`, `notifications` | SMTP mail · multi-channel notifications (Apprise) |
| `view` | Jinja2 templating |
| `s3`, `gcs`, `azure`, `supabase` | filesystem disks |
| `image`, `video`, `media` | media handling (Pillow / PyAV) |
| `search`, `vector` | Meilisearch · pgvector |
| `i18n`, `telemetry` | Babel localization · OpenTelemetry/Sentry |

`arvel[standard]` bundles the everyday set; `arvel[all]` installs everything.

## Development

```bash
uv venv && uv pip install -e '.[dev]'
./tools/validate.sh    # ruff · mypy · pyright · import-linter · bandit · pip-audit · pytest
```

## Inspired by

If you're coming from PHP, arvel will feel familiar: facades, the service container, providers,
migrations, factories, gates & policies, and a project layout are all here.
Arvel is built natively for async Python, embraces the type system end to end, and stands
on best-in-class Python engines (Litestar · SQLAlchemy Core · Alembic · whenever · Typer · taskiq ·
Babel · msgspec · cashews · fsspec · Jinja2).

## License

[MIT](LICENSE).
