Metadata-Version: 2.4
Name: duo-orm
Version: 0.1.1
Summary: An opinionated, modern ORM for Python combining the power of SQLAlchemy 2.0 with a clean, symmetrical API for sync and async operations.
Author-email: Siddhanth Bhimakari <siddhanth.n.b@gmail.com>
Project-URL: Homepage, https://github.com/SiddhanthNB/duo-orm
Project-URL: Bug Tracker, https://github.com/SiddhanthNB/duo-orm/issues
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Topic :: Database
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: sqlalchemy>=2.0
Requires-Dist: alembic>=1.8
Requires-Dist: click>=8.0
Requires-Dist: toml>=0.10
Requires-Dist: aiosqlite>=0.17
Provides-Extra: all
Requires-Dist: psycopg[binary]>=3.1; extra == "all"
Requires-Dist: oracledb>=2.0; extra == "all"
Requires-Dist: pymysql>=1.1; extra == "all"
Requires-Dist: asyncmy>=0.2; extra == "all"
Requires-Dist: pyodbc>=5.0; extra == "all"
Requires-Dist: aioodbc>=0.5; extra == "all"
Requires-Dist: aiosqlite>=0.20; extra == "all"
Provides-Extra: postgresql
Requires-Dist: psycopg[binary]>=3.1; extra == "postgresql"
Provides-Extra: oracle
Requires-Dist: oracledb>=2.0; extra == "oracle"
Provides-Extra: mysql
Requires-Dist: pymysql>=1.1; extra == "mysql"
Requires-Dist: asyncmy>=0.2; extra == "mysql"
Provides-Extra: mssql
Requires-Dist: pyodbc>=5.0; extra == "mssql"
Requires-Dist: aioodbc>=0.5; extra == "mssql"
Provides-Extra: sqlite
Requires-Dist: aiosqlite>=0.20; extra == "sqlite"
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.20; extra == "dev"
Requires-Dist: pytest-cov>=4.1; extra == "dev"
Requires-Dist: ruff>=0.1; extra == "dev"
Requires-Dist: black>=23.0; extra == "dev"
Requires-Dist: mkdocs>=1.5; extra == "dev"
Requires-Dist: mkdocs-material>=9.0; extra == "dev"
Requires-Dist: mkdocstrings-python>=1.0; extra == "dev"
Dynamic: license-file

# DuoORM

[![PyPI version](https://img.shields.io/pypi/v/duo-orm.svg?cacheSeconds=300)](https://pypi.org/project/duo-orm/)
[![Python versions](https://img.shields.io/pypi/pyversions/duo-orm.svg?cacheSeconds=300)](https://pypi.org/project/duo-orm/)
[![Docs](https://img.shields.io/badge/docs-readthedocs-blue.svg)](https://duo-orm.readthedocs.io/)

An opinionated ORM with symmetrical sync/async APIs, explicit unit-of-work control, and ready-to-use Alembic scaffolding. DuoORM manages drivers for you—use driverless URLs like `postgresql://...` or `sqlite:///app.db` and it wires up the correct sync/async engines under the hood (SQLAlchemy 2.x).

## Highlights

- One API for sync *and* async (add `await` when needed).
- Explicit unit of work: single-statement by default; opt into `db.transaction()` for shared sessions and cascades.
- Driverless URLs; DuoORM injects the right sync/async driver per dialect.
- Import common SQLAlchemy types and helpers directly from `duo_orm` (e.g., `String`, `JSON`, `PG_ARRAY`, `text`, `func`).
- Built-in Alembic CLI scaffolding and migration commands.
- Tested across PostgreSQL, MySQL, MSSQL, Oracle, and SQLite (coverage matrix).

## Install

```bash
pip install duo-orm                  # core + sqlite drivers
pip install "duo-orm[postgresql]"    # psycopg (sync+async)
pip install "duo-orm[mysql]"         # pymysql + asyncmy
pip install "duo-orm[mssql]"         # pyodbc + aioodbc
pip install "duo-orm[oracle]"        # oracledb (sync+async)
pip install "duo-orm[all]"           # install everything
```

SQLite fallback (only if your Python lacks stdlib `sqlite3`, e.g., minimal Docker/Lambda):

```bash
pip install pysqlite3-binary
python - <<'PY'
import sys, pysqlite3
sys.modules["sqlite3"] = pysqlite3
PY
```

## Quickstart

```python
from duo_orm import Database, Mapped, mapped_column, String

db = Database("sqlite:///./app.db")  # driverless URL

class User(db.Model):
    __tablename__ = "users"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column(String(100))

# One-shot (single statement/session)
user = User.where(User.name == "Ada").first()

# Transactional work (shared session)
async def create_user():
    async with db.transaction():
        u = User(name="New")
        await u.save()

# When you're done (scripts/CLIs), optionally tear down engines
db.disconnect()
```

### Engine lifecycle helpers

- `db.connect()` eagerly initializes sync/async engines so misconfiguration surfaces early (optional; engines still initialize lazily).
- `db.disconnect()` disposes any initialized engines and clears cached factories; use it at the end of scripts/CLIs to release pools explicitly. Context managers (`db.transaction()`, `standalone_session()`, `sync_standalone_session()`) already close sessions on exit.

## Documentation

- Full docs & guides: https://duo-orm.readthedocs.io/
- Quickstart: https://duo-orm.readthedocs.io/en/latest/quickstart/
- API reference: https://duo-orm.readthedocs.io/en/latest/reference/

## License

MIT
