Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Internal Execution Model

Up to this point, you have seen:

  • How plugins are declared.
  • How shared keys are routed.
  • How long tasks are enforced.
  • How HostAPI v1 and v2 separate storage ownership.

Now we step inside the system.

This chapter explains what actually happens when a user clicks a button.

The Three Phases of Execution

Every QuickAddon tool goes through three distinct phases:

  1. Build-time compilation
  2. Registration-time binding
  3. Runtime execution

Understanding these phases clarifies every design decision.

Phase 1 – Build-Time Compilation

When you run:


quickaddon build my_tool.py

QuickAddon performs:

  1. Module loading
  2. Decorator extraction
  3. Structural validation
  4. Contract validation (QA10)
  5. Render model construction
  6. Template emission

The source file is not executed inside Blender.

It is compiled into a structural add-on module.

At this stage:

  • Shared keys are registered.
  • Types are verified.
  • long_task contracts are enforced.
  • No Blender API calls occur.

Build-time is deterministic.

If it succeeds, the add-on structure is valid.

Phase 2 – Registration-Time Binding

When Blender enables the add-on:

  • Classes are registered.
  • PropertyGroups are created.
  • HostAPI is injected.

In v1:

  • A fallback HostAPI is used if none is injected.

In v2:

  • A named instance is mounted explicitly.
  • The runtime binds a scope for that mounted instance.
  • A bound HostAPI instance is used internally.
  • All shared routing resolves through the bound API.

Registration does not execute tool logic.

It prepares execution boundaries.

Phase 3 – Runtime Execution

When a user clicks a tool button:

Blender invokes the generated Operator.

The operator:

  1. Collects local parameters (if any)
  2. Resolves shared values via HostAPI
  3. Calls the user function

At this point execution diverges:

  • Short task
  • Long task

Short Task Execution Path

For short tasks:

fn(...)
return {'FINISHED'}

The function runs immediately.

If an exception occurs:

  • The operator reports the error.
  • Execution returns CANCELLED.

Short tasks are synchronous and simple.

Long Task Execution Path

If long_task=True:

Execution becomes cooperative.

The operator:

  1. Calls the user function.
  2. Verifies that a generator was returned (QA20).
  3. Starts a modal timer.
  4. Schedules incremental execution.

Instead of running to completion:

User Click
    ↓
Operator.invoke()
    ↓
Start generator
    ↓
Modal loop
    ↓
next(generator)
    ↓
Yield progress
    ↓
Repeat until StopIteration

Each yield returns control to Blender.

The UI remains responsive.

The long task operator owns:

  • Timer creation
  • Modal handler registration
  • Progress bar lifecycle
  • Generator advancement
  • Cleanup on completion or cancellation

The plugin never touches:

  • Timers
  • Modal handlers
  • Progress API

This separation guarantees stability.

Runtime Contract Enforcement (QA20)

During modal execution:

Each yield payload is validated:

  • Must be dict
  • Must contain progress
  • Must contain total

If validation fails:

  • Execution stops
  • Progress UI closes
  • Error is reported
  • Operator returns CANCELLED

Contracts are enforced at runtime to prevent silent corruption.

Cancellation Behavior

If the user cancels:

  • The modal loop stops.
  • The generator is discarded.
  • Progress UI closes.
  • Operator returns CANCELLED.

The generator must tolerate interruption.

Completion is not guaranteed.

Native Blender Parity

QuickAddon long tasks intentionally mirror Blender’s “Render Animation” execution model.

Characteristics:

  • Modal timer-driven execution
  • Progress displayed in status area
  • Visible cancel control
  • ESC cancels only while render is active
  • UI remains stable

This alignment ensures:

  • Native user expectations
  • Predictable cancellation
  • Familiar interaction patterns
  • Professional integration into Blender workflows

Scoped Execution (v2)

In v2:

Each plugin instance is bound to a scope.

All shared value resolution happens through:

(scope, key) → value

Long tasks remain isolated per scope.

Multiple instances may run independently.

Isolation is enforced at the HostAPI level.

No Threads, No Background Workers

QuickAddon intentionally does not use:

  • Threads
  • Background tasks
  • Async execution

Why?

Blender’s Python environment is single-threaded for UI safety.

Cooperative scheduling preserves:

  • Stability
  • Predictability
  • Debuggability

The generator model is sufficient.

Determinism Guarantees

The execution model guarantees:

  • Build-time structural correctness
  • Registration-time binding clarity
  • Runtime contract enforcement
  • No hidden shared state
  • No dynamic introspection

Execution is explicit at every layer.

Why This Model Exists

The internal execution model enforces:

  • Separation of structure and behavior
  • Host-controlled lifecycle
  • Plugin-declared intent
  • Deterministic routing
  • Safe incremental processing

Without this model:

  • Long tasks freeze Blender
  • Nested composition breaks
  • State leaks between instances
  • Bugs become nondeterministic

The model is strict because Blender is strict.

What You Now Understand

When you click a button:

You are not running a script.

You are entering a controlled execution pipeline:

  • Validated
  • Routed
  • Bound
  • Scheduled
  • Enforced

QuickAddon is not merely generating operators.

It is generating execution boundaries.