Metadata-Version: 2.4
Name: pytisan-framework
Version: 0.6.0
Summary: Async-first web framework for Python — elegant, productive, fast.
Project-URL: Homepage, https://github.com/pytisan/framework
Project-URL: Repository, https://github.com/pytisan/framework
Project-URL: Documentation, https://github.com/pytisan/framework#readme
Project-URL: Issues, https://github.com/pytisan/framework/issues
Author: Pytisan Contributors
License: MIT
Keywords: api,asgi,async,framework,web
Classifier: Development Status :: 2 - Pre-Alpha
Classifier: Framework :: AsyncIO
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Typing :: Typed
Requires-Python: >=3.12
Requires-Dist: aiosqlite>=0.20
Requires-Dist: bcrypt>=4.0
Requires-Dist: rich>=13.0
Provides-Extra: dev
Requires-Dist: httpx>=0.27; extra == 'dev'
Requires-Dist: pyright>=1.1; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Requires-Dist: uvicorn>=0.30; extra == 'dev'
Description-Content-Type: text/markdown

# Pytisan

Async-first web framework for Python — elegant, productive, and fast.

## Create an application (no install)

Like `django-admin startproject`, use **uv** and **uvx** (never `pip`):

```bash
uvx pytisan new my-blog
uvx pytisan new              # interactive prompt
uvx pytisan                    # same as `pytisan new`
```

This scaffolds a full app, runs `uv sync --group dev`, then:

```bash
cd my-blog
uv run anvil serve
```

## Framework development

```bash
cd framework
uv sync --group dev
uv run python -m pytest
```

```python
from pytisan import Application, Database, DatabaseConfig, Route, json

app = Application()


@Route.get("/users")
async def users():
    rows = await Database.table("users").where("active", True).order_by("name").get()
    return json(rows)


async def boot():
    await Database.connect(DatabaseConfig.sqlite("app.db"))
    await Database.execute(
        "CREATE TABLE IF NOT EXISTS users ("
        "id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, active INTEGER DEFAULT 1)"
    )


asgi = app.asgi()
```

### Database (SQLite)

Uses **aiosqlite** (async driver over SQLite / `sqlite3`):

```python
from pytisan import Database, DatabaseConfig

await Database.connect(DatabaseConfig.sqlite(":memory:"))  # or "storage/app.db"

user_id = await Database.table("users").insert({"name": "Ana", "email": "a@b.com", "active": 1})
user = await Database.table("users").find(user_id)
rows = await Database.table("users").where_like("name", "%An%").get()
```

### Active Record (Model)

```python
from pytisan import Database, DatabaseConfig, Model

class User(Model):
    __table__ = "users"

    id: int | None
    name: str
    email: str
    active: bool

    __casts__ = {"active": "bool"}
    __fillable__ = ("name", "email", "active")

await Database.connect(DatabaseConfig.sqlite("app.db"))

user = await User.create(name="Ana", email="ana@example.com", active=True)
users = await User.query().where(active=True).order_by("name").get()
user.name = "Ana Paula"
await user.save()
```

### Migrations

```python
from pytisan.database.migrations import Migration, schema
from pytisan.database.schema import Blueprint

class CreateUsersTable(Migration):
    async def up(self) -> None:
        await schema.create("users", self._define)

    async def down(self) -> None:
        await schema.drop("users")

    @staticmethod
    def _define(table: Blueprint) -> None:
        table.id()
        table.string("name").not_null()
        table.string("email").not_null().unique()
        table.timestamps()
```

```bash
anvil make:migration create_users_table
anvil migrate
anvil migrate:status
anvil migrate:rollback
anvil migrate:fresh
```

```bash
uv run uvicorn examples.hello.main:asgi --reload
```

## CLI

**Installer** (new projects): `uvx pytisan new [name]`

**Anvil** (inside a project):

```bash
uv run anvil --help

# Generators
anvil make:model User -m
anvil make:controller UserController
anvil make:request StoreUserRequest
anvil make:resource UserResource
anvil make:migration create_posts_table

# Migrations (reads .env — DB_CONNECTION, DB_DATABASE, …)
anvil migrate
anvil migrate:status
anvil migrate:rollback
anvil migrate:fresh
anvil migrate --database storage/other.sqlite  # optional override

# Routes
anvil route:list
anvil route:list --method GET

# Dev server
anvil serve

# Routes
anvil route:list
anvil route:list --method GET
```

## Layout

```txt
framework/
├── core/           # Application
├── web/            # Request, Response, ASGI
├── routing/        # Router, Route
├── database/       # SQLite async connection
├── query/          # Fluent query builder
├── tests/
├── examples/
└── pyproject.toml
```

## License

MIT
