← All ideas
  • Published 23 Mar 2026

Batch-friendly bpy.ops operators and deferred view layer updates

Using bpy.ops in loops for creating many objects (e.g., hundreds of cubes with bpy.ops.mesh.primitive_cube_add ) is extremely slow in Blender. Previously, Python developers could monkey-patch _BPyOpsSubModOp._view_layer_update since Blender 2.93 all the way to Blender 5.0 (see this thread ) to reduce Python-side overhead, but this no longer works in Blender 5.1+. The slowdown now perhaps comes from C/C++ dependency graph updates and undo stack operations triggered by each operator, which cannot be bypassed from Python.

Use case:

  • Artists or addon developers want to create grids, arrays, or large numbers of objects using bpy.ops for convenience.
  • Current Python hacks are fragile and break in newer Blender versions.

We need a way to defer dependency graph and view layer updates when running multiple operators in a loop. This would allow bulk execution of bpy.ops without the per-operator performance penalty, while still keeping the convenience of the operators.

100%

Development Status: Open

Upvote 2 Downvote 0
harrymckenzie
23 Mar 2026

4 Comments

Lauloque

I’m not against this idea, but I wonder if it's the right direction to begin with?

The issue with bpy.ops isn’t just the DG updates: it’s the whole package of context requirements, mode switching, undo stack interaction, dependency graph updates, maybe other things I don't even know about. Even if we make it possible to defer DG updates, those problems remain. It's just giving us another band-aid to keep doing things inneficiently.

In practice, once a script grows beyond a few lines, you end up stacking workarounds to context overrides, handling modes, … And the initial convenience of (some) operators mostly disappears. At that point, the data API is often just as fast to write while being far more predictable and fast to execute.

Hence, a direction I personally find more interesting would be a simpler, more consistent data-oriented API. Easier said than done, of course. But if it could be done, I'd like something in the spirit of Autodesk Maya’s cmds which follows a very clear “create / query / edit” pattern:

import maya.cmds as cmds  

cube = cmds.polyCube(name="myCube")[0]  

# Query
pos = cmds.xform(cube, query=True, translation=True)  

# Edit
cmds.xform(cube, edit=True, translation=(1, 0, 0))

So imagine creating 50 offset grid objects:

import maya.cmds as cmds  

for i in range(50):
    grid = cmds.polyGrid(name=f"myGrid{i:02d}", subdivisions=6)[0]
    cmds.xform(grid, edit=True, translation=(i,0,0))

This kind of KISS design scales much better than layering abstractions over UI operators.

It also enables something Blender currently struggles with but Maya does very well:

Almost everything you do in the UI is echoed as usable commands in the script editor. That makes learning and scripting much more intuitive: you can just perform an action and get the exact code to reproduce it programmatically and efficiently.
It optionally can also show GUI-related commands like when you open/close a menu or open a new window, e.g. making it easy to know what a button does or which menu ID you need to add/remove things in it.

In contrast, Blender’s Info editor output is inconsistent and, by essence, will show operator calls that you shouldn't use programmatically and will never tell you which menu ID you just opened.

1
harrymckenzie Author

Totally agree with your point 🙂 A full data-oriented API would be ideal, but that’s a huge change. In the meantime, my API provides a practical way to speed up batch bpy.ops operations, especially in large loops, without relying on fragile hacks. Once a better solution exists, it can be swapped out easily.

Lauloque

True that

1
user61

Really need this! I conducted some research and discovered that the _BPyOpsSubModOp function has been removed in Blender 5.1. It appears that all control over View Layers is now managed in C++ code, meaning operations involving bpy.ops.object.* can no longer temporarily halt viewport refreshes. as the link: projects.blender.org/blender/blender/pulls/145344

1