Gravlax Python client
The gravlax-client package is a small, dependency-free wrapper around the
aie executable. It uses argument arrays—never a shell—and parses the
versioned project, resolved-plan, doctor, result-envelope, and MEX contracts.
The Rust executable remains the only implementation of archive semantics.
Install
Build or install aie, ensure it is on PATH, then install the client from a
checkout:
python -m pip install ./python
Install only the integrations a notebook needs:
python -m pip install './python[pandas,arrow]'
python -m pip install './python[anndata]'
Projects, plans, and diagnostics
from gravlax import Client
aie = Client() # or Client(binary="/opt/gravlax/bin/aie")
project = aie.project_show(project="analysis")
resolved = aie.plan_check(
"analysis/plans/replay.yaml",
project="analysis",
explain=True,
)
for step in resolved.steps:
print(step.id, step.args)
report = aie.doctor(["analysis/data/sample.aie"], project="analysis")
for check in report.checks:
print(check.status, check.summary, check.remedy or "")
if report.ok:
aie.plan_run("analysis/plans/replay.yaml", project="analysis")
The client reads resolved-plan v3, v4, v5, and v6. Version 4 adds biological
intent, assembly-compatibility evidence, output-schema IDs, and conservative
I/O estimates. Version 5 adds paired annotation-comparison intent and validates
the annotation roles and typed output schemas for comparison and transcript-
equivalence steps. Version 6 exposes each explicit uniform result/report format,
stdout or atomic-file publication mode, and destination through
step.uniform_io. All preserve access to resolved.producer, embedded cohort-
design resources, typed prior-step inputs, and every step's named inputs,
canonical prepared inputs, final outputs, semantic output roles, and staging
paths. These provenance fields are validated rather than silently ignored by
the client. Plan fields accept step:<id> or
step:<id>:<output-name> to consume a compatible output from an earlier
declaration.
Use project_add(..., external=True) only for an intentional absolute,
read-only input outside the otherwise portable project. An interrupted plan
can be continued with plan_run(..., resume=True); aie verifies the input,
step, and output identities in its versioned completion records before it
skips anything.
doctor() returns its complete report even when checks fail; inspect
report.ok or report.exit_code. Other unsuccessful commands raise
CommandError, whose .result retains stdout, stderr, return code, and the
exact argument vector.
Command-specific JSON and large output
Commands with their own --json modes retain those command-specific schemas
for byte compatibility. Parse one without treating it as a shared envelope:
raw = aie.result_raw([
"query", "sample.aie", "region", "chr1:1000000-2000000", "--json"
])
print(raw["schema"])
For a large JSON, TSV, or binary response, stream stdout to a new file instead of retaining it in Python memory:
written = aie.run_to_file(
["query", "sample.aie", "junctions", "chr1:1-100000000", "--tsv"],
"junctions.tsv",
)
print(written.bytes)
The destination is installed only after a successful command and is not
overwritten unless replace=True is explicit.
Uniform named-table bundles
Region and exact-junction queries have dedicated Python convenience methods for their opt-in uniform JSON interface. They return a strict named-table bundle; other commands that emit this contract can use the generic bundle methods shown below:
region = aie.query_region(
"sample.aie",
"chr16:89550000-89575000",
top=20,
)
print(region.summary.umis, region.summary.cells)
counts = region.table("counts")
print(counts.semantics.row_semantics, counts.semantics.key)
print(counts.selection.available_rows, counts.selection.truncated)
print(counts.records())
query_junction() returns the corresponding typed junction summary and the
same count-table shape. top=0 means all rows in this uniform interface.
Physical row order remains distinct from logical set/multiset/sequence
semantics; the parser validates declared keys and ordering-field references but
does not invent an ordering.
Use the file variants when a result may be large. They keep subprocess stdout out of Python memory and atomically install the completed file:
written = aie.query_junction_to_file(
"sample.aie",
"chr16:89562391-89562883",
"junction.json",
top=0,
)
bundle = aie.result_bundle_from_file(written.output_path)
Boolean evidence-unit queries and atlas-wide event discovery have argument-safe wrappers and matching bounded-memory file variants:
cooccurrence = aie.query_cooccurrence(
"sample.aie",
{
"locus": "region:chr1:155230000-155240000:+",
"splice": "junction:chr1:155234452-155235327:+",
"tail": "terminal:chr1:155239900-155240025:+",
},
"locus & splice & !tail",
universe="locus",
)
for pattern in cooccurrence.table("patterns").records():
print(pattern["pattern_mask"], pattern["selection_state"])
written = aie.collection_find_events_to_file(
"atlas.aicollection",
"events.json",
kinds=("junction", "cassette", "terminal-tail"),
design="donors.tsv",
groups="groups.tsv",
min_donors=3,
)
selection_state="unknown" preserves an unresolvable absence when a
two-representative chain omitted middle read placements; a positive predicate
always has a retained witness. unit="umi-class" requires
allow_full_scan=True and describes a barcode-corrected cell plus exact raw
UMI-value class; it does not collapse one-mismatch UMI edges and is not proof
of one physical molecule. The default placements="unique" excludes
multimappers; direct and all are explicitly diagnostic placement modes.
collection_find_events_to_file() rebuilds unique-chain, exact raw-UMI-value
class counts by sample, donor, and group from the collection's rooted source
archives and keeps the result stream out of Python memory.
For any command that emits the same JSON contract, result_bundle(args) and
parse_uniform_bundle(document) expose unique named tables, typed fields,
row semantics, and exact or deferred selection metadata. A deferred one-pass
selection represents unknown availability and truncation explicitly as null;
it is never silently treated as complete.
Shared typed results
The package also implements the gravlax.result-envelope.v1 contract for
producers that explicitly advertise it. It is deliberately separate from
result_raw() because each command's own --json output keeps its own schema:
from gravlax import ResultEnvelope
resolved = aie.resolve(
"gencode.v49.aic",
["TP53", "transcript:ENST00000269305"],
assembly="GRCh38.p14",
annotation="GENCODE 49",
)
print(resolved.table.records())
print(resolved.provenance.annotation_digest)
comparison = aie.compare_annotations(
"sample.aie",
"gencode.v44.gtf",
"gencode.v49.aic",
assembly="GRCh38.p14",
annotation_a_label="GENCODE 44",
annotation_b_label="GENCODE 49",
)
print(comparison.count_deltas.records())
ecs = aie.transcript_ecs(
"sample.aie",
"gencode.v49.aic",
assembly="GRCh38.p14",
annotation_label="GENCODE 49",
feature="gene:ENSG00000141510",
aggregation="bulk",
)
print(ecs.catalog.records())
result = ResultEnvelope.from_file("typed-result.json")
records = result.table.records() # dependency-free list of dictionaries
frame = result.to_pandas() # requires the pandas extra
arrow = result.to_arrow() # requires the arrow extra
observations = result.to_anndata(obs_names="cell")
Annotation comparison is exact only within the retained archive quotient and fixed alignment/barcode policy; its causes are non-additive explanations. Transcript ECs are retained-evidence compatibility sets rather than abundance, isoform calls, or phasing. They are derived from the representatives retained in the archive rather than every source read. Their conflict and no-compatible-transcript flags are non-exclusive.
Command-specific query --json responses retain their own schemas. Every
supported result-streaming query family exposes the typed contract explicitly
through --format=json. Commands that advertise a separate typed operation
report use --report-format=json; that is a per-command interface, not a rule
for every artifact producer. Native Arrow IPC is not yet a general CLI output,
and no R client is currently provided.
The AnnData conversion above preserves a row-oriented envelope in .obs and
does not guess a count matrix. For a MEX result carrying the shared
metadata.json completion marker, use the matrix-aware reader instead:
from gravlax import read_mex
mex = read_mex("analysis/results/counts")
matrix = mex.to_scipy() # exact feature-by-barcode orientation
adata = mex.to_anndata() # conventional cell-by-feature orientation
MEX loading requires metadata.json, validates every coordinate, bounds,
duplicate, declared nonzero, label count, and file path, and carries the result
schema and provenance into adata.uns["gravlax"].
Test
The core test suite needs no optional scientific Python packages:
cd python
PYTHONPATH=src python -m unittest discover -s tests -v
GeneFull replay
report = client.replay(
"nuclei.aie", "annotation.aic", "called-nuclei.tsv", "genefull",
gene_full=True,
)
The barcode list orders columns and must cover all counted barcodes; it does
not call nuclei. Gene is the
default model. gene_full=True includes intronic gene-span overlaps and
conflicts with velocity=True. Assignment statistics cover all input and distinguish records, representatives, and UMI
classes from collapsed UMI counts.
Metadata
Release files for gravlax-client 0.2.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| gravlax_client-0.2.3.tar.gz | 66.9 kB | With provenance | Details for gravlax_client-0.2.3.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| gravlax_client-0.2.3-py3-none-any.whl | Python 3 | none | any | Details for gravlax_client-0.2.3-py3-none-any.whl |
Total release size: 110.5 kB
Release files / gravlax_client-0.2.3.tar.gz
| Download URL | gravlax_client-0.2.3.tar.gz |
|---|---|
| Size | 66.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e76ada582712c25142f6f0d6185871e07158abfec41ddb285e37d6c4aaf09869
|
|
BLAKE2b-256 checksum How to use checksums |
7dbbc13ac2c8db805c6cf5a37b00c8a98e3ffce51577eb8420a6081703f621a0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 12, 2026.
Transparency logRelease files / gravlax_client-0.2.3-py3-none-any.whl
| Download URL | gravlax_client-0.2.3-py3-none-any.whl |
|---|---|
| Size | 43.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4634171e6bd9440721e929565e32f84c494ee51ab05f85ee1b9b503cd71e462c
|
|
BLAKE2b-256 checksum How to use checksums |
fe0b245c7ce22269fb4a572dd8f3746795e59b734d8e70a9499938719e0b1e0c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 12, 2026.
Transparency logSecurity for gravlax-client 0.2.3
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for gravlax_client-0.2.3.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 12, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.