Skip to content

Protocol API Reference

The protocol module provides classes and utilities for working with the Spartan protocol.

Quick example

from teyaotlani import (
    SpartanRequest,
    SpartanResponse,
    StatusCode,
    is_success,
    is_redirect,
    is_error,
)

# Check response status
if is_success(response.status):
    print(f"Content type: {response.meta}")
    print(response.body)
elif is_redirect(response.status):
    print(f"Redirect to: {response.meta}")
elif is_error(response.status):
    print(f"Error: {response.meta}")

Request and Response

SpartanRequest

teyaotlani.SpartanRequest dataclass

SpartanRequest(hostname, path, content_length, content=b'')

Represents a Spartan protocol request.

Request format: host SP path SP content-length CRLF [data-block]

Unlike Gemini which sends a full URL, Spartan sends: - hostname (no scheme, no port) - absolute path (starting with /) - content length in bytes

Attributes:

Name Type Description
hostname str

Target hostname (no scheme, no port).

path str

Absolute path starting with /.

content_length int

Size of content in bytes (0 for GET requests).

content bytes

Optional data block (for queries/uploads).

Examples:

>>> request = SpartanRequest.from_line("example.com /index.gmi 0")
>>> request.hostname
'example.com'
>>> request.path
'/index.gmi'
>>> request.is_upload
False

Attributes

is_upload property

is_upload

Check if this request contains upload data.

query property

query

Get query string from content if this is a query request.

In Spartan, query strings are sent as the data block. Returns the content decoded as UTF-8 if present.

Functions

from_line classmethod

from_line(line)

Parse a Spartan request from a request line.

Parameters:

Name Type Description Default
line str

The request line (without CRLF). Format: "host path content-length"

required

Returns:

Type Description
SpartanRequest

A SpartanRequest instance.

Raises:

Type Description
ValueError

If the request line is invalid or malformed.

Examples:

>>> request = SpartanRequest.from_line("example.com / 0")
>>> request.path
'/'
Source code in src/teyaotlani/protocol/request.py
@classmethod
def from_line(cls, line: str) -> "SpartanRequest":
    """Parse a Spartan request from a request line.

    Args:
        line: The request line (without CRLF).
              Format: "host path content-length"

    Returns:
        A SpartanRequest instance.

    Raises:
        ValueError: If the request line is invalid or malformed.

    Examples:
        >>> request = SpartanRequest.from_line("example.com / 0")
        >>> request.path
        '/'
    """
    parts = line.split(" ")
    if len(parts) != 3:
        raise ValueError(
            f"Invalid Spartan request format: expected 'host path size', "
            f"got {len(parts)} parts"
        )

    hostname, path, size_str = parts

    if not hostname:
        raise ValueError("Hostname cannot be empty")

    if not path.startswith("/"):
        raise ValueError("Path must be absolute (start with /)")

    try:
        content_length = int(size_str)
    except ValueError as e:
        raise ValueError(f"Invalid content length: {size_str}") from e

    if content_length < 0:
        raise ValueError(f"Content length must be non-negative: {content_length}")

    return cls(hostname=hostname, path=path, content_length=content_length)

__str__

__str__()

Return human-readable string representation.

Source code in src/teyaotlani/protocol/request.py
def __str__(self) -> str:
    """Return human-readable string representation."""
    return f"SpartanRequest({self.hostname}{self.path}, {self.content_length} bytes)"

SpartanResponse

teyaotlani.SpartanResponse dataclass

SpartanResponse(status, meta, body=None, url=None)

Represents a Spartan protocol response.

Response format: status SP meta CRLF [body]

Attributes:

Name Type Description
status int

Single-digit status code (2, 3, 4, or 5).

meta str

Status-dependent metadata string. - For success (2): MIME type (e.g., "text/gemini") - For redirect (3): Redirect URL - For errors (4, 5): Error message

body str | bytes | None

Response body content (only present for success responses).

url str | None

Optional URL this response came from.

Examples:

>>> response = SpartanResponse(status=2, meta="text/gemini", body="# Hello")
>>> response.is_success()
True
>>> response.mime_type
'text/gemini'

Attributes

mime_type property

mime_type

Get the MIME type from a success response.

Returns:

Type Description
str | None

The MIME type if this is a success response, None otherwise.

redirect_url property

redirect_url

Get the redirect URL from a redirect response.

Returns:

Type Description
str | None

The redirect URL if this is a redirect response, None otherwise.

charset property

charset

Extract charset from MIME type parameters, defaulting to utf-8.

Returns:

Type Description
str

The charset specified in the meta field, or 'utf-8' if not specified.

Functions

is_success

is_success()

Check if this response indicates success (status 2).

Source code in src/teyaotlani/protocol/response.py
def is_success(self) -> bool:
    """Check if this response indicates success (status 2)."""
    return is_success(self.status)

is_redirect

is_redirect()

Check if this response indicates a redirect (status 3).

Source code in src/teyaotlani/protocol/response.py
def is_redirect(self) -> bool:
    """Check if this response indicates a redirect (status 3)."""
    return is_redirect(self.status)

to_bytes

to_bytes()

Serialize response to bytes for transmission.

Returns:

Type Description
bytes

ASCII-encoded header followed by optional body.

Source code in src/teyaotlani/protocol/response.py
def to_bytes(self) -> bytes:
    """Serialize response to bytes for transmission.

    Returns:
        ASCII-encoded header followed by optional body.
    """
    header = f"{self.status} {self.meta}\r\n".encode("ascii")

    if self.body:
        if isinstance(self.body, bytes):
            return header + self.body
        return header + self.body.encode("utf-8")

    return header

success classmethod

success(mime_type, body)

Create a success response.

Parameters:

Name Type Description Default
mime_type str

The MIME type of the content.

required
body str | bytes

The response body.

required

Returns:

Type Description
SpartanResponse

A SpartanResponse with status 2.

Source code in src/teyaotlani/protocol/response.py
@classmethod
def success(cls, mime_type: str, body: str | bytes) -> "SpartanResponse":
    """Create a success response.

    Args:
        mime_type: The MIME type of the content.
        body: The response body.

    Returns:
        A SpartanResponse with status 2.
    """
    return cls(status=2, meta=mime_type, body=body)

redirect classmethod

redirect(url)

Create a redirect response.

Parameters:

Name Type Description Default
url str

The redirect URL.

required

Returns:

Type Description
SpartanResponse

A SpartanResponse with status 3.

Source code in src/teyaotlani/protocol/response.py
@classmethod
def redirect(cls, url: str) -> "SpartanResponse":
    """Create a redirect response.

    Args:
        url: The redirect URL.

    Returns:
        A SpartanResponse with status 3.
    """
    return cls(status=3, meta=url)

client_error classmethod

client_error(message)

Create a client error response.

Parameters:

Name Type Description Default
message str

The error message.

required

Returns:

Type Description
SpartanResponse

A SpartanResponse with status 4.

Source code in src/teyaotlani/protocol/response.py
@classmethod
def client_error(cls, message: str) -> "SpartanResponse":
    """Create a client error response.

    Args:
        message: The error message.

    Returns:
        A SpartanResponse with status 4.
    """
    return cls(status=4, meta=message)

server_error classmethod

server_error(message)

Create a server error response.

Parameters:

Name Type Description Default
message str

The error message.

required

Returns:

Type Description
SpartanResponse

A SpartanResponse with status 5.

Source code in src/teyaotlani/protocol/response.py
@classmethod
def server_error(cls, message: str) -> "SpartanResponse":
    """Create a server error response.

    Args:
        message: The error message.

    Returns:
        A SpartanResponse with status 5.
    """
    return cls(status=5, meta=message)

__str__

__str__()

Return human-readable string representation.

Source code in src/teyaotlani/protocol/response.py
def __str__(self) -> str:
    """Return human-readable string representation."""
    lines = [f"Status: {self.status} - {self.meta}"]
    if self.url:
        lines.append(f"URL: {self.url}")
    if self.body:
        size = len(self.body) if isinstance(self.body, bytes) else len(self.body)
        lines.append(f"Body: {size} bytes")
    return "\n".join(lines)

Status Codes

StatusCode

teyaotlani.StatusCode

Bases: IntEnum

Spartan protocol status codes.

Status codes

2 - Success: Meta contains MIME type, body follows 3 - Redirect: Meta contains redirect URL 4 - Client error: Meta contains error message 5 - Server error: Meta contains error message

Status Helper Functions

is_success

teyaotlani.is_success

is_success(status)

Check if status indicates success.

Source code in src/teyaotlani/protocol/status.py
def is_success(status: int) -> bool:
    """Check if status indicates success."""
    return status == StatusCode.SUCCESS

is_redirect

teyaotlani.is_redirect

is_redirect(status)

Check if status indicates redirect.

Source code in src/teyaotlani/protocol/status.py
def is_redirect(status: int) -> bool:
    """Check if status indicates redirect."""
    return status == StatusCode.REDIRECT

is_error

teyaotlani.is_error

is_error(status)

Check if status indicates any error (client or server).

Source code in src/teyaotlani/protocol/status.py
def is_error(status: int) -> bool:
    """Check if status indicates any error (client or server)."""
    return status in (StatusCode.CLIENT_ERROR, StatusCode.SERVER_ERROR)

interpret_status

teyaotlani.interpret_status

interpret_status(status)

Return human-readable status category name.

Parameters:

Name Type Description Default
status int

Single-digit status code (2-5).

required

Returns:

Type Description
str

Human-readable status category.

Examples:

>>> interpret_status(2)
'SUCCESS'
>>> interpret_status(4)
'CLIENT ERROR'
Source code in src/teyaotlani/protocol/status.py
def interpret_status(status: int) -> str:
    """Return human-readable status category name.

    Args:
        status: Single-digit status code (2-5).

    Returns:
        Human-readable status category.

    Examples:
        >>> interpret_status(2)
        'SUCCESS'
        >>> interpret_status(4)
        'CLIENT ERROR'
    """
    if status == 2:
        return "SUCCESS"
    if status == 3:
        return "REDIRECT"
    if status == 4:
        return "CLIENT ERROR"
    if status == 5:
        return "SERVER ERROR"
    return "UNKNOWN"

Constants

DEFAULT_PORT

teyaotlani.DEFAULT_PORT module-attribute

DEFAULT_PORT = 300