Skip to main content

aero-jmespath

A native JMESPath kernel written in Aero, compiled to a Python C-extension. It parses JSON, evaluates a JMESPath expression, and serializes the result entirely in native code — no Python objects are built in between.

What it is

jmespath.py is a pure-Python implementation. Its standard usage is:

import jmespath
result = jmespath.search("servers[?status == `active`].name", data_dict)

aero-jmespath provides a drop-in equivalent that takes the raw JSON string and returns a JSON string, skipping the Python-object round-trip:

import kernel
result = kernel.search("servers[?status == `active`].name", json_str)

The Aero kernel runs the full pipeline — JSON parse, expression parse, evaluation and serialization — in one native pass over the input.

Build

Requires the Aero toolchain (64-bit Windows / macOS / Linux). From the repo root:

aero build src/kernel.aero --pyext     # produces src/kernel.pyd / kernel.so

Usage

import sys
sys.path.insert(0, "src")
import kernel

kernel.search("@", '{"a": 1}')                       # '{"a":1}'
kernel.search("a.b", '{"a": {"b": "x"}}')            # '"x"'
kernel.search("servers[*].name", open("d.json").read())

search(expr, json_str) returns the serialized result as a JSON string, or "\x1eERR1" (syntax error) / "\x1eERR2" (invalid-value error).

Compile-once API

Repeated queries on the same expression skip the lexer/parser entirely: compile the expression once to an opaque blob, then evaluate the blob against any number of documents. This is what aero_jmespath.search() uses internally (with a bounded in-process cache), and it is also exposed directly:

import aero_jmespath

blob = aero_jmespath.compile("servers[?status == `active`].name")   # once
result = aero_jmespath.search_cached(blob, json_str)                 # per document

Batch queries on one document

multi() parses the JSON document exactly once and evaluates many compiled expressions against the same parsed document in native code — the batch workload where jmespath.py reuses a pre-parsed dict:

import aero_jmespath

results = aero_jmespath.multi(
    ["servers[*].name",
     "servers[?status == `active`].instances[0].id",
     "servers[0].instances[*].cpu"],
    json_str,
)   # list of JSON result strings, one per expression

compile raises ValueError on a bad expression; multi raises ValueError on a bad expression or a non-JSON document.

Compliance

Passes all 578 official jmespath compliance tests (identifiers, indices, slices, filters, projections, multiselect, literals, escapes, wildcards, booleans).

tests/run_compliance.py

basic.json       ok=18   fail=0
boolean.json     ok=60   fail=0
...
TOTAL ok=578 fail=0

Integration with jmespath.py

aero-jmespath is designed to be the optional native accelerator for jmespath.py. Once installed (pip install aero-jmespath), jmespath.search_json(expr, json_string) runs the whole parse + eval + serialize pipeline in native code and falls back to pure Python when the package is absent, so behaviour never changes:

import jmespath
result = jmespath.search_json("servers[?status == `active`].name", json_str)

aero_jmespath.search(expression, json_string) itself returns a JSON string, so it can also be used directly.

Performance

For one-shot queries — parse + eval + serialize per call, the typical CLI / stream / per-request workload where the input is a JSON string — the native kernel is 1.6–2.2× faster than jmespath.py (json.loads + compiled search), and the advantage grows with document size:

document aero-jmespath jmespath.py speedup
0.71 MB 5.6 ms 9.1 ms 1.63×
3.57 MB 30.2 ms 56.9 ms 1.88×
7.19 MB 61.7 ms 133.8 ms 2.17×

Query: servers[?status == active].instances[0].id on a synthetic server fleet. See bench/ for the scripts.

In a batch workload (many queries on a document that is already parsed once, e.g. compiled.search(dict) on a reused dict), a single search/search_cached call is slower because it re-parses the JSON string every time. But the native kernel has its own batch primitive that wins: multi() parses the document once and evaluates every expression against that same parsed arena, avoiding the pure-Python object walk that jmespath.py pays per expression:

8 expressions on one 0.71 MB doc (per batch of 8):
  aero_multi   5.98 ms/batch
  jmespath    14.34 ms/batch
  ratio        2.40x

The one-shot speedup (fresh parse per call) remains the string-input workload; multi() is the answer when one document drives many queries.

JSON parsing alone beats CPython's json.loads and the Rust-based orjson (on a 7.19 MB / 20000-server document, bench/parse.py):

aero parse_only  41.00 ms   (175 MB/s)
json.loads       82.40 ms   ( 87 MB/s)   -> 2.01x slower
orjson.loads     64.63 ms   (111 MB/s)   -> 1.58x slower

Expression-level comparison on a 0.71 MB / 2000-server document (queries/sec, higher is better; jp+str = jmespath including result json.dumps):

expression             aero (q/s)   jp+str (q/s)   vs jp+str
------------------------------------------------------------
simple field                  153           153      1.00x
wildcard projection           132           130      1.01x
filter + projection           121            96      1.27x
nested projection             111           113      0.98x
multiselect hash               89            87      1.02x
comparison filter             107            98      1.10x
slice                         102           152      0.67x

Architecture

  • Flat value arena — every JSON value lives in one Vec<Jv>; arrays and objects store value indices, objects keep [key_index, value_index, ...] pairs so key names survive serialization.
  • Fixed-point numbers — Aero has no i64→f64 conversion and extern "C" cannot return f64, so numbers are stored as i64 in micro-units (value × 1e6); all compliance numbers are short decimals well within range.
  • Index-based serializer — serialization recurses over value indices rather than passing the 32-byte enum by value, so flat arrays of 1M+ elements serialize without exhausting the ~1 MB Python thread stack.
  • Expression layer — a Pratt parser mirrors jmespath.py's binding-power table, producing an AST arena that the tree-walking evaluator consumes.

Files

src/kernel.aero          the Aero kernel (parser + evaluator + serializer)
tests/run_compliance.py  runs the official compliance suite
tests/test_parse.py      JSON parser smoke tests
tests/test_compile.py    compile() + search_cached() round-trip vs search()
tests/test_multi.py      multi() batch results vs per-expression search()
tests/test_native.py     integration test: search_json() accelerated path vs pure Python
bench/bench.py           expression-level benchmark vs jmespath.py
bench/batch.py           batch vs one-shot benchmark
bench/scale.py           speedup vs document size
bench/parse.py           JSON parse-only vs json.loads / orjson

The compiled extension is built locally (aero build src/kernel.aero --pyext) and is not committed to the repository.

License

MIT

Metadata

Release files for aero-jmespath 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distribution (wheel)

Table of built distributions (wheels) for aero-jmespath 0.3.0
File Interpreter ABI Platform
aero_jmespath-0.3.0-cp312-cp312-win_amd64.whl CPython 3.12 CPython 3.12 Windows x86-64 Details

Release files / aero_jmespath-0.3.0-cp312-cp312-win_amd64.whl

Download URL aero_jmespath-0.3.0-cp312-cp312-win_amd64.whl
Size 80.6 kB
Tags CPython 3.12 Windows x86-64
SHA-256 checksum
How to use checksums
9c53f81f56bb04f68268154ac5e3c2692b743c1ce979ab67fa253bc2c79d57f5
BLAKE2b-256 checksum
How to use checksums
169a485ac5f99c985bb5212a97f5f79b6aaedaf9cd5eaf6c50a5b2e0db5ec554
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.10

Security for aero-jmespath 0.3.0

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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

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.

Release history Release notifications | RSS feed

This release

0.3.0 This release

1 release file No provenance

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