about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/jmespath/visitor.py
diff options
context:
space:
mode:
authorS. Solomon Darnell2025-03-28 21:52:21 -0500
committerS. Solomon Darnell2025-03-28 21:52:21 -0500
commit4a52a71956a8d46fcb7294ac71734504bb09bcc2 (patch)
treeee3dc5af3b6313e921cd920906356f5d4febc4ed /.venv/lib/python3.12/site-packages/jmespath/visitor.py
parentcc961e04ba734dd72309fb548a2f97d67d578813 (diff)
downloadgn-ai-master.tar.gz
two version of R2R are here HEAD master
Diffstat (limited to '.venv/lib/python3.12/site-packages/jmespath/visitor.py')
-rw-r--r--.venv/lib/python3.12/site-packages/jmespath/visitor.py328
1 files changed, 328 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/jmespath/visitor.py b/.venv/lib/python3.12/site-packages/jmespath/visitor.py
new file mode 100644
index 00000000..15fb1774
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/jmespath/visitor.py
@@ -0,0 +1,328 @@
+import operator
+
+from jmespath import functions
+from jmespath.compat import string_type
+from numbers import Number
+
+
+def _equals(x, y):
+    if _is_special_number_case(x, y):
+        return False
+    else:
+        return x == y
+
+
+def _is_special_number_case(x, y):
+    # We need to special case comparing 0 or 1 to
+    # True/False.  While normally comparing any
+    # integer other than 0/1 to True/False will always
+    # return False.  However 0/1 have this:
+    # >>> 0 == True
+    # False
+    # >>> 0 == False
+    # True
+    # >>> 1 == True
+    # True
+    # >>> 1 == False
+    # False
+    #
+    # Also need to consider that:
+    # >>> 0 in [True, False]
+    # True
+    if _is_actual_number(x) and x in (0, 1):
+        return isinstance(y, bool)
+    elif _is_actual_number(y) and y in (0, 1):
+        return isinstance(x, bool)
+
+
+def _is_comparable(x):
+    # The spec doesn't officially support string types yet,
+    # but enough people are relying on this behavior that
+    # it's been added back.  This should eventually become
+    # part of the official spec.
+    return _is_actual_number(x) or isinstance(x, string_type)
+
+
+def _is_actual_number(x):
+    # We need to handle python's quirkiness with booleans,
+    # specifically:
+    #
+    # >>> isinstance(False, int)
+    # True
+    # >>> isinstance(True, int)
+    # True
+    if isinstance(x, bool):
+        return False
+    return isinstance(x, Number)
+
+
+class Options(object):
+    """Options to control how a JMESPath function is evaluated."""
+    def __init__(self, dict_cls=None, custom_functions=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
+        #  create a dictionary.  By default we use a dict() type.
+        #  You can set this value to change what dict type is used.
+        #  The most common reason you would change this is if you
+        #  want to set a collections.OrderedDict so that you can
+        #  have predictable key ordering.
+        self.dict_cls = dict_cls
+        self.custom_functions = custom_functions
+
+
+class _Expression(object):
+    def __init__(self, expression, interpreter):
+        self.expression = expression
+        self.interpreter = interpreter
+
+    def visit(self, node, *args, **kwargs):
+        return self.interpreter.visit(node, *args, **kwargs)
+
+
+class Visitor(object):
+    def __init__(self):
+        self._method_cache = {}
+
+    def visit(self, node, *args, **kwargs):
+        node_type = node['type']
+        method = self._method_cache.get(node_type)
+        if method is None:
+            method = getattr(
+                self, 'visit_%s' % node['type'], self.default_visit)
+            self._method_cache[node_type] = method
+        return method(node, *args, **kwargs)
+
+    def default_visit(self, node, *args, **kwargs):
+        raise NotImplementedError("default_visit")
+
+
+class TreeInterpreter(Visitor):
+    COMPARATOR_FUNC = {
+        'eq': _equals,
+        'ne': lambda x, y: not _equals(x, y),
+        'lt': operator.lt,
+        'gt': operator.gt,
+        'lte': operator.le,
+        'gte': operator.ge
+    }
+    _EQUALITY_OPS = ['eq', 'ne']
+    MAP_TYPE = dict
+
+    def __init__(self, options=None):
+        super(TreeInterpreter, self).__init__()
+        self._dict_cls = self.MAP_TYPE
+        if options is None:
+            options = Options()
+        self._options = options
+        if options.dict_cls is not None:
+            self._dict_cls = self._options.dict_cls
+        if options.custom_functions is not None:
+            self._functions = self._options.custom_functions
+        else:
+            self._functions = functions.Functions()
+
+    def default_visit(self, node, *args, **kwargs):
+        raise NotImplementedError(node['type'])
+
+    def visit_subexpression(self, node, value):
+        result = value
+        for node in node['children']:
+            result = self.visit(node, result)
+        return result
+
+    def visit_field(self, node, value):
+        try:
+            return value.get(node['value'])
+        except AttributeError:
+            return None
+
+    def visit_comparator(self, node, value):
+        # Common case: comparator is == or !=
+        comparator_func = self.COMPARATOR_FUNC[node['value']]
+        if node['value'] in self._EQUALITY_OPS:
+            return comparator_func(
+                self.visit(node['children'][0], value),
+                self.visit(node['children'][1], value)
+            )
+        else:
+            # Ordering operators are only valid for numbers.
+            # Evaluating any other type with a comparison operator
+            # will yield a None value.
+            left = self.visit(node['children'][0], value)
+            right = self.visit(node['children'][1], value)
+            num_types = (int, float)
+            if not (_is_comparable(left) and
+                    _is_comparable(right)):
+                return None
+            return comparator_func(left, right)
+
+    def visit_current(self, node, value):
+        return value
+
+    def visit_expref(self, node, value):
+        return _Expression(node['children'][0], self)
+
+    def visit_function_expression(self, node, value):
+        resolved_args = []
+        for child in node['children']:
+            current = self.visit(child, value)
+            resolved_args.append(current)
+        return self._functions.call_function(node['value'], resolved_args)
+
+    def visit_filter_projection(self, node, value):
+        base = self.visit(node['children'][0], value)
+        if not isinstance(base, list):
+            return None
+        comparator_node = node['children'][2]
+        collected = []
+        for element in base:
+            if self._is_true(self.visit(comparator_node, element)):
+                current = self.visit(node['children'][1], element)
+                if current is not None:
+                    collected.append(current)
+        return collected
+
+    def visit_flatten(self, node, value):
+        base = self.visit(node['children'][0], value)
+        if not isinstance(base, list):
+            # Can't flatten the object if it's not a list.
+            return None
+        merged_list = []
+        for element in base:
+            if isinstance(element, list):
+                merged_list.extend(element)
+            else:
+                merged_list.append(element)
+        return merged_list
+
+    def visit_identity(self, node, value):
+        return value
+
+    def visit_index(self, node, value):
+        # Even though we can index strings, we don't
+        # want to support that.
+        if not isinstance(value, list):
+            return None
+        try:
+            return value[node['value']]
+        except IndexError:
+            return None
+
+    def visit_index_expression(self, node, value):
+        result = value
+        for node in node['children']:
+            result = self.visit(node, result)
+        return result
+
+    def visit_slice(self, node, value):
+        if not isinstance(value, list):
+            return None
+        s = slice(*node['children'])
+        return value[s]
+
+    def visit_key_val_pair(self, node, value):
+        return self.visit(node['children'][0], value)
+
+    def visit_literal(self, node, value):
+        return node['value']
+
+    def visit_multi_select_dict(self, node, value):
+        if value is None:
+            return None
+        collected = self._dict_cls()
+        for child in node['children']:
+            collected[child['value']] = self.visit(child, value)
+        return collected
+
+    def visit_multi_select_list(self, node, value):
+        if value is None:
+            return None
+        collected = []
+        for child in node['children']:
+            collected.append(self.visit(child, value))
+        return collected
+
+    def visit_or_expression(self, node, value):
+        matched = self.visit(node['children'][0], value)
+        if self._is_false(matched):
+            matched = self.visit(node['children'][1], value)
+        return matched
+
+    def visit_and_expression(self, node, value):
+        matched = self.visit(node['children'][0], value)
+        if self._is_false(matched):
+            return matched
+        return self.visit(node['children'][1], value)
+
+    def visit_not_expression(self, node, value):
+        original_result = self.visit(node['children'][0], value)
+        if _is_actual_number(original_result) and original_result == 0:
+            # Special case for 0, !0 should be false, not true.
+            # 0 is not a special cased integer in jmespath.
+            return False
+        return not original_result
+
+    def visit_pipe(self, node, value):
+        result = value
+        for node in node['children']:
+            result = self.visit(node, result)
+        return result
+
+    def visit_projection(self, node, value):
+        base = self.visit(node['children'][0], value)
+        if not isinstance(base, list):
+            return None
+        collected = []
+        for element in base:
+            current = self.visit(node['children'][1], element)
+            if current is not None:
+                collected.append(current)
+        return collected
+
+    def visit_value_projection(self, node, value):
+        base = self.visit(node['children'][0], value)
+        try:
+            base = base.values()
+        except AttributeError:
+            return None
+        collected = []
+        for element in base:
+            current = self.visit(node['children'][1], element)
+            if current is not None:
+                collected.append(current)
+        return collected
+
+    def _is_false(self, value):
+        # This looks weird, but we're explicitly using equality checks
+        # 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)
+
+    def _is_true(self, value):
+        return not self._is_false(value)
+
+
+class GraphvizVisitor(Visitor):
+    def __init__(self):
+        super(GraphvizVisitor, self).__init__()
+        self._lines = []
+        self._count = 1
+
+    def visit(self, node, *args, **kwargs):
+        self._lines.append('digraph AST {')
+        current = '%s%s' % (node['type'], self._count)
+        self._count += 1
+        self._visit(node, current)
+        self._lines.append('}')
+        return '\n'.join(self._lines)
+
+    def _visit(self, node, current):
+        self._lines.append('%s [label="%s(%s)"]' % (
+            current, node['type'], node.get('value', '')))
+        for child in node.get('children', []):
+            child_name = '%s%s' % (child['type'], self._count)
+            self._count += 1
+            self._lines.append('  %s -> %s' % (current, child_name))
+            self._visit(child, child_name)