Application¶
The main application class and lifecycle management.
Xitzin¶
The core application class that handles routing, middleware, and server lifecycle.
Xitzin
¶
Xitzin(
*,
title: str = "Xitzin App",
version: str = "0.1.0",
templates_dir: Path | str | None = None,
)
Gemini Application Framework.
Xitzin provides an interface for building Gemini applications.
Example
app = Xitzin(title="My Capsule")
@app.gemini("/") def homepage(request: Request): return "# Welcome to my capsule!"
@app.gemini("/user/{username}") def profile(request: Request, username: str): return f"# {username}'s Profile"
if name == "main": app.run()
Create a new Xitzin application.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
title
|
str
|
Application title (for documentation). |
'Xitzin App'
|
version
|
str
|
Application version. |
'0.1.0'
|
templates_dir
|
Path | str | None
|
Directory containing Gemtext templates. |
None
|
Source code in src/xitzin/application.py
template
¶
Render a template.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Template filename (e.g., "page.gmi"). |
required |
**context
|
Any
|
Variables to pass to the template. |
{}
|
Returns:
| Type | Description |
|---|---|
Any
|
TemplateResponse that can be returned from handlers. |
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If no templates directory was configured. |
Source code in src/xitzin/application.py
reverse
¶
Build URL for a named route.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Route name. |
required |
**params
|
Any
|
Path parameters. |
{}
|
Returns:
| Type | Description |
|---|---|
str
|
URL path string. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If route name not found or parameters missing. |
Source code in src/xitzin/application.py
redirect
¶
Create a redirect to a named route.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Route name. |
required |
permanent
|
bool
|
If True, use status 31 (permanent redirect). |
False
|
**params
|
Any
|
Path parameters. |
{}
|
Returns:
| Type | Description |
|---|---|
Redirect
|
Redirect response object. |
Example
@app.gemini("/old-profile/{username}") def old_profile(request: Request, username: str): return app.redirect("user_profile", username=username, permanent=True)
Source code in src/xitzin/application.py
gemini
¶
gemini(
path: str, *, name: str | None = None
) -> Callable[[Callable[..., Any]], Callable[..., Any]]
Register a route handler.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str
|
URL path pattern (e.g., "/user/{id}"). |
required |
name
|
str | None
|
Optional route name for URL reversing. Defaults to function name. |
None
|
Returns:
| Type | Description |
|---|---|
Callable[[Callable[..., Any]], Callable[..., Any]]
|
Decorator function. |
Example
@app.gemini("/") def home(request: Request): return "# Home"
@app.gemini("/user/{username}", name="user_profile") def profile(request: Request, username: str): return f"# {username}"
Source code in src/xitzin/application.py
input
¶
input(
path: str,
*,
prompt: str,
sensitive: bool = False,
name: str | None = None,
) -> Callable[[Callable[..., Any]], Callable[..., Any]]
Register an input route (status 10/11 flow).
When a request arrives without a query string, the client is prompted
for input. When the request includes a query string, the handler is
called with the decoded input as the query parameter.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str
|
URL path pattern. |
required |
prompt
|
str
|
Prompt text shown to the user. |
required |
sensitive
|
bool
|
If True, use status 11 (sensitive input). |
False
|
name
|
str | None
|
Optional route name for URL reversing. Defaults to function name. |
None
|
Returns:
| Type | Description |
|---|---|
Callable[[Callable[..., Any]], Callable[..., Any]]
|
Decorator function. |
Example
@app.input("/search", prompt="Enter search query:", name="search") def search(request: Request, query: str): return f"# Results for: {query}"
Source code in src/xitzin/application.py
titan
¶
titan(
path: str,
*,
name: str | None = None,
auth_tokens: list[str] | None = None,
) -> Callable[[Callable[..., Any]], Callable[..., Any]]
Register a Titan upload handler.
Titan is the upload companion protocol to Gemini. This decorator registers a handler for Titan upload requests.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str
|
URL path pattern (e.g., "/upload/{filename}"). |
required |
name
|
str | None
|
Optional route name. Defaults to function name. |
None
|
auth_tokens
|
list[str] | None
|
List of valid authentication tokens. If provided, requests without a valid token are rejected with status 60. |
None
|
Returns:
| Type | Description |
|---|---|
Callable[[Callable[..., Any]], Callable[..., Any]]
|
Decorator function. |
Example
@app.titan("/upload/{filename}", auth_tokens=["secret123"]) def upload(request: TitanRequest, content: bytes, mime_type: str, token: str | None, filename: str): if request.is_delete(): Path(f"./uploads/{filename}").unlink() return "# Deleted" Path(f"./uploads/{filename}").write_bytes(content) return "# Upload successful"
Source code in src/xitzin/application.py
mount
¶
Mount a handler at a path prefix.
Mounted handlers receive requests for any path starting with the prefix. The handler receives (request, path_info) where path_info is the remaining path after the mount prefix.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str
|
Mount point prefix (e.g., "/cgi-bin", "/api"). |
required |
handler
|
Callable[..., Any]
|
Callable that takes (request, path_info) and returns a response. |
required |
name
|
str | None
|
Optional name for the mount. |
None
|
Example
from xitzin.cgi import CGIHandler
app.mount("/cgi-bin", CGIHandler(script_dir="./scripts"))
Requests to /cgi-bin/hello.py will call:¶
handler(request, path_info="/hello.py")¶
Source code in src/xitzin/application.py
vhost
¶
vhost(
hosts: dict[str, "Xitzin"],
*,
default_app: "Xitzin | None" = None,
fallback_status: int = 53,
fallback_handler: Callable[[Request], Any]
| None = None,
) -> None
Configure virtual hosting for this application.
This is a convenience method that creates and registers VirtualHostMiddleware. The middleware routes requests to different apps based on hostname.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
hosts
|
dict[str, 'Xitzin']
|
Mapping of hostname patterns to Xitzin apps. Patterns can be exact ("example.com") or wildcards ("*.example.com"). Exact matches are checked first, then wildcards in definition order. |
required |
default_app
|
'Xitzin | None'
|
Default app when no pattern matches. |
None
|
fallback_status
|
int
|
Status code for unmatched hosts (default: 53). Common values: 53 (Proxy Refused), 51 (Not Found), 59 (Bad Request). |
53
|
fallback_handler
|
Callable[[Request], Any] | None
|
Custom handler for unmatched hosts. Takes precedence over default_app and fallback_status. |
None
|
Example
main_app = Xitzin(title="Main") blog_app = Xitzin(title="Blog") api_app = Xitzin(title="API")
@blog_app.gemini("/") def blog_home(request: Request): return "# Blog Home"
@api_app.gemini("/") def api_home(request: Request): return "# API Home"
@main_app.gemini("/") def main_home(request: Request): return "# Main Home"
Configure as gateway¶
main_app.vhost({ "blog.example.com": blog_app, "*.api.example.com": api_app, }, default_app=main_app)
main_app.run()
Source code in src/xitzin/application.py
task
¶
task(
*,
interval: str | int | float | None = None,
cron: str | None = None,
) -> Callable[[Callable[[], Any]], Callable[[], Any]]
Register a background task.
Tasks run continuously while the server is running. They are started after startup handlers and stopped before shutdown handlers.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
interval
|
str | int | float | None
|
Run every N seconds (int) or duration string ("1h", "30m", "1d"). |
None
|
cron
|
str | None
|
Cron expression string ("0 * * * *" runs hourly). Requires croniter: pip install 'xitzin[tasks]' |
None
|
Exactly one of interval or cron must be provided.
Returns:
| Type | Description |
|---|---|
Callable[[Callable[[], Any]], Callable[[], Any]]
|
Decorator function. |
Raises:
| Type | Description |
|---|---|
TaskConfigurationError
|
If neither or both parameters provided, or if cron is used but croniter is not installed. |
Example
@app.task(interval="1h") async def cleanup(): await app.state.db.cleanup_old_records()
@app.task(cron="0 2 * * *") # 2 AM daily def backup(): backup_database()
Source code in src/xitzin/application.py
on_startup
¶
Register a startup event handler.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
handler
|
Callable[[], Any]
|
Function to call on startup. |
required |
Example
@app.on_startup async def startup(): app.state.db = await create_db_pool()
Source code in src/xitzin/application.py
on_shutdown
¶
Register a shutdown event handler.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
handler
|
Callable[[], Any]
|
Function to call on shutdown. |
required |
Example
@app.on_shutdown async def shutdown(): await app.state.db.close()
Source code in src/xitzin/application.py
middleware
¶
Register middleware as a decorator.
Middleware receives (request, call_next) and must call call_next to continue processing.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
handler
|
Callable[..., Any]
|
Middleware function. |
required |
Example
@app.middleware async def log_requests(request: Request, call_next): print(f"Request: {request.path}") response = await call_next(request) print(f"Response: {response.status}") return response
Source code in src/xitzin/application.py
run
¶
run(
host: str = "localhost",
port: int = 1965,
certfile: Path | str | None = None,
keyfile: Path | str | None = None,
) -> None
Run the server (blocking).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
host
|
str
|
Host address to bind to. |
'localhost'
|
port
|
int
|
Port to bind to. |
1965
|
certfile
|
Path | str | None
|
Path to TLS certificate file. |
None
|
keyfile
|
Path | str | None
|
Path to TLS private key file. |
None
|
Source code in src/xitzin/application.py
run_async
async
¶
run_async(
host: str = "localhost",
port: int = 1965,
certfile: Path | str | None = None,
keyfile: Path | str | None = None,
) -> None
Run the server asynchronously.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
host
|
str
|
Host address to bind to. |
'localhost'
|
port
|
int
|
Port to bind to. |
1965
|
certfile
|
Path | str | None
|
Path to TLS certificate file. |
None
|
keyfile
|
Path | str | None
|
Path to TLS private key file. |
None
|
Source code in src/xitzin/application.py
831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 | |
AppState¶
Application-level state storage for shared resources like database connections.
AppState
¶
Application-level state storage.
Store shared resources like database connections here.
Example
app.state.db = create_db_connection()