You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 2d4e769
Browse filesBrowse the repository at this point in the historyBrowse files
fix(skills): headless guidance matches what --background actually runs
Measured on .scratch/blender-4.5.11-windows-x64 and
.scratch/blender-5.2.1-windows-x64 (--background --factory-startup), same
results on both:
- bpy.ops.object.select_all / object.delete / object.transform_apply ->
{'FINISHED'} with no override. The skill called select_all a failure
("fails: no view3d") and said the only true-headless option was a bpy.data
equivalent.
- window_manager.windows has 1 window under --background (the loaded file's);
its screen has a VIEW_3D area. view3d.snap_cursor_to_selected fails poll with
no override and returns {'FINISHED'} with the rule's window/area/region
override. The skill said --background has "no windows at all".
So the issue's IndexError premise for the rule's windows[0] example was wrong;
the example works. What was wrong was the skill. Changes:
- headless-batch-scripting: new "Which operators run headless" section
(object/data operators run as-is, narrow with an object/selection override;
editor operators need window/area/region borrowed from the loaded file);
select_all reframed as fragile/slow, not failing; table rows and the
common-mistake example use view3d.snap_cursor_to_selected; the helper reads
bpy.context.window_manager and demos an editor operator.
- prefer-temp-override-over-context-copy: the window example now guards the
VIEW_3D lookup (a file whose layout has no 3D viewport has nothing to
borrow) and says why it works under --background.
Closes#329
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: skills/headless-batch-scripting/SKILL.md
+28-13Lines changed: 28 additions & 13 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -43,24 +43,26 @@ The Blender API is the same in headless mode but several context-dependent behav
43
43
| --- | --- |
44
44
|`bpy.context.window`, `bpy.context.area`, `bpy.context.region`| Often `None`|
45
45
|`bpy.context.active_object`|`None` if no scene is loaded or no active object set |
46
-
|`bpy.context.selected_objects`|Empty by default|
47
-
|Most `bpy.ops.<editor>.*` operators | Fail with "context is incorrect" because they expect an editor that does not exist|
46
+
|`bpy.context.selected_objects`|Whatever the loaded file saved (the factory startup selects its cube); never assume it|
47
+
|Editor operators (`view3d.*`, `screen.*`) | Fail with "context is incorrect": the script's context has no area/region. Override one from the loaded file's screen (below)|
48
48
| Modal operators | Cannot be used; no event loop |
49
49
| Drag and drop, file dialogs, keymaps | All gone |
50
50
| Scene rendering (`bpy.ops.render.render`) | Works fine, this is the standard headless render path |
51
51
52
-
The headless API surface is essentially "everything that operates on `bpy.data` directly" plus a small whitelist of operators that do not depend on UI state (most `render.*`, most `wm.*` save/load, scene-level edits).
52
+
The headless API surface is "everything that operates on `bpy.data` directly" plus every operator that polls on objects or data rather than on an editor (`object.*`, import/export, `render.*`, `wm.*` save/load). Editor operators need the override described below.
53
53
54
54
## Rule of thumb: `bpy.data` is your friend
55
55
56
56
The single most important pattern: **prefer `bpy.data.*` over `bpy.ops.*`** in batch scripts.
57
57
58
58
```python
59
-
# WRONG: requires a 3D viewport context to even poll.
59
+
# FRAGILE: object operators do run under --background, but they act on
60
+
# whatever the selection happens to be, and each call is a full operator
The `mesh-editing-and-bmesh` skill covers the rest of the `bpy.data` and `bmesh` patterns. This skill is about **when** the headless context forces you toward those patterns.
77
79
78
-
## When you must use a `bpy.ops` that wants UI: `temp_override`
80
+
## Which operators run headless
81
+
82
+
Two kinds of operator behave differently under `--background`:
83
+
84
+
-**Object and data operators** (`object.*`, `mesh.*` outside edit-mode UI, `export_scene.*`, `wm.*_export`, `render.render`): they poll on `context.object` / selection, not on an editor, so they run headless. `bpy.ops.object.select_all(action='DESELECT')` and `bpy.ops.object.transform_apply(...)` both return `{'FINISHED'}` under `--background` on 4.5 LTS and 5.2 LTS. Narrow them with an object/selection override, no window needed:
85
+
86
+
```python
87
+
with bpy.context.temp_override(object=obj, active_object=obj, selected_editable_objects=[obj]):
-**Editor operators** (`view3d.*`, `screen.*`, `uv.*` that read the editor): they poll on an area/region and fail with `poll() failed, context is incorrect` unless you override one. A file loaded under `--background` still has its window and screen, so you can borrow the 3D viewport area from it.
92
+
93
+
## When you must use an editor operator: `temp_override` with window/area/region
79
94
80
95
A few `bpy.ops` calls have no `bpy.data` equivalent and genuinely need an editor context. For these, use `bpy.context.temp_override`:
81
96
82
97
```python
83
98
import bpy
84
99
85
100
deffind_window_and_area():
86
-
for window in bpy.data.window_managers[0].windows:
In **pure** headless mode (`--background`), there are no windows at all, so this pattern only works if you have a "GUI but headless" setup (e.g. `blender --python` without `--background`, running on a virtual framebuffer).
107
-
108
-
For true headless, your only option is to find a `bpy.data` equivalent or pre-bake the operation into the .blend file.
121
+
This works under `--background` too: the window and screen come from the loaded file (the factory startup has one window whose screen includes a `VIEW_3D` area), so `bpy.ops.view3d.snap_cursor_to_selected()` returns `{'FINISHED'}` with the override and fails its poll without it. It fails only when the loaded file's screen has no 3D viewport, which is why the helper raises instead of assuming one. Prefer a `bpy.data` equivalent whenever one exists; it does not depend on what the file's UI layout happened to be.
109
122
110
123
## Argument parsing after `--`
111
124
@@ -222,12 +235,14 @@ except Exception as e:
222
235
223
236
## Common AI mistakes
224
237
225
-
1.**Calling UI-dependent `bpy.ops`in headless** without a `temp_override`:
238
+
1.**Calling an editor operator in headless** without a window/area/region`temp_override`:
226
239
227
240
```python
228
-
bpy.ops.object.select_all(action='DESELECT') #fails: no view3d
241
+
bpy.ops.view3d.snap_cursor_to_selected() #poll() failed, context is incorrect
229
242
```
230
243
244
+
Object operators such as `object.select_all` and `object.transform_apply` do not need this; they run under `--background` as-is.
245
+
231
246
2.**Using `bpy.context.scene` before a scene is loaded**. After Blender starts, the default startup file is loaded, so `context.scene` works. But if you've called `bpy.ops.wm.read_factory_settings(use_empty=True)`, dereferencing `context.scene.collection` may surprise you.
232
247
233
248
3.**Forgetting the `--` argv split**. Without it, `argparse` sees Blender's own flags and rejects them.
0 commit comments