|
| 1 | +# Add-on register lifecycle |
| 2 | + |
| 3 | +Check-only witness for the first things every add-on author writes: the |
| 4 | +package layout, the `bl_info` → manifest migration, a Panel, and a |
| 5 | +PropertyGroup on `Scene`, registered and unregistered symmetrically. It |
| 6 | +builds a dual-format add-on in a temp directory, validates it with Blender's |
| 7 | +own extension validator, registers it and unregisters it. There is no |
| 8 | +gallery still, because the witness is registry state and the validator's |
| 9 | +exit code (same class as |
| 10 | +[`extension-package-lifecycle`](../extension-package-lifecycle/)). |
| 11 | + |
| 12 | +Follows [`addon-scaffolding`](../../skills/addon-scaffolding/SKILL.md), |
| 13 | +[`bl-info-migration`](../../skills/bl-info-migration/SKILL.md), |
| 14 | +[`ui-panels`](../../skills/ui-panels/SKILL.md) and |
| 15 | +[`custom-properties`](../../skills/custom-properties/SKILL.md). |
| 16 | + |
| 17 | +**What it witnesses:** |
| 18 | + |
| 19 | +- `blender --command extension validate` accepts a package with both |
| 20 | + `blender_manifest.toml` and a legacy `bl_info` (exit 0). Keeping `bl_info` |
| 21 | + for pre-4.2 Blender does not break the Extensions Platform path. |
| 22 | +- The same `__init__.py` without a manifest is rejected (exit 1, |
| 23 | + `blender_manifest.toml not found`): adding the manifest is the migration. |
| 24 | +- `register()` creates the Panel class, the PropertyGroup class and the |
| 25 | + `Scene.addon_probe` pointer, which reads its default (1.5). |
| 26 | +- `unregister()` that deletes the pointer first, then unregisters the |
| 27 | + classes in reverse, leaves nothing behind. Unregistering the classes first |
| 28 | + leaves `Scene.addon_probe` registered and pointing at a type that no |
| 29 | + longer exists. In a probe, *reading* that dangling property returned an |
| 30 | + empty stub on 4.5.14 and **crashed Blender 5.2.1** |
| 31 | + (`EXCEPTION_ACCESS_VIOLATION`). This example only checks that the pointer |
| 32 | + survived, and never reads it. |
| 33 | + |
| 34 | +Also measured, not checked (no falsifier can reach them through Blender |
| 35 | +behaviour alone): a Panel `bl_idname` without `_PT_` only prints a warning |
| 36 | +and still registers, while a Panel with no `bl_region_type` is rejected at |
| 37 | +`register_class` with `AttributeError` (4.5.14 and 5.2.1). |
| 38 | + |
| 39 | +**What failure each check would catch:** |
| 40 | + |
| 41 | +- exit 3 — the dual-format package did not validate (`--drop-manifest` lands here) |
| 42 | +- exit 4 — a package without a manifest validated (`--legacy-gets-manifest` lands here) |
| 43 | +- exit 5 — `register()` did not create the panel, group and pointer, or the |
| 44 | + default did not read back (`--skip-pointer` lands here) |
| 45 | +- exit 6 — `unregister()` left something behind (`--classes-first` lands here: the pointer) |
| 46 | + |
| 47 | +## Re-verified |
| 48 | + |
| 49 | +| Measurement | 4.5.14 | 5.2.1 | |
| 50 | +| --- | --- | --- | |
| 51 | +| validate dual-format / bl_info-only | 0 / 1 | 0 / 1 | |
| 52 | +| after register: panel, group, pointer | all present | all present | |
| 53 | +| after unregister (right order / classes first) | nothing / pointer left | nothing / pointer left | |
| 54 | +| reading the dangling pointer (probe) | empty stub | crash | |
| 55 | +| default exit | 0 | 0 | |
| 56 | +| falsifier exits | 3 / 4 / 5 / 6 | 3 / 4 / 5 / 6 | |
| 57 | + |
| 58 | +## API reference |
| 59 | + |
| 60 | +- [Extensions: command line](https://docs.blender.org/manual/en/latest/advanced/command_line/extension_arguments.html) |
| 61 | +- [`bpy.utils.register_class`](https://docs.blender.org/api/current/bpy.utils.html#bpy.utils.register_class), |
| 62 | + [`unregister_class`](https://docs.blender.org/api/current/bpy.utils.html#bpy.utils.unregister_class) |
| 63 | +- [`bpy.props.PointerProperty`](https://docs.blender.org/api/current/bpy.props.html#bpy.props.PointerProperty) |
| 64 | +- [`bpy.types.Panel`](https://docs.blender.org/api/current/bpy.types.Panel.html) |
| 65 | + |
| 66 | +## Run |
| 67 | + |
| 68 | +```bash |
| 69 | +blender --background --python addon_register_lifecycle.py -- |
| 70 | +blender --background --python addon_register_lifecycle.py -- --drop-manifest |
| 71 | +blender --background --python addon_register_lifecycle.py -- --legacy-gets-manifest |
| 72 | +blender --background --python addon_register_lifecycle.py -- --skip-pointer |
| 73 | +blender --background --python addon_register_lifecycle.py -- --classes-first |
| 74 | +``` |
| 75 | + |
| 76 | +## Exit codes |
| 77 | + |
| 78 | +Per-script sequential checks. |
| 79 | + |
| 80 | +| Code | Meaning | |
| 81 | +| --- | --- | |
| 82 | +| 0 | Success | |
| 83 | +| 1 | Uncaught exception (FATAL wrapper) | |
| 84 | +| 2 | argparse / usage | |
| 85 | +| 3 | Dual-format package did not validate (`--drop-manifest`) | |
| 86 | +| 4 | Package without a manifest validated (`--legacy-gets-manifest`) | |
| 87 | +| 5 | `register()` missed the panel, group or pointer (`--skip-pointer`) | |
| 88 | +| 6 | `unregister()` left something behind (`--classes-first`) | |
| 89 | + |
| 90 | +The `blender-smoke` workflow runs the check on Blender 5.2 LTS and 4.5 LTS |
| 91 | +(5.1 on the weekly cron, the `needs-5.1` PR label, or manual dispatch). |
| 92 | +Smoke passes no extra flags on the happy path. Its catalog falsifiers are |
| 93 | +`--drop-manifest` (exit 3), `--legacy-gets-manifest` (exit 4), |
| 94 | +`--skip-pointer` (exit 5) and `--classes-first` (exit 6). |
0 commit comments