Skip to main content

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)

Source distribution for gravlax-client 0.2.3
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)

Table of built distributions (wheels) for gravlax-client 0.2.3
File Interpreter ABI Platform Actions
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 log

Release 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 log

Security 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.

Select from 2 files

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 log

A 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.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page