# Blender Python API notes for extension authors These notes collect the failure modes most likely to matter in this repository. They are not a replacement for the [current Blender Python API](https://docs.blender.org/api/current/). ## Context, modes, and operators `bpy.context` is global ambient state. Operator and panel callbacks receive a `context` argument that may be overridden, so use that argument inside the callback. An operator that works when clicked can still fail from a script or in background mode. Common implicit requirements include: - active object and selected objects; - Object/Edit/Sculpt mode; - active view layer and collection visibility; - active image texture node in every material; - current editor area/region; - render engine and bake settings. Put cheap requirements in `poll()` and validate again in `execute()` when a specific error message helps. Prefer direct data manipulation over synthesizing UI context overrides. ## Data-block ownership Objects reference meshes; material slots live primarily on meshes but can be overridden per object; nodes reference images. Copy the level whose ownership must become independent. ```python object_copy = source.copy() # independent object, shared mesh object_copy.data = source.data.copy() # independent object and mesh ``` `collection.clear()` and similar operations may normalize dependent indices. For example, clearing mesh material slots can reset polygon material indices. Snapshot indices before clearing and restore them after rebuilding the slots. Blender data collections do not behave like ordinary Python ownership. Clean up temporary data explicitly: ```python bpy.data.objects.remove(temp_object, do_unlink=True) if temp_mesh.users == 0: bpy.data.meshes.remove(temp_mesh) ``` Do not remove a data-block with active users merely to avoid an orphan. ## Registration lifecycle Keep a deterministic tuple of registered classes. Dependencies go first: 1. `PropertyGroup` and `AddonPreferences`; 2. operators; 3. panels, menus, UI lists, and gizmos. Unregister in reverse. Any side effect outside class registration needs its own inverse operation: - `bpy.types.SomeMenu.append()` → `.remove()`; - `bpy.app.handlers.*.append()` → `.remove()`; - `bpy.app.timers.register()` → unregister or make the callback stop; - created keymaps → remove their keymap items; - preview collections → close/remove them; - `bpy.types.Scene.some_property = ...` → `del bpy.types.Scene.some_property`. Reloading exposes incomplete cleanup quickly. A package that only works after restarting Blender has a lifecycle bug. ## Properties and persistence Blender properties are RNA definitions, not normal instance attributes. Declare them on registered Blender classes with `bpy.props` annotations. Register a `PropertyGroup` before using it as a `PointerProperty` or `CollectionProperty`. Choose storage based on intended lifetime: - operator property: one invocation and redo panel; - `Scene`/`Object` property: saved in the `.blend`; - `AddonPreferences`: current Blender user profile; - module global: only the current enabled Python module lifetime; - `extension_path_user()`: persistent files owned by the extension. Property update callbacks can run in surprising contexts. Keep them small, avoid expensive operators, and guard against recursion. ## Meshes, modifiers, and UVs `source.data.copy()` copies the base mesh. It does not apply modifiers. `bpy.data.meshes.new_from_object(source.evaluated_get(depsgraph), ...)` captures evaluated geometry. Decide explicitly which topology the result should use. Evaluated geometry can add/remove UV layers or material slots. After evaluation: - confirm a UV layer still exists; - choose and set the intended active/render UV layer; - compute the required material-slot count from both slots and polygon indices; - do not assume source and evaluated polygon counts match. ## Materials, nodes, and images For materials used by common exporters, a Principled BSDF with an Image Texture connected to Base Color is more portable than a custom shader tree. For a bake target, every material involved in the bake needs an active Image Texture node that points at the destination image. Use `Non-Color` for ID maps, masks, normal maps, roughness, metallic, and packed data textures. Color-space configuration affects both values returned to Python and values saved to disk. Byte images quantize floats. A value recorded as `0.1` in metadata may become byte 25 or 26 depending on the conversion path. If another tool consumes an ID map, prefer exact byte matching with a narrowly bounded fallback, and reject overlapping color masks. Generated images are Blender data-blocks. Decide whether they should be saved, packed, or intentionally remain generated data before the user closes the file. ## Baking Image baking is context-sensitive. A robust bake typically controls and restores: - active/selected bake object; - Object mode; - `scene.render.engine` (Cycles owns the image-bake pipeline); - Cycles samples (one sample is sufficient for a deterministic flat emission bake); - `scene.render.bake.target`, margin, clear behavior, and selected-to-active; - active target Image Texture nodes; - temporary materials and material indices. Use a flat emission shader for material-ID colors so lights, normals, and sampling do not alter the values. Always restore the previous render engine and bake settings in `finally`. ## Files, paths, and external effects Blender accepts `//` paths relative to the current `.blend`. Resolve them with `bpy.path.abspath()`. Do not assume the `.blend` has been saved, and do not silently write into the extension package directory. `UNDO` only covers Blender's undo-aware data changes. It does not remove a PNG, undo a JSON/GLB write, retract a network request, or restore another program's state. Validate first and order external effects late. ## Compatibility checks Blender's Python API changes between releases. Avoid relying on memory for enum names, node socket names, or RNA properties. Query the target build when needed: ```bash blender --factory-startup --background --python-expr \ "import bpy; print(bpy.app.version_string); print(bpy.types.BakeSettings.bl_rna.properties.keys())" ``` Feature-detect optional properties with `hasattr` or guarded assignment, but do not use broad exception handling to hide genuine failures. When support differs materially by version, set an honest manifest minimum or branch on `bpy.app.version` and test both branches. Useful API entry points: - [`bpy.types.Operator`](https://docs.blender.org/api/current/bpy.types.Operator.html) - [`bpy.props`](https://docs.blender.org/api/current/bpy.props.html) - [`bpy.utils`](https://docs.blender.org/api/current/bpy.utils.html) - [`bpy.types.Image`](https://docs.blender.org/api/current/bpy.types.Image.html) - [`bpy.types.Mesh`](https://docs.blender.org/api/current/bpy.types.Mesh.html) - [`bpy.types.Depsgraph`](https://docs.blender.org/api/current/bpy.types.Depsgraph.html)