The Long Task Contract
In Chapter 10, we introduced cooperative long-running tasks.
In this chapter, we define the contract precisely.
This is not an implementation detail. It is an execution model.
Why the Contract Exists
Blender operators execute on the main thread.
If a function performs long-running work in a single call:
- The UI freezes
- The user cannot cancel
- Progress is invisible
- Blender appears unstable
The long task contract solves this by enforcing cooperative execution.
Declaring a Long Task
A plugin declares:
@op(
label="Process Frames",
long_task=True,
)
def process_frames(...):
...
If long_task=True, the function must follow the long task contract.
Strict Conformance
If long_task=True is declared:
- The function must be a python generator.
- The generator must yield at least once.
If the function:
- Does not contain
yield - Returns normally without yielding
- Raises
StopIterationimmediately
The build must fail.
QuickAddon enforces this at build time.
Long tasks are not optional behavior. They are a contract.
Strict conformance guarantees:
- Non-blocking execution
- Predictable scheduling
- Safe cancellation
- Stable host control
The Core Rule
A long task must:
Perform incremental work and yield control back to the host after each step.
It must not block until completion.
Required Execution Model
A long task function must behave as a generator.
Conceptually:
def process_frames(...):
total = compute_total()
for i in range(total):
do_one_unit_of_work(i)
yield {
"progress": i,
"total": total,
}
The function:
- Executes a small unit of work
- Yields a progress payload
- Allows the host to resume later
It does not run to completion in a single call.
Yield Payload
Each yield must return a dictionary.
Minimum required fields:
progress
total
Optional fields:
message
Example:
yield {
"progress": i,
"total": total,
"message": f"Processing frame {i}",
}
The host uses this data to:
- Update the progress bar
- Display status messages
- Determine completion
Completion
When the generator finishes:
- The task is considered complete.
- The host performs cleanup.
- Progress UI closes.
- Execution returns
FINISHED.
Cancellation
The host must provide an explicit cancellation mechanism.
Recommended UI model:
- Status bar progress indicator
- Visible cancel button (“X”)
- ESC key support only while modal operator owns execution
When cancellation is triggered:
- Host stops calling
next() - Generator is discarded
- Progress UI closes
- Operator returns
CANCELLED
Cancellation must be:
- Explicit
- Predictable
- Scope-aware (v2)
- Safe
Plugins must not rely on guaranteed completion.
They must tolerate interruption at any yield boundary.
Error Handling
If a long task generator raises an exception:
- The host must stop execution.
- Progress UI must close.
- The error must be reported via Blender operator report.
If a yield payload:
- Is not a dictionary
- Omits required fields
- Contains invalid values
The host must:
- Cancel execution
- Report a contract violation
The long task contract is enforced at runtime as well as at build time.
Host Responsibilities
The host must:
- Detect
long_task=True - Wrap execution in a modal operator
- Schedule incremental
next()calls - Update progress display
- Provide cancellation
- Ensure scope isolation (if scoped)
The host owns scheduling.
Plugins must not implement modal logic directly.
Interaction with Scoped Instances
If HostAPI v2 is used:
- Each scope maintains its own long-task state.
- Progress and cancellation apply only to that scope.
- Multiple scoped instances may run independently.
Isolation must be enforced at the host level.
Forbidden Patterns
A long task must not:
- Execute blocking loops without yielding
- Spawn uncontrolled threads
- Access scene storage directly
- Assume uninterrupted execution
- Manipulate modal handlers directly
The contract enforces discipline.
Short Tasks vs Long Tasks
Short task:
@op(...)
def simple_task(...):
perform_all_work()
Long task:
@op(long_task=True)
def incremental_task(...):
yield ...
The decorator determines the execution strategy.
Why This Matters
The long task contract provides:
- Non-blocking UI
- Visible progress
- Safe cancellation
- Predictable lifecycle
- Stable host-controlled execution
It allows complex processing without destabilizing Blender. In chapter 15 we will expand further we examine how the generator implements this model internally.
In the next chapter, we will examine how the HostAPI paradigm can be used for more complex need.