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:
- Build-time compilation
- Registration-time binding
- Runtime execution
Understanding these phases clarifies every design decision.
Phase 1 – Build-Time Compilation
When you run:
quickaddon build my_tool.py
QuickAddon performs:
- Module loading
- Decorator extraction
- Structural validation
- Contract validation (QA10)
- Render model construction
- 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:
- Collects local parameters (if any)
- Resolves shared values via HostAPI
- 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:
- Calls the user function.
- Verifies that a generator was returned (QA20).
- Starts a modal timer.
- 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.
Modal Scheduler Model
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.