- 2026-10-27 -- Moved names survive
from package import Name; record hooks re-read on a new path; fingerprints pin a record's fields (core_utils/module_resolver.py,core_utils/engines/scene_export/scene_records.py,file_utils/mesh_convert/export_fingerprint.py). CPython turns an AttributeError underfrom package import Nameinto a bare "cannot import name", so that statement now getsMovedImportError(an ImportError) with the same message, while a plain read keepsMovedNameErrorandhasattrstays safe.SceneRecords.registered_hooksreads its metadata again whensys.pathchanges, warns when two distributions name one entry (the first on the path wins), andstager_rowsskips a slot naming no method, with a warning.ExportFingerprintkeeps a record's fields, list lengths and numbers (a JSON user property read as its shape), so a record written with the right key but a wrong value is a difference; its text reads as "text". These add 78 source lines (line_budget659,503 -> 659,581).- Test(s):
test_module_resolver.py+1,test_scene_records.py+3,test_export_verify.py+1;test/surface_snapshot.jsongainsExportFingerprint.
- Test(s):
- 2026-10-27 --
SceneRecords.hook_row: a library's single call into tool code, registrable (core_utils/engines/scene_export/scene_records.py). A seam such as the take stash or the offset fold is one(module, class, method)row: the library's own while the tool lives there, else the one an installed package registers for the host and role, so the call survives the tool's move without importing upward. Several registered: the first by key is used and the rest warned about. With the two hosts'FbxUtils.HOOKSthis adds 74 source lines (line_budget659,429 -> 659,503).- Test(s):
test_scene_records.py+1.
- Test(s):
- 2026-10-27 -- Record hooks register through an entry-point group (
core_utils/engines/scene_export/scene_records.py,scene_store.py,core_utils/package_manager.py). A tool outside a library registers its record producer, stager or owner as an entry point in groupSceneRecords.HOOK_GROUP(m3trik.scene_records), named<host>.<role>.<key>and pointing atmodule:Class.method(module:Classfor an owner).SceneRecords.registered_hooks(host, role)reads them from metadata alone, once a session (forget_registered_hooksreads again), so nothing is imported and a batch export that imports one library still writes an installed tool's record.SceneRecords.producer_rows/stager_rowsmerge them under a host's own tables, andSceneStoreBase.HOST+owner_rows()do the same for record owners; a key both name keeps the library's row.PackageManager.entry_points(group)is the one reader across importlib versions (uitk's external-app handler now uses it). Nothing registers yet, so no export changes. The hooks add 149 source lines across the four libraries (line_budget659,280 -> 659,429).- Test(s):
test_scene_records.py+5 (TestRegisteredHooks),test_package_manager.py+2 (EntryPointsTests).
- Test(s):
- 2026-10-27 -- A name that left a package answers with its new home (
core_utils/module_resolver.py,__init__.py).bootstrap_package(..., moved=MOVED_NAMES)takes the package's moved-names table,{name: (new home, version)}, and a read of a listed name raises the newMovedNameError, anAttributeErrornaming where the name went and from which release, sohasattrandgetattr(..., default)behave as for any missing name. A move to tentacle gets no alias (no library imports it), and one table per library replaces an alias per name. pythontk declares its table, empty for now. With the four libraries' tables this adds 48 source lines (line_budget659,232 -> 659,280).- Test(s):
test_module_resolver.py+2 (MovedNamesTests).
- Test(s):
- 2026-10-27 --
ExportFingerprintpins what an export wrote, andFbxFile.object_nameis public (file_utils/mesh_convert/export_fingerprint.py, new;fbx_file.py).ExportFingerprint.of(path)reads a GLB or FBX into plain JSON-able data: a GLB's node tree by name path with its transforms, meshes with their attribute and index counts, materials, skins and each clip's channels and span; an FBX's object census, each model's type and local transform, geometry sizes (read without inflating an array), materials with their texture slots, each take's curves with their key spans, and user-property names. What legitimately differs run over run (generator strings, buffer packing, the paths and dates inside records) stays out.diff(baseline, current, tolerance)names each change by path, andagainst(path, baseline, update=False)compares an export with a saved fingerprint, so a code move that must change no output can prove it.FbxFile._object_namebecomesobject_name, the display name of a recorditer_objectsyields. The module adds 367 source lines (line_budget658,865 -> 659,232).- Test(s):
test_export_verify.py+7 (TestExportFingerprint).
- Test(s):