From 4e27da6b12478df0ee9f013b3452eb07ef2b1c0c Mon Sep 17 00:00:00 2001 From: Sanjays2402 <51058514+Sanjays2402@users.noreply.github.com> Date: Thu, 2 Jul 2026 05:51:48 -0700 Subject: [PATCH] Type check every argument of variadic functions Variadic functions such as merge() only validated their first argument. _type_check iterated over range(len(signature)), and for a variadic function the signature has a single entry, so any argument after the first bypassed type validation entirely and reached the raw function body. As a result, merge(`{"a": 1}`, `null`) raised a bare Python TypeError ("'NoneType' object is not iterable") instead of the spec-mandated invalid-type error (JMESPathTypeError). Non-object second arguments leaked TypeError/ValueError for numbers, arrays and strings as well. Iterate over the actual arguments instead, clamping the index to the final signature entry once it is exhausted, so each trailing variadic argument is checked against the variadic type. not_null() is unaffected: its variadic entry allows any type, so the check is skipped exactly as before. Added compliance cases covering an invalid variadic argument at positions 2 and 3 for merge(). Fixes #329 --- CHANGELOG.rst | 5 ++++- jmespath/functions.py | 10 ++++++++-- tests/compliance/functions.json | 17 +++++++++++++++++ 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index dc673df4..cf9dbb53 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,7 +1,10 @@ Next Release (TBD) ================== -* No changes yet. +* Fix ``merge()`` and other variadic functions leaking a raw Python + ``TypeError``/``ValueError`` instead of a ``JMESPathTypeError`` when an + argument after the first had an invalid type + (`issue #329 `__) 1.1.0 diff --git a/jmespath/functions.py b/jmespath/functions.py index 627b569d..05fd6d10 100644 --- a/jmespath/functions.py +++ b/jmespath/functions.py @@ -91,8 +91,14 @@ def _validate_arguments(self, args, signature, function_name): return self._type_check(args, signature, function_name) def _type_check(self, actual, signature, function_name): - for i in range(len(signature)): - allowed_types = signature[i]['types'] + for i in range(len(actual)): + if i < len(signature): + allowed_types = signature[i]['types'] + else: + # Variadic function: every trailing argument is described + # by the final signature entry, so keep validating against + # it instead of leaving the extra args unchecked. + allowed_types = signature[-1]['types'] if allowed_types: self._type_check_single(actual[i], allowed_types, function_name) diff --git a/tests/compliance/functions.json b/tests/compliance/functions.json index 7b554450..b889e1c3 100644 --- a/tests/compliance/functions.json +++ b/tests/compliance/functions.json @@ -267,6 +267,23 @@ "expression": "merge(`{\"a\": 1, \"b\": 2}`, `{\"a\": 2, \"c\": 3}`, `{\"d\": 4}`)", "result": {"a": 2, "b": 2, "c": 3, "d": 4} }, + { + "comment": "variadic argument after the first must also be type checked", + "expression": "merge(`{\"a\": 1}`, `null`)", + "error": "invalid-type" + }, + { + "expression": "merge(`{\"a\": 1}`, null_key)", + "error": "invalid-type" + }, + { + "expression": "merge(`{\"a\": 1}`, `{\"b\": 2}`, `5`)", + "error": "invalid-type" + }, + { + "expression": "merge(`{\"a\": 1}`, str)", + "error": "invalid-type" + }, { "expression": "min(numbers)", "result": -1