Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#186] options: add custom not found value option #311

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions jmespath/visitor.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import operator
from numbers import Number

from jmespath import functions
from jmespath.compat import string_type
from numbers import Number


def _equals(x, y):
Expand Down Expand Up @@ -58,7 +58,7 @@ def _is_actual_number(x):

class Options(object):
"""Options to control how a JMESPath function is evaluated."""
def __init__(self, dict_cls=None, custom_functions=None):
def __init__(self, dict_cls=None, custom_functions=None, not_found_value=None):
#: The class to use when creating a dict. The interpreter
# may create dictionaries during the evaluation of a JMESPath
# expression. For example, a multi-select hash will
Expand All @@ -69,6 +69,7 @@ def __init__(self, dict_cls=None, custom_functions=None):
# have predictable key ordering.
self.dict_cls = dict_cls
self.custom_functions = custom_functions
self.not_found_value = not_found_value


class _Expression(object):
Expand Down Expand Up @@ -133,9 +134,9 @@ def visit_subexpression(self, node, value):

def visit_field(self, node, value):
try:
return value.get(node['value'])
return value.get(node['value'], self._options.not_found_value)
except AttributeError:
return None
return self._options.not_found_value

def visit_comparator(self, node, value):
# Common case: comparator is == or !=
Expand Down Expand Up @@ -298,7 +299,7 @@ def _is_false(self, value):
# because the truth/false values are different between
# python and jmespath.
return (value == '' or value == [] or value == {} or value is None or
value is False)
value is False or value == self._options.not_found_value)

def _is_true(self, value):
return not self._is_false(value)
Expand Down
12 changes: 12 additions & 0 deletions tests/test_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,15 @@ def test_can_handle_decimals_as_numeric_type(self):
result = decimal.Decimal('3')
self.assertEqual(jmespath.search('[?a >= `1`].a', [{'a': result}]),
[result])


class TestNotFoundValueOption(unittest.TestCase):
opt = jmespath.Options(not_found_value='foo')

def test_can_supply_custom_not_found_value(self):
value = jmespath.search('b', {'a': 1}, self.opt)
self.assertEqual(value, 'foo')

def test_custom_value_is_treated_as_false(self):
value = jmespath.search('b || a', {'a': 1}, self.opt)
self.assertEqual(value, 1)