Skip to main content

Experimental thread-aware ASGI framework and runtime prototype.

Project description

tasgi

tasgi means Thread ASGI. It is an experimental ASGI-compatible framework/runtime that keeps transport and protocol handling on the event loop while allowing handler execution on either the asyncio loop or a thread runtime.

Runtime design goal:

  • the tasgi package itself depends only on the Python standard library
  • external tools like coverage, build, and twine are used only for CI, testing, and packaging

Status

tasgi is currently an alpha-stage project.

  • public APIs are still settling
  • auth APIs are still evolving
  • router/module composition is still evolving
  • native HTTP/2 support is still a prototype subset

Treat the current release line as experimental, not stable.

Install

From source:

pip install -e .

tasgi does not require Poetry at runtime. The project is packaged from pyproject.toml using the standard PEP 517 build flow, so CI and publishing use pip, build, and twine directly instead of a Poetry-specific workflow.

After packaging to TestPyPI, the intended trial install flow is:

pip install --index-url https://test.pypi.org/simple/ tasgi

Hello World

from tasgi import TasgiApp, TextResponse

app = TasgiApp(docs=True, debug=True)

@app.route.get("/")
async def home(request):
    return TextResponse("hello from tasgi")

Run it:

tasgi

Or run the bundled demo app:

python3 examples/service_api/main.py

Router Usage

tasgi now treats app.route as the main HTTP registration surface.

from tasgi import JsonResponse, Router, TasgiApp

users = Router(tags=["users"])

@users.get("/users")
def list_users(request):
    return ["alice", "bob"]

app = TasgiApp()
app.include_router(users, prefix="/api")

@app.route.get("/status")
async def status(request):
    return JsonResponse({"ok": True})

Core Model

tasgi is layered like this:

socket/transport
  -> ASGI boundary
  -> tasgi runtime
  -> tasgi framework
  -> user handlers

Rules:

  • network I/O stays on the event loop
  • HTTP protocol handling stays on the event loop
  • async handlers run on the asyncio loop
  • sync handlers run in the tasgi thread runtime
  • worker threads never write directly to sockets

Dual Execution Model

App-level execution policy is explicit:

from tasgi import ASYNC_EXECUTION, THREAD_EXECUTION, TasgiApp

app = TasgiApp(default_execution=THREAD_EXECUTION)

@app.route.get("/cpu")
def cpu(request):
    ...

@app.route.get("/status", execution=ASYNC_EXECUTION)
async def status(request):
    ...

Request And Response Types

Main public HTTP types:

  • TasgiApp
  • Router
  • Request
  • Response
  • TextResponse
  • JsonResponse
  • StreamingResponse

Handlers may return Response objects directly, or return typed values when a response model is declared.

OpenAPI And Docs

Built-in docs can be enabled from app config:

from dataclasses import dataclass
from tasgi import TasgiApp

@dataclass
class EchoIn:
    message: str

@dataclass
class EchoOut:
    echoed: str

app = TasgiApp(
    docs=True,
    title="tasgi demo",
    version="0.1.0a1",
)

@app.route.post("/echo", request_model=EchoIn, response_model=EchoOut)
def echo(request, body: EchoIn) -> EchoOut:
    return EchoOut(echoed=body.message)

Default docs endpoints:

  • /openapi.json
  • /docs

Auth

tasgi includes an experimental pluggable auth layer.

Built-in starters:

  • BearerTokenBackend
  • APIKeyBackend
  • BasicAuthBackend
  • RequireAuthenticated
  • RequireScope
  • RequireRole

Example:

from tasgi import BearerTokenBackend, Identity, RequireScope, TasgiApp

def validate_token(token: str):
    if token == "demo-token":
        return Identity(subject="alice", scopes=frozenset({"profile"}))
    if token == "admin-token":
        return Identity(subject="admin", scopes=frozenset({"admin"}))
    return None

app = TasgiApp(auth_backend=BearerTokenBackend(validate_token), docs=True)

@app.route.get("/public", auth=False)
async def public_route(request):
    return {"public": True}

@app.route.get("/me", auth=True)
async def me(request):
    return {"subject": request.identity.subject}

@app.route.get("/admin", auth=RequireScope("admin"))
async def admin(request):
    return {"subject": request.identity.subject}

Auth metadata is also reflected automatically in OpenAPI for built-in auth backends.

Demo App

The bundled service example includes:

  • HTTP routes
  • router/module composition
  • OpenAPI + Swagger UI
  • auth examples
  • streaming responses
  • WebSocket echo
  • thread-executed sync handlers

Run:

python3 examples/service_api/main.py

There is also a cleaner modular composition example:

python3 examples/modular_api/main.py

Benchmarks

The benchmark suite exercises loopback TCP requests against a dedicated benchmark app.

Run:

python3 benchmarks/run_benchmarks.py

CI And Publishing

GitHub Actions workflows are set up for both validation and releases:

  • CI runs on every push and pull request
  • the primary CI target is free-threaded CPython 3.14t
  • standard compatibility tests also run on Python 3.14, 3.13, 3.12, and 3.11
  • coverage is reported in CI using coverage run -m unittest discover -s tests
  • package builds are verified in CI with python -m build and python -m twine check --strict dist/*

Publishing is separated from CI:

  • Publish runs only on version tags like v0.1.0a1 or on published GitHub Releases
  • release builds run on CPython 3.14t
  • PyPI upload is configured for Trusted Publishing via GitHub Actions
  • before enabling real PyPI releases, do one TestPyPI dry run and confirm a clean install path

Local commands:

python3 -m unittest discover -s tests -v
python3 -m pip install coverage build twine
coverage run -m unittest discover -s tests
coverage report -m
python3 -m build
python3 -m twine check --strict dist/*

Current Limits

  • HTTP/2 support is prototype-grade, not production-complete
  • auth API is still settling
  • route registration APIs changed recently and may still evolve
  • no middleware ecosystem yet
  • no dependency injection container beyond explicit lightweight helpers
  • no production-hardening claims

License

tasgi is licensed under Apache-2.0. See LICENSE and NOTICE.

That keeps the project free to use, including in companies, while preserving the attribution notices when the software is redistributed.

Release Notes

This repository is currently prepared for an alpha-style release, not a stable release.

Before a public non-alpha release, the project still needs:

  • final public API freeze
  • repository/homepage URLs in package metadata
  • a TestPyPI install-and-verify pass

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

tasgi-0.1.0a1.tar.gz (72.0 kB view details)

Uploaded Source

Built Distribution

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

tasgi-0.1.0a1-py3-none-any.whl (62.2 kB view details)

Uploaded Python 3

File details

Details for the file tasgi-0.1.0a1.tar.gz.

File metadata

  • Download URL: tasgi-0.1.0a1.tar.gz
  • Upload date:
  • Size: 72.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for tasgi-0.1.0a1.tar.gz
Algorithm Hash digest
SHA256 7ec12ab1489e769ee76b4f1037cc248a8106d933d7a57813c696e31576c225a8
MD5 29e495032d704f6a526135035b82773c
BLAKE2b-256 8fa75a78efdf6ce2086ea375980bf49c97d82b84ce2c5cbb40aaaec35c3ab8b1

See more details on using hashes here.

Provenance

The following attestation bundles were made for tasgi-0.1.0a1.tar.gz:

Publisher: publish.yml on VachaganGrigoryan/tasgi

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

File details

Details for the file tasgi-0.1.0a1-py3-none-any.whl.

File metadata

  • Download URL: tasgi-0.1.0a1-py3-none-any.whl
  • Upload date:
  • Size: 62.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for tasgi-0.1.0a1-py3-none-any.whl
Algorithm Hash digest
SHA256 259f7854b48ebb7b6e2bd02b422e697ba52e060791935abad458a0f4de521870
MD5 44bdcc5eb981d573a82301dcbfc083e2
BLAKE2b-256 0bae3e9ab031691d815570c2c6921db5d9e0e7f0af4aee8629d83d9533ba7547

See more details on using hashes here.

Provenance

The following attestation bundles were made for tasgi-0.1.0a1-py3-none-any.whl:

Publisher: publish.yml on VachaganGrigoryan/tasgi

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