jsonatapy 2.2.2__tar.gz → 2.2.4__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.2 → jsonatapy-2.2.4}/CHANGELOG.md +71 -0
- {jsonatapy-2.2.2 → jsonatapy-2.2.4}/Cargo.lock +171 -5
- {jsonatapy-2.2.2 → jsonatapy-2.2.4}/Cargo.toml +10 -1
- {jsonatapy-2.2.2 → jsonatapy-2.2.4}/PKG-INFO +21 -2
- {jsonatapy-2.2.2 → jsonatapy-2.2.4}/README.md +16 -1
- {jsonatapy-2.2.2 → jsonatapy-2.2.4}/pyproject.toml +12 -1
- {jsonatapy-2.2.2 → jsonatapy-2.2.4}/python/jsonatapy/__init__.py +51 -0
- jsonatapy-2.2.4/python/jsonatapy/__main__.py +46 -0
- jsonatapy-2.2.4/python/jsonatapy/_cli/__init__.py +1 -0
- jsonatapy-2.2.4/python/jsonatapy/_cli/bindings.py +51 -0
- jsonatapy-2.2.4/python/jsonatapy/_cli/error_format.py +25 -0
- jsonatapy-2.2.4/python/jsonatapy/_cli/mcp_server.py +192 -0
- jsonatapy-2.2.4/python/jsonatapy/_cli/resolve.py +85 -0
- jsonatapy-2.2.4/python/jsonatapy/_cli/run.py +204 -0
- jsonatapy-2.2.4/src/bin/jsonata/bindings.rs +62 -0
- jsonatapy-2.2.4/src/bin/jsonata/error_format.rs +47 -0
- jsonatapy-2.2.4/src/bin/jsonata/main.rs +182 -0
- jsonatapy-2.2.4/src/bin/jsonata/resolve.rs +148 -0
- {jsonatapy-2.2.2 → jsonatapy-2.2.4}/src/evaluator.rs +582 -77
- {jsonatapy-2.2.2 → jsonatapy-2.2.4}/src/functions.rs +109 -3
- jsonatapy-2.2.4/src/lazy.rs +232 -0
- {jsonatapy-2.2.2 → jsonatapy-2.2.4}/src/lib.rs +74 -89
- {jsonatapy-2.2.2 → jsonatapy-2.2.4}/src/parser.rs +39 -0
- {jsonatapy-2.2.2 → jsonatapy-2.2.4}/src/signature.rs +4 -0
- {jsonatapy-2.2.2 → jsonatapy-2.2.4}/src/value.rs +76 -1
- {jsonatapy-2.2.2 → jsonatapy-2.2.4}/src/vm.rs +4 -2
- jsonatapy-2.2.4/study/cli_fixtures.json +138 -0
- jsonatapy-2.2.4/study/cli_fixtures_testdata.json +1 -0
- jsonatapy-2.2.4/study/cli_spec.md +99 -0
- jsonatapy-2.2.4/tests/cli_fixtures_test.rs +69 -0
- jsonatapy-2.2.4/tests/cli_test.rs +352 -0
- {jsonatapy-2.2.2 → jsonatapy-2.2.4}/tests/datetime_picture_suite.rs +2 -0
- {jsonatapy-2.2.2 → jsonatapy-2.2.4}/tests/parent_and_focus_binding_suite.rs +2 -0
- {jsonatapy-2.2.2 → jsonatapy-2.2.4}/.gitignore +0 -0
- {jsonatapy-2.2.2 → jsonatapy-2.2.4}/.gitmodules +0 -0
- {jsonatapy-2.2.2 → jsonatapy-2.2.4}/.serena/.gitignore +0 -0
- {jsonatapy-2.2.2 → jsonatapy-2.2.4}/.serena/memories/architecture.md +0 -0
- {jsonatapy-2.2.2 → jsonatapy-2.2.4}/.serena/memories/conventions.md +0 -0
- {jsonatapy-2.2.2 → jsonatapy-2.2.4}/.serena/memories/core.md +0 -0
- {jsonatapy-2.2.2 → jsonatapy-2.2.4}/.serena/memories/memory_maintenance.md +0 -0
- {jsonatapy-2.2.2 → jsonatapy-2.2.4}/.serena/memories/suggested_commands.md +0 -0
- {jsonatapy-2.2.2 → jsonatapy-2.2.4}/.serena/memories/task_completion.md +0 -0
- {jsonatapy-2.2.2 → jsonatapy-2.2.4}/.serena/memories/tech_stack.md +0 -0
- {jsonatapy-2.2.2 → jsonatapy-2.2.4}/.serena/project.yml +0 -0
- {jsonatapy-2.2.2 → jsonatapy-2.2.4}/LICENSE +0 -0
- {jsonatapy-2.2.2 → jsonatapy-2.2.4}/benches/evaluator_bench.rs +0 -0
- {jsonatapy-2.2.2 → jsonatapy-2.2.4}/examples/evaluator_demo.rs +0 -0
- {jsonatapy-2.2.2 → jsonatapy-2.2.4}/examples/parser_demo.rs +0 -0
- {jsonatapy-2.2.2 → jsonatapy-2.2.4}/examples/simd_json_bench.rs +0 -0
- {jsonatapy-2.2.2 → jsonatapy-2.2.4}/python/jsonatapy/py.typed +0 -0
- {jsonatapy-2.2.2 → jsonatapy-2.2.4}/src/ast.rs +0 -0
- {jsonatapy-2.2.2 → jsonatapy-2.2.4}/src/ast_transform.rs +0 -0
- {jsonatapy-2.2.2 → jsonatapy-2.2.4}/src/compiler.rs +0 -0
- {jsonatapy-2.2.2 → jsonatapy-2.2.4}/src/datetime.rs +0 -0
- {jsonatapy-2.2.2 → jsonatapy-2.2.4}/src/parser/README.md +0 -0
- {jsonatapy-2.2.2 → jsonatapy-2.2.4}/tests/integration_test.rs +0 -0
|
@@ -19,6 +19,77 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
19
19
|
|
|
20
20
|
### Security
|
|
21
21
|
|
|
22
|
+
## [2.2.4] - 2026-07-13
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
- `JSONATAPY_FORCE_TREE_WALKER=1` environment variable (testing/debugging): forces every
|
|
26
|
+
evaluation through the tree-walking evaluator, bypassing the default lazy/VM-preferred path.
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
- `evaluate(dict)` now converts Python data lazily by default. Previously every call eagerly
|
|
30
|
+
converted the entire input `dict` (and nested structures) to the internal value tree before
|
|
31
|
+
evaluation began; now only the fields an expression actually touches are converted, and
|
|
32
|
+
untouched input subtrees pass straight through to the output unchanged. Measured on the dev
|
|
33
|
+
machine (min-of-5, vs. the prior eager `evaluate(dict)`):
|
|
34
|
+
- `products.price` (100 objects): 34.9µs → 11.3µs (3.1x)
|
|
35
|
+
- Filter by category (100 products, 9 fields each): 129.3µs → 23.4µs (5.5x)
|
|
36
|
+
- `$sum(products[inStock].price)`: 116.4µs → 17.1µs (6.8x)
|
|
37
|
+
- Complex dense transformation: 160.9µs → 79µs (2.0x)
|
|
38
|
+
|
|
39
|
+
Every measured row now beats jsonata-js on identical data and machine (14.6/93.7/67.6/415.1µs
|
|
40
|
+
respectively). See `benchmarks/python/lazy_check.py` for the reproducible gate.
|
|
41
|
+
- **Behavior change:** results containing an unmodified input subtree now reference the
|
|
42
|
+
caller's *original* Python `dict`/`list` objects (result aliasing, matching jsonata-js),
|
|
43
|
+
rather than a fresh copy. Mutating such a result mutates the corresponding input — copy
|
|
44
|
+
explicitly (e.g. `copy.deepcopy`) first if you plan to mutate. Passed-through values also keep
|
|
45
|
+
their exact Python type (an `int` field the expression never reads stays an `int`); fields the
|
|
46
|
+
expression does touch still round-trip through the engine's number representation (whole
|
|
47
|
+
values come back as Python `int`), as before.
|
|
48
|
+
- **Behavior change:** an unconvertible input value (e.g. a `set`) now raises `TypeError` only
|
|
49
|
+
when the expression actually touches it, instead of eagerly for the whole input at the start
|
|
50
|
+
of `evaluate()`. Any Python exception raised while lazily reading a field (e.g. `OverflowError`
|
|
51
|
+
on an integer too large to represent) is likewise normalized to `TypeError` at this boundary,
|
|
52
|
+
rather than propagating as its original exception type.
|
|
53
|
+
|
|
54
|
+
### Deprecated
|
|
55
|
+
|
|
56
|
+
### Removed
|
|
57
|
+
|
|
58
|
+
### Fixed
|
|
59
|
+
|
|
60
|
+
### Security
|
|
61
|
+
|
|
62
|
+
## [2.2.3] - 2026-07-12
|
|
63
|
+
|
|
64
|
+
### Added
|
|
65
|
+
- `evaluate_json_or_none()`'s `json_str` parameter now accepts `None` (in addition to a JSON
|
|
66
|
+
string), binding the top-level context (`$`) to a true JSONata `Undefined` rather than an
|
|
67
|
+
explicit `null`. (#68)
|
|
68
|
+
- `docs/cli.md`: a full command-line reference for both `jsonata` (Rust) and `jsonatapy`
|
|
69
|
+
(Python), covering flags, input resolution, output/exit-code semantics, and the MCP
|
|
70
|
+
subcommand — previously undocumented outside internal spec/plan files. (#68)
|
|
71
|
+
|
|
72
|
+
### Changed
|
|
73
|
+
- Release CLI binary archives are now named `jsonata-v<version>-<target>.tar.gz`/`.zip`
|
|
74
|
+
(previously `jsonata-<target>.tar.gz`/`.zip` with no version embedded).
|
|
75
|
+
|
|
76
|
+
### Deprecated
|
|
77
|
+
|
|
78
|
+
### Removed
|
|
79
|
+
|
|
80
|
+
### Fixed
|
|
81
|
+
- The Python CLI's `-n`/`--null-input` now binds `$` to a true `Undefined`, matching the Rust
|
|
82
|
+
CLI exactly (`jsonatapy -n '$'` now prints nothing, as `jsonata -n '$'` already did). It
|
|
83
|
+
previously passed an explicit JSON `null` context instead, observable only for expressions
|
|
84
|
+
referencing `$` directly. (#68)
|
|
85
|
+
- Release workflow: `publish-pypi` and `publish-crates` now require `build-cli-binaries` to
|
|
86
|
+
succeed first. Previously the two registry publishes were independent of the CLI binary
|
|
87
|
+
build, so a CLI build failure (untested in a real release prior to this) would have shipped
|
|
88
|
+
a version to PyPI/crates.io with no CLI binaries attached and no way to reuse that version
|
|
89
|
+
number.
|
|
90
|
+
|
|
91
|
+
### Security
|
|
92
|
+
|
|
22
93
|
## [2.2.2] - 2026-07-09
|
|
23
94
|
|
|
24
95
|
### Added
|
|
@@ -41,12 +41,56 @@ version = "0.1.6"
|
|
|
41
41
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
42
42
|
checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
|
|
43
43
|
|
|
44
|
+
[[package]]
|
|
45
|
+
name = "anstream"
|
|
46
|
+
version = "1.0.0"
|
|
47
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
48
|
+
checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d"
|
|
49
|
+
dependencies = [
|
|
50
|
+
"anstyle",
|
|
51
|
+
"anstyle-parse",
|
|
52
|
+
"anstyle-query",
|
|
53
|
+
"anstyle-wincon",
|
|
54
|
+
"colorchoice",
|
|
55
|
+
"is_terminal_polyfill",
|
|
56
|
+
"utf8parse",
|
|
57
|
+
]
|
|
58
|
+
|
|
44
59
|
[[package]]
|
|
45
60
|
name = "anstyle"
|
|
46
61
|
version = "1.0.14"
|
|
47
62
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
48
63
|
checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
|
|
49
64
|
|
|
65
|
+
[[package]]
|
|
66
|
+
name = "anstyle-parse"
|
|
67
|
+
version = "1.0.0"
|
|
68
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
69
|
+
checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e"
|
|
70
|
+
dependencies = [
|
|
71
|
+
"utf8parse",
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
[[package]]
|
|
75
|
+
name = "anstyle-query"
|
|
76
|
+
version = "1.1.5"
|
|
77
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
78
|
+
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
|
|
79
|
+
dependencies = [
|
|
80
|
+
"windows-sys",
|
|
81
|
+
]
|
|
82
|
+
|
|
83
|
+
[[package]]
|
|
84
|
+
name = "anstyle-wincon"
|
|
85
|
+
version = "3.0.11"
|
|
86
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
87
|
+
checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
|
|
88
|
+
dependencies = [
|
|
89
|
+
"anstyle",
|
|
90
|
+
"once_cell_polyfill",
|
|
91
|
+
"windows-sys",
|
|
92
|
+
]
|
|
93
|
+
|
|
50
94
|
[[package]]
|
|
51
95
|
name = "ar_archive_writer"
|
|
52
96
|
version = "0.5.2"
|
|
@@ -56,6 +100,21 @@ dependencies = [
|
|
|
56
100
|
"object",
|
|
57
101
|
]
|
|
58
102
|
|
|
103
|
+
[[package]]
|
|
104
|
+
name = "assert_cmd"
|
|
105
|
+
version = "2.2.2"
|
|
106
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
107
|
+
checksum = "2aa3a22042e45de04255c7bf3626e239f450200fd0493c1e382263544b20aea6"
|
|
108
|
+
dependencies = [
|
|
109
|
+
"anstyle",
|
|
110
|
+
"bstr",
|
|
111
|
+
"libc",
|
|
112
|
+
"predicates",
|
|
113
|
+
"predicates-core",
|
|
114
|
+
"predicates-tree",
|
|
115
|
+
"wait-timeout",
|
|
116
|
+
]
|
|
117
|
+
|
|
59
118
|
[[package]]
|
|
60
119
|
name = "autocfg"
|
|
61
120
|
version = "1.5.1"
|
|
@@ -89,6 +148,17 @@ version = "2.13.0"
|
|
|
89
148
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
90
149
|
checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
|
|
91
150
|
|
|
151
|
+
[[package]]
|
|
152
|
+
name = "bstr"
|
|
153
|
+
version = "1.12.3"
|
|
154
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
155
|
+
checksum = "5cee35f73844aa3014bb606320a6c1f010249dbdf43342fe54b5a4f6a8ed4b79"
|
|
156
|
+
dependencies = [
|
|
157
|
+
"memchr",
|
|
158
|
+
"regex-automata",
|
|
159
|
+
"serde_core",
|
|
160
|
+
]
|
|
161
|
+
|
|
92
162
|
[[package]]
|
|
93
163
|
name = "bumpalo"
|
|
94
164
|
version = "3.20.3"
|
|
@@ -175,6 +245,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
|
175
245
|
checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51"
|
|
176
246
|
dependencies = [
|
|
177
247
|
"clap_builder",
|
|
248
|
+
"clap_derive",
|
|
178
249
|
]
|
|
179
250
|
|
|
180
251
|
[[package]]
|
|
@@ -183,8 +254,22 @@ version = "4.6.0"
|
|
|
183
254
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
184
255
|
checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f"
|
|
185
256
|
dependencies = [
|
|
257
|
+
"anstream",
|
|
186
258
|
"anstyle",
|
|
187
259
|
"clap_lex",
|
|
260
|
+
"strsim",
|
|
261
|
+
]
|
|
262
|
+
|
|
263
|
+
[[package]]
|
|
264
|
+
name = "clap_derive"
|
|
265
|
+
version = "4.6.1"
|
|
266
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
267
|
+
checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9"
|
|
268
|
+
dependencies = [
|
|
269
|
+
"heck",
|
|
270
|
+
"proc-macro2",
|
|
271
|
+
"quote",
|
|
272
|
+
"syn",
|
|
188
273
|
]
|
|
189
274
|
|
|
190
275
|
[[package]]
|
|
@@ -193,6 +278,12 @@ version = "1.1.0"
|
|
|
193
278
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
194
279
|
checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
|
|
195
280
|
|
|
281
|
+
[[package]]
|
|
282
|
+
name = "colorchoice"
|
|
283
|
+
version = "1.0.5"
|
|
284
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
285
|
+
checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
|
|
286
|
+
|
|
196
287
|
[[package]]
|
|
197
288
|
name = "core-foundation-sys"
|
|
198
289
|
version = "0.8.7"
|
|
@@ -274,6 +365,12 @@ version = "0.2.4"
|
|
|
274
365
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
275
366
|
checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
|
|
276
367
|
|
|
368
|
+
[[package]]
|
|
369
|
+
name = "difflib"
|
|
370
|
+
version = "0.4.0"
|
|
371
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
372
|
+
checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
|
|
373
|
+
|
|
277
374
|
[[package]]
|
|
278
375
|
name = "either"
|
|
279
376
|
version = "1.16.0"
|
|
@@ -455,6 +552,12 @@ dependencies = [
|
|
|
455
552
|
"hashbrown 0.17.1",
|
|
456
553
|
]
|
|
457
554
|
|
|
555
|
+
[[package]]
|
|
556
|
+
name = "is_terminal_polyfill"
|
|
557
|
+
version = "1.70.2"
|
|
558
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
559
|
+
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
|
|
560
|
+
|
|
458
561
|
[[package]]
|
|
459
562
|
name = "itertools"
|
|
460
563
|
version = "0.13.0"
|
|
@@ -483,14 +586,17 @@ dependencies = [
|
|
|
483
586
|
|
|
484
587
|
[[package]]
|
|
485
588
|
name = "jsonata-core"
|
|
486
|
-
version = "2.2.
|
|
589
|
+
version = "2.2.4"
|
|
487
590
|
dependencies = [
|
|
591
|
+
"assert_cmd",
|
|
488
592
|
"base64",
|
|
489
593
|
"chrono",
|
|
594
|
+
"clap",
|
|
490
595
|
"criterion",
|
|
491
596
|
"indexmap",
|
|
492
597
|
"num-traits",
|
|
493
598
|
"percent-encoding",
|
|
599
|
+
"predicates",
|
|
494
600
|
"proptest",
|
|
495
601
|
"pyo3",
|
|
496
602
|
"rand 0.10.2",
|
|
@@ -526,6 +632,12 @@ version = "2.8.3"
|
|
|
526
632
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
527
633
|
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
|
|
528
634
|
|
|
635
|
+
[[package]]
|
|
636
|
+
name = "normalize-line-endings"
|
|
637
|
+
version = "0.3.0"
|
|
638
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
639
|
+
checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
|
|
640
|
+
|
|
529
641
|
[[package]]
|
|
530
642
|
name = "num-traits"
|
|
531
643
|
version = "0.2.19"
|
|
@@ -550,6 +662,12 @@ version = "1.21.4"
|
|
|
550
662
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
551
663
|
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
|
552
664
|
|
|
665
|
+
[[package]]
|
|
666
|
+
name = "once_cell_polyfill"
|
|
667
|
+
version = "1.70.2"
|
|
668
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
669
|
+
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
|
|
670
|
+
|
|
553
671
|
[[package]]
|
|
554
672
|
name = "oorandom"
|
|
555
673
|
version = "11.1.5"
|
|
@@ -621,6 +739,36 @@ dependencies = [
|
|
|
621
739
|
"zerocopy",
|
|
622
740
|
]
|
|
623
741
|
|
|
742
|
+
[[package]]
|
|
743
|
+
name = "predicates"
|
|
744
|
+
version = "3.1.4"
|
|
745
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
746
|
+
checksum = "ada8f2932f28a27ee7b70dd6c1c39ea0675c55a36879ab92f3a715eaa1e63cfe"
|
|
747
|
+
dependencies = [
|
|
748
|
+
"anstyle",
|
|
749
|
+
"difflib",
|
|
750
|
+
"float-cmp",
|
|
751
|
+
"normalize-line-endings",
|
|
752
|
+
"predicates-core",
|
|
753
|
+
"regex",
|
|
754
|
+
]
|
|
755
|
+
|
|
756
|
+
[[package]]
|
|
757
|
+
name = "predicates-core"
|
|
758
|
+
version = "1.0.10"
|
|
759
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
760
|
+
checksum = "cad38746f3166b4031b1a0d39ad9f954dd291e7854fcc0eed52ee41a0b50d144"
|
|
761
|
+
|
|
762
|
+
[[package]]
|
|
763
|
+
name = "predicates-tree"
|
|
764
|
+
version = "1.0.13"
|
|
765
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
766
|
+
checksum = "d0de1b847b39c8131db0467e9df1ff60e6d0562ab8e9a16e568ad0fdb372e2f2"
|
|
767
|
+
dependencies = [
|
|
768
|
+
"predicates-core",
|
|
769
|
+
"termtree",
|
|
770
|
+
]
|
|
771
|
+
|
|
624
772
|
[[package]]
|
|
625
773
|
name = "proc-macro2"
|
|
626
774
|
version = "1.0.106"
|
|
@@ -840,9 +988,9 @@ dependencies = [
|
|
|
840
988
|
|
|
841
989
|
[[package]]
|
|
842
990
|
name = "regex"
|
|
843
|
-
version = "1.
|
|
991
|
+
version = "1.13.0"
|
|
844
992
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
845
|
-
checksum = "
|
|
993
|
+
checksum = "2a0e75113e14dc5acb068cd0786884f214f1312650a3d36d269f5c4f3cdee8a2"
|
|
846
994
|
dependencies = [
|
|
847
995
|
"aho-corasick",
|
|
848
996
|
"memchr",
|
|
@@ -965,9 +1113,9 @@ checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
|
|
|
965
1113
|
|
|
966
1114
|
[[package]]
|
|
967
1115
|
name = "simd-json"
|
|
968
|
-
version = "0.17.
|
|
1116
|
+
version = "0.17.3"
|
|
969
1117
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
970
|
-
checksum = "
|
|
1118
|
+
checksum = "e32d7ab2678282d21e53374fbead7119b7eacbede73685dcaac472870a29a11c"
|
|
971
1119
|
dependencies = [
|
|
972
1120
|
"halfbrown",
|
|
973
1121
|
"ref-cast",
|
|
@@ -1002,6 +1150,12 @@ dependencies = [
|
|
|
1002
1150
|
"windows-sys",
|
|
1003
1151
|
]
|
|
1004
1152
|
|
|
1153
|
+
[[package]]
|
|
1154
|
+
name = "strsim"
|
|
1155
|
+
version = "0.11.1"
|
|
1156
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1157
|
+
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
|
1158
|
+
|
|
1005
1159
|
[[package]]
|
|
1006
1160
|
name = "syn"
|
|
1007
1161
|
version = "2.0.118"
|
|
@@ -1032,6 +1186,12 @@ dependencies = [
|
|
|
1032
1186
|
"windows-sys",
|
|
1033
1187
|
]
|
|
1034
1188
|
|
|
1189
|
+
[[package]]
|
|
1190
|
+
name = "termtree"
|
|
1191
|
+
version = "0.5.1"
|
|
1192
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1193
|
+
checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683"
|
|
1194
|
+
|
|
1035
1195
|
[[package]]
|
|
1036
1196
|
name = "thiserror"
|
|
1037
1197
|
version = "2.0.18"
|
|
@@ -1074,6 +1234,12 @@ version = "1.0.24"
|
|
|
1074
1234
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1075
1235
|
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
|
1076
1236
|
|
|
1237
|
+
[[package]]
|
|
1238
|
+
name = "utf8parse"
|
|
1239
|
+
version = "0.2.2"
|
|
1240
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1241
|
+
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
|
1242
|
+
|
|
1077
1243
|
[[package]]
|
|
1078
1244
|
name = "value-trait"
|
|
1079
1245
|
version = "0.12.1"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "jsonata-core"
|
|
3
|
-
version = "2.2.
|
|
3
|
+
version = "2.2.4"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
authors = ["txjmb <txjmb@users.noreply.github.com>"]
|
|
6
6
|
description = "High-performance Rust implementation of JSONata query and transformation language"
|
|
@@ -36,6 +36,11 @@ exclude = [
|
|
|
36
36
|
name = "jsonata_core"
|
|
37
37
|
crate-type = ["cdylib", "rlib"]
|
|
38
38
|
|
|
39
|
+
[[bin]]
|
|
40
|
+
name = "jsonata"
|
|
41
|
+
path = "src/bin/jsonata/main.rs"
|
|
42
|
+
required-features = ["cli"]
|
|
43
|
+
|
|
39
44
|
[dependencies]
|
|
40
45
|
pyo3 = { version = "0.29", optional = true }
|
|
41
46
|
serde = { version = "1.0", features = ["derive"] }
|
|
@@ -50,16 +55,20 @@ percent-encoding = "2.3"
|
|
|
50
55
|
rand = "0.10"
|
|
51
56
|
stacker = "0.1"
|
|
52
57
|
simd-json = { version = "0.17", optional = true }
|
|
58
|
+
clap = { version = "4.6", features = ["derive"], optional = true }
|
|
53
59
|
|
|
54
60
|
[features]
|
|
55
61
|
default = ["simd"]
|
|
56
62
|
simd = ["dep:simd-json"]
|
|
57
63
|
python = ["dep:pyo3"]
|
|
64
|
+
cli = ["dep:clap"]
|
|
58
65
|
bench = [] # exposes _bench facade for Criterion benchmarks
|
|
59
66
|
|
|
60
67
|
[dev-dependencies]
|
|
61
68
|
criterion = "0.8"
|
|
62
69
|
proptest = "1.10"
|
|
70
|
+
assert_cmd = "2.0"
|
|
71
|
+
predicates = "3.1"
|
|
63
72
|
|
|
64
73
|
[[bench]]
|
|
65
74
|
name = "evaluator_bench"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: jsonatapy
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.4
|
|
4
4
|
Classifier: Development Status :: 4 - Beta
|
|
5
5
|
Classifier: Intended Audience :: Developers
|
|
6
6
|
Classifier: License :: OSI Approved :: MIT License
|
|
@@ -20,16 +20,20 @@ Requires-Dist: matplotlib>=3.5 ; extra == 'bench'
|
|
|
20
20
|
Requires-Dist: pytest>=7.0 ; extra == 'dev'
|
|
21
21
|
Requires-Dist: pytest-cov>=4.0 ; extra == 'dev'
|
|
22
22
|
Requires-Dist: pytest-xdist>=3.0 ; extra == 'dev'
|
|
23
|
+
Requires-Dist: pytest-asyncio>=0.24 ; extra == 'dev'
|
|
23
24
|
Requires-Dist: ruff>=0.3.0 ; extra == 'dev'
|
|
24
25
|
Requires-Dist: mypy>=1.0 ; extra == 'dev'
|
|
25
26
|
Requires-Dist: maturin>=1.0 ; extra == 'dev'
|
|
27
|
+
Requires-Dist: fastmcp>=3.0 ; extra == 'dev'
|
|
26
28
|
Requires-Dist: mkdocs>=1.5 ; extra == 'docs'
|
|
27
29
|
Requires-Dist: mkdocs-material>=9.0 ; extra == 'docs'
|
|
28
30
|
Requires-Dist: mkdocstrings[python]>=0.24 ; extra == 'docs'
|
|
29
31
|
Requires-Dist: pymdown-extensions>=10.0 ; extra == 'docs'
|
|
32
|
+
Requires-Dist: fastmcp>=3.0 ; extra == 'mcp'
|
|
30
33
|
Provides-Extra: bench
|
|
31
34
|
Provides-Extra: dev
|
|
32
35
|
Provides-Extra: docs
|
|
36
|
+
Provides-Extra: mcp
|
|
33
37
|
License-File: LICENSE
|
|
34
38
|
Summary: High-performance Python/Rust implementation of JSONata query and transformation language
|
|
35
39
|
Keywords: jsonata,json,query,transform
|
|
@@ -50,7 +54,7 @@ High-performance [JSONata](https://jsonata.org/) implementation in Rust, with Py
|
|
|
50
54
|
Much of this project was built using Claude Code with significant human oversight. There was no performant
|
|
51
55
|
JSONata implementation in Python, so the goal was to port JSONata to Rust (with a PyO3 wrapper
|
|
52
56
|
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.
|
|
57
|
+
workloads, and faster than the next pure-Rust implementation. The rust versions are published on crates.io, and the python wheels on pypi.
|
|
54
58
|
|
|
55
59
|
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
60
|
|
|
@@ -135,6 +139,20 @@ Supports Python 3.10, 3.11, 3.12, 3.13, 3.14 on Linux, macOS (Intel & ARM), and
|
|
|
135
139
|
|
|
136
140
|
---
|
|
137
141
|
|
|
142
|
+
## Command-line quick start
|
|
143
|
+
|
|
144
|
+
Both packages also ship a CLI, `jq`-shaped, with an identical contract:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
pip install jsonatapy
|
|
148
|
+
echo '{"orders":[{"product":"Laptop","price":1200}]}' | jsonatapy 'orders[price > 100].product'
|
|
149
|
+
# "Laptop"
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
See [CLI Reference](docs/cli.md) for the full flag/exit-code contract.
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
138
156
|
## What is JSONata?
|
|
139
157
|
|
|
140
158
|
JSONata is a query and transformation language for JSON data:
|
|
@@ -189,6 +207,7 @@ benchmark methodology.
|
|
|
189
207
|
- [Installation](docs/installation.md)
|
|
190
208
|
- [API Reference](docs/api.md)
|
|
191
209
|
- [Usage Guide](docs/usage.md)
|
|
210
|
+
- [CLI Reference](docs/cli.md)
|
|
192
211
|
- [Performance](docs/performance.md)
|
|
193
212
|
- [Optimization Tips](docs/optimization-tips.md)
|
|
194
213
|
- [Building from Source](docs/development/building.md)
|
|
@@ -5,7 +5,7 @@ High-performance [JSONata](https://jsonata.org/) implementation in Rust, with Py
|
|
|
5
5
|
Much of this project was built using Claude Code with significant human oversight. There was no performant
|
|
6
6
|
JSONata implementation in Python, so the goal was to port JSONata to Rust (with a PyO3 wrapper
|
|
7
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.
|
|
8
|
+
workloads, and faster than the next pure-Rust implementation. The rust versions are published on crates.io, and the python wheels on pypi.
|
|
9
9
|
|
|
10
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
11
|
|
|
@@ -90,6 +90,20 @@ Supports Python 3.10, 3.11, 3.12, 3.13, 3.14 on Linux, macOS (Intel & ARM), and
|
|
|
90
90
|
|
|
91
91
|
---
|
|
92
92
|
|
|
93
|
+
## Command-line quick start
|
|
94
|
+
|
|
95
|
+
Both packages also ship a CLI, `jq`-shaped, with an identical contract:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
pip install jsonatapy
|
|
99
|
+
echo '{"orders":[{"product":"Laptop","price":1200}]}' | jsonatapy 'orders[price > 100].product'
|
|
100
|
+
# "Laptop"
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
See [CLI Reference](docs/cli.md) for the full flag/exit-code contract.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
93
107
|
## What is JSONata?
|
|
94
108
|
|
|
95
109
|
JSONata is a query and transformation language for JSON data:
|
|
@@ -144,6 +158,7 @@ benchmark methodology.
|
|
|
144
158
|
- [Installation](docs/installation.md)
|
|
145
159
|
- [API Reference](docs/api.md)
|
|
146
160
|
- [Usage Guide](docs/usage.md)
|
|
161
|
+
- [CLI Reference](docs/cli.md)
|
|
147
162
|
- [Performance](docs/performance.md)
|
|
148
163
|
- [Optimization Tips](docs/optimization-tips.md)
|
|
149
164
|
- [Building from Source](docs/development/building.md)
|
|
@@ -4,7 +4,7 @@ build-backend = "maturin"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "jsonatapy"
|
|
7
|
-
version = "2.2.
|
|
7
|
+
version = "2.2.4"
|
|
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"}
|
|
@@ -30,14 +30,22 @@ classifiers = [
|
|
|
30
30
|
]
|
|
31
31
|
dependencies = []
|
|
32
32
|
|
|
33
|
+
[project.scripts]
|
|
34
|
+
jsonatapy = "jsonatapy.__main__:main"
|
|
35
|
+
|
|
33
36
|
[project.optional-dependencies]
|
|
34
37
|
dev = [
|
|
35
38
|
"pytest>=7.0",
|
|
36
39
|
"pytest-cov>=4.0",
|
|
37
40
|
"pytest-xdist>=3.0", # Parallel test execution
|
|
41
|
+
"pytest-asyncio>=0.24", # Async tests for the FastMCP server (Task 9)
|
|
38
42
|
"ruff>=0.3.0", # Linting and formatting
|
|
39
43
|
"mypy>=1.0",
|
|
40
44
|
"maturin>=1.0",
|
|
45
|
+
"fastmcp>=3.0", # So `dev` installs can run the full test suite (Task 9)
|
|
46
|
+
]
|
|
47
|
+
mcp = [
|
|
48
|
+
"fastmcp>=3.0",
|
|
41
49
|
]
|
|
42
50
|
bench = [
|
|
43
51
|
"jsonata>=0.2.0", # jsonata-python for benchmarking
|
|
@@ -67,9 +75,11 @@ dev = [
|
|
|
67
75
|
"pytest>=7.0",
|
|
68
76
|
"pytest-cov>=4.0",
|
|
69
77
|
"pytest-xdist>=3.0",
|
|
78
|
+
"pytest-asyncio>=0.24",
|
|
70
79
|
"ruff>=0.3.0",
|
|
71
80
|
"mypy>=1.0",
|
|
72
81
|
"maturin>=1.0",
|
|
82
|
+
"fastmcp>=3.0",
|
|
73
83
|
]
|
|
74
84
|
|
|
75
85
|
[tool.ruff]
|
|
@@ -112,6 +122,7 @@ python_files = "test_*.py"
|
|
|
112
122
|
python_classes = "Test*"
|
|
113
123
|
python_functions = "test_*"
|
|
114
124
|
addopts = "-v --strict-markers"
|
|
125
|
+
asyncio_mode = "auto"
|
|
115
126
|
markers = [
|
|
116
127
|
"slow: marks tests as slow (deselect with '-m \"not slow\"')",
|
|
117
128
|
"compatibility: marks tests that verify JS compatibility",
|
|
@@ -230,6 +230,57 @@ class JsonataExpression:
|
|
|
230
230
|
json_str, bindings, timeout, max_stack_depth, max_sequence_length
|
|
231
231
|
)
|
|
232
232
|
|
|
233
|
+
def evaluate_json_or_none(
|
|
234
|
+
self,
|
|
235
|
+
json_str: str | None,
|
|
236
|
+
bindings: dict[str, Any] | None = None,
|
|
237
|
+
*,
|
|
238
|
+
timeout: int | None = None,
|
|
239
|
+
max_stack_depth: int | None = None,
|
|
240
|
+
max_sequence_length: int | None = None,
|
|
241
|
+
) -> str | None:
|
|
242
|
+
"""
|
|
243
|
+
Evaluate with JSON string input, distinguishing Undefined from null.
|
|
244
|
+
|
|
245
|
+
Unlike evaluate_json(), which serializes both a JSONata Undefined
|
|
246
|
+
result and an explicit JSON null result to the same text "null",
|
|
247
|
+
this method returns None (the Python value) for Undefined and the
|
|
248
|
+
string "null" for an explicit null result.
|
|
249
|
+
|
|
250
|
+
Args:
|
|
251
|
+
json_str: Input data as a JSON string, or None for no input
|
|
252
|
+
document at all -- the top-level context (`$`) is then a
|
|
253
|
+
true JSONata Undefined, distinct from passing "null" (an
|
|
254
|
+
explicit JSON null context).
|
|
255
|
+
bindings: Optional additional variable bindings
|
|
256
|
+
timeout: Maximum evaluation time in milliseconds (raises ValueError
|
|
257
|
+
with a D1012 code on timeout). Overrides any default set via
|
|
258
|
+
`compile(timeout=...)` for this call only.
|
|
259
|
+
max_stack_depth: Maximum recursion stack depth (raises ValueError
|
|
260
|
+
with a D1011 code when exceeded). Overrides any compile-time default.
|
|
261
|
+
max_sequence_length: Maximum length of a query-result sequence
|
|
262
|
+
(map/filter/wildcard/descendants/etc; raises ValueError with a
|
|
263
|
+
D2015 code when exceeded). Overrides any compile-time default.
|
|
264
|
+
|
|
265
|
+
Returns:
|
|
266
|
+
None if the result is JSONata Undefined, otherwise the result
|
|
267
|
+
as a JSON string (e.g. "null" for an explicit null result).
|
|
268
|
+
|
|
269
|
+
Raises:
|
|
270
|
+
ValueError: If JSON parsing or evaluation fails, or a guardrail is exceeded
|
|
271
|
+
|
|
272
|
+
Example:
|
|
273
|
+
>>> expr = compile("nonexistent")
|
|
274
|
+
>>> expr.evaluate_json_or_none('{"a": 1}') is None
|
|
275
|
+
True
|
|
276
|
+
>>> expr2 = compile("a")
|
|
277
|
+
>>> expr2.evaluate_json_or_none('{"a": null}')
|
|
278
|
+
'null'
|
|
279
|
+
"""
|
|
280
|
+
return self._expr.evaluate_json_or_none(
|
|
281
|
+
json_str, bindings, timeout, max_stack_depth, max_sequence_length
|
|
282
|
+
)
|
|
283
|
+
|
|
233
284
|
def evaluate_with_data(
|
|
234
285
|
self,
|
|
235
286
|
data: "JsonataData",
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Console-script entry point for the `jsonatapy` CLI.
|
|
2
|
+
|
|
3
|
+
Dispatches to the MCP server subcommand (`jsonatapy mcp ...`) or evaluate
|
|
4
|
+
mode (everything else). See study/cli_spec.md for the full contract.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import argparse
|
|
10
|
+
import sys
|
|
11
|
+
|
|
12
|
+
from ._cli.run import run
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _run_mcp(argv: list[str]) -> int:
|
|
16
|
+
parser = argparse.ArgumentParser(prog="jsonatapy mcp", description="Run the JSONata MCP server")
|
|
17
|
+
parser.add_argument("--http", action="store_true", help="Serve over HTTP instead of stdio")
|
|
18
|
+
parser.add_argument(
|
|
19
|
+
"--port", type=int, default=None, metavar="N", help="HTTP port (default 8000)"
|
|
20
|
+
)
|
|
21
|
+
args = parser.parse_args(argv)
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
from ._cli.mcp_server import serve
|
|
25
|
+
except ImportError:
|
|
26
|
+
print(
|
|
27
|
+
"error: the 'mcp' extra is not installed. Run:\n"
|
|
28
|
+
' uvx --from "jsonatapy[mcp]" jsonatapy mcp\n'
|
|
29
|
+
"or: pip install jsonatapy[mcp]",
|
|
30
|
+
file=sys.stderr,
|
|
31
|
+
)
|
|
32
|
+
return 2
|
|
33
|
+
|
|
34
|
+
serve(args.http, args.port)
|
|
35
|
+
return 0
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def main() -> int:
|
|
39
|
+
argv = sys.argv[1:]
|
|
40
|
+
if argv[:1] == ["mcp"]:
|
|
41
|
+
return _run_mcp(argv[1:])
|
|
42
|
+
return run(argv)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
if __name__ == "__main__":
|
|
46
|
+
sys.exit(main())
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Internal CLI implementation for the `jsonatapy` console script. Not public API."""
|