Skip to main content

FastTcpAPI

FastAPI-style command routing for TCP services with customizable frames.

Install

pip install fasttcpapi

Server

import asyncio
from fasttcpapi import Server

server = Server()

@server.command("echo")
def echo(message: str, prefix: str = ""):
    return {"message": prefix + message}

@server.command("async_echo")
async def async_echo(message: str):
    return {"message": message}

@server.command("count", response_frames=2)
def count():
    yield 1
    yield 2

@server.command("async_count", response_frames=2)
async def async_count():
    yield {"tick": 1}
    yield {"tick": 2}

if __name__ == "__main__":
    server.exec("127.0.0.1", 9000)

Both def and async def handlers may use return for one response. Both regular generators and async generators may use yield for multiple responses; set response_frames to the exact number of yielded values. Use timeout to control the client wait time. Any exception raised by a handler is converted by the frame implementation and returned to the client.

After await server.start(...), server.address, server.host, and server.port expose the actual bound listener address. This is especially useful when starting with port=0.

Pushes

import time

@server.push("clock.push")
async def clock_push():
    while True:
        yield {"unix_time": time.time()}
        await asyncio.sleep(1)

The push handler starts once for each connected client.

Routers

Use Router to group commands and include them in a server. Routers can be nested; there is no automatic prefix, so command names remain unchanged.

from fasttcpapi import Router, Server

common = Router()

@common.command("ping")
def ping():
    return "pong"

admin = Router()
admin.include_router(common)

server = Server()
server.include_router(admin)

Client

import asyncio
from fasttcpapi import Client

async def main():
    client = Client(server_host="127.0.0.1", server_port=9000)
    print(await client.echo("FastTcpAPI", prefix="Hello, "))
    push = await client.next_push()
    print(push.command, push.args)
    await client.close()

asyncio.run(main())

client.server_address exposes the target server address. client.self_address exposes the connected local socket address, or the configured local bind address before connecting.

The built-in JsonFrame assigns a UUID session ID to each newly created frame. Custom frame implementations may keep the base default of None or provide their own session ID strategy.

The command proxy uses __call__ and validates arguments from the server definition before sending a request. Multi-frame commands return a list. A missing response raises TimeoutError; remote errors are raised by Frame.result().

With the built-in JsonFrame, responses use a four-byte big-endian length prefix followed by JSON. The response object has command: "response", the request session_id, an empty args array, and kwargs. Successful responses contain {"success": true, "data": value}. Failed responses contain {"success": false, "data": [exception arguments], "exception": "TypeName", "traceback": "..."}; built-in exceptions are reconstructed by the client.

Command parameters follow normal Python signatures, including default values. Client and Server accept strict_type_check (default True). In strict mode values must already have the annotated type; with False, annotated values are converted with that type. Any and unannotated parameters skip checking and conversion. bytes and bytearray parameters are supported by custom binary frames and must be the final parameter.

For blocking applications:

client = Client("127.0.0.1", 9000, sync=True)
value = client.echo("FastTcpAPI")

For a concurrent.futures.Future:

future = client.submit("echo", "FastTcpAPI")
value = future.result()

One Client instance can be used from multiple threads and event loops. Call await client.close() when it is no longer needed.

Use the same custom frame type on the client when the service does not use the built-in JSON frame:

client = Client("127.0.0.1", 9000, frame_type=MyFrame)
client.set_service_definition([...])

The client normally fetches the service definition automatically. It can also be supplied manually with set_service_definition:

client.set_service_definition([
    {
        "command": "echo",
        "parameters": [
            {"name": "message", "type": "str", "kind": "POSITIONAL_OR_KEYWORD",
             "has_default": False, "default": None},
        ],
        "response_frames": 1,
        "timeout": 30.0,
    },
    {"command": "clock.push", "push": True, "parameters": []},
])

Each normal command definition contains command, parameters, response_frames, and timeout. Each parameter contains name, type, kind, has_default, and default. A push definition contains command, push: true, and optionally parameters. Supplying a definition skips the automatic definition request.

Custom Frames

Implement Frame and pass the class to Server. The framework does not impose a wire layout. Your frame defines how to encode and decode the command, arguments, keyword arguments, session ID, and protocol-specific metadata.

from fasttcpapi import Frame, Server

class MyFrame(Frame):
async def decode(self, reader): ...
    def parse_args(self, param_list): ...
    def set_result(self, result, request): ...
    def set_exception(self, exception, request): ...
    def result(self): ...
    def encode(self) -> bytes: ...

server = Server(MyFrame)

set_result and set_exception receive the original request frame, so responses can reuse its session ID and custom metadata.

For binary payloads, decode_typed_arguments supports int, float, bool, NUL-terminated str, and ctypes types:

from fasttcpapi import decode_typed_arguments
values = decode_typed_arguments(payload, param_list, byteorder="little")

See examples/ for complete JSON and binary frame implementations.

Callbacks

Both Server and Client support optional synchronous or asynchronous logging callbacks:

server.add_request_callback(on_request)
server.add_response_callback(on_response)
server.add_push_callback(on_push)

client.add_request_callback(on_request)
client.add_response_callback(on_response)
client.add_push_callback(on_push)
client.add_retry_connect_callback(on_retry_connect)

Functions registered with @server.on_xxx or @client.on_xxx omit the owning server/client parameter; the decorator supplies and removes it internally. Callbacks registered through add_xxx_callback keep the owning object as their first parameter.

The retry callback can also be registered as @client.on_retry_connect.

Client connected, disconnected, and retry_connect callbacks receive the Client instance. Client request callbacks receive the assembled request frame; response and push callbacks also receive their frame.

Server connection, request, response, and push callbacks receive a ClientConnection object as their first argument. It exposes reader, writer, address, and a mutable metadata dictionary for connection-local state.

Both constructors accept a logger class. Subclass ServerLogger or ClientLogger, implement its constructor with the owning server/client, and override its on_xxx methods; a default logger is enabled when no logger is supplied.

Release files for fasttcpapi 0.1.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for fasttcpapi 0.1.3
File Size Uploaded
fasttcpapi-0.1.3.tar.gz 28.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for fasttcpapi 0.1.3
File Interpreter ABI Platform
fasttcpapi-0.1.3-py3-none-any.whl Python 3 none any Details

Total release size: 54.0 kB

Release files / fasttcpapi-0.1.3.tar.gz

Download URL fasttcpapi-0.1.3.tar.gz
Size 28.0 kB
Tags Source
SHA-256 checksum
How to use checksums
01c2edf64c4f4a799b00121554f012c06545e9d3d01ed81ff2d7b9571ae2bf90
BLAKE2b-256 checksum
How to use checksums
39248530e3bfb06b80867453a5ab342ef7bf8f8c081115be7525e8aa92092b9f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.3

Release files / fasttcpapi-0.1.3-py3-none-any.whl

Download URL fasttcpapi-0.1.3-py3-none-any.whl
Size 26.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4e6b201c673d1aa5f029cbfb75f838ce3ff5f839995e3c4e58593d1c5d091584
BLAKE2b-256 checksum
How to use checksums
eb4ddf24587e46f86ce1239cae9ede0cb4fc1806b4365129d53b7d3fcffde330
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.3

Release history Release notifications | RSS feed

This release

0.1.3 This release

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page