Skip to content

Latest commit

 

History

History

README.md

Vinary Tree Python interop binding

This package exposes the language-native representation of the stable Vinary Tree resource ABI. It is the neutral handoff layer used by dictionary, automaton, WFST, lattice, and semiring packages, and it can export custom Python providers without owning algorithm-specific policy.

Support and package contract

Property Contract
Binding Python
Languages/runtime Python 3.10+
Support tier Tier 1
Distribution PyPI package vinary-tree-interop
Native boundary This adapter represents the two-word VtResource capability and its versioned interfaces; it does not implement a dictionary or automaton.
Canonical facade source vinary-tree-interop/bindings/python

The support tier controls release gating, not semantic quality: every tier has the same snapshot, ownership, status, and ABI compatibility laws. Consult the binding architecture before implementing a custom provider and the family hub when combining independently packaged projects.

The host-language facade crosses one project ABI and retains a versioned family resource rather than sharing Rust object layouts.

Package surface and verification

src/vinary_tree_interop/__init__.py is the complete public facade. The wheel carries the standard py.typed marker so downstream type checkers consume its inline annotations. The package includes hosted-provider conformance tests and a maintained public example:

python -m pip install ./bindings/python
python bindings/python/examples/host_providers.py

Project-specific algorithm constructors live in their owning packages. This neutral module models and validates the shared handoff, exports Python dictionary, WFST, lattice, and semiring providers, and supplies ScalarWfst as the reusable consuming view for any compatible WFST resource. See the Python resource guide for complete APIs, semantics, and runnable integration patterns.

Public API and data model

The idiomatic facade groups the stable surface into these concepts:

Concept Semantics
VtResource Two pointer-sized words: an opaque context and a base vtable. A borrowed value transfers no ownership.
Base vtable struct_size, ABI version, retain, release, and query_interface; it is the only mandatory interface.
Dictionary interface Snapshot capture, node paging, finality, optional values, unit/value domains, and capability flags.
Dictionary entries interface Optional finite lexicographic stream over one captured revision, with bounded arena batches, exact generation leases, cancellation, and a reducer path.
Scalar-WFST interface ScalarWfstResource exports Python graphs; ScalarWfst retains and traverses any compatible resource through snapshot capture, start state, final weights, bounded arc pages, explicit domains, and capability flags.
Lattice interface Host-defined immutable join/meet values with exact domain IDs, borrowed operands, stable bytes, diagnostics, and bounded bulk operations.
Semiring interfaces Host-defined algebra with provider-scoped value leases, optional division/star/numeric capabilities, declared laws, and bounded folds.

Unit and value domains are explicit enum fields on the discovered interface; adapters must never infer them from host container types. Empty terms, embedded zero bytes, non-ASCII text, and the full unsigned 64-bit identifier range are represented explicitly; no facade may use a sentinel value that removes a valid input from the domain.

For the exhaustive native function contract—including exact preconditions, returnable statuses, complexity, and thread-safety—use the family resource ABI reference. The facade source linked above is the authoritative idiomatic symbol inventory; its exhaustive coverage is pinned by the canonical C header and the Rust layout and discriminant tests.

Ownership, snapshots, and resource handoff

Use with for every hosted resource. Finalizers are leak containment, not a deterministic resource policy. Close every entries cursor, and release its current generation before advancing or closing it.

A borrowed resource becomes owned only after a successful retain. Interface discovery does not transfer ownership, and a failed validation must release any retain already acquired. A captured snapshot owns an independent revision and may outlive the producing project handle. Release exactly once for every successful retain; never release an unretained borrowed pair. An entries cursor is move-only and owns its captured revision until close. Exactly one generation may be live: release that exact generation before advancing, reducing, or closing; reducer batch views expire when their callback returns.

ScalarWfst(resource) obtains an independent retain, so the producing facade may close as soon as construction succeeds. snapshot() returns another independently owned view pinned to the captured revision. Use both as context managers. Project packages alone should call ScalarWfst.adopt(raw_resource): that constructor transfers one already-owned retain and deliberately avoids a second retain/release pair.

Borrowed results are intentionally lexical. Copy data that must outlive the callback; retaining a raw address, slice, memory segment, or foreign pointer is an API violation even when the next operation happens to reuse the same arena.

Errors and failure containment

Interop validation failures preserve VtStatus; project facades map that status into their own public error currency.

Null resource words, truncated vtables, incompatible interface identities or versions, invalid domains, forged node/state identifiers, malformed page counts or entry arenas, stale or mismatched batch generations, live-batch conflicts, provider faults, and contained panics are distinct failures. Never parse diagnostic prose to branch on an error: inspect the typed status/exception first and treat the message as human context. Diagnostics must be copied before another native call on the same thread.

Concurrency and reentrancy

A retained resource may cross threads only when its advertised interface flags permit it. Retain and release remain balanced under every failure path. One entries cursor and its live batch are single-consumer; reducer callbacks must not reenter that cursor.

Snapshot capture is a linearization point, not a dictionary-wide query lock. First-party immutable snapshots can be walked concurrently. A foreign provider that does not advertise parallel callbacks is serialized at its callback gate; the host language must not add a weaker promise.

Performance and marshalling

  • Pass the two-word resource by value; do not serialize or copy the graph.
  • Capture one immutable snapshot and page nodes/arcs through bounded buffers.
  • Use ScalarWfst.state_info() when arcs are unnecessary and choose a bounded batch_size for arcs() or state(); the consumer rejects non-progressing or internally inconsistent pages.
  • Negotiate entries-v1 when exact lexicographic enumeration is needed; honor all entry/unit/value limits on every batch.
  • Supply hosted lattice and semiring bulk methods to amortize Python/native callback cost.
  • Keep semiring values immutable; the facade uses provider-scoped leases rather than copying Python objects into native memory.
  • Cache a validated optional interface only while the owning resource remains retained.
  • Respect capability flags before enabling parallel callback entry.
  • Prefer a compact immutable graph interface when advertised; retain the paged callback fallback for compatibility.

Security model

Treat a foreign resource provider and all user-controlled queries as untrusted inputs. Validate lengths before allocation, preserve paging bounds, reject unknown enum values, contain callbacks/panics at the boundary, and never trust capability flags until interface negotiation succeeds. The normative duties are in the binding trust model.

Compatibility and troubleshooting

The project ABI revision, family ABI version, interface identity/version, package version, and umbrella-runtime version are independent counters. Follow the ABI evolution policy; never infer compatibility from a package version alone.

When loading fails, check—in order—the documented runtime/toolchain version, CPU/OS artifact, native-access permission, loader search path, dependent interop package pin, and process-wide JavaScript runtime identity. When a query fails after construction, report the typed status and copied diagnostic before reducing the case to the smallest dictionary/query pair.

Maintainer checklist

  1. Update the interop generator model before changing a layout, identifier, flag, or enum.
  2. Regenerate headers/constants and the API coverage matrix.
  3. Extend the canonical executable example and negative-path tests.
  4. Run the language package, snapshot, leak, property, and cross-project suites.
  5. Verify package staging contains this guide and uses coherent sibling pins.
  6. Render diagrams headlessly and run the documentation/link/math gates.