Skip to main content

Ergon Framework โ€” Python SDK

Python 3.10+ License: MIT Code Style: Ruff

The official Python implementation of the Ergon Frameworkโ€”a transaction-first, observability-native execution engine for building high-throughput automation pipelines.


Installation

pip install ergon-framework-python

Connector integrations are optional extras, so a core installation does not pull in client libraries that your application does not use:

pip install 'ergon-framework-python[rabbitmq]'
pip install 'ergon-framework-python[sqs,postgres]'
pip install 'ergon-framework-python[all]'

Available extras are rabbitmq, sqs, postgres, pipefy, excel, ergon-platform, and nylas.

For local development, install in editable mode:

pip install -e /path/to/ergon-framework/sdks/python

๐Ÿ“– Full Getting Started Guide โ€” Complete setup, project configuration, and task registration


Quick Start

1. Define a Consumer Task

Create a task by inheriting from ConsumerTask and implementing process_transaction:

from typing import Any
from ergon.task.mixins import ConsumerTask
from ergon.connector import Transaction

class OrderProcessor(ConsumerTask):
    """Consumes order transactions and processes them."""

    def process_transaction(self, transaction: Transaction) -> Any:
        # Pure business logicโ€”no I/O, no retries, no protocol details
        order = transaction.payload
        total = order["quantity"] * order["unit_price"]
        return {"order_id": order["id"], "total": total, "status": "processed"}

    def handle_process_success(self, transaction: Transaction, result: Any):
        # Called after successful processing
        self.output_connector.dispatch_transactions([
            Transaction(id=f"out-{transaction.id}", payload=result, metadata={})
        ])

    def handle_process_exception(self, transaction: Transaction, exc: Exception):
        # Called when processing fails (after all retries exhausted)
        self.dlq_connector.dispatch_transactions([transaction])

2. Create a Connector

Connectors bridge external systems to the transaction interface:

from typing import List
from ergon.connector import Connector, Transaction
import uuid

class RabbitMQConnector(Connector):
    def __init__(self, queue_name: str, connection_url: str):
        self.queue_name = queue_name
        # Initialize your RabbitMQ client here
        self.client = create_rabbitmq_client(connection_url)

    def fetch_transactions(self, batch_size: int, **kwargs) -> List[Transaction]:
        messages = self.client.consume(self.queue_name, max_messages=batch_size)
        return [
            Transaction(
                id=str(uuid.uuid4()),
                payload=msg.body,
                metadata={"delivery_tag": msg.delivery_tag, "routing_key": msg.routing_key}
            )
            for msg in messages
        ]

    def dispatch_transactions(self, transactions: List[Transaction], **kwargs):
        for tx in transactions:
            self.client.publish(self.queue_name, tx.payload)

3. Configure and Run

Use TaskConfig to wire everything together:

from ergon.task import TaskConfig, runner, policies
from ergon.connector import ConnectorConfig

# Configure the consumer policy
consumer_policy = policies.ConsumerPolicy()
consumer_policy.name = "consumer"
consumer_policy.loop.concurrency.value = 5      # Process 5 transactions in parallel
consumer_policy.loop.batch.size = 10            # Fetch 10 at a time
consumer_policy.loop.streaming = True           # Keep polling for new messages
consumer_policy.process.retry.max_attempts = 3  # Retry failed processing 3 times
consumer_policy.process.retry.backoff = 1.0     # Start with 1s backoff
consumer_policy.process.retry.backoff_multiplier = 2.0  # Double each retry

# Create the task configuration
config = TaskConfig(
    name="order-processor",
    task=OrderProcessor,
    max_workers=1,
    connectors={
        "input": ConnectorConfig(
            connector=RabbitMQConnector,
            kwargs={"queue_name": "orders", "connection_url": "amqp://localhost"}
        ),
        "output": ConnectorConfig(
            connector=RabbitMQConnector,
            kwargs={"queue_name": "processed-orders", "connection_url": "amqp://localhost"}
        ),
    },
    policies=[consumer_policy],
)

# Run the task
if __name__ == "__main__":
    runner.run(config)

The framework handles the loop, retry logic, telemetry, and graceful shutdown automatically.


The Transaction Object

In the Python SDK, transactions are implemented as immutable Pydantic models:

from ergon.connector import Transaction

# Transactions are created by Connectors when fetching data
tx = Transaction(
    id="tx-001",                           # Unique identifier for tracing
    payload={"order_id": 123, "amount": 99.99},  # The actual data
    metadata={"source": "rabbitmq", "queue": "orders"}  # Contextual info
)

Transactions are frozen (immutable) once created via Pydantic's frozen=True configuration.


Task Classes

The Python SDK provides ready-to-use task classes:

from ergon.task.mixins import (
    ConsumerTask,      # Sync consumer
    ProducerTask,      # Sync producer
    HybridTask,        # Sync consumer + producer
    AsyncConsumerTask, # Async consumer
    AsyncProducerTask, # Async producer
    AsyncHybridTask,   # Async consumer + producer
)

Synchronous Tasks

Best for CPU-bound processing or legacy libraries:

from ergon.task.mixins import ConsumerTask

class SyncProcessor(ConsumerTask):
    def process_transaction(self, transaction):
        # Runs in ThreadPoolExecutor
        return heavy_computation(transaction.payload)
  • Concurrency controlled by policy.loop.concurrency.value
  • Each transaction processed in its own thread
  • Multi-process scaling via max_workers > 1

Asynchronous Tasks

Best for I/O-bound workloads with high concurrency:

from ergon.task.mixins import AsyncConsumerTask

class AsyncProcessor(AsyncConsumerTask):
    async def process_transaction(self, transaction):
        # Runs in asyncio event loop
        result = await self.http_client.post(transaction.payload)
        return result
  • Concurrency controlled by asyncio.Semaphore
  • Thousands of concurrent operations with minimal overhead
  • Perfect for API calls, database queries, network I/O

Dependency Injection

Tasks receive all dependencies automatically via the TaskMeta metaclass:

class EnrichmentTask(ConsumerTask):
    def process_transaction(self, transaction: Transaction) -> Any:
        # Access connectors as self.{name}_connector
        count = self.input_connector.get_transactions_count()
      
        # Access services as self.{name}_service
        enriched = self.openai_service.complete(transaction.payload["text"])
      
        # Access policies as self.{name}_policy
        timeout = self.consumer_policy.loop.timeout
      
        return {"enriched": enriched, "pending": count}

Configure services in TaskConfig:

from ergon.connector import ServiceConfig

config = TaskConfig(
    name="enrichment-task",
    task=EnrichmentTask,
    connectors={"input": input_connector_config},
    services={
        "openai": ServiceConfig(
            service=OpenAIService,
            kwargs={"api_key": "sk-...", "model": "gpt-4"}
        )
    },
    policies=[consumer_policy],
)

Policy Configuration

Policies are created as instances and configured by setting properties directly:

from ergon.task import policies

# Consumer policy with full configuration
policy = policies.ConsumerPolicy()
policy.name = "my-consumer"

# Loop behavior
policy.loop.concurrency.value = 10      # Parallel transaction processing
policy.loop.batch.size = 50             # Transactions per fetch
policy.loop.streaming = True            # Continuous polling mode
policy.loop.timeout = 3600              # Max loop duration (seconds)
policy.loop.limit = 1000                # Max transactions to process

# Empty queue behavior (for streaming mode)
policy.loop.empty_queue.backoff = 1.0
policy.loop.empty_queue.backoff_multiplier = 2.0
policy.loop.empty_queue.backoff_cap = 30.0

# Per-step retry configuration
policy.fetch.retry.max_attempts = 5
policy.fetch.retry.timeout = 30

policy.process.retry.max_attempts = 3
policy.process.retry.backoff = 1.0
policy.process.retry.backoff_multiplier = 2.0
policy.process.retry.backoff_cap = 60.0

policy.success.retry.max_attempts = 2
policy.exception.retry.max_attempts = 2

Observability

The Python SDK integrates OpenTelemetry natively:

from ergon.telemetry import logging, tracing, metrics

config = TaskConfig(
    name="observed-task",
    task=MyTask,
    connectors={...},
    logging=logging.LoggingConfig(
        level="INFO",
        handlers=[
            logging.ConsoleLogHandler(),
            logging.OTLPLogHandler(endpoint="http://collector:4317"),
        ]
    ),
    tracing=tracing.TracingConfig(
        processors=[
            tracing.SpanProcessor(
                processor=tracing.BatchSpanProcessor,
                exporters=[tracing.OTLPSpanExporter(endpoint="http://collector:4317")]
            )
        ]
    ),
    metrics=metrics.MetricsConfig(
        readers=[
            metrics.MetricReader(
                reader=metrics.PeriodicExportingMetricReader,
                exporters=[metrics.OTLPMetricExporter(endpoint="http://collector:4317")]
            )
        ]
    ),
)

Logging

  • Structured JSON logging via python-json-logger
  • Automatic trace_id and span_id injection
  • Multiple handlers: Console, File, RotatingFile, OTLP

Tracing

  • Hierarchical spans: Task โ†’ Batch โ†’ Transaction โ†’ Attempt
  • Full context propagation across async boundaries
  • Export to Jaeger, Tempo, or any OTLP-compatible backend

Metrics

  • Push-based via PeriodicExportingMetricReader
  • Automatic resource attributes (task name, host, PID, execution ID)
  • Export to Prometheus, OTLP, or console

Scaling

Axis Mechanism Configuration
Process ProcessPoolExecutor TaskConfig.max_workers
Thread ThreadPoolExecutor policy.loop.concurrency.value
Async asyncio.Semaphore policy.loop.concurrency.value
Connector External system partitioning Service-specific (consumer groups, shards)
# Multi-process scaling (sync tasks only)
config = TaskConfig(
    name="scaled-task",
    task=MyTask,
    max_workers=4,  # Spawn 4 isolated worker processes
    connectors={...},
)

Project Structure

After scaffolding with ergon init:

my-project/
โ”œโ”€โ”€ main.py                  # Application entry point
โ”œโ”€โ”€ _observability/          # Telemetry infrastructure configs
โ”œโ”€โ”€ connectors/              # Custom connectors and services
โ”‚   โ””โ”€โ”€ {connector_name}/    # One submodule per connector
โ”‚       โ”œโ”€โ”€ connector.py     # Transaction interface
โ”‚       โ””โ”€โ”€ service.py       # Protocol mechanics
โ””โ”€โ”€ tasks/                   # Task definitions and shared modules
    โ”œโ”€โ”€ settings.py          # Global configs (connectors, services, telemetry)
    โ”œโ”€โ”€ constants.py         # Global constants and enums
    โ”œโ”€โ”€ schemas.py           # Shared Pydantic models
    โ”œโ”€โ”€ exceptions.py        # Shared exception classes
    โ”œโ”€โ”€ helpers.py           # Shared utility functions
    โ””โ”€โ”€ {task_name}/         # Per-task submodule
        โ”œโ”€โ”€ task.py          # Task implementation
        โ”œโ”€โ”€ config.py        # TaskConfig definition
        โ”œโ”€โ”€ schemas.py       # Task-specific models
        โ”œโ”€โ”€ exceptions.py    # Task-specific exceptions
        โ””โ”€โ”€ helpers.py       # Task-specific utilities

๐Ÿ“– Full Project Structure Guide โ€” Detailed documentation on organizing connectors, tasks, and shared modules.


Documentation

Python SDK Guides

Framework Concepts


License

This project is licensed under the MIT License.


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ergon_framework_python-0.2.1.tar.gz (102.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ergon_framework_python-0.2.1-py3-none-any.whl (130.3 kB view details)

Uploaded Python 3

File details

Details for the file ergon_framework_python-0.2.1.tar.gz.

File metadata

  • Download URL: ergon_framework_python-0.2.1.tar.gz
  • Upload date:
  • Size: 102.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.3 {"installer":{"name":"uv","version":"0.12.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for ergon_framework_python-0.2.1.tar.gz
Algorithm Hash digest
SHA256 4668c172f45b7e14c37c51a141ba3c3a43fa1a754a2dc9f7adb6f2fcff9d074f
MD5 fa935b52416dd8aaab500cad9ff4a005
BLAKE2b-256 9bd4a23d19ad4d5e14c4fe6822a6b0fe11bdec4993df4172aa8215ae97cb6de6

See more details on using hashes here.

File details

Details for the file ergon_framework_python-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: ergon_framework_python-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 130.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.3 {"installer":{"name":"uv","version":"0.12.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for ergon_framework_python-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 c6cfbc2fbff33919f19936bd2e16dbb77d1439470469fe83c07892f1a0b48a1b
MD5 8f6fc963628a6260aa4e1daa8a9c6393
BLAKE2b-256 ddc9390cd4220b03120ecb1ca1569c6081f80277f6c7f43cd4f5fb56975db1f6

See more details on using hashes here.

Release history Release notifications | RSS feed

0.2.4

2 files

0.2.3

2 files

0.2.2

2 files

This release

0.2.1 This release

2 files

0.2.0

2 files

0.1.9

2 files

0.1.8

2 files

0.1.7

2 files

0.1.6

2 files

0.1.5

2 files

0.1.4

2 files

0.1.3

2 files

0.1.2

2 files

0.1.1

2 files

0.1.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page