synx-format 3.7.1__tar.gz → 3.7.2__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.
- {synx_format-3.7.1 → synx_format-3.7.2}/Cargo.lock +20 -60
- {synx_format-3.7.1 → synx_format-3.7.2}/Cargo.toml +1 -1
- {synx_format-3.7.1 → synx_format-3.7.2}/PKG-INFO +1 -1
- {synx_format-3.7.1 → synx_format-3.7.2}/bindings/python/Cargo.toml +6 -2
- {synx_format-3.7.1 → synx_format-3.7.2}/bindings/python/src/lib.rs +58 -13
- {synx_format-3.7.1 → synx_format-3.7.2}/bindings/python/src/synxl.rs +105 -51
- synx_format-3.7.2/bindings/python/tests/test_synxl_hardening.py +186 -0
- {synx_format-3.7.1 → synx_format-3.7.2}/crates/synx-core/Cargo.toml +1 -1
- {synx_format-3.7.1 → synx_format-3.7.2}/crates/synx-core/src/binary.rs +11 -0
- {synx_format-3.7.1 → synx_format-3.7.2}/crates/synx-core/src/engine.rs +8 -0
- {synx_format-3.7.1 → synx_format-3.7.2}/crates/synx-core/src/parser.rs +153 -86
- {synx_format-3.7.1 → synx_format-3.7.2}/crates/synx-core/src/synxl.rs +179 -26
- {synx_format-3.7.1 → synx_format-3.7.2}/crates/synx-core/src/value.rs +24 -1
- {synx_format-3.7.1 → synx_format-3.7.2}/crates/synx-core/src/wasm.rs +22 -5
- {synx_format-3.7.1 → synx_format-3.7.2}/crates/synx-core/tests/conformance_synxl.rs +61 -0
- synx_format-3.7.2/crates/synx-core/tests/property_synxl.rs +358 -0
- {synx_format-3.7.1 → synx_format-3.7.2}/pyproject.toml +1 -1
- {synx_format-3.7.1 → synx_format-3.7.2}/README.md +0 -0
- {synx_format-3.7.1 → synx_format-3.7.2}/bindings/python/tests/test_synxl.py +0 -0
- {synx_format-3.7.1 → synx_format-3.7.2}/crates/synx-core/src/calc.rs +0 -0
- {synx_format-3.7.1 → synx_format-3.7.2}/crates/synx-core/src/diff.rs +0 -0
- {synx_format-3.7.1 → synx_format-3.7.2}/crates/synx-core/src/lib.rs +0 -0
- {synx_format-3.7.1 → synx_format-3.7.2}/crates/synx-core/src/rng.rs +0 -0
- {synx_format-3.7.1 → synx_format-3.7.2}/crates/synx-core/src/schema_json.rs +0 -0
- {synx_format-3.7.1 → synx_format-3.7.2}/crates/synx-core/src/signing.rs +0 -0
- {synx_format-3.7.1 → synx_format-3.7.2}/crates/synx-core/tests/conformance.rs +0 -0
|
@@ -89,7 +89,7 @@ dependencies = [
|
|
|
89
89
|
|
|
90
90
|
[[package]]
|
|
91
91
|
name = "as-cli"
|
|
92
|
-
version = "3.7.
|
|
92
|
+
version = "3.7.2"
|
|
93
93
|
dependencies = [
|
|
94
94
|
"clap",
|
|
95
95
|
"dirs",
|
|
@@ -976,15 +976,6 @@ dependencies = [
|
|
|
976
976
|
"hashbrown 0.16.1",
|
|
977
977
|
]
|
|
978
978
|
|
|
979
|
-
[[package]]
|
|
980
|
-
name = "indoc"
|
|
981
|
-
version = "2.0.7"
|
|
982
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
983
|
-
checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706"
|
|
984
|
-
dependencies = [
|
|
985
|
-
"rustversion",
|
|
986
|
-
]
|
|
987
|
-
|
|
988
979
|
[[package]]
|
|
989
980
|
name = "ipnet"
|
|
990
981
|
version = "2.12.0"
|
|
@@ -1156,15 +1147,6 @@ version = "2.8.0"
|
|
|
1156
1147
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1157
1148
|
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
|
1158
1149
|
|
|
1159
|
-
[[package]]
|
|
1160
|
-
name = "memoffset"
|
|
1161
|
-
version = "0.9.1"
|
|
1162
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1163
|
-
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
|
|
1164
|
-
dependencies = [
|
|
1165
|
-
"autocfg",
|
|
1166
|
-
]
|
|
1167
|
-
|
|
1168
1150
|
[[package]]
|
|
1169
1151
|
name = "miniz_oxide"
|
|
1170
1152
|
version = "0.8.9"
|
|
@@ -1466,38 +1448,32 @@ dependencies = [
|
|
|
1466
1448
|
|
|
1467
1449
|
[[package]]
|
|
1468
1450
|
name = "pyo3"
|
|
1469
|
-
version = "0.
|
|
1451
|
+
version = "0.29.2"
|
|
1470
1452
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1471
|
-
checksum = "
|
|
1453
|
+
checksum = "4688ddedf473e32662b9b067670129a8afb8c18e351482c70d62ba4a88171e8b"
|
|
1472
1454
|
dependencies = [
|
|
1473
|
-
"cfg-if",
|
|
1474
|
-
"indoc",
|
|
1475
1455
|
"libc",
|
|
1476
|
-
"memoffset",
|
|
1477
1456
|
"once_cell",
|
|
1478
1457
|
"portable-atomic",
|
|
1479
1458
|
"pyo3-build-config",
|
|
1480
1459
|
"pyo3-ffi",
|
|
1481
1460
|
"pyo3-macros",
|
|
1482
|
-
"unindent",
|
|
1483
1461
|
]
|
|
1484
1462
|
|
|
1485
1463
|
[[package]]
|
|
1486
1464
|
name = "pyo3-build-config"
|
|
1487
|
-
version = "0.
|
|
1465
|
+
version = "0.29.2"
|
|
1488
1466
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1489
|
-
checksum = "
|
|
1467
|
+
checksum = "f41027e41b4bd03f6e60f9f417fe24a6341a6bb744edd62b6f709f2a52ea30e9"
|
|
1490
1468
|
dependencies = [
|
|
1491
|
-
"once_cell",
|
|
1492
|
-
"python3-dll-a",
|
|
1493
1469
|
"target-lexicon",
|
|
1494
1470
|
]
|
|
1495
1471
|
|
|
1496
1472
|
[[package]]
|
|
1497
1473
|
name = "pyo3-ffi"
|
|
1498
|
-
version = "0.
|
|
1474
|
+
version = "0.29.2"
|
|
1499
1475
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1500
|
-
checksum = "
|
|
1476
|
+
checksum = "e591a95526fead067432c3b3a33fc74770b87b1e04e73671090d9c2055a2b327"
|
|
1501
1477
|
dependencies = [
|
|
1502
1478
|
"libc",
|
|
1503
1479
|
"pyo3-build-config",
|
|
@@ -1505,9 +1481,9 @@ dependencies = [
|
|
|
1505
1481
|
|
|
1506
1482
|
[[package]]
|
|
1507
1483
|
name = "pyo3-macros"
|
|
1508
|
-
version = "0.
|
|
1484
|
+
version = "0.29.2"
|
|
1509
1485
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1510
|
-
checksum = "
|
|
1486
|
+
checksum = "73225868fc1cd84eef2c3c230ddb91273bf1de46aeb8a4248da76d32a0924a1c"
|
|
1511
1487
|
dependencies = [
|
|
1512
1488
|
"proc-macro2",
|
|
1513
1489
|
"pyo3-macros-backend",
|
|
@@ -1517,26 +1493,16 @@ dependencies = [
|
|
|
1517
1493
|
|
|
1518
1494
|
[[package]]
|
|
1519
1495
|
name = "pyo3-macros-backend"
|
|
1520
|
-
version = "0.
|
|
1496
|
+
version = "0.29.2"
|
|
1521
1497
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1522
|
-
checksum = "
|
|
1498
|
+
checksum = "571575aa3749fa6216757dd47d2a3e7ef360f329a40f0666a9fbd14889024952"
|
|
1523
1499
|
dependencies = [
|
|
1524
1500
|
"heck",
|
|
1525
1501
|
"proc-macro2",
|
|
1526
|
-
"pyo3-build-config",
|
|
1527
1502
|
"quote",
|
|
1528
1503
|
"syn",
|
|
1529
1504
|
]
|
|
1530
1505
|
|
|
1531
|
-
[[package]]
|
|
1532
|
-
name = "python3-dll-a"
|
|
1533
|
-
version = "0.2.14"
|
|
1534
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1535
|
-
checksum = "d381ef313ae70b4da5f95f8a4de773c6aa5cd28f73adec4b4a31df70b66780d8"
|
|
1536
|
-
dependencies = [
|
|
1537
|
-
"cc",
|
|
1538
|
-
]
|
|
1539
|
-
|
|
1540
1506
|
[[package]]
|
|
1541
1507
|
name = "quote"
|
|
1542
1508
|
version = "1.0.45"
|
|
@@ -2038,7 +2004,7 @@ dependencies = [
|
|
|
2038
2004
|
|
|
2039
2005
|
[[package]]
|
|
2040
2006
|
name = "synx-bench"
|
|
2041
|
-
version = "3.7.
|
|
2007
|
+
version = "3.7.2"
|
|
2042
2008
|
dependencies = [
|
|
2043
2009
|
"criterion",
|
|
2044
2010
|
"synx-core",
|
|
@@ -2046,7 +2012,7 @@ dependencies = [
|
|
|
2046
2012
|
|
|
2047
2013
|
[[package]]
|
|
2048
2014
|
name = "synx-c"
|
|
2049
|
-
version = "3.7.
|
|
2015
|
+
version = "3.7.2"
|
|
2050
2016
|
dependencies = [
|
|
2051
2017
|
"serde_json",
|
|
2052
2018
|
"synx-core",
|
|
@@ -2054,7 +2020,7 @@ dependencies = [
|
|
|
2054
2020
|
|
|
2055
2021
|
[[package]]
|
|
2056
2022
|
name = "synx-cli"
|
|
2057
|
-
version = "3.7.
|
|
2023
|
+
version = "3.7.2"
|
|
2058
2024
|
dependencies = [
|
|
2059
2025
|
"clap",
|
|
2060
2026
|
"dirs",
|
|
@@ -2069,7 +2035,7 @@ dependencies = [
|
|
|
2069
2035
|
|
|
2070
2036
|
[[package]]
|
|
2071
2037
|
name = "synx-core"
|
|
2072
|
-
version = "3.7.
|
|
2038
|
+
version = "3.7.2"
|
|
2073
2039
|
dependencies = [
|
|
2074
2040
|
"ed25519-dalek",
|
|
2075
2041
|
"itoa",
|
|
@@ -2087,14 +2053,14 @@ dependencies = [
|
|
|
2087
2053
|
|
|
2088
2054
|
[[package]]
|
|
2089
2055
|
name = "synx-format"
|
|
2090
|
-
version = "3.7.
|
|
2056
|
+
version = "3.7.2"
|
|
2091
2057
|
dependencies = [
|
|
2092
2058
|
"synx-core",
|
|
2093
2059
|
]
|
|
2094
2060
|
|
|
2095
2061
|
[[package]]
|
|
2096
2062
|
name = "synx-lsp"
|
|
2097
|
-
version = "3.7.
|
|
2063
|
+
version = "3.7.2"
|
|
2098
2064
|
dependencies = [
|
|
2099
2065
|
"serde_json",
|
|
2100
2066
|
"synx-core",
|
|
@@ -2104,7 +2070,7 @@ dependencies = [
|
|
|
2104
2070
|
|
|
2105
2071
|
[[package]]
|
|
2106
2072
|
name = "synx-node"
|
|
2107
|
-
version = "3.7.
|
|
2073
|
+
version = "3.7.2"
|
|
2108
2074
|
dependencies = [
|
|
2109
2075
|
"napi",
|
|
2110
2076
|
"napi-build",
|
|
@@ -2114,7 +2080,7 @@ dependencies = [
|
|
|
2114
2080
|
|
|
2115
2081
|
[[package]]
|
|
2116
2082
|
name = "synx-python"
|
|
2117
|
-
version = "3.7.
|
|
2083
|
+
version = "3.7.2"
|
|
2118
2084
|
dependencies = [
|
|
2119
2085
|
"pyo3",
|
|
2120
2086
|
"serde_json",
|
|
@@ -2123,7 +2089,7 @@ dependencies = [
|
|
|
2123
2089
|
|
|
2124
2090
|
[[package]]
|
|
2125
2091
|
name = "synx-wasm"
|
|
2126
|
-
version = "3.7.
|
|
2092
|
+
version = "3.7.2"
|
|
2127
2093
|
dependencies = [
|
|
2128
2094
|
"serde-wasm-bindgen",
|
|
2129
2095
|
"serde_json",
|
|
@@ -2387,12 +2353,6 @@ version = "1.12.0"
|
|
|
2387
2353
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2388
2354
|
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
|
2389
2355
|
|
|
2390
|
-
[[package]]
|
|
2391
|
-
name = "unindent"
|
|
2392
|
-
version = "0.2.4"
|
|
2393
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2394
|
-
checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3"
|
|
2395
|
-
|
|
2396
2356
|
[[package]]
|
|
2397
2357
|
name = "untrusted"
|
|
2398
2358
|
version = "0.9.0"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "synx-python"
|
|
3
|
-
version = "3.7.
|
|
3
|
+
version = "3.7.2"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
authors = ["APERTURESyndicate <support@aperturesyndicate.com>"]
|
|
6
6
|
description = "SYNX Python native module via PyO3"
|
|
@@ -22,5 +22,9 @@ synx-core = { path = "../../crates/synx-core", features = ["serde", "wasm"] }
|
|
|
22
22
|
# Windows linker needs a python3x.lib, and without this feature maturin stops
|
|
23
23
|
# with "compiling for Windows without an interpreter requires PyO3's
|
|
24
24
|
# generate-import-lib feature" — which is what failed the 3.7.0 run.
|
|
25
|
-
|
|
25
|
+
#
|
|
26
|
+
# 0.29 is the first line that knows CPython 3.14; 0.24 refused to build against
|
|
27
|
+
# it at all, and the `PYO3_USE_ABI3_FORWARD_COMPATIBILITY` escape hatch that
|
|
28
|
+
# worked around that is no longer needed.
|
|
29
|
+
pyo3 = { version = "0.29", features = ["generate-import-lib"] }
|
|
26
30
|
serde_json = "1"
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
//! SYNXL ("SYNX Lines") lives in [`synxl`].
|
|
3
3
|
|
|
4
4
|
use pyo3::prelude::*;
|
|
5
|
+
use pyo3::exceptions::{PyTypeError, PyValueError};
|
|
5
6
|
use pyo3::types::{PyDict, PyList};
|
|
6
7
|
use synx_core::{self, Value, Mode, Options};
|
|
7
8
|
|
|
@@ -33,7 +34,7 @@ fn encode_hex(bytes: &[u8]) -> String {
|
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
/// Convert synx_core::Value to a Python object.
|
|
36
|
-
fn value_to_py(py: Python<'_>, val: &Value) ->
|
|
37
|
+
fn value_to_py(py: Python<'_>, val: &Value) -> Py<PyAny> {
|
|
37
38
|
match val {
|
|
38
39
|
Value::Null => py.None(),
|
|
39
40
|
Value::Bool(b) => b.into_pyobject(py).unwrap().to_owned().into_any().unbind(),
|
|
@@ -42,7 +43,7 @@ fn value_to_py(py: Python<'_>, val: &Value) -> PyObject {
|
|
|
42
43
|
Value::String(s) => s.into_pyobject(py).unwrap().into_any().unbind(),
|
|
43
44
|
Value::Secret(_) => "[SECRET]".into_pyobject(py).unwrap().into_any().unbind(),
|
|
44
45
|
Value::Array(arr) => {
|
|
45
|
-
let items: Vec<
|
|
46
|
+
let items: Vec<Py<PyAny>> = arr.iter().map(|v| value_to_py(py, v)).collect();
|
|
46
47
|
let list = PyList::new(py, &items).unwrap();
|
|
47
48
|
list.into_pyobject(py).unwrap().into_any().unbind()
|
|
48
49
|
}
|
|
@@ -56,39 +57,83 @@ fn value_to_py(py: Python<'_>, val: &Value) -> PyObject {
|
|
|
56
57
|
}
|
|
57
58
|
}
|
|
58
59
|
|
|
60
|
+
/// Nesting depth accepted when converting a Python value.
|
|
61
|
+
///
|
|
62
|
+
/// Matched to the SYNX parser's own cap, so anything this crate can *parse*
|
|
63
|
+
/// can also be converted back. Past it the conversion errors out instead of
|
|
64
|
+
/// recursing: `py_to_value` walks the object graph, and Python containers can
|
|
65
|
+
/// be arbitrarily deep or outright cyclic (`x["self"] = x`), neither of which
|
|
66
|
+
/// the caller can catch once the native stack is gone.
|
|
67
|
+
const MAX_PY_VALUE_DEPTH: usize = 128;
|
|
68
|
+
|
|
59
69
|
/// Convert a Python object back to synx_core::Value.
|
|
60
70
|
fn py_to_value(py: Python<'_>, obj: &Bound<'_, PyAny>) -> PyResult<Value> {
|
|
71
|
+
py_to_value_at(py, obj, 0)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
fn py_to_value_at(py: Python<'_>, obj: &Bound<'_, PyAny>, depth: usize) -> PyResult<Value> {
|
|
75
|
+
if depth > MAX_PY_VALUE_DEPTH {
|
|
76
|
+
// A cycle reaches this too: it has no bottom, so it hits the cap. The
|
|
77
|
+
// alternative is a stack overflow, which on Windows kills the process
|
|
78
|
+
// outright — no traceback, nothing for `except` to catch.
|
|
79
|
+
return Err(PyValueError::new_err(format!(
|
|
80
|
+
"value nests deeper than {} levels, or contains a reference cycle",
|
|
81
|
+
MAX_PY_VALUE_DEPTH
|
|
82
|
+
)));
|
|
83
|
+
}
|
|
61
84
|
if obj.is_none() {
|
|
62
85
|
Ok(Value::Null)
|
|
63
86
|
} else if let Ok(b) = obj.extract::<bool>() {
|
|
64
87
|
Ok(Value::Bool(b))
|
|
65
88
|
} else if let Ok(n) = obj.extract::<i64>() {
|
|
66
89
|
Ok(Value::Int(n))
|
|
90
|
+
} else if obj.is_instance_of::<pyo3::types::PyInt>() {
|
|
91
|
+
// A Python int that did not fit i64. Falling through to `f64` would
|
|
92
|
+
// round it — a 20-digit id becomes a different id — and then to the
|
|
93
|
+
// catch-all, which would turn it into a string. Neither is a number
|
|
94
|
+
// the caller wrote, so this is refused instead.
|
|
95
|
+
Err(PyValueError::new_err(
|
|
96
|
+
"integer is outside the i64 range SYNX can represent",
|
|
97
|
+
))
|
|
67
98
|
} else if let Ok(f) = obj.extract::<f64>() {
|
|
68
99
|
Ok(Value::Float(f))
|
|
69
100
|
} else if let Ok(s) = obj.extract::<String>() {
|
|
70
101
|
Ok(Value::String(s))
|
|
71
|
-
} else if let Ok(list) = obj.
|
|
102
|
+
} else if let Ok(list) = obj.cast::<PyList>() {
|
|
72
103
|
let mut arr = Vec::new();
|
|
73
104
|
for item in list.iter() {
|
|
74
|
-
arr.push(
|
|
105
|
+
arr.push(py_to_value_at(py, &item, depth + 1)?);
|
|
106
|
+
}
|
|
107
|
+
Ok(Value::Array(arr))
|
|
108
|
+
} else if let Ok(tuple) = obj.cast::<pyo3::types::PyTuple>() {
|
|
109
|
+
// A tuple is a sequence, so it maps to an array. The catch-all below
|
|
110
|
+
// would have stringified it to `(1, 2, 3)`.
|
|
111
|
+
let mut arr = Vec::new();
|
|
112
|
+
for item in tuple.iter() {
|
|
113
|
+
arr.push(py_to_value_at(py, &item, depth + 1)?);
|
|
75
114
|
}
|
|
76
115
|
Ok(Value::Array(arr))
|
|
77
|
-
} else if let Ok(dict) = obj.
|
|
116
|
+
} else if let Ok(dict) = obj.cast::<PyDict>() {
|
|
78
117
|
let mut map = std::collections::HashMap::new();
|
|
79
118
|
for (k, v) in dict.iter() {
|
|
80
119
|
let key: String = k.extract()?;
|
|
81
|
-
map.insert(key,
|
|
120
|
+
map.insert(key, py_to_value_at(py, &v, depth + 1)?);
|
|
82
121
|
}
|
|
83
122
|
Ok(Value::Object(map))
|
|
84
123
|
} else {
|
|
85
|
-
|
|
124
|
+
// Anything else has no SYNX representation. `str(obj)` used to stand in
|
|
125
|
+
// for one, which silently turned a `set`, a `bytes` or a custom class
|
|
126
|
+
// into its repr and wrote that to disk as if it were data.
|
|
127
|
+
Err(PyTypeError::new_err(format!(
|
|
128
|
+
"cannot convert {} to a SYNX value; pass a str, int, float, bool, None, list, tuple or dict",
|
|
129
|
+
obj.get_type().name()?
|
|
130
|
+
)))
|
|
86
131
|
}
|
|
87
132
|
}
|
|
88
133
|
|
|
89
134
|
/// Parse a SYNX string and return a Python dict.
|
|
90
135
|
#[pyfunction]
|
|
91
|
-
fn parse(py: Python<'_>, text: &str) -> PyResult<
|
|
136
|
+
fn parse(py: Python<'_>, text: &str) -> PyResult<Py<PyAny>> {
|
|
92
137
|
let result = synx_core::parse(text);
|
|
93
138
|
Ok(value_to_py(py, &result.root))
|
|
94
139
|
}
|
|
@@ -193,7 +238,7 @@ fn parse_active(
|
|
|
193
238
|
text: &str,
|
|
194
239
|
env: Option<std::collections::HashMap<String, String>>,
|
|
195
240
|
base_path: Option<String>,
|
|
196
|
-
) -> PyResult<
|
|
241
|
+
) -> PyResult<Py<PyAny>> {
|
|
197
242
|
let mut result = synx_core::parse(text);
|
|
198
243
|
if result.mode == Mode::Active {
|
|
199
244
|
let mut opts = Options::default();
|
|
@@ -218,7 +263,7 @@ fn parse_tool(
|
|
|
218
263
|
text: &str,
|
|
219
264
|
env: Option<std::collections::HashMap<String, String>>,
|
|
220
265
|
base_path: Option<String>,
|
|
221
|
-
) -> PyResult<
|
|
266
|
+
) -> PyResult<Py<PyAny>> {
|
|
222
267
|
let mut result = synx_core::parse(text);
|
|
223
268
|
if result.mode == Mode::Active {
|
|
224
269
|
let mut opts = Options::default();
|
|
@@ -285,7 +330,7 @@ fn to_prompt_block(text: &str, label: &str) -> PyResult<String> {
|
|
|
285
330
|
/// Structural diff between two parsed SYNX dicts.
|
|
286
331
|
/// Returns `{ "added": {...}, "removed": {...}, "changed": {...}, "unchanged": [...] }`.
|
|
287
332
|
#[pyfunction]
|
|
288
|
-
fn diff(py: Python<'_>, a: &Bound<'_, PyAny>, b: &Bound<'_, PyAny>) -> PyResult<
|
|
333
|
+
fn diff(py: Python<'_>, a: &Bound<'_, PyAny>, b: &Bound<'_, PyAny>) -> PyResult<Py<PyAny>> {
|
|
289
334
|
let val_a = py_to_value(py, a)?;
|
|
290
335
|
let val_b = py_to_value(py, b)?;
|
|
291
336
|
let map_a = match val_a { Value::Object(m) => m, _ => std::collections::HashMap::new() };
|
|
@@ -307,7 +352,7 @@ fn diff_json(text_a: &str, text_b: &str) -> PyResult<String> {
|
|
|
307
352
|
|
|
308
353
|
/// Load and parse a .synx file. Returns a Python dict.
|
|
309
354
|
#[pyfunction]
|
|
310
|
-
fn load(py: Python<'_>, file_path: &str) -> PyResult<
|
|
355
|
+
fn load(py: Python<'_>, file_path: &str) -> PyResult<Py<PyAny>> {
|
|
311
356
|
let text = std::fs::read_to_string(file_path)
|
|
312
357
|
.map_err(|e| PyErr::new::<pyo3::exceptions::PyIOError, _>(e.to_string()))?;
|
|
313
358
|
let map = synx_core::Synx::parse(&text);
|
|
@@ -317,7 +362,7 @@ fn load(py: Python<'_>, file_path: &str) -> PyResult<PyObject> {
|
|
|
317
362
|
/// Load, parse and resolve a .synx file with !active engine. Returns a Python dict.
|
|
318
363
|
#[pyfunction]
|
|
319
364
|
#[pyo3(signature = (file_path, base_path=None))]
|
|
320
|
-
fn load_active(py: Python<'_>, file_path: &str, base_path: Option<&str>) -> PyResult<
|
|
365
|
+
fn load_active(py: Python<'_>, file_path: &str, base_path: Option<&str>) -> PyResult<Py<PyAny>> {
|
|
321
366
|
let text = std::fs::read_to_string(file_path)
|
|
322
367
|
.map_err(|e| PyErr::new::<pyo3::exceptions::PyIOError, _>(e.to_string()))?;
|
|
323
368
|
let mut opts = synx_core::Options::default();
|