Type check every argument of variadic functions (fixes #329)#361
Open
Sanjays2402 wants to merge 1 commit into
Open
Type check every argument of variadic functions (fixes #329)#361Sanjays2402 wants to merge 1 commit into
Sanjays2402 wants to merge 1 commit into
Conversation
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 jmespath#329
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #329
The bug
Calling
merge()(or any variadic function) with an invalid type in any argument after the first leaks a raw Python exception instead of the JMESPath-specinvalid-typeerror:The original report (#329) hits this via
merge(a, b || \null`)where a branch resolves tonull. The same leak occurs for numbers and arrays, and it is aValueError` for strings:merge(\{"a":1}`, `null`)`TypeError: 'NoneType' object is not iterableJMESPathTypeError(invalid-type)merge(\{"a":1}`, `5`)`TypeError: 'int' object is not iterableJMESPathTypeErrormerge(\{"a":1}`, `[1,2]`)`TypeError: cannot convert dictionary update sequence ...JMESPathTypeErrormerge(\{"a":1}`, `"s"`)`ValueError: dictionary update sequence element #0 has length 1; 2 is requiredJMESPathTypeErrormerge(\{"a":1}`, `{"b":2}`)`{"a":1,"b":2}{"a":1,"b":2}(unchanged)Note the first argument was already validated correctly —
merge(\null`)` raised the proper error. Only the trailing variadic arguments slipped through.Root cause
Functions._type_checkiterates overrange(len(signature)):For a variadic function the signature has a single entry (e.g.
merge's is{'types': ['object'], 'variadic': True}), so the loop only ever checksactual[0]. Every argument at index ≥ 1 skips validation and reaches the raw function body, where the un-typed value raises whatever low-level Python error the implementation happens to hit._validate_argumentsalready guarantees the arity (len(actual) >= len(signature)for variadic), so the trailing arguments are known to exist — they were simply never checked.The fix
Iterate over the actual arguments and, once the fixed signature entries are exhausted, keep validating against the final (variadic) entry:
_validate_argumentsenforceslen(actual) == len(signature), soinever exceeds the signature and the behaviour is identical to before.not_null()is unaffected: its variadic entry is{'types': [], 'variadic': True}; with an emptytypeslist theif allowed_typesguard skips the check, exactly as it did previously.Tests
Added four compliance cases to
tests/compliance/functions.jsonexercising an invalid variadic argument at positions 2 and 3 ofmerge(anullliteral, anull-valued key reference, a number literal, and a string key reference), each expectinginvalid-type.Proof they guard the regression (source fix stashed, tests kept):
Full suite: 995 passed, 1 skipped (baseline was 991 passed, 1 skipped — exactly the 4 new cases; no regressions).
flake8clean on the changed source.