jsonatapy 2.1.6__tar.gz → 2.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- jsonatapy-2.2.1/CHANGELOG.md +172 -0
- {jsonatapy-2.1.6 → jsonatapy-2.2.1}/Cargo.lock +3 -3
- {jsonatapy-2.1.6 → jsonatapy-2.2.1}/Cargo.toml +1 -1
- {jsonatapy-2.1.6 → jsonatapy-2.2.1}/PKG-INFO +26 -55
- {jsonatapy-2.1.6 → jsonatapy-2.2.1}/README.md +25 -54
- {jsonatapy-2.1.6 → jsonatapy-2.2.1}/pyproject.toml +1 -1
- {jsonatapy-2.1.6 → jsonatapy-2.2.1}/python/jsonatapy/__init__.py +133 -22
- {jsonatapy-2.1.6 → jsonatapy-2.2.1}/src/ast_transform.rs +804 -108
- {jsonatapy-2.1.6 → jsonatapy-2.2.1}/src/evaluator.rs +619 -82
- {jsonatapy-2.1.6 → jsonatapy-2.2.1}/src/lib.rs +104 -23
- {jsonatapy-2.1.6 → jsonatapy-2.2.1}/src/parser.rs +100 -1
- {jsonatapy-2.1.6 → jsonatapy-2.2.1}/src/vm.rs +178 -20
- jsonatapy-2.2.1/tests/integration_test.rs +1720 -0
- jsonatapy-2.1.6/CHANGELOG.md +0 -45
- jsonatapy-2.1.6/tests/integration_test.rs +0 -687
- {jsonatapy-2.1.6 → jsonatapy-2.2.1}/.gitignore +0 -0
- {jsonatapy-2.1.6 → jsonatapy-2.2.1}/.gitmodules +0 -0
- {jsonatapy-2.1.6 → jsonatapy-2.2.1}/.serena/.gitignore +0 -0
- {jsonatapy-2.1.6 → jsonatapy-2.2.1}/.serena/memories/architecture.md +0 -0
- {jsonatapy-2.1.6 → jsonatapy-2.2.1}/.serena/memories/conventions.md +0 -0
- {jsonatapy-2.1.6 → jsonatapy-2.2.1}/.serena/memories/core.md +0 -0
- {jsonatapy-2.1.6 → jsonatapy-2.2.1}/.serena/memories/memory_maintenance.md +0 -0
- {jsonatapy-2.1.6 → jsonatapy-2.2.1}/.serena/memories/suggested_commands.md +0 -0
- {jsonatapy-2.1.6 → jsonatapy-2.2.1}/.serena/memories/task_completion.md +0 -0
- {jsonatapy-2.1.6 → jsonatapy-2.2.1}/.serena/memories/tech_stack.md +0 -0
- {jsonatapy-2.1.6 → jsonatapy-2.2.1}/.serena/project.yml +0 -0
- {jsonatapy-2.1.6 → jsonatapy-2.2.1}/LICENSE +0 -0
- {jsonatapy-2.1.6 → jsonatapy-2.2.1}/benches/evaluator_bench.rs +0 -0
- {jsonatapy-2.1.6 → jsonatapy-2.2.1}/examples/evaluator_demo.rs +0 -0
- {jsonatapy-2.1.6 → jsonatapy-2.2.1}/examples/parser_demo.rs +0 -0
- {jsonatapy-2.1.6 → jsonatapy-2.2.1}/python/jsonatapy/py.typed +0 -0
- {jsonatapy-2.1.6 → jsonatapy-2.2.1}/src/ast.rs +0 -0
- {jsonatapy-2.1.6 → jsonatapy-2.2.1}/src/compiler.rs +0 -0
- {jsonatapy-2.1.6 → jsonatapy-2.2.1}/src/datetime.rs +0 -0
- {jsonatapy-2.1.6 → jsonatapy-2.2.1}/src/functions.rs +0 -0
- {jsonatapy-2.1.6 → jsonatapy-2.2.1}/src/parser/README.md +0 -0
- {jsonatapy-2.1.6 → jsonatapy-2.2.1}/src/signature.rs +0 -0
- {jsonatapy-2.1.6 → jsonatapy-2.2.1}/src/value.rs +0 -0
- {jsonatapy-2.1.6 → jsonatapy-2.2.1}/tests/datetime_picture_suite.rs +0 -0
- {jsonatapy-2.1.6 → jsonatapy-2.2.1}/tests/parent_and_focus_binding_suite.rs +0 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
### Deprecated
|
|
15
|
+
|
|
16
|
+
### Removed
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
### Security
|
|
21
|
+
|
|
22
|
+
## [2.2.1] - 2026-07-08
|
|
23
|
+
|
|
24
|
+
> Same code as [2.1.7](#217---2026-07-08) below, renumbered. This project's release versions
|
|
25
|
+
> track the jsonata-js major/minor version they target (patch numbers are independent — see
|
|
26
|
+
> README). `2.1.7` incorrectly continued the old `2.1.x` patch series even though this release's
|
|
27
|
+
> guardrails feature and signature-engine fixes target jsonata-js `2.2.0`/`2.2.1`; `2.1.7` is
|
|
28
|
+
> superseded immediately by this release and should not be used.
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
- Guardrails: `timeout` (ms, error code `D1012`), `max_stack_depth` (error code `D1011`), and
|
|
32
|
+
`max_sequence_length` (error code `D2015`) keyword arguments on `compile()` and every
|
|
33
|
+
`evaluate*()` call, enforced consistently across all three execution engines (tree-walker,
|
|
34
|
+
compiled-expression fast path, bytecode VM). All default to `None` (unlimited) — no behavior
|
|
35
|
+
change unless configured. See [Guardrails](docs/api.md#guardrails). (jsonata-js 2.2.1 Phase 2, #56)
|
|
36
|
+
- Documented the guardrails API in `docs/api.md`, `docs/usage.md`, and `docs/error-handling.md`
|
|
37
|
+
(previously shipped with accurate Python docstrings but no user-facing docs), and corrected
|
|
38
|
+
`docs/migration-from-js.md`'s stale claim that Python had no built-in timeout support.
|
|
39
|
+
|
|
40
|
+
### Fixed
|
|
41
|
+
- A deeply-nested expression (arithmetic chains, parenthesized/grouped expressions) no longer
|
|
42
|
+
crashes the whole process (previously a native stack overflow) — now raises a graceful `U1002`
|
|
43
|
+
error instead, via a depth guard in the parser and a second, defense-in-depth guard in the
|
|
44
|
+
post-parse AST pass.
|
|
45
|
+
- `Instr::MakeArray`/`MakeObject`/`BlockEnd`'s bytecode operands (and `CallBuiltin`'s argument
|
|
46
|
+
count, and internal constant-pool bookkeeping) no longer silently produce wrong, truncated
|
|
47
|
+
results for oversized literals/blocks/calls (e.g. array literals with more than 65,535
|
|
48
|
+
elements) — such cases now fall back to the always-correct tree-walker instead.
|
|
49
|
+
- `ast_transform.rs`'s depth-guard error messages no longer imply `%`/`@`/`#` ancestor-operator
|
|
50
|
+
usage (e.g. "...while resolving ancestor/path metadata") when the guard fires for any
|
|
51
|
+
sufficiently-nested expression, including plain arithmetic.
|
|
52
|
+
- Release workflow now fails loudly on a fresh dispatch when the target version tag already
|
|
53
|
+
exists at a different commit, instead of silently reusing the wrong commit (#53).
|
|
54
|
+
|
|
55
|
+
## [2.1.7] - 2026-07-08
|
|
56
|
+
|
|
57
|
+
**Superseded by [2.2.1](#221---2026-07-08) above, published the same day.** This version was
|
|
58
|
+
numbered following a simple patch-increment from `2.1.6` rather than this project's actual
|
|
59
|
+
versioning policy (track jsonata-js's major/minor). It is fully functional and was not yanked —
|
|
60
|
+
package registries don't allow deleting a published version — but `2.2.1` is the version that
|
|
61
|
+
should be used going forward.
|
|
62
|
+
|
|
63
|
+
### Added
|
|
64
|
+
- Guardrails: `timeout` (ms, error code `D1012`), `max_stack_depth` (error code `D1011`), and
|
|
65
|
+
`max_sequence_length` (error code `D2015`) keyword arguments on `compile()` and every
|
|
66
|
+
`evaluate*()` call, enforced consistently across all three execution engines (tree-walker,
|
|
67
|
+
compiled-expression fast path, bytecode VM). All default to `None` (unlimited) — no behavior
|
|
68
|
+
change unless configured. See [Guardrails](docs/api.md#guardrails). (jsonata-js 2.2.1 Phase 2, #56)
|
|
69
|
+
- Documented the guardrails API in `docs/api.md`, `docs/usage.md`, and `docs/error-handling.md`
|
|
70
|
+
(previously shipped with accurate Python docstrings but no user-facing docs), and corrected
|
|
71
|
+
`docs/migration-from-js.md`'s stale claim that Python had no built-in timeout support.
|
|
72
|
+
|
|
73
|
+
### Fixed
|
|
74
|
+
- A deeply-nested expression (arithmetic chains, parenthesized/grouped expressions) no longer
|
|
75
|
+
crashes the whole process (previously a native stack overflow) — now raises a graceful `U1002`
|
|
76
|
+
error instead, via a depth guard in the parser and a second, defense-in-depth guard in the
|
|
77
|
+
post-parse AST pass.
|
|
78
|
+
- `Instr::MakeArray`/`MakeObject`/`BlockEnd`'s bytecode operands (and `CallBuiltin`'s argument
|
|
79
|
+
count, and internal constant-pool bookkeeping) no longer silently produce wrong, truncated
|
|
80
|
+
results for oversized literals/blocks/calls (e.g. array literals with more than 65,535
|
|
81
|
+
elements) — such cases now fall back to the always-correct tree-walker instead.
|
|
82
|
+
- `ast_transform.rs`'s depth-guard error messages no longer imply `%`/`@`/`#` ancestor-operator
|
|
83
|
+
usage (e.g. "...while resolving ancestor/path metadata") when the guard fires for any
|
|
84
|
+
sufficiently-nested expression, including plain arithmetic.
|
|
85
|
+
- Release workflow now fails loudly on a fresh dispatch when the target version tag already
|
|
86
|
+
exists at a different commit, instead of silently reusing the wrong commit (#53).
|
|
87
|
+
|
|
88
|
+
## [2.1.6] - 2026-07-07
|
|
89
|
+
|
|
90
|
+
### Added
|
|
91
|
+
- `%` (parent-reference) and `@`/`#` (focus/index binding) operators.
|
|
92
|
+
- jsonata-js 2.2.1 Phase 1: signature engine rewrite adding `+`/`-` arity support (#36).
|
|
93
|
+
- Versioned documentation via `mike`, fixing a gh-pages deploy race (#39).
|
|
94
|
+
|
|
95
|
+
### Fixed
|
|
96
|
+
- Reference test-suite coverage gaps: loader fix, datetime picture-strings,
|
|
97
|
+
`formatInteger`/`parseInteger` (Phases 0-2), and array-constructor/`distinct` stragglers
|
|
98
|
+
(Phase 5, #44).
|
|
99
|
+
- `release.yml` never actually built macOS wheels, due to a broken `actions/setup-python`
|
|
100
|
+
invocation (#41).
|
|
101
|
+
- Several benchmark accuracy/fairness corrections (await `jsonata-js` calls properly, substantiate
|
|
102
|
+
pre-converted-data speedup claims, use clean CI-sourced numbers, use jsonata-python's `Context`
|
|
103
|
+
for fair repeated-eval timing) (#45, #46, #49, #50).
|
|
104
|
+
|
|
105
|
+
### Changed
|
|
106
|
+
- macOS temporarily dropped from the main release matrix pending a self-hosted runner fix (#45,
|
|
107
|
+
#47) — later restored via a self-hosted Mac Mini runner.
|
|
108
|
+
|
|
109
|
+
## [2.1.5] - 2026-07-04
|
|
110
|
+
|
|
111
|
+
### Fixed
|
|
112
|
+
- Native stack overflow on deep recursion — replaced with a graceful, coded error (fixes #34).
|
|
113
|
+
- Lambda IDs are now generated from a monotonic counter instead of an AST pointer address, fixing
|
|
114
|
+
a ~0.5%-frequency wrong-closure bug from id aliasing across recursive/repeated evaluation
|
|
115
|
+
(fixes #35).
|
|
116
|
+
- Tree-walker missing-path/field access now correctly produces `Undefined` instead of `Null` in
|
|
117
|
+
the ~20 sites that predated the `Null`/`Undefined` distinction (fixes #32).
|
|
118
|
+
- CI repairs following the default-branch rename to `main`; patched `RUSTSEC-2026-0097`.
|
|
119
|
+
|
|
120
|
+
### Changed
|
|
121
|
+
- Bumped `pyo3` to 0.29 and `rand` to 0.10.
|
|
122
|
+
|
|
123
|
+
## [2.1.4] - 2026-03-22
|
|
124
|
+
|
|
125
|
+
### Added
|
|
126
|
+
- `cargo publish` step in the release workflow.
|
|
127
|
+
|
|
128
|
+
### Fixed
|
|
129
|
+
- Release workflow is now idempotent for version-bump and tag-creation steps.
|
|
130
|
+
- Corrected an incorrect expected value in the `substring` test suite.
|
|
131
|
+
|
|
132
|
+
## [2.1.3] - 2026-03-22
|
|
133
|
+
|
|
134
|
+
> Versions 2.1.1 and 2.1.2 were bumped internally but never published as standalone tagged
|
|
135
|
+
> releases — their changes are folded into this entry, the next version actually released.
|
|
136
|
+
|
|
137
|
+
### Added
|
|
138
|
+
- Bytecode VM (`compiler.rs` + `vm.rs`, "Phase 4") restored and wired into the Python execution
|
|
139
|
+
path, with Criterion benchmarks comparing it against the tree-walker.
|
|
140
|
+
- `pyo3` made an optional dependency; Rust crate renamed to `jsonata-core` and published to
|
|
141
|
+
crates.io independently of the `jsonatapy` PyPI package.
|
|
142
|
+
|
|
143
|
+
### Fixed
|
|
144
|
+
- PyO3 0.28 compatibility (`PyObject` → `Py<PyAny>`).
|
|
145
|
+
- Clippy deprecations and `cargo-deny` license-check failures.
|
|
146
|
+
- Upgraded Pillow (dev/docs dependency) to 12.1.1 for a CVE fix.
|
|
147
|
+
|
|
148
|
+
### Changed
|
|
149
|
+
- Multiple benchmark documentation and accuracy corrections.
|
|
150
|
+
|
|
151
|
+
## [2.1.0] - 2026-02-08
|
|
152
|
+
|
|
153
|
+
### Added
|
|
154
|
+
- Initial public release: Rust-based JSONata implementation targeting jsonata-js v2.1.0 semantics.
|
|
155
|
+
- Full jsonata-js v2.1.0 reference test-suite compatibility.
|
|
156
|
+
- Python bindings (PyO3), published as `jsonatapy` on PyPI.
|
|
157
|
+
- Rust core, published as `jsonata-core` on crates.io.
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Reference Implementation Tracking
|
|
162
|
+
|
|
163
|
+
This project tracks the [jsonata-js](https://github.com/jsonata-js/jsonata) reference implementation.
|
|
164
|
+
|
|
165
|
+
**Current status:** Full test-suite compatibility with jsonata-js v2.1.0 (+3 commits). jsonata-js
|
|
166
|
+
v2.2.1 compatibility work is in progress — Phase 1 (signature engine, `+`/`-` arity support) and
|
|
167
|
+
Phase 2 (resource guardrails) are done; see
|
|
168
|
+
`docs/superpowers/specs/2026-07-04-jsonata-2.2.1-design.md` for the authoritative, up-to-date
|
|
169
|
+
status of this effort.
|
|
170
|
+
|
|
171
|
+
### Version History
|
|
172
|
+
- Target tracking v2.1.0 - Project initialization (2025-01-17)
|
|
@@ -255,9 +255,9 @@ dependencies = [
|
|
|
255
255
|
|
|
256
256
|
[[package]]
|
|
257
257
|
name = "crossbeam-epoch"
|
|
258
|
-
version = "0.9.
|
|
258
|
+
version = "0.9.20"
|
|
259
259
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
260
|
-
checksum = "
|
|
260
|
+
checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f"
|
|
261
261
|
dependencies = [
|
|
262
262
|
"crossbeam-utils",
|
|
263
263
|
]
|
|
@@ -483,7 +483,7 @@ dependencies = [
|
|
|
483
483
|
|
|
484
484
|
[[package]]
|
|
485
485
|
name = "jsonata-core"
|
|
486
|
-
version = "2.1
|
|
486
|
+
version = "2.2.1"
|
|
487
487
|
dependencies = [
|
|
488
488
|
"base64",
|
|
489
489
|
"chrono",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: jsonatapy
|
|
3
|
-
Version: 2.1
|
|
3
|
+
Version: 2.2.1
|
|
4
4
|
Classifier: Development Status :: 4 - Beta
|
|
5
5
|
Classifier: Intended Audience :: Developers
|
|
6
6
|
Classifier: License :: OSI Approved :: MIT License
|
|
@@ -47,10 +47,14 @@ Project-URL: Repository, https://github.com/txjmb/jsonata-core
|
|
|
47
47
|
|
|
48
48
|
High-performance [JSONata](https://jsonata.org/) implementation in Rust, with Python bindings.
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
Much of this project was built using Claude Code with significant human oversight. There was no performant
|
|
51
|
+
JSONata implementation in Python, so the goal was to port JSONata to Rust (with a PyO3 wrapper
|
|
52
|
+
for Python) and see how fast it could go. The answer: faster than V8 for most expression
|
|
53
|
+
workloads, and faster than the next pure-Rust implementation.
|
|
54
|
+
|
|
55
|
+
Many, many thanks to the incredible work of all the maintainers of the [JSONata](https://github.com/jsonata-js/jsonata) reference library. JSONata is a very powerful, well-designed, and useful language that has made an impact on many projects. This project leverages their outstanding work to extend that capability to Python and Rust and would not be possible without that project. The implementation in Rust was strongly influenced by their implementation. The 1600+ tests they created provided the scaffolding and validation for all of this project. This project will continue to follow and be a derivative of the reference project as the JSONata reference library evolves.
|
|
56
|
+
|
|
57
|
+
Release versions will follow the reference jsonata-js project major and minor release numbers, but not necessarily patches. This will make it easier for adopters of this library to understand each release's JSONata API compatibility. As an example, 2.1.7 should be compliant with 2.1.x jsonata-js tests, but may have fixes specific to this library. If a patch release for jsonata-js is relevant for this project, it will be included in a patch release that may or may not follow the patch numbers of the upstream project.
|
|
54
58
|
|
|
55
59
|
[](https://crates.io/crates/jsonata-core)
|
|
56
60
|
[](https://pypi.org/project/jsonatapy/)
|
|
@@ -91,10 +95,10 @@ let result = Evaluator::new().evaluate(&ast, &data)?;
|
|
|
91
95
|
```toml
|
|
92
96
|
# Cargo.toml
|
|
93
97
|
[dependencies]
|
|
94
|
-
jsonata-core = "2.1.
|
|
98
|
+
jsonata-core = "2.1.6" # pure Rust, no Python dependency
|
|
95
99
|
|
|
96
100
|
# Optional: disable SIMD for constrained targets
|
|
97
|
-
jsonata-core = { version = "2.1.
|
|
101
|
+
jsonata-core = { version = "2.1.6", default-features = false }
|
|
98
102
|
```
|
|
99
103
|
|
|
100
104
|
---
|
|
@@ -124,10 +128,10 @@ print(result) # 2450
|
|
|
124
128
|
|
|
125
129
|
# Pre-convert data once for maximum throughput
|
|
126
130
|
data = jsonatapy.JsonataData(large_dataset)
|
|
127
|
-
result = expr.evaluate_with_data(data) #
|
|
131
|
+
result = expr.evaluate_with_data(data) # 3–15x faster than evaluate(dict)
|
|
128
132
|
```
|
|
129
133
|
|
|
130
|
-
Supports Python 3.10, 3.11, 3.12, 3.13 on Linux, macOS (Intel & ARM), and Windows.
|
|
134
|
+
Supports Python 3.10, 3.11, 3.12, 3.13, 3.14 on Linux, macOS (Intel & ARM), and Windows.
|
|
131
135
|
|
|
132
136
|
---
|
|
133
137
|
|
|
@@ -148,58 +152,25 @@ See [official JSONata docs](https://docs.jsonata.org/) for the full language ref
|
|
|
148
152
|
## Performance
|
|
149
153
|
|
|
150
154
|
`jsonata-core` passes **1682/1682** JSONata reference tests and is the fastest JSONata
|
|
151
|
-
implementation available in either Rust or Python
|
|
152
|
-
|
|
153
|
-
### Pure Rust (Criterion benchmarks, no Python overhead)
|
|
154
|
-
|
|
155
|
-
| Category | jsonata-core | vs jsonata-rs |
|
|
156
|
-
|----------|-------------|----------------|
|
|
157
|
-
| Simple path lookup | 81 ns | ~40x faster |
|
|
158
|
-
| Arithmetic expression | 140 ns | ~40x faster |
|
|
159
|
-
| Conditional | 106 ns | ~30x faster |
|
|
160
|
-
| String operations | 126–284 ns | ~30x faster |
|
|
161
|
-
| $sum (100 elements) | 287 ns | ~70x faster |
|
|
162
|
-
| Filter predicate (100 objects) | 7.9 µs | ~50x faster |
|
|
163
|
-
| Realistic workload (100 products) | 9–44 µs | ~40x faster |
|
|
155
|
+
implementation available in either Rust or Python:
|
|
164
156
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
157
|
+
- **~6x faster on average** than the JavaScript reference implementation (V8), across all
|
|
158
|
+
benchmark categories — up to ~16x for complex transformations and string operations
|
|
159
|
+
- **~40x faster** than jsonata-rs (the next pure-Rust JSONata implementation) on pure-Rust
|
|
160
|
+
Criterion benchmarks with no Python overhead on either side (`cargo bench`)
|
|
161
|
+
- **hundreds of times faster** than jsonata-python, even when it reuses its fastest
|
|
162
|
+
(`Context`-based) repeated-evaluation path
|
|
169
163
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
`jsonatapy` is the fastest Python JSONata implementation by a large margin, and faster than
|
|
173
|
-
the JavaScript reference implementation for most pure expression workloads:
|
|
174
|
-
|
|
175
|
-
| Category | vs JavaScript (V8) | vs jsonata-python |
|
|
176
|
-
|----------|--------------------|-------------------|
|
|
177
|
-
| Simple paths | **5–8x faster** (array index access: roughly tied) | ~20–40x faster |
|
|
178
|
-
| Conditionals | **15x faster** | ~40x faster |
|
|
179
|
-
| String operations | **8–15x faster** | ~30–45x faster |
|
|
180
|
-
| Complex transformations | **5–15x faster** | ~20–40x faster |
|
|
181
|
-
| Higher-order functions | **12–19x faster** | ~50–70x faster |
|
|
182
|
-
| Array-heavy workloads | **~1–3x slower to ~4x faster**, depending on access pattern | ~10–50x faster |
|
|
183
|
-
|
|
184
|
-
### The Python boundary
|
|
185
|
-
|
|
186
|
-
For large array workloads, the dominant cost is converting Python dicts to Rust values
|
|
187
|
-
on each `evaluate()` call — not expression evaluation itself. Two API paths avoid this:
|
|
164
|
+
For large array workloads, pre-convert data once with `jsonatapy.JsonataData` and reuse it
|
|
165
|
+
across queries — this avoids the Python↔Rust conversion cost that otherwise dominates:
|
|
188
166
|
|
|
189
167
|
```python
|
|
190
|
-
# Path 1: Pre-convert data once, reuse across many queries (4–15x faster than evaluate(dict))
|
|
191
168
|
data = jsonatapy.JsonataData(large_dataset)
|
|
192
|
-
result = expr.evaluate_with_data(data)
|
|
193
|
-
|
|
194
|
-
# Path 2: Data arrives as a raw JSON string — pass it directly
|
|
195
|
-
result_str = expr.evaluate_json(raw_json_string)
|
|
169
|
+
result = expr.evaluate_with_data(data) # 3–15x faster than evaluate(dict)
|
|
196
170
|
```
|
|
197
171
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
path without pre-conversion is roughly at parity with V8 (0.5–2x either direction).
|
|
201
|
-
|
|
202
|
-
See [Performance docs](docs/performance.md) for full benchmark results and methodology.
|
|
172
|
+
See [Performance docs](docs/performance.md) for the full category-by-category breakdown and
|
|
173
|
+
benchmark methodology.
|
|
203
174
|
|
|
204
175
|
---
|
|
205
176
|
|
|
@@ -208,7 +179,7 @@ See [Performance docs](docs/performance.md) for full benchmark results and metho
|
|
|
208
179
|
- **1682/1682 JSONata reference tests passing**
|
|
209
180
|
- **Pure Rust core** — no JavaScript runtime, no Node.js dependency
|
|
210
181
|
- **Optional Python bindings** — PyO3/maturin, zero-copy where possible
|
|
211
|
-
- **Cross-platform** — Linux, macOS (Intel & ARM), Windows; Python 3.10–3.
|
|
182
|
+
- **Cross-platform** — Linux, macOS (Intel & ARM), Windows; Python 3.10–3.14
|
|
212
183
|
- **SIMD-accelerated JSON parsing** — via `simd-json` (optional feature)
|
|
213
184
|
|
|
214
185
|
---
|
|
@@ -2,10 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
High-performance [JSONata](https://jsonata.org/) implementation in Rust, with Python bindings.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
Much of this project was built using Claude Code with significant human oversight. There was no performant
|
|
6
|
+
JSONata implementation in Python, so the goal was to port JSONata to Rust (with a PyO3 wrapper
|
|
7
|
+
for Python) and see how fast it could go. The answer: faster than V8 for most expression
|
|
8
|
+
workloads, and faster than the next pure-Rust implementation.
|
|
9
|
+
|
|
10
|
+
Many, many thanks to the incredible work of all the maintainers of the [JSONata](https://github.com/jsonata-js/jsonata) reference library. JSONata is a very powerful, well-designed, and useful language that has made an impact on many projects. This project leverages their outstanding work to extend that capability to Python and Rust and would not be possible without that project. The implementation in Rust was strongly influenced by their implementation. The 1600+ tests they created provided the scaffolding and validation for all of this project. This project will continue to follow and be a derivative of the reference project as the JSONata reference library evolves.
|
|
11
|
+
|
|
12
|
+
Release versions will follow the reference jsonata-js project major and minor release numbers, but not necessarily patches. This will make it easier for adopters of this library to understand each release's JSONata API compatibility. As an example, 2.1.7 should be compliant with 2.1.x jsonata-js tests, but may have fixes specific to this library. If a patch release for jsonata-js is relevant for this project, it will be included in a patch release that may or may not follow the patch numbers of the upstream project.
|
|
9
13
|
|
|
10
14
|
[](https://crates.io/crates/jsonata-core)
|
|
11
15
|
[](https://pypi.org/project/jsonatapy/)
|
|
@@ -46,10 +50,10 @@ let result = Evaluator::new().evaluate(&ast, &data)?;
|
|
|
46
50
|
```toml
|
|
47
51
|
# Cargo.toml
|
|
48
52
|
[dependencies]
|
|
49
|
-
jsonata-core = "2.1.
|
|
53
|
+
jsonata-core = "2.1.6" # pure Rust, no Python dependency
|
|
50
54
|
|
|
51
55
|
# Optional: disable SIMD for constrained targets
|
|
52
|
-
jsonata-core = { version = "2.1.
|
|
56
|
+
jsonata-core = { version = "2.1.6", default-features = false }
|
|
53
57
|
```
|
|
54
58
|
|
|
55
59
|
---
|
|
@@ -79,10 +83,10 @@ print(result) # 2450
|
|
|
79
83
|
|
|
80
84
|
# Pre-convert data once for maximum throughput
|
|
81
85
|
data = jsonatapy.JsonataData(large_dataset)
|
|
82
|
-
result = expr.evaluate_with_data(data) #
|
|
86
|
+
result = expr.evaluate_with_data(data) # 3–15x faster than evaluate(dict)
|
|
83
87
|
```
|
|
84
88
|
|
|
85
|
-
Supports Python 3.10, 3.11, 3.12, 3.13 on Linux, macOS (Intel & ARM), and Windows.
|
|
89
|
+
Supports Python 3.10, 3.11, 3.12, 3.13, 3.14 on Linux, macOS (Intel & ARM), and Windows.
|
|
86
90
|
|
|
87
91
|
---
|
|
88
92
|
|
|
@@ -103,58 +107,25 @@ See [official JSONata docs](https://docs.jsonata.org/) for the full language ref
|
|
|
103
107
|
## Performance
|
|
104
108
|
|
|
105
109
|
`jsonata-core` passes **1682/1682** JSONata reference tests and is the fastest JSONata
|
|
106
|
-
implementation available in either Rust or Python
|
|
107
|
-
|
|
108
|
-
### Pure Rust (Criterion benchmarks, no Python overhead)
|
|
109
|
-
|
|
110
|
-
| Category | jsonata-core | vs jsonata-rs |
|
|
111
|
-
|----------|-------------|----------------|
|
|
112
|
-
| Simple path lookup | 81 ns | ~40x faster |
|
|
113
|
-
| Arithmetic expression | 140 ns | ~40x faster |
|
|
114
|
-
| Conditional | 106 ns | ~30x faster |
|
|
115
|
-
| String operations | 126–284 ns | ~30x faster |
|
|
116
|
-
| $sum (100 elements) | 287 ns | ~70x faster |
|
|
117
|
-
| Filter predicate (100 objects) | 7.9 µs | ~50x faster |
|
|
118
|
-
| Realistic workload (100 products) | 9–44 µs | ~40x faster |
|
|
110
|
+
implementation available in either Rust or Python:
|
|
119
111
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
112
|
+
- **~6x faster on average** than the JavaScript reference implementation (V8), across all
|
|
113
|
+
benchmark categories — up to ~16x for complex transformations and string operations
|
|
114
|
+
- **~40x faster** than jsonata-rs (the next pure-Rust JSONata implementation) on pure-Rust
|
|
115
|
+
Criterion benchmarks with no Python overhead on either side (`cargo bench`)
|
|
116
|
+
- **hundreds of times faster** than jsonata-python, even when it reuses its fastest
|
|
117
|
+
(`Context`-based) repeated-evaluation path
|
|
124
118
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
`jsonatapy` is the fastest Python JSONata implementation by a large margin, and faster than
|
|
128
|
-
the JavaScript reference implementation for most pure expression workloads:
|
|
129
|
-
|
|
130
|
-
| Category | vs JavaScript (V8) | vs jsonata-python |
|
|
131
|
-
|----------|--------------------|-------------------|
|
|
132
|
-
| Simple paths | **5–8x faster** (array index access: roughly tied) | ~20–40x faster |
|
|
133
|
-
| Conditionals | **15x faster** | ~40x faster |
|
|
134
|
-
| String operations | **8–15x faster** | ~30–45x faster |
|
|
135
|
-
| Complex transformations | **5–15x faster** | ~20–40x faster |
|
|
136
|
-
| Higher-order functions | **12–19x faster** | ~50–70x faster |
|
|
137
|
-
| Array-heavy workloads | **~1–3x slower to ~4x faster**, depending on access pattern | ~10–50x faster |
|
|
138
|
-
|
|
139
|
-
### The Python boundary
|
|
140
|
-
|
|
141
|
-
For large array workloads, the dominant cost is converting Python dicts to Rust values
|
|
142
|
-
on each `evaluate()` call — not expression evaluation itself. Two API paths avoid this:
|
|
119
|
+
For large array workloads, pre-convert data once with `jsonatapy.JsonataData` and reuse it
|
|
120
|
+
across queries — this avoids the Python↔Rust conversion cost that otherwise dominates:
|
|
143
121
|
|
|
144
122
|
```python
|
|
145
|
-
# Path 1: Pre-convert data once, reuse across many queries (4–15x faster than evaluate(dict))
|
|
146
123
|
data = jsonatapy.JsonataData(large_dataset)
|
|
147
|
-
result = expr.evaluate_with_data(data)
|
|
148
|
-
|
|
149
|
-
# Path 2: Data arrives as a raw JSON string — pass it directly
|
|
150
|
-
result_str = expr.evaluate_json(raw_json_string)
|
|
124
|
+
result = expr.evaluate_with_data(data) # 3–15x faster than evaluate(dict)
|
|
151
125
|
```
|
|
152
126
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
path without pre-conversion is roughly at parity with V8 (0.5–2x either direction).
|
|
156
|
-
|
|
157
|
-
See [Performance docs](docs/performance.md) for full benchmark results and methodology.
|
|
127
|
+
See [Performance docs](docs/performance.md) for the full category-by-category breakdown and
|
|
128
|
+
benchmark methodology.
|
|
158
129
|
|
|
159
130
|
---
|
|
160
131
|
|
|
@@ -163,7 +134,7 @@ See [Performance docs](docs/performance.md) for full benchmark results and metho
|
|
|
163
134
|
- **1682/1682 JSONata reference tests passing**
|
|
164
135
|
- **Pure Rust core** — no JavaScript runtime, no Node.js dependency
|
|
165
136
|
- **Optional Python bindings** — PyO3/maturin, zero-copy where possible
|
|
166
|
-
- **Cross-platform** — Linux, macOS (Intel & ARM), Windows; Python 3.10–3.
|
|
137
|
+
- **Cross-platform** — Linux, macOS (Intel & ARM), Windows; Python 3.10–3.14
|
|
167
138
|
- **SIMD-accelerated JSON parsing** — via `simd-json` (optional feature)
|
|
168
139
|
|
|
169
140
|
---
|
|
@@ -4,7 +4,7 @@ build-backend = "maturin"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "jsonatapy"
|
|
7
|
-
version = "2.1
|
|
7
|
+
version = "2.2.1"
|
|
8
8
|
description = "High-performance Python/Rust implementation of JSONata query and transformation language"
|
|
9
9
|
authors = [
|
|
10
10
|
{name = "txjmb", email = "txjmb@users.noreply.github.com"}
|