Skip to main content

Pico-ioc integration for SQLAlchemy. Adds Spring-style transactional support, configuration, and helpers.

Project description

๐Ÿ“ฆ pico-sqlalchemy

PyPI Ask DeepWiki License: MIT CI (tox matrix) codecov Quality Gate Status Duplicated Lines (%) Maintainability Rating Docs

Pico-SQLAlchemy

Pico-SQLAlchemy integrates Pico-IoC with SQLAlchemy, providing a true inversion of control persistence layer with Spring Data-style declarative features.

It brings constructor-based dependency injection, implicit transaction management, and powerful declarative queries using pure Python and SQLAlchemyโ€™s Async ORM.

๐Ÿ Requires Python 3.11+ ๐Ÿš€ Async-Native: Built entirely on AsyncSession and create_async_engine. โœจ Zero-Boilerplate: Repositories are transactional by default. ๐Ÿ” Declarative Queries: Define SQL or expressions in decorators; the library executes them for you.


๐ŸŽฏ Why pico-sqlalchemy?

Most Python apps suffer from manual session handling (async with session...), scattered transaction logic, and verbose repository patterns.

Pico-SQLAlchemy solves this by offering:

Feature SQLAlchemy Default pico-sqlalchemy
Transactions Manual commit() / rollback() Implicit (Auto-managed)
Repositories DIY Classes @repository (Transactional by default)
Queries Manual implementation @query (Declarative execution)
Injection None / Global variables Constructor Injection (IoC)
Pagination Manual calculation Automatic (PageRequest / Page)

๐Ÿงฑ Core Features

  • Implicit Transactions: Methods inside @repository are automatically Read-Write transactional.
  • Declarative Queries: Use @query to run SQL or Expressions automatically (defaults to Read-Only).
  • AOP-Based Propagation: REQUIRED, REQUIRES_NEW, MANDATORY, NEVER, etc.
  • Session Lifecycle: Centralized SessionManager handles engine creation and cleanup.
  • Pagination: Built-in support for paged results via @query(paged=True).

๐Ÿ“ฆ Installation

pip install pico-sqlalchemy

You will also need an async database driver:

pip install aiosqlite   # for SQLite
pip install asyncpg     # for PostgreSQL

๐Ÿš€ Quick Example

1. Define Model

from sqlalchemy import Integer, String
from pico_sqlalchemy import AppBase, Mapped, mapped_column

class User(AppBase):
    __tablename__ = "users"
    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    username: Mapped[str] = mapped_column(String(50))

2. Define Repository (The "Magic" Part)

Notice we don't need @transactional here.

  • save: Automatically runs in a Read-Write transaction.
  • find_by_name: Automatically runs in a Read-Only transaction and executes the query logic.
from pico_sqlalchemy import repository, query, SessionManager, get_session

@repository(entity=User)
class UserRepository:
    def __init__(self, manager: SessionManager):
        self.manager = manager

    # IMPLICIT: Read-Write Transaction
    async def save(self, user: User) -> User:
        session = get_session(self.manager)
        session.add(user)
        return user

    # DECLARATIVE: Read-Only Transaction + Auto-Execution
    @query(expr="username = :username", unique=True)
    async def find_by_name(self, username: str) -> User | None:
        ... # Body is ignored; the library executes the query

3. Define Service

Use @transactional here to define business logic boundaries.

from pico_ioc import component
from pico_sqlalchemy import transactional

@component
class UserService:
    def __init__(self, repo: UserRepository):
        self.repo = repo

    @transactional
    async def create(self, name: str) -> User:
        # 1. Check existence (Read-Only tx from repo)
        existing = await self.repo.find_by_name(name)
        if existing:
            raise ValueError("User exists")
            
        # 2. Save new user (Joins current transaction)
        return await self.repo.save(User(username=name))

4. Run it

import asyncio
from pico_ioc import init, configuration, DictSource

config = configuration(DictSource({
    "database": {
        "url": "sqlite+aiosqlite:///:memory:",
        "echo": False
    }
}))

async def main():
    container = init(modules=["pico_sqlalchemy", "__main__"], config=config)
    service = await container.aget(UserService)
    
    user = await service.create("alice")
    print(f"Created: {user.id}")
    
    await container.cleanup_all_async()

if __name__ == "__main__":
    asyncio.run(main())

โšก Transaction Hierarchy & Rules

Pico-SQLAlchemy applies a "Best Effort" strategy to determine transaction configuration. The priority order (highest wins) is:

Priority Decorator Default Mode Use Case
1 (High) @transactional(...) Explicit Config Overriding defaults, Service layer logic.
2 @query(...) Read-Only Efficient data fetching.
3 (Base) @repository Read-Write Default for CRUD (saves, updates, deletes).

Example Scenarios

  1. Plain Method in Repository:

    async def update_user(self): ...
    

    ๐Ÿ‘‰ Result: Active Read-Write Transaction (Implicit from @repository).

  2. Query Method:

    @query("SELECT ...")
    async def get_data(self): ...
    

    ๐Ÿ‘‰ Result: Active Read-Only Transaction (Implicit from @query).

  3. Manual Override:

    @transactional(read_only=True)
    async def complex_report(self): ...
    

    ๐Ÿ‘‰ Result: Active Read-Only Transaction (Explicit override).


๐Ÿ” Declarative Queries in Depth

The @query decorator eliminates boilerplate for common fetches.

Expression Mode (expr)

Requires @repository(entity=Model). Injects the expression into a SELECT * FROM table WHERE ....

@query(expr="age > :min_age", unique=False)
async def find_adults(self, min_age: int) -> list[User]: ...

SQL Mode (sql)

Executes raw SQL. Useful for complex joins or specific DTOs.

@query(sql="SELECT count(*) as cnt FROM users")
async def count_users(self) -> int: ...

Automatic Pagination

Just add paged=True and a page: PageRequest parameter.

from pico_sqlalchemy import Page, PageRequest

@query(expr="active = true", paged=True)
async def find_active(self, page: PageRequest) -> Page[User]: ...

๐Ÿงช Testing

Testing is simple because you can override the configuration or the components easily using Pico-IoC.

@pytest.mark.asyncio
async def test_service():
    # Setup container with in-memory DB
    container = ... 
    
    service = await container.aget(UserService)
    user = await service.create("test")
    
    assert user.id is not None

๐Ÿ’ก Architecture Overview

                 โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                 โ”‚          Your App           โ”‚
                 โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                โ”‚
                        Constructor Injection
                                โ”‚
                 โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                 โ”‚          Pico-IoC            โ”‚
                 โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                โ”‚
                 โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                 โ”‚       pico-sqlalchemy        โ”‚
                 โ”‚ 1. Implicit Repo Transactionsโ”‚
                 โ”‚ 2. Declarative @query        โ”‚
                 โ”‚ 3. Explicit @transactional   โ”‚
                 โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                โ”‚
                           SQLAlchemy
                           (Async ORM)

AI Coding Skills

Install Claude Code or OpenAI Codex skills for AI-assisted development with pico-sqlalchemy:

curl -sL https://raw.githubusercontent.com/dperezcabrera/pico-skills/main/install.sh | bash -s -- sqlalchemy
Command Description
/add-repository Add SQLAlchemy entities and repositories with transactions
/add-component Add components, factories, interceptors, settings
/add-tests Generate tests for pico-framework components

All skills: curl -sL https://raw.githubusercontent.com/dperezcabrera/pico-skills/main/install.sh | bash

See pico-skills for details.


๐Ÿ“ License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pico_sqlalchemy-0.3.0.tar.gz (73.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pico_sqlalchemy-0.3.0-py3-none-any.whl (25.8 kB view details)

Uploaded Python 3

File details

Details for the file pico_sqlalchemy-0.3.0.tar.gz.

File metadata

  • Download URL: pico_sqlalchemy-0.3.0.tar.gz
  • Upload date:
  • Size: 73.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for pico_sqlalchemy-0.3.0.tar.gz
Algorithm Hash digest
SHA256 ebb9094fb67cbeaa9310c0ef8f80fec983e16485e4cc2dd747d1a41069b89999
MD5 f942bfe1091b5502ac1c95019c2911f4
BLAKE2b-256 ef1d36c19ad583385f97a4514c264bf1a264826bd68149cc5b18f5ab00e91eb3

See more details on using hashes here.

Provenance

The following attestation bundles were made for pico_sqlalchemy-0.3.0.tar.gz:

Publisher: publish-to-pypi.yml on dperezcabrera/pico-sqlalchemy

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pico_sqlalchemy-0.3.0-py3-none-any.whl.

File metadata

File hashes

Hashes for pico_sqlalchemy-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c7af88f264cdf8c33a597fb04711eb4cde569cd3698da58f93313b81982ba176
MD5 2dfb762fd592d0f117f1c912e3feec74
BLAKE2b-256 64888420fff6750189fa9100cc7697daaaae5d5be0dfa8a1706e14fa24413678

See more details on using hashes here.

Provenance

The following attestation bundles were made for pico_sqlalchemy-0.3.0-py3-none-any.whl:

Publisher: publish-to-pypi.yml on dperezcabrera/pico-sqlalchemy

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page