Tina4 Python - Quick Reference#
๐ฅ Hot Tips
- Routes go in
src/routes/, templates insrc/templates/, static files insrc/public/ - GET routes are public by default; POST/PUT/PATCH/DELETE require a token
- Return a
dictfromresponse()and the framework auto-setsapplication/json - Use
uv run tina4 startto 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#
pip install tina4-pythontina4 init my-projectcd my-projecttina4 startMore details around project setup and some customizations.
Static Websites#
Put .twig files in ./src/templates โข assets in ./src/public
<!-- src/templates/index.twig --><h1>Hello Static World</h1>More details on static website routing.
Basic Routing#
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#
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]AfterFollow the links for more on Middleware Declaration and Linking to Routes.
Template Rendering#
Put .twig files in ./src/templates โข assets in ./src/public
<!-- src/templates/index.twig --><h1>Hello {{name}}</h1>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
| Handler | Backend | Required package |
|---|---|---|
SessionFileHandler (default) | File system | - |
SessionRedisHandler | Redis | redis |
SessionValkeyHandler | Valkey | valkey |
SessionMongoHandler | MongoDB | pymongo |
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@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
// 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.dbimport 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.
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#
<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
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#
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#
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#
tina4 migrate:create create_users_table-- migrations/00001_create_users_table.sqlCREATE TABLE users( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT);tina4 migrateMigrations do have some limitations and considerations when used extensively.
ORM#
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#
@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)}){{ crud }}More details on how CRUD works, where it puts the generated files is worth some investigation.
Consuming REST APIs#
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#
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 / bRun: tina4 test
Services#
Due to the nature of python, services are not necessary.
Websockets#
Requires simple-websocket, add with uv add simple-websocket
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.
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#
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.
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.
from tina4_python.Localization import AVAILABLE_LANGUAGES# ['en', 'fr', 'af']