github m3trik/pythontk v0.8.94
pythontk v0.8.94

3 hours ago
  • 2026-07-25 — New StrUtils.resolve_placeholders(text, **kwargs) -> dict: a verbose companion to replace_placeholders for live previews / diagnostics. Where replace_placeholders returns only the substituted string, this returns {result, fields, resolved, unresolved} — the same substituted string, every distinct {token} base name in first-seen order (format specs like {n:03d} and attribute / index access {obj.attr} / {seq[0]} reduce to the base name; positional {} / {0} skipped), the {token: value} map for supplied keys, and the tokens left unresolved. Raises ValueError on a malformed format string, matching replace_placeholders. Powers the ecosystem's new resolved-placeholder tooltips (uitk TooltipFormat.placeholder_preview). Additive; test_str.py +6. Release note: publish pythontk before uitk / mayatk / blendertk — the new tooltip depends on this.
  • 2026-07-25 — SchemaSpec now expresses list-of-nested sub-records via nested=[Spec] (mirroring typing.List[X]). The schema base could nest a single mapping (nested=Spec) but not a list of them — from_dict only recursed when the value was a dict, validate errored "expected a mapping" on a list, and to_dict deep-copied a list of SchemaSpec instances as opaque objects instead of serialising them. Any schema with a repeated sub-record (a rack's bays, a bay's occupants, a table's columns) had to drop to a hand-rolled validate callable plus manual from_dict reconstruction at each call site — the exact per-schema duplication the base exists to remove. A one-element nested=[Spec] now signals a list of that schema, resolved uniformly by new internal _nested_of(meta) -> (schema, is_list): from_dict builds a list of instances, to_dict serialises each element, validate recurses per element with an indexed path (occupants[1].label), skeleton emits a one-element example list (still self-valid), and to_markdown marks the field (list) and renders the element schema's table. Fully additive — bare nested=Spec is unchanged; a malformed multi-element [A, B] raises SchemaError. FieldDoc gained nested_is_list: bool = False. test_schema_spec.py +10 → 36/0. No other public API change; downstream (uitk/mayatk/blendertk/tentacle) unaffected. Release note: publish pythontk before mayatk (its new RackBuilder schema depends on this).
  • 2026-07-20 — New CachedArtifact: the produce-once/reuse-forever lifecycle every headless-conversion pipeline was re-implementing. mayatk's blender_bridge and blendertk's maya_bridge each carried a hand-rolled copy of the same ~40-line block — hash the inputs into a key, serve the cached file if present, else produce into a scoped scratch store and atomically os.replace-promote into the cache slot (plus sidecar promotion and stale-sidecar cleanup). Adding an FBX→native-scene bake stage to both would have made that four copies, so it moved to pythontk beside TempArtifacts (which it composes: "detached" owns the cache, "scoped" owns the scratch). CachedArtifact.key(*parts, files=…) folds each file's path+mtime+size in, so a template fix invalidates stale payloads rather than replaying the old bug on retry; get(key, produce, sidecars=…, use_cache=…) returns (path, hit, scratch). The scratch hop is load-bearing, not ceremony: producing straight into the slot lets a timeout-killed partial write poison every later run and lets two concurrent producers interleave into one file. Both bridges' _cache_key now delegate to it (cache prefixes and on-disk layout unchanged). test_temp_artifacts.py +13 → 34/0.
  • 2026-07-19 — bootstrap_package now derives __all__ automatically from the resolved public surface, so it no longer has to be hand-listed or kept in sync with DEFAULT_INCLUDE (new set_all param, default on). pythontk's root __init__.py carried a 113-name __all__ maintained by hand — and it had already drifted: 20 symbols were registered in DEFAULT_INCLUDE yet missing from __all__ (Git, QcLog/QcGate/GateError, MapFactory/MapRegistry/MapType, OutputSpec/OutputTemplate/OutputTemplates, MatReport, Op, Workspace/WORKSPACE_MARKER/DEFAULT_FILE_RULES/RULE_NICE_NAMES, RpcClient, Call/Result, ImageFormat) — so from pythontk import * and every introspection tool that reads __all__ under-reported the surface. bootstrap_package now populates __all__ after resolver build + namespace-alias creation as the sorted union of any __all__ the module declared before the call and the resolver's class_to_module keys — every module-level class/function/constant and created namespace alias reachable as pkg.Name. Flat method aliases (method_to_module, e.g. ptk.make_iterable) and bare submodule names are intentionally excluded — they still resolve lazily via __getattr__ but aren't advertised, matching the prior convention. The pre-declared list is snapshotted once so a runtime configure()/build_dictionaries() reconfigure re-derives cleanly without accumulating stale names. The derived pythontk __all__ is a strict superset of the old curated list (113 → 133, zero regressions, every name resolves — test_all_names_resolve unchanged and green), and the importable surface is identical (those 20 names were always reachable via __getattr__; __all__ now just reflects reality, so no registry regen). The manual __all__ block is deleted from pythontk/__init__.py. Behavior is opt-out via bootstrap_package(..., set_all=False). Shared-infra change (core_utils/module_resolver.py): every ecosystem package (uitk, mayatk, blendertk, tentacle) that previously shipped no __all__ now gets a clean auto-derived one for free — no from <pkg> import * exists anywhere in the monorepo, so the default is purely additive. Full suite green (2115 passed, 0 failed, 7 skipped); uitk verified (115-name __all__, all resolve).

Don't miss a new pythontk release

NewReleases is sending notifications on new releases.