Skip to main content

Tina4 Python v3 — Zero-dependency, lightweight web framework

Project description

Tina4

Tina4 Python

This Is Now A 4Framework

Laravel joy. Python speed. 10x less code. Zero third-party dependencies.

PyPI Tests Features Zero Deps Docs

DocumentationGetting StartedFeaturesCLI Referencetina4.com


Quick Start

# Install the Tina4 CLI
cargo install tina4  # or download binary from https://github.com/tina4stack/tina4/releases

# Create a project
tina4 init python ./my-app

# Run it
cd my-app && tina4 serve

Open http://localhost:7145 — your app is running.

Without the Tina4 CLI
# 1. Create project
mkdir my-app && cd my-app
uv init && uv add tina4-python

# 2. Create entry point
echo 'from tina4_python.core import run; run()' > app.py

# 3. Create .env
echo 'TINA4_DEBUG=true' > .env
echo 'TINA4_LOG_LEVEL=ALL' >> .env

# 4. Create route directory
mkdir -p src/routes

# 5. Run
uv run python app.py

Open http://localhost:7145


What's Included

Every feature is built from scratch -- no pip install, no node_modules, no third-party runtime dependencies in core.

Category Features
HTTP ASGI server, decorator routing, path params ({id:int}, {p:path}), middleware pipeline, CORS, rate limiting, graceful shutdown
Templates Frond engine (Twig-compatible), inheritance, partials, 35+ filters, macros, fragment caching, sandboxing
ORM Active Record, typed fields with validation, soft delete, relationships (has_one/has_many/belongs_to), scopes, result caching, multi-database
Database SQLite, PostgreSQL, MySQL, MSSQL, Firebird -- unified adapter interface, query caching (TINA4_DB_CACHE=true for 4x speedup)
Auth Zero-dep JWT (HS256), sessions (file/Redis/Valkey/MongoDB/database), password hashing, form tokens
API Swagger/OpenAPI auto-generation, GraphQL with ORM auto-schema and GraphiQL IDE, WSDL/SOAP with auto WSDL
Background Queue (SQLite/RabbitMQ/Kafka/MongoDB) with priority, delayed jobs, retry, batch processing
Real-time Native asyncio WebSocket (RFC 6455), per-path routing, connection manager
Frontend tina4-css (~24 KB), frond.js helper, SCSS compiler, live reload, CSS hot-reload
DX Dev admin dashboard (11 tabs), error overlay, request inspector, AI tool integration, Carbonah green benchmarks
Data Migrations with rollback, 50+ fake data generators, ORM and table seeders
Mail SMTP send (plain/HTML/attachments), IMAP read/search, dev mailbox capture
Other REST client, localization (6 languages), cache (memory/Redis/file), event system, inline testing, messenger (.env driven), configurable error pages, HTML element builder

1,633 tests across 38 built-in features. Zero dependencies. All Carbonah benchmarks rated A+.

For full documentation visit tina4.com.


Install

pip install tina4-python

Or with uv (recommended):

uv add tina4-python

Optional database drivers

Install only what you need:

pip install tina4-python[postgres]    # PostgreSQL (psycopg2-binary)
pip install tina4-python[mysql]       # MySQL / MariaDB (mysql-connector-python)
pip install tina4-python[mssql]       # Microsoft SQL Server (pymssql)
pip install tina4-python[firebird]    # Firebird (firebird-driver)
pip install tina4-python[mongo]       # MongoDB (pymongo)
pip install tina4-python[odbc]        # ODBC (pyodbc)
pip install tina4-python[all-db]      # All of the above
pip install tina4-python[dev-reload]  # Hot-patching via jurigged

Getting Started

1. Create a project

tina4python init my-app
cd my-app

This creates:

my-app/
├── app.py              # Entry point
├── .env                # Configuration
├── src/
│   ├── routes/         # API + page routes (auto-discovered)
│   ├── orm/            # Database models
│   ├── app/            # Service classes and shared helpers
│   ├── templates/      # Frond/Twig templates
│   ├── seeds/          # Database seeders
│   ├── scss/           # SCSS (auto-compiled to public/css/)
│   └── public/         # Static assets served at /
├── migrations/         # SQL migration files
└── tests/              # pytest tests

2. Create a route

Create src/routes/hello.py:

from tina4_python.core.router import get, post

@get("/api/hello")
async def hello(request, response):
    return response({"message": "Hello from Tina4!"})

@get("/api/hello/{name}")
async def hello_name(request, response):
    name = request.param("name")
    return response({"message": f"Hello, {name}!"})

Visit http://localhost:7145/api/hello -- routes are auto-discovered, no imports needed.

3. Add a database

Edit .env:

DATABASE_URL=sqlite:///data/app.db

Create and run a migration:

tina4python migrate:create "create users table"

Edit the generated SQL:

CREATE TABLE IF NOT EXISTS users (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    email TEXT NOT NULL,
    created_at TEXT DEFAULT CURRENT_TIMESTAMP
);
tina4python migrate

4. Create an ORM model

Create src/orm/User.py:

from tina4_python.orm import ORM, IntegerField, StringField, DateTimeField

class User(ORM):
    table_name = "users"
    id = IntegerField(primary_key=True, auto_increment=True)
    name = StringField(required=True, min_length=1, max_length=100)
    email = StringField(regex=r'^[^@]+@[^@]+\.[^@]+$')
    created_at = DateTimeField()

5. Build a REST API

Create src/routes/users.py:

from tina4_python.core.router import get, post, noauth

@get("/api/users")
async def list_users(request, response):
    from src.orm.User import User
    return response(User().select(limit=100).to_array())

@get("/api/users/{id:int}")
async def get_user(id, request, response):
    from src.orm.User import User
    user = User()
    if user.load("id = ?", [id]):
        return response(user.to_dict())
    return response({"error": "Not found"}, 404)

@noauth()
@post("/api/users")
async def create_user(request, response):
    from src.orm.User import User
    user = User(request.body)
    user.save()
    return response(user.to_dict(), 201)

6. Add a template

Create src/templates/base.twig:

<!DOCTYPE html>
<html>
<head>
    <title>{% block title %}My App{% endblock %}</title>
    <link rel="stylesheet" href="/css/tina4.min.css">
    {% block stylesheets %}{% endblock %}
</head>
<body>
    {% block content %}{% endblock %}
    <script src="/js/frond.js"></script>
    {% block javascripts %}{% endblock %}
</body>
</html>

Create src/templates/pages/home.twig:

{% extends "base.twig" %}
{% block content %}
<div class="container mt-4">
    <h1>{{ title }}</h1>
    <ul>
    {% for user in users %}
        <li>{{ user.name }} -- {{ user.email }}</li>
    {% endfor %}
    </ul>
</div>
{% endblock %}

Render it from a route:

@get("/")
async def home(request, response):
    from src.orm.User import User
    users = User().select(limit=20).to_array()
    return response.render("pages/home.twig", {"title": "Users", "users": users})

7. Seed, test, deploy

tina4python seed                          # Run seeders from src/seeds/
tina4python test                          # Run test suite
tina4python build                         # Build distributable

For the complete step-by-step guide, visit tina4.com.


Features

Routing

from tina4_python.core.router import get, post, put, delete, noauth, secured, middleware

@get("/api/items")               # Public by default
async def list_items(request, response):
    return response({"items": []})

@noauth()                         # Make a write route public
@post("/api/webhook")
async def webhook(request, response):
    return response({"ok": True})

@secured()                        # Protect a GET route
@get("/api/admin/stats")
async def admin_stats(request, response):
    return response({"secret": True})

Path parameter types: {id} (string), {id:int}, {price:float}, {path:path} (greedy).

ORM

Active Record with typed fields, validation, soft delete, relationships, scopes, and multi-database support.

from tina4_python.orm import ORM, IntegerField, StringField, Field, orm_bind

class User(ORM):
    table_name = "users"
    id = IntegerField(primary_key=True, auto_increment=True)
    name = StringField(required=True, min_length=1, max_length=100)
    email = StringField(regex=r'^[^@]+@[^@]+\.[^@]+$')
    role = StringField(choices=["admin", "user", "guest"], default="user")
    age = Field(int, min_value=0, max_value=150)

# CRUD
user = User({"name": "Alice", "email": "alice@example.com"})
user.save()
user.load("email = ?", ["alice@example.com"])
user.delete()

# Relationships
orders = user.has_many("Order", "user_id")
profile = user.has_one("Profile", "user_id")

# Soft delete, scopes, caching
user.soft_delete()
active_admins = User.scope("active").scope("admin").select()
users = User.cached("SELECT * FROM users", ttl=300)

# Multi-database
orm_bind(main_db)                     # Default
orm_bind(audit_db, name="audit")      # Named

class AuditLog(ORM):
    _db = "audit"                     # Uses named connection

Database

Unified interface across 7 engines:

from tina4_python.database.connection import Database

db = Database("sqlite:///data/app.db")
db = Database("postgresql://user:pass@localhost:5432/mydb")
db = Database("mysql://user:pass@localhost:3306/mydb")
db = Database("mssql://sa:pass@localhost:1433/mydb")
db = Database("firebird://SYSDBA:masterkey@localhost:3050//path/to/db")
db = Database("mongodb://localhost:27017/mydb")
db = Database("odbc://DSN=mydsn")

result = db.fetch("SELECT * FROM users WHERE age > ?", [18], limit=20, offset=0)
row = db.fetch_one("SELECT * FROM users WHERE id = ?", [1])
db.insert("users", {"name": "Alice", "email": "alice@test.com"})
db.commit()

Middleware

class AuthCheck:
    @staticmethod
    def before_auth(request, response):
        if "authorization" not in request.headers:
            return request, response("Unauthorized", 401)
        return request, response

@middleware(AuthCheck)
@get("/protected")
async def protected(request, response):
    return response({"secret": True})

JWT Authentication

from tina4_python.auth import Auth

auth = Auth(secret="your-secret")
token = auth.get_token({"user_id": 42})
payload = auth.valid_token(token)

POST/PUT/PATCH/DELETE routes require Authorization: Bearer <token> by default. Use @noauth() to make public, @secured() to protect GET routes.

Sessions

request.session.set("user_id", 42)
user_id = request.session.get("user_id")

Backends: file (default), Redis, Valkey, MongoDB, database. Set via TINA4_SESSION_HANDLER in .env.

Queues

from tina4_python.queue import Queue, Producer, Consumer

Producer(Queue(topic="emails")).push({"to": "alice@example.com"})

for job in Consumer(Queue(topic="emails")).poll():
    send_email(job.data)
    job.complete()

GraphQL

from tina4_python.graphql import GraphQL

gql = GraphQL()
gql.schema.from_orm(User)
gql.register_route("/graphql")   # GET = GraphiQL IDE, POST = queries

WebSocket

from tina4_python.websocket import WebSocketManager

ws = WebSocketManager()

@ws.route("/ws/chat")
async def chat(connection, message):
    await ws.broadcast("/ws/chat", f"User said: {message}")

Swagger / OpenAPI

Auto-generated at /swagger:

@description("Get all users")
@tags(["users"])
@get("/api/users")
async def users(request, response):
    return response(User().select().to_array())

Event System

from tina4_python.core.events import on, emit, once

@on("user.created", priority=10)
def notify_admin(user):
    send_notification(f"New user: {user['name']}")

emit("user.created", {"name": "Alice"})

Template Engine (Frond)

Twig-compatible, 35+ filters, macros, inheritance, fragment caching, sandboxing:

{% extends "base.twig" %}
{% block content %}
<h1>{{ title | upper }}</h1>
{% for item in items %}
    <p>{{ item.name }} -- {{ item.price | number_format(2) }}</p>
{% endfor %}

{% cache "sidebar" 300 %}
    {% include "partials/sidebar.twig" %}
{% endcache %}
{% endblock %}

CRUD Scaffolding

@get("/admin/users")
async def admin_users(request, response):
    return response(CRUD.to_crud(request, {
        "sql": "SELECT id, name, email FROM users",
        "title": "User Management",
        "primary_key": "id",
    }))

WSDL / SOAP

from tina4_python.wsdl import WSDL, wsdl_operation

class Calculator(WSDL):
    @wsdl_operation({"Result": int})
    def Add(self, a: int, b: int):
        return {"Result": a + b}

REST Client

from tina4_python.api import Api

api = Api("https://api.example.com", auth_header="Bearer xyz")
result = api.send_request("/users/42")

Data Seeder

from tina4_python.seeder import FakeData, seed_orm

fake = FakeData()
fake.name()      # "Alice Johnson"
fake.email()     # "alice.johnson@example.com"

seed_orm(User, count=50)

Email / Messenger

from tina4_python.messenger import create_messenger

mail = create_messenger()
mail.send(to="user@test.com", subject="Welcome", body="<h1>Hi!</h1>", html=True)

In-Memory Cache

from tina4_python.core.cache import Cache

cache = Cache()
cache.set("key", "value", ttl=300)
cache.tag("users").flush()

SCSS, Localization, Inline Testing

  • SCSS: Drop .scss in src/scss/ -- auto-compiled to CSS. Variables, nesting, mixins, @import, @extend.
  • i18n: JSON translation files, 6 languages (en, fr, af, zh, ja, es), placeholder interpolation.
  • Inline tests: @tests(assert_equal((5, 3), 8)) decorator on any function.

Dev Mode

Set TINA4_DEBUG=true in .env to enable:

  • Live reload -- browser auto-refreshes on code changes
  • CSS hot-reload -- SCSS changes apply without page refresh
  • Error overlay -- rich error display in the browser
  • Dev admin at /__dev/ with 11 tabs: Routes, Queue, Mailbox, Messages, Database, Requests, Errors, WebSocket, System, Tools, Tina4

CLI Reference

tina4python init [dir]             # Scaffold a new project
tina4python serve [port]           # Start dev server (default: 7145)
tina4python serve --production     # Auto-install and use best production server (uvicorn)
tina4python migrate                # Run pending migrations
tina4python migrate:create <desc>  # Create a migration file
tina4python migrate:rollback       # Rollback last batch
tina4python generate model <name>  # Generate ORM model scaffold
tina4python generate route <name>  # Generate route scaffold
tina4python generate migration <d> # Generate migration file
tina4python generate middleware <n># Generate middleware scaffold
tina4python seed                   # Run seeders from src/seeds/
tina4python routes                 # List all registered routes
tina4python test                   # Run test suite
tina4python build                  # Build distributable package
tina4python ai [--all]             # Detect AI tools and install context

Production Server Auto-Detection

tina4 serve automatically detects and uses the best available production server:

  • Python: uvicorn (if installed), otherwise built-in asyncio
  • Use tina4python serve --production to auto-install the production server

Scaffolding with tina4 generate

Quickly scaffold new components:

tina4python generate model User          # Creates src/orm/User.py with field stubs
tina4python generate route users         # Creates src/routes/users.py with CRUD stubs
tina4python generate migration "add age" # Creates migration SQL file
tina4python generate middleware AuthLog   # Creates middleware class

ORM Relationships & Eager Loading

# Define relationships
orders = user.has_many("Order", "user_id")
profile = user.has_one("Profile", "user_id")
customer = order.belongs_to("Customer", "customer_id")

# Eager loading with include=
users = User().select(include=["orders", "profile"])

DB Query Caching

Enable query caching for up to 4x speedup on read-heavy workloads:

# .env
TINA4_DB_CACHE=true
# Check cache stats
from tina4_python.orm import cache_stats, cache_clear
stats = cache_stats()   # {"hits": 42, "misses": 7, "size": 15}
cache_clear()           # Flush all cached queries

Frond Pre-Compilation

Templates are pre-compiled for 2.8x faster rendering. Clear the cache when needed:

from tina4_python.frond import Frond
Frond.clear_cache()

Gallery

7 interactive examples with Try It deploy — visit the dev admin at /__dev/ to explore.

Environment

SECRET=your-jwt-secret
DATABASE_URL=sqlite:///data/app.db
TINA4_DEBUG=true                     # Enable dev toolbar, error overlay
TINA4_LOG_LEVEL=ALL                  # ALL, DEBUG, INFO, WARNING, ERROR
TINA4_LOCALE=en                      # en, fr, af, zh, ja, es
TINA4_SESSION_HANDLER=SessionFileHandler
SWAGGER_TITLE=My API

AI Tool Integration

tina4python ai              # Detect and install context
tina4python ai --all        # Install for ALL supported tools

Supported: Claude Code, Cursor, GitHub Copilot, Windsurf, Aider, Cline, OpenAI Codex CLI. Generates framework-aware context so AI assistants understand Tina4's conventions.

Carbonah Green Benchmarks

All 9 benchmarks rated A+ (South Africa grid, 1000 iterations each):

Benchmark SCI (gCO2eq) Grade
JSON Hello World 0.000864 A+
Single DB Query 0.000538 A+
Multiple DB Queries 0.001350 A+
Template Rendering 0.003237 A+
Large JSON Payload 0.000983 A+
Plaintext Response 0.000377 A+
CRUD Cycle 0.000456 A+
Paginated Query 0.000990 A+
Framework Startup 0.000801 A+

Run locally: python benchmarks/run_carbonah.py


Documentation

Full guides, API reference, and examples at tina4.com.

License

MIT (c) 2007-2026 Tina4 Stack https://opensource.org/licenses/MIT


Tina4 -- The framework that keeps out of the way of your coding.


Our Sponsors

Sponsored with 🩵 by Code Infinity

Code Infinity

Supporting open source communities Innovate Code Empower

Project details


Release history Release notifications | RSS feed

Download files

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

Source Distribution

tina4_python-3.10.39.tar.gz (325.7 kB view details)

Uploaded Source

Built Distribution

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

tina4_python-3.10.39-py3-none-any.whl (409.6 kB view details)

Uploaded Python 3

File details

Details for the file tina4_python-3.10.39.tar.gz.

File metadata

  • Download URL: tina4_python-3.10.39.tar.gz
  • Upload date:
  • Size: 325.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.2 {"installer":{"name":"uv","version":"0.11.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for tina4_python-3.10.39.tar.gz
Algorithm Hash digest
SHA256 98368149f02e3f9311a2903eee4083c4375a331443a98774793f7f40ad059fb5
MD5 d443ae4fb5d64df7d7e218f3d0593382
BLAKE2b-256 87d04ca4fbc4a1acaa71c5ce484a922eb0c96b92ad7575eaf955ec9daaa11655

See more details on using hashes here.

File details

Details for the file tina4_python-3.10.39-py3-none-any.whl.

File metadata

  • Download URL: tina4_python-3.10.39-py3-none-any.whl
  • Upload date:
  • Size: 409.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.2 {"installer":{"name":"uv","version":"0.11.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for tina4_python-3.10.39-py3-none-any.whl
Algorithm Hash digest
SHA256 49052c8bdf534432f6a020389b2de4c76fb948250a466522ea3fecf358d49236
MD5 3a98f49f6e18da214d7947191c5d66ab
BLAKE2b-256 b666053fafe86e56eaef70b0b6176ae7629ff5a827330aaaad1f33825b7d4558

See more details on using hashes here.

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