From 93164e0e8a519c7c4db6ac212a989e9693ee90f1 Mon Sep 17 00:00:00 2001 From: Robert Yokota Date: Thu, 25 Jun 2026 14:31:15 -0700 Subject: [PATCH] Simpifly passing of bindings --- src/jsonata/jsonata.py | 8 +++++--- tests/jsonata_test.py | 23 +++++++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/jsonata/jsonata.py b/src/jsonata/jsonata.py index 77659f2..5707f30 100644 --- a/src/jsonata/jsonata.py +++ b/src/jsonata/jsonata.py @@ -31,7 +31,7 @@ import sys import threading from dataclasses import dataclass -from typing import Any, Callable, Mapping, MutableSequence, Optional, Sequence, Type, MutableMapping +from typing import Any, Callable, Mapping, MutableSequence, Optional, Sequence, Type, MutableMapping, Union from jsonata import functions, jexception, parser, signature as sig, timebox, utils @@ -1976,7 +1976,7 @@ def is_output_convert_nulls(self) -> bool: def set_output_convert_nulls(self, output_convert_nulls: bool) -> None: self.output_convert_nulls = output_convert_nulls - def evaluate(self, input: Optional[Any], bindings: Optional[Frame] = None) -> Optional[Any]: + def evaluate(self, input: Optional[Any], bindings: Optional[Union[Frame, Mapping[str, Any]]] = None) -> Optional[Any]: # throw if the expression compiled with syntax errors if self.errors is not None: raise jexception.JException("S0500", 0) @@ -1986,7 +1986,9 @@ def evaluate(self, input: Optional[Any], bindings: Optional[Frame] = None) -> Op # var exec_env # the variable bindings have been passed in - create a frame to hold these exec_env = self.create_frame(self.environment) - for k, v in bindings.bindings.items(): + # accept either a Frame or a plain mapping (e.g. dict) of variable bindings + items = bindings.bindings if isinstance(bindings, Jsonata.Frame) else bindings + for k, v in items.items(): exec_env.bind(k, v) else: exec_env = self.environment diff --git a/tests/jsonata_test.py b/tests/jsonata_test.py index 12fcdbf..2ee8615 100644 --- a/tests/jsonata_test.py +++ b/tests/jsonata_test.py @@ -104,6 +104,29 @@ def test_path(self): print(str(data)) self.eval_expr("foo.bar", data, None, 42, None) + def test_dict_bindings(self): + data = { + "products": [ + {"name": "Apple", "price": 1.20}, + {"name": "Banana", "price": 0.50}, + {"name": "Cherry", "price": 2.50}, + ] + } + expr = jsonata.Jsonata( + "products[price <= $maxPrice]" + ".(name & ' costs ' & $currencySymbol & $string(price))" + ) + expected = ["Apple costs $1.2", "Banana costs $0.5"] + + # A plain dict can be passed directly as the bindings. + assert expr.evaluate(data, bindings={"maxPrice": 1.50, "currencySymbol": "$"}) == expected + + # A Frame is still accepted for backward compatibility. + binding_frame = jsonata.Jsonata.Frame(None) + binding_frame.bind("maxPrice", 1.50) + binding_frame.bind("currencySymbol", "$") + assert expr.evaluate(data, bindings=binding_frame) == expected + def run_case(self, name): if not self.run_test_suite(name): raise Exception()