Tina4

Tina4 Python - Quick Reference#

๐Ÿ”ฅ Hot Tips

  • Routes go in src/routes/, templates in src/templates/, static files in src/public/
  • GET routes are public by default; POST/PUT/PATCH/DELETE require a token
  • Return a dict from response() and the framework auto-sets application/json
  • Use uv run tina4 start to launch the dev server on port 7145 :::

Installation โ€ข Static Websites โ€ข Routing โ€ข Middleware โ€ข Templates โ€ข Sessions โ€ข SCSS โ€ข Environments โ€ข Authentication โ€ข Forms & Tokens โ€ข AJAX โ€ข OpenAPI โ€ข Databases โ€ข Database Results โ€ข Migrations โ€ข ORM โ€ข CRUD โ€ข REST Client โ€ข Testing โ€ข Services โ€ข Websockets Threads โ€ข Queues โ€ข WSDL โ€ข Localization

Installation#

bash
pip install tina4-pythontina4 init my-projectcd my-projecttina4 start

More details around project setup and some customizations.

Static Websites#

Put .twig files in ./src/templates โ€ข assets in ./src/public

twig
<!-- src/templates/index.twig --><h1>Hello Static World</h1>

More details on static website routing.

Basic Routing#

python
from tina4_python.Router import get, postโ€‹@get("/")async def get_home(request, response):    return response("<h1>Hello Tina4 Python</h1>")โ€‹# post requires a formToken in body or Bearer auth@post("/api")async def post_api(request, response):    return response({"data": request.params})โ€‹# redirect after post@post("/register")async def post_register(request, response):    return response.redirect("/welcome")

Follow the links for basic routing and dynamic routing with variables.

Middleware#

python
class RunSomething:โ€‹    @staticmethod    def before_something(request, response):        response.content += "Before"        return request, responseโ€‹    @staticmethod    def after_something(request, response):        response.content += "After"        return request, responseโ€‹    @staticmethod    def before_and_after_something(request, response):        response.content += "[Before / After Something]"        return request, responseโ€‹@middleware(RunSomething)@get("/middleware")async def get_middleware(request, response):    return response("Route") # Before[Before / After Something]Route[Before / After Something]After

Follow the links for more on Middleware Declaration and Linking to Routes.

Template Rendering#

Put .twig files in ./src/templates โ€ข assets in ./src/public

twig
<!-- src/templates/index.twig --><h1>Hello {{name}}</h1>
python
from tina4_python.Router import getโ€‹@get("/")async def get_home(request, response):    return response.render("index.twig", {"name": "World!"})

Sessions#

The default session handling is SessionFileHandler, override TINA4_SESSION_HANDLER in .env

HandlerBackendRequired package
SessionFileHandler (default)File system-
SessionRedisHandlerRedisredis
SessionValkeyHandlerValkeyvalkey
SessionMongoHandlerMongoDBpymongo
bash
TINA4_SESSION_HANDLER=SessionMongoHandlerTINA4_SESSION_MONGO_HOST=localhostTINA4_SESSION_MONGO_PORT=27017TINA4_SESSION_MONGO_URI=TINA4_SESSION_MONGO_USERNAME=TINA4_SESSION_MONGO_PASSWORD=TINA4_SESSION_MONGO_DB=tina4_sessionsTINA4_SESSION_MONGO_COLLECTION=sessions
python
@get("/session/set")async def get_session_set(request, response):    request.session.set("name", "Joe")    request.session.set("info", {"info": ["one", "two", "three"]})    return response("Session Set!")โ€‹โ€‹@get("/session/get")async def get_session_set(request, response):    name = request.session.get("name")    info = request.session.get("info")โ€‹    return response({"name": name, "info": info})โ€‹

SCSS Stylesheets#

Drop in ./src/scss โ†’ auto-compiled to ./src/public/css

scss
// src/scss/main.scss$primary: #2c3e50;body {  background: $primary;  color: white;}

More details on css and scss.

Environments#

Default development environment can be found in .env

PROJECT_NAME="My Project"VERSION=1.0.0TINA4_LOCALE=enTINA4_DEBUG_LEVEL=ALLTINA4_API_KEY=ABC1234TINA4_TOKEN_LIMIT=1DATABASE_NAME=sqlite3:test.db
python
import osโ€‹api_key = os.getenv("TINA4_API_KEY", "ABC1234")

Authentication#

Pass Authorization: Bearer TINA4_API_KEY to secured routes in requests. See .env for default TINA4_API_KEY.

python
from tina4_python.Router import get, post, noauth, securedโ€‹@post("/login")@noauth()async def login(request, response):    return response("Logged in")โ€‹@get("/protected")@secured()async def secret(request, response):    return response("Welcome!")

HTML Forms and Tokens#

twig
<form method="POST" action="/register">    {{ ("Register" ~ RANDOM()) | form_token }}    <input name="email">    <button>Save</button></form>

More details on posting form data, basic form handling, how to generate form tokens, dealing with file uploads, returning errors, disabling route auth and a full login example.

AJAX and tina4helper.js#

Tina4 ships with a small javascript library, in the bin folder, to assist with the heavy lifting of ajax calls.

More details on available features.

OpenAPI and Swagger UI#

Visit http://localhost:7145/swagger

python
from tina4_python.Router import getfrom tina4_python import descriptionโ€‹@get("/users")@description("Get all users")async def users(request, response):    return response(User().select("*"))

Follow the links for more on Configuration, Usage and Decorators.

Databases#

python
from tina4_python.Database import Databaseโ€‹# dba = Database("<driver>:<hostname>/<port>:database_name", username, password)dba = Database("sqlite3:data.db") 

Follow the links for more on Available Connections, Core Methods, Usage and Full transaction control.

Database Results#

python
result = dba.fetch("select * from test_record order by id", limit=3, skip=1)โ€‹array = result.to_array()paginated = result.to_paginate()csv_data = result.to_csv()json_data = result.to_json()

Looking at detailed Usage will improve deeper understanding.

Migrations#

bash
tina4 migrate:create create_users_table
sql
-- migrations/00001_create_users_table.sqlCREATE TABLE users(    id   INTEGER PRIMARY KEY AUTOINCREMENT,    name TEXT);
bash
tina4 migrate

Migrations do have some limitations and considerations when used extensively.

ORM#

python
from tina4_python.ORM import ORM, IntegerField, StringFieldโ€‹class User(ORM):    id   = IntegerField(primary_key=True, auto_increment=True)    name = StringField()โ€‹User({"name": "Alice"}).save()โ€‹user = User()user.load("id = ?", [1])

ORM functionality is quite extensive and needs more study of the Advanced Detail to get the full value from ORM.

CRUD#

python
@get("/users/dashboard")async def dashboard(request, response):    users = User().select("id, name, email")    return response.render("users/dashboard.twig", {"crud": users.to_crud(request)})
twig
{{ crud }}

More details on how CRUD works, where it puts the generated files is worth some investigation.

Consuming REST APIs#

python
from tina4_python import Apiโ€‹api = Api("https://api.example.com", auth_header="Bearer xyz")result = api.get("/users/42")print(result["body"])

More details are available on sending a post data body, authorizations and other finer controls of sending api requests.

Inline Testing#

python
from tina4_python import testsโ€‹โ€‹@tests(    assert_equal((7, 7), 1),    assert_equal((-1, 1), -1),    assert_raises(ZeroDivisionError, (5, 0)),)def divide(a: int, b: int) -> float:    if b == 0:        raise ZeroDivisionError("division by zero")    return a / b

Run: tina4 test

Services#

Due to the nature of python, services are not necessary.

Websockets#

Requires simple-websocket, add with uv add simple-websocket

python
from tina4_python.Websocket import Websocketโ€‹@get("/ws/chat")async def chat_ws(request, response):    ws = await Websocket(request).connection()    try:        while True:            data = await ws.receive()            await ws.send(f"Echo: {data}")    finally:        await ws.close()    return response("")

Have a look at out PubSub example under Websockets

Threads#

Due to the nature of python, threads are not necessary.

Queues#

Supports litequeue (default/SQLite), RabbitMQ, Kafka, and MongoDB backends.

python
from tina4_python.Queue import Queue, Producer, Consumerโ€‹# Produce a messagequeue = Queue(topic="emails")Producer(queue).produce({"to": "alice@example.com", "subject": "Welcome"})โ€‹# Consume messagesconsumer = Consumer(queue)for msg in consumer.messages():    print(msg.data)

Full details on backend configuration, batching, multi-queue consumers, and error handling.

WSDL#

python
from tina4_python.WSDL import WSDL, wsdl_operationfrom typing import Listโ€‹โ€‹class Calculator(WSDL):    SERVICE_URL = "http://localhost:7145/calculator"โ€‹    def Add(self, a: int, b: int):        return {"Result": a + b}โ€‹    def SumList(self, Numbers: List[int]):        return {            "Numbers": Numbers,            "Total": sum(Numbers),            "Error": None        }โ€‹โ€‹@wsdl("/calculator")async def wsdl_cis(request, response):    return response.wsdl(Calculator(request))โ€‹

More Details are available for WSDL

Localization (i18n)#

Set TINA4_LOCALE in .env to change framework language. Supported: en, fr, af.

python
from tina4_python.Localization import localizeโ€‹_ = localize()print(_("Server stopped."))  # "Bediener gestop." (af)

Translations use Python's gettext module. Falls back to English for unsupported languages.

python
from tina4_python.Localization import AVAILABLE_LANGUAGES# ['en', 'fr', 'af']

โ†‘ Back to top