github m3trik/pythontk v0.11.0
pythontk v0.11.0

5 hours ago
  • 2026-09-19 -- one legal-name rule, explained rather than repaired (str_utils/_str_utils.py, core_utils/engines/shots/shot_model.py). New on StrUtils: LEGAL_NAME_PATTERN ([A-Za-z0-9_]+) and LEGAL_NAME_RULE, is_legal_name(name), illegal_name_chars(name) (the distinct offending characters, in order), name_error(name, subject=, reason=) (the sentence a field or a store shows -- it never rewrites the name) and legal_name_matcher(legal_name) (the inverse of to_legal_name: every original that converts to a legal name). to_legal_name still converts; it is for identities two sides derive the same way, never for what a user typed. ShotStore.NAME_PATTERN / NAME_RULE are now these constants and ShotStore.name_error builds its character refusal from StrUtils.name_error, so a UI field validating a shot name (uitk's set_validator("name")) enforces the same set the store refuses. Pinned in test_str.py.

  • 2026-09-19 -- 0.11.0 retires what 0.10 deprecated (core_utils/git.py, net_utils/preview_server.py, file_utils/_file_utils.py, core_utils/engines/textures/map_factory/conversions.py). Removed after a release of warnings, none with a caller in any ecosystem package: Git (no callers, no tests); the JSON key-value cluster FileUtils.set_json_file / get_json_file / set_json / get_json (a process-global path, assert validation, a truncate-then-write -- use FileUtils.write_json / read_json); ConversionRegistry.register_from_class (define register_conversions(registry) and use add_plugin); and the pythontk.net_utils.preview_server import path (ptk.PreviewServer / PreviewDeliverer / PreviewBridge are unchanged). test_file.RetiredSurfaceTest pins that the freed names stay unused, and the non-atomic-JSON-writer guard now expects none.

  • 2026-09-18 -- A shot name is refused at entry instead of respelled on export (core_utils/engines/shots/shot_model.py, shot_transfer.py, shot_detection.py). A shot's name is its clip name on every carrier (FBX take, Unity AnimationClip, glTF animation, the shot_metadata join key), and the export used to rewrite whatever the tools accepted -- "Step 9.1" shipped as Step_9_1, a second "Shot 1" as Shot_1_1, with nothing said. New on ShotStore: NAME_PATTERN ([A-Za-z0-9_]+) and NAME_RULE, name_error(name, shot_id=None) (the reason a name cannot be used: empty, a character outside the pattern, or another shot's name ignoring case -- Unity joins clips case-insensitively; compared by EXPORTED spelling, so Shot_1 is refused beside a legacy "Shot 1" it would otherwise have shipped as Shot_1_1), unique_name(base, first=None), the pure unique_among(name, taken, first=None) (puts name into its clip spelling first, so its result is always legal) and default_name(wanted=None) (a tool-proposed name kept when free, else the next free Shot_<n>). name_error also refuses a name another shot EXPORTS as after de-duplication (intro_1, beside legacy case twins Intro / intro). Behaviour change: define_shot and a renaming update_shot raise ValueError for a name name_error refuses (checked before any field changes). A legal name now exports VERBATIM -- the legacy respelling collapsed A__B to A_B and stripped a trailing _; it now applies only to a held name the rule REFUSES ("Step 9.1"), and to_export_view warns with each old -> new when it does. A legacy scene's LEGAL names the old rule respelled (A__B, Shot_) therefore ship under a new clip name on their next export, with no warning -- nothing distinguishes one from a name typed today. Clip de-duplication ignores case. Detection names its candidates Shot_<n> (was Shot <n>) and detect_and_define names each through default_name (beside Shot_1 / Shot_2 the next are Shot_3 / Shot_4, not Shot_1_2), and ShotTransfer.merge numbers an incoming name the scene already has -- legally, so a legacy "Shot 1" merges as Shot_1_2, never "Shot 1_2". ShotManifest.sync checks a new step's id against the store after its removals and reports one the store refuses as the new action "refused" (logged, left unbuilt) instead of raising part-way through the batch. A boundary restore puts names back without re-validating (undoing a swap). Descriptions are unchanged: JSON carries any text, now guarded by a round-trip test. test_shots_core + test_shot_transfer + test_shots_manifest_core 244.

  • 2026-09-18 -- SceneRecords: every tool-authored scene record is declared ONCE, and the export publishes them in one assembly with its decisions as input (core_utils/scene_records.py, core_utils/engines/shots/shot_model.py, core_utils/export_profile.py, file_utils/mesh_convert/_mesh_convert.py, export_verify.py). Before this, a channel's name lived in five places (the DCC carrier classes, each producer, MeshConvert.*_KEY, the handoff descriptions, unitytk's settings) and the owner doc was seven records short. New: Scope (PRIVATE / DELIVERABLE), Kind (AUTHORED / DERIVED), RecordSpec (key, scope, version, owner, description, kind, after, consumers; make / load / save / clear; a falsy payload CLEARS and never creates; a record newer than its declaration is refused with a warning), Record, the SceneRecords registry (15 records; ordered, check_producers, describe, handoff_block, and declared_takes(read) -- THE reader of the take list: the shot_metadata clips' own ranges, else the legacy fbx_takes), SceneStoreBase (the whole DCC storage contract: read / write / values per scope, plus the inherited dump / format_dump; _dumped holds dump's value rules for a store that dumps one carrier of several), ExportContext (clip mode, clip span, provenance, and every record produced so far -- how a reader gets another producer's output) and ExportSnapshot (assemble runs producers in dependency order, each isolated -- a raise or a bare-payload return fails that record only -- on a context it starts empty, so a reused context never feeds a producer an earlier assembly's records; commit writes once, each write isolated the same way, and stamps the handoff from what the carrier holds; publish is the authoring-time form and refuses a PRIVATE record, whose key can be a deliverable one's (the emissive registry and manifest share one); summary lists only what shipped). RecordSpec.version_key names the payload key an enveloped record versions under: emissive_groups uses schema, the key its readers read, so it never carries two versions. A HANDOFF context refreshes only DERIVED records, so a bridge can no longer clear an authored manifest it is not the authority on. The deliverable is leaner (data_export): each shot_metadata clip carries its own start / end -- the take list IS the clips, one record and one join key -- and an empty description / section is left out rather than written as "" (objects stays, an array Unity reads). fbx_takes is no longer written: ShotStore.to_export_view() returns {"shot_metadata": ...} only (it lost its fbx_takes key) and export_records the one shot record; a scene that still holds the legacy list has it cleared by its next shots publish, and FBX_TAKES stays declared (no remove_in) so a file written before 0.11.0 still reads. MeshConvert.build_visibility_tracks rounds every float to 6 places (VISIBILITY_TRACK_DIGITS): float32 attributes read back as doubles published their noise (0.49952034551044694). The handoff instructions are tighter and name no container format -- the same records are read out of a GLB, a sidecar or a dump, where "the FBX" misled (the shot record's description dropped it too) -- and their claim that the manifest names no folder is now true: the DCC lightmap records stop publishing absolute dir / dirs (the GLB embeds its maps, so a build-time folder hint had no business in the deliverable; the host hands the build its folders through lightmap_dirs), and a manifest written before still reads. ShotStore.export_records(ctx) / produce_export_records / CLIP_MODE_KEY; ExportRun.animation_clips_mode; ExportVerifier, GlbPipeline and every GLB reader of the takes (the declared-takes-but-no-animations gate, apply_glb_clips, the take windows the visibility gates and the clip manifest are cut against) read them through declared_takes, so none reads only the legacy list; fbx_takes lists glb among its consumers. MeshConvert.SHADOW_METADATA_VERSION derives from the registry like its siblings. MeshConvert.FBX_HANDOFF_*, *_METADATA_KEY, VISIBILITY_TRACKS_* now derive from the registry, and build_fbx_handoff delegates to SceneRecords.handoff_block, which describes only the carrier's STRING channels (a keyable float weight was listed as a JSON channel). test_scene_records 48; test_shots_core, test_mesh_convert updated.

Don't miss a new pythontk release

NewReleases is sending notifications on new releases.