Temporal.io Python SDK

raw JSON →
1.24.0 verified Tue May 12 auth: no python install: draft

The Temporal.io Python SDK (current version 1.24.0) provides a framework for authoring durable workflows and activities using the Python programming language. It enables developers to build resilient, scalable, and fault-tolerant applications that can execute long-running business logic reliably. The library maintains a regular release cadence, often with monthly or bi-monthly updates, introducing new features, improvements, and bug fixes.

pip install temporalio
error grpc._channel._MultiThreadedRendezvous: <_MultiThreadedRendezvous of RPC that terminated with: status = StatusCode.UNAVAILABLE
cause The Temporal client cannot establish a connection with the Temporal server, usually because the server is not running or is unreachable at the specified address.
fix
Ensure the Temporal server is running and confirm the target_host (e.g., localhost:7233) used in Client.connect() matches the server's address and port.
error ApplicationError: type='temporal.workflow.NotFound', message='Workflow with name "MyWorkflow" is not registered on the worker.'
cause The worker instance has not been configured to register the workflow class, or the task queue used to start the workflow does not match the worker's task queue.
fix
Add the workflow class to the worker's workflows list and ensure the worker's task_queue argument matches the task queue specified when starting the workflow. Example: Worker(client, task_queue='my-task-queue', workflows=[MyWorkflow]).
error RuntimeError: Workflow must not 'await' non-Temporal futures. Instead, use 'temporalio.workflow.asyncio.sleep', 'temporalio.workflow.asyncio.wait', etc.
cause Temporal workflows require deterministic execution and cannot directly await arbitrary Python `asyncio` futures or blocking calls; they must use Temporal's specific asynchronous primitives or delegate to activities.
fix
Replace non-Temporal await calls (e.g., await asyncio.sleep(), await aiohttp.get()) with their deterministic Temporal counterparts (e.g., await workflow.sleep()) or encapsulate the non-deterministic logic within a Temporal Activity.
error ModuleNotFoundError: No module named 'temporalio'
cause The `temporalio` Python SDK library has not been installed in the current Python environment.
fix
Install the library using pip: pip install temporalio.
breaking Python 3.9 support was removed in version 1.19.0 as it reached End-of-Life. Users on Python 3.9 must upgrade to Python 3.10 or newer.
fix Upgrade Python environment to 3.10 or a later supported version.
breaking In version 1.23.0, fields `workflow_id`, `workflow_namespace`, `workflow_run_id`, and `workflow_type` within `activity.Info` were made optional. Additionally, `converter.BaseWorkflowSerializationContext` was removed.
fix Adjust code that manually accesses `activity.Info` fields to handle potential `None` values. Refactor any custom `BaseWorkflowSerializationContext` subclasses.
breaking Version 1.24.0 introduces general availability for Nexus and OpenAI Agents SDK Integration. It also includes new OpenTelemetry integration for OpenAI Agents, which may cause conflicts or require adjustments for existing custom OpenTelemetry setups.
fix Review and update OpenTelemetry tracing configurations if using OpenAI Agents with custom instrumentation. Leverage the GA features of Nexus and OpenAI Agents.
gotcha Beginning with version 1.21.0, providing an `api_key` to `Client.connect` automatically enables TLS.
fix If you wish to use an `api_key` without TLS, explicitly pass `tls=False` to `Client.connect`.
gotcha The Workflow Sandbox isolates workflow code to ensure determinism. Importing non-standard library or non-`temporalio` modules directly into workflow files can lead to `RestrictedWorkflowAccessError` or non-determinism.
fix For modules needed only by activities, import them within the activity function, or use `with workflow.unsafe.imports_passed_through(): import my_module` when importing them into workflow files. For performance, pass through any side-effect-free third-party libraries explicitly.
breaking Installing `temporalio` on Alpine Linux (especially with newer Python versions for which pre-built wheels may not exist) can fail due to missing build dependencies like `libgcc_s.so.1` or incompatible Rust compilation. Alpine's musl libc often requires specific system packages or a different Rust target to successfully compile native extensions.
fix On Alpine Linux, ensure `build-base`, `gcc`, `g++`, and `rustup` (with the `x86_64-unknown-linux-musl` target) are installed in the build environment. For simpler deployments, consider using a glibc-based Python image (e.g., Debian-based) where pre-built `temporalio` wheels are more commonly available.
python os / libc status wheel install import disk
3.10 alpine (musl) build_error - - - -
3.10 alpine (musl) - - - -
3.10 slim (glibc) wheel 3.0s 0.43s 75M
3.10 slim (glibc) - - 0.43s 73M
3.11 alpine (musl) build_error - - - -
3.11 alpine (musl) - - - -
3.11 slim (glibc) wheel 2.9s 1.08s 78M
3.11 slim (glibc) - - 1.07s 75M
3.12 alpine (musl) build_error - - - -
3.12 alpine (musl) - - - -
3.12 slim (glibc) wheel 2.6s 1.25s 70M
3.12 slim (glibc) - - 1.31s 67M
3.13 alpine (musl) build_error - - - -
3.13 alpine (musl) - - - -
3.13 slim (glibc) wheel 2.9s 1.17s 69M
3.13 slim (glibc) - - 1.24s 67M
3.9 alpine (musl) build_error - - - -
3.9 alpine (musl) - - - -
3.9 slim (glibc) wheel 3.4s 0.41s 67M
3.9 slim (glibc) - - 0.47s 67M

This quickstart demonstrates how to define an activity and a workflow, configure and start a Temporal worker to process tasks, and execute a workflow. It connects to a local Temporal server (defaulting to `localhost:7233`) and includes `os.environ.get` for flexible configuration.

import asyncio
from datetime import timedelta
from temporalio.client import Client
from temporalio.worker import Worker
from temporalio import activity, workflow

# Define an activity
@activity.defn
async def say_hello(name: str) -> str:
    return f"Hello, {name}!"

# Define a workflow
@workflow.defn
class GreetingWorkflow:
    @workflow.run
    async def run(self, name: str) -> str:
        return await workflow.execute_activity(
            say_hello,
            name,
            schedule_to_close_timeout=timedelta(seconds=5),
        )

async def main():
    # Connect to Temporal server (default to localhost:7233)
    # Use os.environ.get('TEMPORAL_HOST_PORT', 'localhost:7233') for dynamic connection
    client = await Client.connect(os.environ.get('TEMPORAL_HOST_PORT', 'localhost:7233'))

    # Run a worker
    task_queue_name = "my-task-queue"
    worker = Worker(
        client,
        task_queue=task_queue_name,
        workflows=[GreetingWorkflow],
        activities=[say_hello],
    )
    # Start worker in background
    worker_task = asyncio.create_task(worker.run())
    print(f"Worker started on task queue '{task_queue_name}'...")

    # Start a workflow execution
    result = await client.execute_workflow(
        GreetingWorkflow.run,
        "Temporal",
        id="greeting-workflow-id",
        task_queue=task_queue_name,
    )
    print(f"Workflow result: {result}") # Expected: "Hello, Temporal!"

    # Clean up worker
    worker_task.cancel()
    await worker_task

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