Metadata-Version: 2.4
Name: ferrum-orm
Version: 0.1.4
Classifier: Development Status :: 3 - Alpha
Classifier: Framework :: AsyncIO
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: Rust
Classifier: Topic :: Database
Classifier: Topic :: Software Development :: Libraries
Classifier: Typing :: Typed
Requires-Dist: pydantic>=2.0
Requires-Dist: ferrum-orm[pg] ; extra == 'all'
Requires-Dist: ferrum-orm[mysql] ; extra == 'all'
Requires-Dist: ferrum-orm[sqlite] ; extra == 'all'
Requires-Dist: ferrum-orm[mssql] ; extra == 'all'
Requires-Dist: ferrum-orm[msgpack] ; extra == 'all'
Requires-Dist: ferrum-orm[cli] ; extra == 'all'
Requires-Dist: ferrum-orm[dotenv] ; extra == 'all'
Requires-Dist: typer>=0.12 ; extra == 'cli'
Requires-Dist: rich>=13 ; extra == 'cli'
Requires-Dist: ferrum-orm[pg,cli,dotenv] ; extra == 'dev'
Requires-Dist: maturin>=1.7,<2 ; extra == 'dev'
Requires-Dist: pytest>=8 ; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23 ; extra == 'dev'
Requires-Dist: hypothesis>=6.100 ; extra == 'dev'
Requires-Dist: pytest-benchmark>=4 ; extra == 'dev'
Requires-Dist: ruff>=0.4 ; extra == 'dev'
Requires-Dist: ty>=0.0.49 ; extra == 'dev'
Requires-Dist: import-linter>=2 ; extra == 'dev'
Requires-Dist: testcontainers[postgres]>=4 ; extra == 'dev'
Requires-Dist: pip-audit>=2 ; extra == 'dev'
Requires-Dist: fastapi>=0.110 ; extra == 'dev'
Requires-Dist: python-dotenv>=1.0 ; extra == 'dotenv'
Requires-Dist: msgpack>=1.0 ; extra == 'msgpack'
Requires-Dist: aioodbc>=0.5 ; extra == 'mssql'
Requires-Dist: asyncmy>=0.2 ; extra == 'mysql'
Requires-Dist: opentelemetry-api>=1.20 ; extra == 'otel'
Requires-Dist: asyncpg>=0.29 ; extra == 'pg'
Requires-Dist: aiosqlite>=0.19 ; extra == 'sqlite'
Requires-Dist: uuid6>=2024.1 ; extra == 'uuid7'
Provides-Extra: all
Provides-Extra: cli
Provides-Extra: dev
Provides-Extra: dotenv
Provides-Extra: fastapi
Provides-Extra: msgpack
Provides-Extra: mssql
Provides-Extra: mysql
Provides-Extra: otel
Provides-Extra: pg
Provides-Extra: sqlite
Provides-Extra: uuid7
License-File: LICENSE
Summary: Next-generation async ORM for Python with a Rust-powered core
Keywords: orm,async,postgresql,mysql,sqlite,mssql,pydantic,rust,asyncpg,query-builder
Author: Ferrum Contributors
License: Apache-2.0
Requires-Python: >=3.11
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
Project-URL: Changelog, https://github.com/ferrum-orm/ferrum/blob/main/CHANGELOG.md
Project-URL: Documentation, https://github.com/ferrum-orm/ferrum/tree/main/docs
Project-URL: Homepage, https://github.com/ferrum-orm/ferrum
Project-URL: Issues, https://github.com/ferrum-orm/ferrum/issues
Project-URL: Repository, https://github.com/ferrum-orm/ferrum

# Ferrum

> A next-generation async ORM for Python.
> Rust-powered engine. Pydantic-native models. Django-inspired developer experience.

Ferrum is an async-first ORM designed for modern Python applications.

Built around a Rust-powered core and a Python-native API, Ferrum combines the ergonomics of Django's ORM, the type safety of Pydantic, and the performance of Rust.

## Why Ferrum?

Existing Python ORMs often force developers to choose between:

- Developer experience
- Async support
- Type safety
- Performance

Ferrum aims to provide all four.

### Goals

- Native async from day one
- Pydantic-first models
- Django-inspired ORM experience
- Rust-powered query engine
- PostgreSQL-first architecture (MySQL, SQLite, and SQL Server via optional extras)
- Type-safe query construction
- Automatic migrations
- High-performance result hydration
- Production-ready observability

---

## Quick Example

```python
from ferrum import Model


class User(Model):
    id: int
    email: str
    is_active: bool = True


user = await User.objects.create(
    conn,
    email="john@example.com",
)

users = await (
    User.objects
    .filter(is_active=True)
    .order_by("-id")
    .limit(10)
    .all(conn)
)

async with conn.transaction() as tx:
    user = await User.objects.create(tx, email="jane@example.com")
    await AuditLog.objects.create(tx, user_id=user.id, action="signup")
```

---

## Features

### Async First

No synchronous compatibility layer.

Ferrum is designed around modern async Python applications.

```python
users = await User.objects.all(conn)
```

### Pydantic Native

Models are built directly on top of Pydantic.

```python
class User(Model):
    id: int
    email: str
```

No duplicate schema definitions.

### Django-Inspired API

Familiar query interface.

```python
users = await (
    User.objects
    .filter(email__contains="@gmail.com")
    .order_by("-created_at")
    .all(conn)
)
```

### Rust-Powered Core

Performance-critical components are implemented in Rust:

- Query compilation
- SQL generation
- Result decoding
- Schema analysis
- Migration planning

This allows Ferrum to maintain a Pythonic API without sacrificing performance.

### Cross-Driver Full-Text Search

Native full-text search across PostgreSQL, MySQL, SQLite FTS5, and SQL Server — one
QuerySet API, dialect-specific SQL emit and migration DDL.

**Query modes** (filter lookups and ranking):

| Mode        | Lookup operator   | Typical use                          |
| ----------- | ----------------- | ------------------------------------ |
| `plain`     | `__match`         | Natural-language terms               |
| `phrase`    | `__match_phrase`  | Exact phrase                         |
| `websearch` | `__match_websearch` | Web-style quotes, `-` negation   |
| `boolean`   | `__match_boolean` | Boolean operators (`&`, `\|`, `!`) |

**Convenience methods:**

```python
# Filter + relevance ranking in one call
hits = await Article.objects.search(
    "python async orm", field="body", mode="websearch"
).limit(10).all(conn)

# Rank without an implicit filter
ranked = await Article.objects.rank_by("body", "rust", mode="plain").all(conn)
```

**Index declaration** — PostgreSQL uses `TSVector` columns; other drivers index base
`text` columns via `Meta.full_text_indexes`:

```python
from ferrum.models import Field, FullTextIndex

class Article(Model):
    search_vector: Annotated[TSVector, Field(fts_config="english")] | None = None
    body: str = ""

    class Meta:
        full_text_indexes = [FullTextIndex(fields=("body",), config="english")]
```

Query strings are always bound parameters; `fts_config` and index names come from
model-metadata allowlists only. See [Getting Started → Vector and full-text columns](docs/getting-started.md)
and [API Reference](docs/api-reference.md) for per-dialect DDL and operator mapping.

## Architecture

```text
┌──────────────────────────┐
│      Python API          │
│  Models / QuerySets      │
└────────────┬─────────────┘
             │
             ▼
┌──────────────────────────┐
│      Ferrum Core         │
│      (Rust Engine)       │
├──────────────────────────┤
│ Query Compiler           │
│ SQL AST                  │
│ Result Decoder           │
│ Migration Planner        │
└────────────┬─────────────┘
             │
             ▼
┌──────────────────────────┐
│       PostgreSQL         │
└──────────────────────────┘
```

## Roadmap

### v0.1 (complete)

- [x] PostgreSQL support
- [x] Basic CRUD operations
- [x] Async query execution
- [x] Pydantic models
- [x] Query builder
- [x] Type-safe filters
- [x] Transactions and savepoints
- [x] Bulk operations (`bulk_create`, `bulk_update`, `bulk_delete`)
- [x] Migrations (schema diff, apply, revert, CLI)
- [x] Relationships (ForeignKey, OneToOne, ManyToMany)
- [x] pgvector KNN search and HNSW/IVFFLAT index DDL
- [x] Full-text search (cross-dialect: PostgreSQL, MySQL, SQLite FTS5, SQL Server)
- [x] Observability hooks (Tier A/B/C)
- [x] CLI (`makemigrations`, `migrate`, `revert`, `showmigrations`, `inspectdb`, `resetdb`)

### v0.2 (in progress)

- [x] Upsert API (`upsert`, `bulk_upsert` with conflict targets and `RETURNING`)
- [x] Composite primary keys
- [x] Array field types (`uuid[]`, `text[]`, scalar arrays)
- [x] JSONB operators (`__contains`, `__has_key`)
- [x] RLS / tenant session helpers (`set_config`, `tenant_session`)
- [x] `call_function` for allowlisted stored-procedure calls
- [x] Migration ops for extensions, RLS policies, and function DDL
- [x] pgvector similarity score projection (`vector_search` helper)
- [ ] Query optimization (deferred fields, prefetch tuning)
- [ ] Advanced relationship loading

### v1.0

- [ ] Production-ready stability
- [ ] Performance benchmarking suite
- [ ] Full documentation site

## Project Status

Ferrum is currently in active development.

The API is not yet stable and breaking changes should be expected until the first public release.

## Installation

```bash
# PostgreSQL (most common)
pip install 'ferrum-orm[pg]'

# PostgreSQL + migrations CLI
pip install 'ferrum-orm[pg,cli]'

# MySQL
pip install 'ferrum-orm[mysql]'

# SQLite + migrations CLI (testing / local dev)
pip install 'ferrum-orm[sqlite,cli]'

# SQL Server (also needs a system ODBC driver, e.g. msodbcsql18)
pip install 'ferrum-orm[mssql]'

# Optional MessagePack wire format for the Python<->Rust boundary
pip install 'ferrum-orm[msgpack]'

# Everything (all drivers + CLI + dotenv)
pip install 'ferrum-orm[all]'

# Core ORM only (no database driver — install a driver extra before connecting)
pip install ferrum-orm
```

Bare `ferrum-orm` installs Pydantic and the Rust core only. Choose a driver extra
(`pg`, `mysql`, `sqlite`, or `mssql`) before calling `ferrum.connect()`.

MySQL, SQLite, and SQL Server are **thin-parity** backends: they support core CRUD
and migrations but not transactions, upsert, `bulk_update`, RLS, or pgvector
(PostgreSQL only). SQL Server connects via `aioodbc`/`pyodbc` and requires a system
ODBC driver such as `msodbcsql18`; DSNs use the `mssql://` or `sqlserver://` scheme.

### Wire format (advanced)

The Python↔Rust IR/hydration boundary defaults to JSON. Installing the `msgpack`
extra lets you switch it to MessagePack, selected via the `FERRUM_WIRE_FORMAT`
environment variable (`json` | `msgpack`) or the `[ferrum] wire_format` key in
`ferrum.toml` / `pyproject.toml`. JSON remains the default; MessagePack is opt-in.

From source, build the native extension with `maturin develop` (or `mise run dev`).

## Examples

Runnable samples live under [`examples/`](examples/):

- [`examples/simple/`](examples/simple/) — async CRUD script (no web framework)
- [`examples/migrations/`](examples/migrations/) — CLI, plan generation, apply, and forward fix-ups
- [`examples/fastapi_quickstart/`](examples/fastapi_quickstart/) — FastAPI integration

## Contributing

Contributions are welcome. Start with [`CONTRIBUTING.md`](CONTRIBUTING.md) for local setup,
scoped verification, architecture rules, and pull request expectations.

## License

Apache License 2.0

