Metadata-Version: 2.4
Name: genro-asgi
Version: 0.7.0
Summary: A minimal, stable ASGI foundation - framework-agnostic toolkit for building high-performance web services
Project-URL: Homepage, https://github.com/genropy/genro-asgi
Project-URL: Documentation, https://genro-asgi.readthedocs.io
Project-URL: Repository, https://github.com/genropy/genro-asgi
Project-URL: Bug Tracker, https://github.com/genropy/genro-asgi/issues
Author-email: "Softwell S.r.l." <info@softwell.it>
License: Apache-2.0
License-File: LICENSE
License-File: NOTICE
Keywords: asgi,async,framework-agnostic,http,server,web
Classifier: Development Status :: 3 - Alpha
Classifier: Framework :: AsyncIO
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.11
Requires-Dist: genro-bag>=0.18.10
Requires-Dist: genro-builders>=0.18.0
Requires-Dist: genro-routes>=0.24.0
Requires-Dist: genro-toolbox>=0.2.0
Requires-Dist: genro-tytx>=0.9.1
Requires-Dist: httpx>=0.27.0
Requires-Dist: uvicorn>=0.30.0
Provides-Extra: all
Requires-Dist: black>=23.0; extra == 'all'
Requires-Dist: mypy>=1.0; extra == 'all'
Requires-Dist: myst-parser>=4.0.0; extra == 'all'
Requires-Dist: orjson>=3.9.0; extra == 'all'
Requires-Dist: pyjwt>=2.0; extra == 'all'
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'all'
Requires-Dist: pytest-cov>=4.0; extra == 'all'
Requires-Dist: pytest>=7.0; extra == 'all'
Requires-Dist: ruff>=0.1.0; extra == 'all'
Requires-Dist: sphinx-autodoc-typehints>=3.0.0; extra == 'all'
Requires-Dist: sphinx-rtd-theme>=3.0.0; extra == 'all'
Requires-Dist: sphinx>=8.0.0; extra == 'all'
Provides-Extra: auth
Requires-Dist: pyjwt>=2.0; extra == 'auth'
Provides-Extra: dev
Requires-Dist: black>=23.0; extra == 'dev'
Requires-Dist: mypy>=1.0; extra == 'dev'
Requires-Dist: orjson>=3.9.0; extra == 'dev'
Requires-Dist: pyjwt>=2.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Requires-Dist: ruff>=0.1.0; extra == 'dev'
Provides-Extra: docs
Requires-Dist: myst-parser>=4.0.0; extra == 'docs'
Requires-Dist: sphinx-autodoc-typehints>=3.0.0; extra == 'docs'
Requires-Dist: sphinx-rtd-theme>=3.0.0; extra == 'docs'
Requires-Dist: sphinx>=8.0.0; extra == 'docs'
Provides-Extra: json
Requires-Dist: orjson>=3.9.0; extra == 'json'
Provides-Extra: test
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'test'
Requires-Dist: pytest-cov>=4.0; extra == 'test'
Requires-Dist: pytest>=7.0; extra == 'test'
Description-Content-Type: text/markdown

# Genro ASGI

**A minimal ASGI server and application toolkit** for building web services with routing, authentication, sessions, and middleware — configured by a Python `config.py` builder recipe.

[![PyPI version](https://img.shields.io/pypi/v/genro-asgi.svg)](https://pypi.org/project/genro-asgi/)
[![Python Support](https://img.shields.io/pypi/pyversions/genro-asgi.svg)](https://pypi.org/project/genro-asgi/)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
[![Documentation](https://readthedocs.org/projects/genro-asgi/badge/?version=latest)](https://genro-asgi.readthedocs.io)

## What it does

- **Serves web applications** mounted on URL prefixes (`/api/`, `/shop/`, etc.) with config-driven loading
- **Authenticates requests** via bearer tokens, basic auth, or JWT — O(1) credential lookup, lazy auth for zero overhead on unprotected routes
- **Manages sessions** with in-memory store, cookie-based reconnection, and user avatars (identity + tags + extensible Bag data)
- **Applies middleware** at two levels: global (CORS, errors, auth) and per-app (session, cache) with independent chains
- **Routes requests** via `genro-routes` with decorator-based routing and auth tag filtering
- **Serves static files** with `StaticRouter` and hierarchical resource loading with fallback chains
- **Handles WebSocket** with the WSX extension protocol for structured request/response messaging
- **Exposes MCP endpoints** via `McpApplication` for AI tool integration (Streamable HTTP transport)
- **Configures plugins at runtime** via the `plugin_config` web UI (mounted under `/_sys/plugin_config/`) with persistence to JSON

## Installation

```bash
pip install genro-asgi            # core
pip install genro-asgi[json]      # + orjson for fast JSON
pip install genro-asgi[dev]       # + test/lint tools
```

## Quick Start

The server boots from a `config.py` whose `ServerConfiguration` (a subclass of
`AsgiConfigBuilder`) is rendered onto it. The recipe builds the server tree with
the builder API; application classes are imported and passed as objects.

```python
from genro_asgi import AsgiServer

server = AsgiServer("config.py")  # the config.py directory is the server dir
server.run()                      # starts uvicorn on the configured host/port
```

**config.py**:

```python
from genro_asgi.config import AsgiConfigBuilder
from shop_app import Application as Shop          # your AsgiApplication subclass


class ServerConfiguration(AsgiConfigBuilder):
    def main(self, root):
        root.server(host="127.0.0.1", port=8000)
        root.middleware(cors=True, auth=True)     # one bool kwarg per middleware
        root.authMiddleware(
            bearer={"api_key": {"token": "sk_live_abc123", "tags": "api,read"}}
        )
        apps = root.applications(default="shop")   # "shop" is served on the empty mount
        apps.application(code="shop", app_class=Shop,
                         middleware={"session": True})
        root.openapi(title="Shop API", version="1.0.0")
```

Each app derives its URL mount from its `code` (or `""` when it is the
collection `default`), unless it declares an explicit `mount`. The server
demultiplexes on the first path segment; each app does its own routing.

## Architecture at a glance

The server is an instance with its own state — no global variables. Every component is an isolated instance connected via semantic parent-child references.

**Request flow**:

```
uvicorn → AsgiServer → global middleware (errors → cors → auth)
  → Dispatcher → per-app middleware (session, cache, ...)
    → handler(**query) → Response → ASGI send
```

### Core components

| Component | Purpose |
|-----------|---------|
| **AsgiServer** | ASGI entry point, loads config, mounts apps, composes BasicAuthMixin |
| **Dispatcher** | Resolves handler via router, applies per-app middleware chains |
| **BasicAuthMixin** | Server-side auth: `server.authenticate(scope)`, `server.verify_credentials()` |
| **SessionMiddleware** | Per-app session lifecycle: cookie extraction, reconnection, Set-Cookie |
| **Session / Avatar** | Session with meta + Bag data + Avatar (identity, tags, extensible data) |
| **MemorySessionStore** | In-memory session store with dump/restore for persistence |
| **AsgiApplication** | Base class for mountable applications |
| **McpApplication** | MCP Streamable HTTP transport for AI tools |
| **StaticRouter** | Filesystem-backed static file serving |
| **LocalStorage** | Filesystem storage with mount system |

### Middleware

| Middleware | Name | Order | Default |
|-----------|------|-------|---------|
| ErrorMiddleware | errors | 100 | on |
| LoggingMiddleware | logging | 200 | off |
| CORSMiddleware | cors | 300 | off |
| AuthMiddleware | auth | 400 | off |
| SessionMiddleware | session | 450 | off |
| CacheMiddleware | cache | 900 | off |
| CompressionMiddleware | compression | 900 | off |

Scope is not a property of a middleware class: a middleware runs **globally**
when the server's `middleware(...)` section names it, and **per-app** when an
application's own `middleware` config names it. The same class can serve in
either chain; `Order` sorts each chain independently.

## Documentation

Full documentation: https://genro-asgi.readthedocs.io

- [Architecture overview](https://genro-asgi.readthedocs.io/en/latest/architecture/00-overview.html)
- [API reference](https://genro-asgi.readthedocs.io/en/latest/api/index.html)

## Development

```bash
git clone https://github.com/genropy/genro-asgi.git
cd genro-asgi
pip install -e .[dev]

pytest                    # run tests
ruff check .              # lint
mypy src                  # type check
```

## License

Copyright 2025-2026 **Softwell S.r.l.**

Licensed under the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). See `NOTICE` for additional attribution.

## Links

- **GitHub**: https://github.com/genropy/genro-asgi
- **PyPI**: https://pypi.org/project/genro-asgi/
- **Documentation**: https://genro-asgi.readthedocs.io
