The execution model is in blender-core. Start from assets/addon_template/ (validated, built, and registered in CI): blender_manifest.toml, __init__.py, operators.py, ui.py.
Procedure
- Copy the template, then set
id(lowercase_underscores, permanent),name,tagline(no trailing period, ≤ 64 chars),maintainer,version(semver),license(SPDX, GPL-3.0-or-later for code using bpy), andblender_version_min = "4.2.0"or higher if you use newer APIs (e.g."5.2.0"for the GNproperties.inputsAPI). - Write classes with the prefix convention
MYTOOL_OT_x(operator),_PT_(panel),_MT_(menu),_PG_(property group),_AP_(preferences),_UL_(UI list).bl_idnamefor operators ismy_tool.verb_noun. - Validate and build:
blender --command extension validate ./my_tool blender --command extension build --source-dir ./my_tool --output-dir ./dist blender --command extension install-file -r user_default -e ./dist/my_tool-1.0.0.zip - Test headless: register the package in-process, call operators with
temp_override, and assert on data (seetests/test_addon.pyin this repo).
Rules (extension-era, 5.x)
- Relative imports only (
from . import ops). The package name is decided by Blender, so preferences usebl_idname = __package__. - No
bl_info(it's ignored for extensions). Everything goes in the manifest. - No network or file access at import/register time. Declare
[permissions](files,network,clipboard,camera,microphone) with a reason. Checkbpy.app.online_accessbefore any network call. - Third-party Python packages: ship
.whlfiles in./wheels/and list them underwheels = [...]. Neverpip installat runtime. Platform-specific wheels requireplatforms = [...]. - Properties (5.0+):
bpy.propsvalues are attributes, not dict entries (scene["my_prop"]no longer reaches them). For read-only props useoptions={'READ_ONLY'}.get_transform/set_transformadapt values while keeping storage. Register withPointerProperty(type=PG)on an ID type, anddelit on unregister. - Operators:
bl_options={"REGISTER","UNDO"}for undo plus a redo panel.poll()guards the context. Return{"FINISHED"}/{"CANCELLED"}.self.report({"WARNING"}, msg)for user feedback. Don't call other operators in loops if a data API exists. - UI:
layout.use_property_split = Truegives modern alignment. Panels in the N-panel usebl_region_type="UI"+bl_category. Append menu entries withVIEW3D_MT_*.append(func)and remove them on unregister. - Unregister everything in reverse order (classes, props, menus, handlers, timers, keymaps, draw handlers). Leaks break reloading.
- Drawing (overlays):
gpumodule +gpu_extras.batch.bglwas removed in 5.0. Textures come fromgpu.texture.from_image(img). For scene-linear images usedraw_texture_2d(..., is_scene_linear_with_rec709_srgb_target=True). - Long tasks: a modal operator with a timer (
wm.event_timer_add), orbpy.app.timers.registerwith chunked work. Never block the UI for seconds. - Handlers: decorate persistent ones with
@bpy.app.handlers.persistent. Keep them cheap (they run every frame/depsgraph update). - Keymaps: add them to
wm.keyconfigs.addon, store(km, kmi)pairs, and remove them on unregister. - Translations / i18n:
bpy.app.translations.register(__package__, dict).
Publishing (extensions.blender.org)
The Blender ID account uploads the built zip. Requirements: a valid manifest, a GPL-compatible license, no obfuscated code, no bundled binaries without source, and permissions declared. Screenshots/README go on the website listing. Update with a higher version.
Gotchas
context.sceneinside a draw function can beNonein some editors. Guard it.- Storing Blender data references (
bpy.types.ID) in Python globals crashes after undo or file load. Store names orsession_uid, and look them up again. bpy.utils.register_classfails if annotations use=instead of:for properties.- 5.0 made several bundled modules private (
rna_info,bl_ui_utils...). Don't import them. - 5.1 no longer loads the user site-packages by default. Bundle wheels instead of relying on user installs.
- 5.2:
PreferencesFilePaths.active_asset_libraryindices are offset (the "All"/"Essentials" entries come first).UILayout.template_palette(color=)is removed. New:UILayout.link,UILayout.textbox,bpy.data.all_ids,Window.screenshot.