kumiki.librarian¶
Flat import path
kumiki/__init__.py re-exports everything on this page via from kumiki import *, so every name below is also available directly as kumiki.Param -- you do not need to import from the submodule path shown in the heading above.
kumiki.librarian
¶
Librarian: discovery and loading of kumiki frame examples and pattern books.
This module owns the full librarian stack: AST-driven discovery, scanning,
pattern index files, and layered search roots. The JS bridge never reads any
of this directly — it always goes through the librarian CLI
(:mod:kumiki.librarian_cli).
Frame and pattern-list detection rules (see analyze_source)¶
Detection never imports the file — it's pure ast inspection. A module-level
statement is recognized as a frame entry if either of these holds:
-
Type-based (the primary path): the statement's type resolves to kumiki's canonical
Frame(through aliases likefrom kumiki import Frame as F, dotted forms likekumiki.timber.Frame, or string annotations): -
name: Frame = ...— annotation isFrame name = Frame(...)/name = Frame.from_joints(...)— RHS call constructs aFrame(checked even when the annotation, if any, isn'tFrame)-
def foo(...) -> Frame:— return annotation isFrame -
Name-based fallback (unconditional, ignores types entirely): a target or function literally named
exampleorbuild_frameis always treated as a frame entry, whether or not it's annotated-> Frame. This is whydocs/agent_usage_instructions.mdcan say "just call itexample" as the simple convention, while the type-based rule is what actually backs it.
A module-level statement is recognized as a pattern list entry only by
name: patterns = [...] or patterns: ... = [...], list literal or
call, no content type-checking at this stage (that happens after import, in
_resolve_pattern_list).
When a file has multiple frame entries, ModuleStaticInfo.chosen_frame is
the last one in source order — that's what downstream consumers render.
Two-phase scan (see _scan_single_file)¶
- Static AST analysis always runs first and is cheap (no import).
- A file is only actually imported (
exec_module) if its static info has apatterns = [...]candidate (needs_import = bool(static_info.pattern_lists)). Frame-only files are never imported during a scan — only their static info (names/kinds/line numbers) is captured. Building the realFrameobject (callingexample()/build_frame()) is deferred entirely to the runner, on demand, when a specific file is opened/rendered.
Pattern index files: the caching path¶
build_pattern_index / refresh_pattern_index / read_pattern_index /
write_pattern_index maintain a JSON cache (keyed by per-file sha256) of
the same scan results above, so unchanged files don't need to be re-scanned
or re-imported:
build_pattern_index(root, prior_index=...)— for each file, reuses the prior entry verbatim if its sha256 still matches; only changed/new files get a real scan. Returns both a per-fileentriesdict (each entry has its ownframes/chosen_frame_name/patternbook) and a flattened, path-sortedframe_examplessummary list, mirroring whatbuild_scan_indexreturns for a live scan.refresh_pattern_index(root, index_path)— reads the index already on disk at index_path asprior_index, rebuilds against root, writes the result back to index_path. This is how to keep an index file up to date: call it (orpython -m kumiki.librarian_cli refresh-index <root> <index_path>) whenever the source tree may have changed, instead of hand-editing or blowing away the cache.load_or_build_pattern_index_for_root(used byscan_all_roots) is the actual "check for an index file first" behavior: for thekumiki/ dependency search roots it trusts a bundled_pattern_index.jsonas-is with no sha-diffing at all (installed packages are assumed immutable — that file is whattools/build_pattern_index.pygenerates at release time and ships in the wheel). Workspace roots always get a freshbuild_pattern_indexcall instead.
Caveat: as of this writing the live kigumi extension bridge
(kigumi/frame-scanner.js) only ever calls the CLI's scan-workspace
action (→ scan_workspace_index → scan_library_folder), which does a
full live AST (+ import-if-needed) scan every time and does not consult
any pattern index file. The index/cache machinery above is real and tested,
but currently only exercised for the bundled kumiki/dependency roots via
scan_all_roots — not (yet) for the workspace's own live-edited files.
Layered search roots¶
discover_search_roots returns, in order: the workspace, the installed
kumiki package directory, and any explicitly declared kumiki-aware
dependencies. A dependency is only included if it's both declared in
.kigumi/config.json (kumiki_dependencies) and its installed
metadata's Requires-Dist actually lists kumiki.
Param
dataclass
¶
Param(default: Any, description: str = '', kind: Optional[Literal['number', 'boolean', 'string', 'enum', 'v3']] = None, options: Optional[Tuple[str, ...]] = None, minimum: Optional[Any] = None, maximum: Optional[Any] = None, optional: Optional[bool] = None)
Author-facing parameter declaration helper.
Use as a callable default value, e.g.:
def build_frame(width=Param(scalar(2), description="Timber width")):
...