Skip to content

Event Handlers

Event handlers attach to a ComputeStep and react to the events it emits. ResultsHandler keeps the best (or last) result, HistoryHandler keeps everything, DataFrameHandler writes a structured table, CallbackHandler forwards selected events to a user callback, and EventForwardHandler forwards events to an EventDispatcher for lock-free dispatch.

The EventDispatcher fans events out to its registered handlers from the asyncio event loop's thread, so handlers shared across concurrent compute steps need no locking.

See Optimization Workflows and Working with Results for usage.

ropt.components.event_handlers.EventHandler

Bases: ABC

Abstract base class for event handlers.

A concrete handler reacts to the events emitted by a ComputeStep it is attached to, by implementing handle_event. Handlers may store state using dictionary-like access ([]).

Note

A handler's handle_event is not re-entrant, and not safe to call from two threads at once: both raise WorkflowError. Register a handler shared across concurrently running steps with an EventDispatcher instead, which serializes the calls. See Optimization Workflows and Parallel Evaluation for usage and pitfalls.

__init__

__init__() -> None

Initialize the EventHandler.

claim

claim() -> None

Claim this handler for exclusive use by one run at a time.

Claiming marks the handler as dedicated to a single consumer, such as one optimization run, until it is released with release. While a claim is held, a second claim raises, so a handler can never be shared by two runs at once; releasing it at the end of a run lets the same handler be reused by a later, sequential run, for example to accumulate results. Handlers meant to aggregate across concurrent runs are not claimed; they are shared explicitly through an EventDispatcher.

This claim is independent of the attachment to a dispatcher or a compute step, and of the transient concurrency guard on handle_event.

Raises:

Type Description
WorkflowError

If the handler is currently claimed.

release

release() -> None

Release a claim taken with claim so the handler can be reused.

Clears the exclusive-use flag, letting a later run claim the handler again, for example to accumulate results across sequential runs. The attachment to a dispatcher or a compute step is left untouched. Releasing an unclaimed handler is a no-op.

event_types abstractmethod property

event_types: set[EnOptEventType]

The event types that are handled.

Returns:

Type Description
set[EnOptEventType]

A set of event types that are handled.

handle_event

handle_event(event: EnOptEvent) -> None

React to an emitted event.

Parameters:

Name Type Description Default
event EnOptEvent

The event object.

required

Raises:

Type Description
WorkflowError

If this handler is already running on another thread.

__getitem__

__getitem__(key: str) -> Any

Retrieve a stored value by key (handler[key]).

Parameters:

Name Type Description Default
key str

The string key identifying the value to retrieve.

required

Returns:

Type Description
Any

The value associated with the specified key.

Raises:

Type Description
AttributeError

If key does not exist in the stored values.

__setitem__

__setitem__(key: str, value: Any) -> None

Store or update a value in the internal state (handler[key] = value).

Parameters:

Name Type Description Default
key str

The string key identifying the value to store (must be an identifier).

required
value Any

The value to associate with the key.

required

Raises:

Type Description
AttributeError

If key is not a valid identifier.

ropt.components.event_handlers.ResultsHandler

Bases: EventHandler

Track a single optimization result based on selection criteria.

Listens for FINISHED_EVALUATION events and retains either the best (lowest weighted objective) or most recent valid result. Optionally filters by constraint tolerance. The selected result is accessible via the result property or handler["results"].

See Result Handlers for full details on selection criteria and scaling.

__init__

__init__(
    *,
    what: Literal["best", "last"] = "best",
    constraint_tolerance: float | None = None,
    filter: Callable[[Results], bool] | None = None,
) -> None

Initialize the ResultsHandler.

Parameters:

Name Type Description Default
what Literal['best', 'last']

Criterion for selecting results ('best' or 'last').

'best'
constraint_tolerance float | None

Optional threshold for filtering constraint violations. Violations are compared in the domain the optimizer works in, so a scale applies to them as well.

None
filter Callable[[Results], bool] | None

Optional callable to filter results based on custom logic.

None

result property

result: FunctionResults | None

The selected (best or last) result, or None if none is available.

event_types property

event_types: set[EnOptEventType]

The event types that are handled.

Returns:

Type Description
set[EnOptEventType]

A set of event types that are handled.

ropt.components.event_handlers.HistoryHandler

Bases: EventHandler

Collect all optimization results into a tuple.

Listens for FINISHED_EVALUATION events and appends every Results object to a growing tuple accessible via the results property or handler["results"].

See Result Handlers for full details on scaling and accumulation behavior.

__init__

__init__() -> None

Initialize the HistoryHandler.

results property

results: tuple[Results, ...]

All results collected so far, in the order received.

event_types property

event_types: set[EnOptEventType]

The event types that are handled.

Returns:

Type Description
set[EnOptEventType]

A set of event types that are handled.

ropt.components.event_handlers.DataFrameHandler

Bases: EventHandler

Build pandas or polars DataFrames from optimization results.

Collects FunctionResults and GradientResults into named tables. Tables are defined via add_table with a column specification, or registered in bulk with set_default_tables.

The frame library is chosen with the engine argument. The "polars" engine, the default, produces long-format tables in which batch and axis labels are ordinary leading columns; the "pandas" engine produces tables indexed by those instead.

Access tables via dictionary syntax: handler["functions"].

Warning

Tables are generated on the fly from internal data when accessing them in this way. When multiple accesses are needed, it is more efficient to first store them in a variable.

See Result Handlers for the column specification, default tables, and callback details.

__init__

__init__(
    *, engine: DataFrameEngine = "polars", sep: str = ","
) -> None

Initialize a default table event handler.

Parameters:

Name Type Description Default
engine DataFrameEngine

Frame library used to build the tables.

'polars'
sep str

Separator used in column names.

','

Raises:

Type Description
ValueError

If engine is not "pandas" or "polars".

UnsupportedError

If the selected frame library is not installed.

engine property

engine: DataFrameEngine

The frame library used to build the tables.

Returns:

Type Description
DataFrameEngine

Either "pandas" or "polars".

set_default_tables

set_default_tables() -> None

Register a standard set of result tables.

Adds the default functions, evaluations, and constraints tables for function results, and the default gradients and perturbations tables for gradient results.

set_callback

set_callback(
    callback: Callable[[Path | None], None],
) -> None

Set a function to call whenever the tables are updated.

The callback receives the output directory configured for the run (OptimizerConfig.output_dir, None if it is not set), and reads the tables from this handler. If it performs blocking operations (for example writing tables to disk), register this handler with run_in_thread=True on the EventDispatcher:

event_dispatcher.add_event_handler(table_handler, run_in_thread=True)

Parameters:

Name Type Description Default
callback Callable[[Path | None], None]

A function that is called when the tables are updated.

required

add_table

add_table(
    name: str,
    table_type: Literal["functions", "gradients"],
    columns: dict[str, str],
) -> None

Register a new table to be populated from incoming results.

Parameters:

Name Type Description Default
name str

Key under which the table is stored and looked up.

required
table_type Literal['functions', 'gradients']

Whether this table is filled from function results ("functions") or gradient results ("gradients").

required
columns dict[str, str]

Mapping from result-field attribute names (using dotted attribute syntax) to display titles.

required

get_tables

get_tables() -> dict[str, pd.DataFrame | pl.DataFrame]

Return the tables stored in the event handler.

Returns:

Type Description
dict[str, DataFrame | DataFrame]

A dictionary mapping table names to their corresponding tables.

Warning

Tables are generated on the fly from internal data. When multiple access is needed, it is more efficient to first store them in a variable.

event_types property

event_types: set[EnOptEventType]

The event types that are handled.

Returns:

Type Description
set[EnOptEventType]

A set of event types that are handled.

__getitem__

__getitem__(key: str) -> Any

Retrieve a of a table from the event handler.

Warning

The table is generated on the fly from internal data hen multiple access are needed, it is more efficient to first store them in a variable.

Parameters:

Name Type Description Default
key str

The string key identifying the table to retrieve.

required

Returns:

Type Description
Any

The table associated with the specified key.

Raises:

Type Description
AttributeError

If the requested table does not exist.

add_column

add_column(table: str, name: str, title: str) -> None

Add a column to a given table.

Parameters:

Name Type Description Default
table str

The name of the table to add the column to.

required
name str

The name of the field to add as a column, using attribute syntax.

required
title str

The title of the column to add.

required

ropt.components.event_handlers.CallbackHandler

Bases: EventHandler

The default event handler for observing events.

This event handler listens for events of matching types and forwards them to a callback function.

If the callback performs blocking operations (file I/O, network calls, etc.), register this handler with run_in_thread=True on the EventDispatcher:

event_dispatcher.add_event_handler(handler, run_in_thread=True)

__init__

__init__(
    *,
    event_types: set[EnOptEventType],
    callback: Callable[[EnOptEvent], None],
) -> None

Initialize the CallbackHandler.

Parameters:

Name Type Description Default
event_types set[EnOptEventType]

The set of event types to respond to.

required
callback Callable[[EnOptEvent], None]

The callable to invoke for matching events.

required

event_types property

event_types: set[EnOptEventType]

The event types that are handled.

Returns:

Type Description
set[EnOptEventType]

A set of event types that are handled.

ropt.components.event_handlers.EventForwardHandler

Bases: EventHandler

Forwards events from a compute step to an EventDispatcher.

See Optimization Workflows for usage.

__init__

__init__(
    dispatcher: EventDispatcher,
    *,
    event_types: set[EnOptEventType],
) -> None

Initialize the EventForwardHandler.

Parameters:

Name Type Description Default
dispatcher EventDispatcher

The EventDispatcher to forward events to.

required
event_types set[EnOptEventType]

The set of event types to forward.

required

event_types property

event_types: set[EnOptEventType]

The event types that are handled.

ropt.components.event_handlers.EventDispatcher

Dispatches events to handlers from the asyncio event loop's thread.

Handlers added with run_in_thread=True run on a thread pool the dispatcher owns and shuts down when it stops, so handler work is isolated from the asyncio loop's shared default pool.

See Parallel Evaluation for usage.

add_event_handler

add_event_handler(
    handler: EventHandler, *, run_in_thread: bool = False
) -> None

Add an event handler.

By default the handler is called directly on the event loop's thread. Pass run_in_thread=True for handlers that perform blocking I/O; see Thread-based dispatch.

Parameters:

Name Type Description Default
handler EventHandler

The handler to add.

required
run_in_thread bool

If True, dispatch via the dispatcher's thread pool instead of the event loop.

False

remove_event_handler

remove_event_handler(handler: EventHandler) -> bool

Remove a previously added handler.

The handler is released, so it can afterwards be added to another dispatcher or registered with a compute step.

Parameters:

Name Type Description Default
handler EventHandler

The handler to remove.

required

Returns:

Type Description
bool

Whether the removed handler was set to run in a thread.

Raises:

Type Description
WorkflowError

If the handler was not added to this dispatcher.

dispatch_event

dispatch_event(event: EnOptEvent) -> None

Submit an event and block until every handler has processed it.

Events are handled in submission order. A handler exception is re-raised here, on the caller's own stack. See Handler failures.

Parameters:

Name Type Description Default
event EnOptEvent

The event to submit.

required

Raises:

Type Description
WorkflowError

If the dispatcher is not running, or if the call is made from the thread running its event loop, or from one of its handler threads.

Exception

Whatever a handler raised while processing the event.

start async

start(task_group: TaskGroup) -> None

Start the dispatcher.

Parameters:

Name Type Description Default
task_group TaskGroup

The task group to use.

required

Raises:

Type Description
WorkflowError

If the dispatcher is already running.

cancel

cancel() -> None

Stop the dispatcher.

May be called from any thread.