Skip to content

Async-only

Every rboto service operation is an async def method. There are no synchronous operation variants or sync_ methods.

While a request waits for the network, the Python event loop can run other coroutines. Sequential await calls still run one at a time. Your application chooses which requests can run concurrently.

Scripts

Put requests in an async function and start the event loop once:

import asyncio

from rboto import STSClient


async def main() -> None:
    client = STSClient(region="us-east-1")
    response = await client.get_caller_identity()
    print(response.account)
    print(response.arn)


if __name__ == "__main__":
    asyncio.run(main())

Constructing the client does not require await. Calling an operation without awaiting or scheduling it creates a coroutine and does not send the request.

Applications and notebooks

Inside an async handler, job, or notebook with an active event loop, await the operation directly. Do not call asyncio.run() inside an already running loop.

from rboto import SSMClient


async def read_parameter(client: SSMClient, name: str) -> str | None:
    response = await client.get_parameter(name=name, with_decryption=True)
    if response.parameter is None:
        return None
    return response.parameter.value

Create clients during application setup and pass them to code that needs them. The current clients do not implement context managers or a close() method.

For a synchronous Lambda entry point that calls async code, see AWS Lambda.

Concurrent requests

Use asyncio.gather() for a small, known set of independent operations:

import asyncio

from rboto import SSMClient


async def read_settings(client: SSMClient) -> None:
    database, queue = await asyncio.gather(
        client.get_parameter(name="/app/database"),
        client.get_parameter(name="/app/queue"),
    )
    for response in (database, queue):
        if response.parameter is not None:
            print(response.parameter.name, response.parameter.value)

Both calls reuse one client. Concurrency does not change service quotas or make an individual request inherently faster.

For large inputs, keep a bounded number of tasks in flight. A semaphore limits active calls, but creating one task for every input still consumes memory. Use bounded batches or a fixed set of workers.

Deadlines

Use asyncio.timeout() to bound your coroutine's wait:

import asyncio

from rboto import STSClient


async def identify(client: STSClient) -> str | None:
    async with asyncio.timeout(5):
        response = await client.get_caller_identity()
        return response.arn

For streams, keep iteration inside the timeout block if the deadline must include reading the body. Cancellation does not prove that a remote write was rejected. Use the operation's idempotency token when available and account for an uncertain result before repeating a write.