Skip to content

Responses

Response types for different Gemini status codes.

Response

Success response with a body (status 20).

Response dataclass

Response(body: str, mime_type: str = 'text/gemini')

Success response with a body.

Example

@app.gemini("/") def home(request: Request): return Response("# Welcome!", mime_type="text/gemini")

Input

Request input from the client (status 10/11).

Input dataclass

Input(prompt: str, sensitive: bool = False)

Request input from the client (status 10/11).

When returned from a handler, the client will prompt the user for input and re-request the same URL with the input as a query string.

Example

@app.gemini("/search") def search(request: Request): if not request.query: return Input("Enter your search query:") return f"# Results for: {request.query}"

Redirect

Redirect to another URL (status 30/31).

Redirect dataclass

Redirect(url: str, permanent: bool = False)

Redirect to another URL (status 30/31).

Example

@app.gemini("/old-page") def old_page(request: Request): return Redirect("/new-page", permanent=True)

Build Gemtext link lines.

Link(url: str, label: str | None = None)

Build Gemtext link lines.

Generates link lines in the format: => URL [LABEL]

Example

link = Link("/about", "About Us") str(link) # "=> /about About Us"

link = Link("/about") str(link) # "=> /about"

link = Link(app.reverse("user_profile", username="alice"), "Alice's Profile") str(link) # "=> /user/alice Alice's Profile"

link = Link.to_route( app, "user_profile", username="alice", label="Alice's Profile" ) str(link) # "=> /user/alice Alice's Profile"

__str__

__str__() -> str

Return Gemtext representation.

Source code in src/xitzin/responses.py
def __str__(self) -> str:
    """Return Gemtext representation."""
    return self.to_gemtext()

to_gemtext

to_gemtext() -> str

Generate Gemtext link line.

Source code in src/xitzin/responses.py
def to_gemtext(self) -> str:
    """Generate Gemtext link line."""
    if self.label:
        return f"=> {self.url} {self.label}"
    return f"=> {self.url}"

to_route classmethod

to_route(
    app: "Xitzin",
    name: str,
    *,
    label: str | None = None,
    **params: Any,
) -> "Link"

Create a link to a named route.

Parameters:

Name Type Description Default
app 'Xitzin'

Xitzin application instance.

required
name str

Route name.

required
label str | None

Optional link label text.

None
**params Any

Path parameters for URL building.

{}

Returns:

Type Description
'Link'

Link instance pointing to the route.

Example

link = Link.to_route(app, "user_profile", username="alice", label="Profile") str(link) # "=> /user/alice Profile"

Source code in src/xitzin/responses.py
@classmethod
def to_route(
    cls,
    app: "Xitzin",
    name: str,
    *,
    label: str | None = None,
    **params: Any,
) -> "Link":
    """Create a link to a named route.

    Args:
        app: Xitzin application instance.
        name: Route name.
        label: Optional link label text.
        **params: Path parameters for URL building.

    Returns:
        Link instance pointing to the route.

    Example:
        link = Link.to_route(app, "user_profile", username="alice", label="Profile")
        str(link)  # "=> /user/alice Profile"
    """
    url = app.reverse(name, **params)
    return cls(url, label)

Response Conversion

convert_response

convert_response(
    result: Any, request: Any = None
) -> GeminiResponse

Convert a handler return value to a GeminiResponse.

Handlers can return: - str: Converted to success response with text/gemini MIME type - Response, Input, Redirect: Converted via to_gemini_response() - GeminiResponse: Returned as-is - tuple: (body, status) or (body, status, meta) - None: Empty success response

Parameters:

Name Type Description Default
result Any

The return value from a handler.

required
request Any

The current request (Request or TitanRequest, for URL tracking).

None

Returns:

Type Description
GeminiResponse

A GeminiResponse instance.

Raises:

Type Description
TypeError

If the result cannot be converted.

Source code in src/xitzin/responses.py
def convert_response(result: Any, request: Any = None) -> GeminiResponse:
    """Convert a handler return value to a GeminiResponse.

    Handlers can return:
    - str: Converted to success response with text/gemini MIME type
    - Response, Input, Redirect: Converted via to_gemini_response()
    - GeminiResponse: Returned as-is
    - tuple: (body, status) or (body, status, meta)
    - None: Empty success response

    Args:
        result: The return value from a handler.
        request: The current request (Request or TitanRequest, for URL tracking).

    Returns:
        A GeminiResponse instance.

    Raises:
        TypeError: If the result cannot be converted.
    """
    url = request._raw_request.normalized_url if request else None

    # Already a GeminiResponse
    if isinstance(result, GeminiResponse):
        return result

    # Objects with to_gemini_response method
    if hasattr(result, "to_gemini_response"):
        response = result.to_gemini_response()
        # Add URL tracking if not present
        if response.url is None and url:
            return GeminiResponse(
                status=response.status,
                meta=response.meta,
                body=response.body,
                url=url,
            )
        return response

    # Plain string -> success with text/gemini
    if isinstance(result, str):
        return GeminiResponse(
            status=StatusCode.SUCCESS,
            meta="text/gemini",
            body=result,
            url=url,
        )

    # Tuple: (body, status) or (body, status, meta)
    if isinstance(result, tuple):
        if len(result) == 2:
            body, status = result
            meta = "text/gemini" if status == StatusCode.SUCCESS else ""
        elif len(result) == 3:
            body, status, meta = result
        else:
            msg = f"Tuple must have 2 or 3 elements, got {len(result)}"
            raise TypeError(msg)

        return GeminiResponse(
            status=status,
            meta=meta,
            body=body if 20 <= status < 30 else None,
            url=url,
        )

    # None -> empty success
    if result is None:
        return GeminiResponse(
            status=StatusCode.SUCCESS,
            meta="text/gemini",
            body="",
            url=url,
        )

    msg = f"Cannot convert {type(result).__name__} to GeminiResponse"
    raise TypeError(msg)