From b39960f330d5f17e377d51ef2c80db9c80424f90 Mon Sep 17 00:00:00 2001 From: Vincent Gao Date: Sat, 20 Jun 2026 20:18:32 +0200 Subject: [PATCH] Return false for contains(string, non-string) instead of raising contains() with a string subject and a non-string search raised an uncaught TypeError ("'in ' requires string as left operand") instead of returning false. The JMESPath spec defines contains('foobar', `123`) as false, and the array case already returns false for a type-mismatched search of any type, so the string and array paths were inconsistent. Guard the membership test: when the subject is a string and the search is not, return false (a non-string can never be a substring). Adds compliance cases for number, boolean and null searches. --- jmespath/functions.py | 8 ++++++++ tests/compliance/functions.json | 12 ++++++++++++ 2 files changed, 20 insertions(+) diff --git a/jmespath/functions.py b/jmespath/functions.py index 627b569d..e0164689 100644 --- a/jmespath/functions.py +++ b/jmespath/functions.py @@ -212,6 +212,14 @@ def _func_to_number(self, arg): @signature({'types': ['array', 'string']}, {'types': []}) def _func_contains(self, subject, search): + # A non-string can never be a substring of a string. The array case + # already returns False for a non-matching search of any type, but + # ``search in subject`` raises TypeError when subject is a string and + # search is not, so guard it explicitly (spec: contains('foobar', 123) + # -> false). + if (isinstance(subject, STRING_TYPE) + and not isinstance(search, STRING_TYPE)): + return False return search in subject @signature({'types': ['string', 'array', 'object']}) diff --git a/tests/compliance/functions.json b/tests/compliance/functions.json index 7b554450..b1536f4f 100644 --- a/tests/compliance/functions.json +++ b/tests/compliance/functions.json @@ -115,6 +115,18 @@ "expression": "contains('abc', 'd')", "result": false }, + { + "expression": "contains('foobar', `123`)", + "result": false + }, + { + "expression": "contains('foobar', `true`)", + "result": false + }, + { + "expression": "contains('foobar', `null`)", + "result": false + }, { "expression": "contains(`false`, 'd')", "error": "invalid-type"