From 6436db61315e479df8b0ee40f147c76e8aa45906 Mon Sep 17 00:00:00 2001 From: James Prior Date: Thu, 28 Dec 2023 09:05:09 +0000 Subject: [PATCH] Convert empty node lists to _Nothing_. --- CHANGELOG.md | 6 ++++++ jsonpath/filter.py | 14 +++++++++++++- jsonpath/function_extensions/length.py | 14 ++++++++++---- tests/cts | 2 +- 4 files changed, 30 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 009850c..4507138 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Python JSONPath Change Log +## Version 0.10.3 (unreleased) + +**Fixes** + +- Fixed handling of relative and root queries when used as arguments to filter functions. Previously, when those queries resulted in an empty node list, we were converting them to an empty regular list before passing it to functions that accept _ValueType_ arguments. Now, in such cases, we convert empty node lists to the special result _Nothing_, which is required by the spec. + ## Version 0.10.2 **Fixes** diff --git a/jsonpath/filter.py b/jsonpath/filter.py index 17673b3..f8de524 100644 --- a/jsonpath/filter.py +++ b/jsonpath/filter.py @@ -623,7 +623,19 @@ def _unpack_node_lists( if func.arg_types[idx] != ExpressionType.NODES and isinstance( arg, NodeList ): - _args.append(arg.values_or_singular()) + if len(arg) == 0: + # If the query results in an empty nodelist, the + # argument is the special result Nothing. + _args.append(UNDEFINED) + elif len(arg) == 1: + # If the query results in a nodelist consisting of a + # single node, the argument is the value of the node + _args.append(arg[0].obj) + else: + # This should not be possible as a non-singular query + # would have been rejected when checking function + # well-typedness. + _args.append(arg) else: _args.append(arg) return _args diff --git a/jsonpath/function_extensions/length.py b/jsonpath/function_extensions/length.py index 3189a51..9de0adc 100644 --- a/jsonpath/function_extensions/length.py +++ b/jsonpath/function_extensions/length.py @@ -1,7 +1,9 @@ """The standard `length` function extension.""" from collections.abc import Sized -from typing import Optional +from typing import Union +from jsonpath.filter import UNDEFINED +from jsonpath.filter import _Undefined from jsonpath.function_extensions import ExpressionType from jsonpath.function_extensions import FilterFunction @@ -12,9 +14,13 @@ class Length(FilterFunction): arg_types = [ExpressionType.VALUE] return_type = ExpressionType.VALUE - def __call__(self, obj: Sized) -> Optional[int]: - """Return an object's length, or `None` if the object does not have a length.""" + def __call__(self, obj: Sized) -> Union[int, _Undefined]: + """Return an object's length. + + If the object does not have a length, the special _Nothing_ value is + returned. + """ try: return len(obj) except TypeError: - return None + return UNDEFINED diff --git a/tests/cts b/tests/cts index ca076c0..446336c 160000 --- a/tests/cts +++ b/tests/cts @@ -1 +1 @@ -Subproject commit ca076c0e55d378236e7b70fc8e6414234dcca294 +Subproject commit 446336cd6651586f416a3b546c70bdd0fa2022c0