Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
visitor.py329 linesDownload Raw Back to jmespath
1import operator2 3from jmespath import functions4from jmespath.compat import string_type5from numbers import Number6 7 8def _equals(x, y):9    if _is_special_number_case(x, y):10        return False11    else:12        return x == y13 14 15def _is_special_number_case(x, y):16    # We need to special case comparing 0 or 1 to17    # True/False.  While normally comparing any18    # integer other than 0/1 to True/False will always19    # return False.  However 0/1 have this:20    # >>> 0 == True21    # False22    # >>> 0 == False23    # True24    # >>> 1 == True25    # True26    # >>> 1 == False27    # False28    #29    # Also need to consider that:30    # >>> 0 in [True, False]31    # True32    if _is_actual_number(x) and x in (0, 1):33        return isinstance(y, bool)34    elif _is_actual_number(y) and y in (0, 1):35        return isinstance(x, bool)36 37 38def _is_comparable(x):39    # The spec doesn't officially support string types yet,40    # but enough people are relying on this behavior that41    # it's been added back.  This should eventually become42    # part of the official spec.43    return _is_actual_number(x) or isinstance(x, string_type)44 45 46def _is_actual_number(x):47    # We need to handle python's quirkiness with booleans,48    # specifically:49    #50    # >>> isinstance(False, int)51    # True52    # >>> isinstance(True, int)53    # True54    if isinstance(x, bool):55        return False56    return isinstance(x, Number)57 58 59class Options(object):60    """Options to control how a JMESPath function is evaluated."""61    def __init__(self, dict_cls=None, custom_functions=None):62        #: The class to use when creating a dict.  The interpreter63        #  may create dictionaries during the evaluation of a JMESPath64        #  expression.  For example, a multi-select hash will65        #  create a dictionary.  By default we use a dict() type.66        #  You can set this value to change what dict type is used.67        #  The most common reason you would change this is if you68        #  want to set a collections.OrderedDict so that you can69        #  have predictable key ordering.70        self.dict_cls = dict_cls71        self.custom_functions = custom_functions72 73 74class _Expression(object):75    def __init__(self, expression, interpreter):76        self.expression = expression77        self.interpreter = interpreter78 79    def visit(self, node, *args, **kwargs):80        return self.interpreter.visit(node, *args, **kwargs)81 82 83class Visitor(object):84    def __init__(self):85        self._method_cache = {}86 87    def visit(self, node, *args, **kwargs):88        node_type = node['type']89        method = self._method_cache.get(node_type)90        if method is None:91            method = getattr(92                self, 'visit_%s' % node['type'], self.default_visit)93            self._method_cache[node_type] = method94        return method(node, *args, **kwargs)95 96    def default_visit(self, node, *args, **kwargs):97        raise NotImplementedError("default_visit")98 99 100class TreeInterpreter(Visitor):101    COMPARATOR_FUNC = {102        'eq': _equals,103        'ne': lambda x, y: not _equals(x, y),104        'lt': operator.lt,105        'gt': operator.gt,106        'lte': operator.le,107        'gte': operator.ge108    }109    _EQUALITY_OPS = ['eq', 'ne']110    MAP_TYPE = dict111 112    def __init__(self, options=None):113        super(TreeInterpreter, self).__init__()114        self._dict_cls = self.MAP_TYPE115        if options is None:116            options = Options()117        self._options = options118        if options.dict_cls is not None:119            self._dict_cls = self._options.dict_cls120        if options.custom_functions is not None:121            self._functions = self._options.custom_functions122        else:123            self._functions = functions.Functions()124 125    def default_visit(self, node, *args, **kwargs):126        raise NotImplementedError(node['type'])127 128    def visit_subexpression(self, node, value):129        result = value130        for node in node['children']:131            result = self.visit(node, result)132        return result133 134    def visit_field(self, node, value):135        try:136            return value.get(node['value'])137        except AttributeError:138            return None139 140    def visit_comparator(self, node, value):141        # Common case: comparator is == or !=142        comparator_func = self.COMPARATOR_FUNC[node['value']]143        if node['value'] in self._EQUALITY_OPS:144            return comparator_func(145                self.visit(node['children'][0], value),146                self.visit(node['children'][1], value)147            )148        else:149            # Ordering operators are only valid for numbers.150            # Evaluating any other type with a comparison operator151            # will yield a None value.152            left = self.visit(node['children'][0], value)153            right = self.visit(node['children'][1], value)154            num_types = (int, float)155            if not (_is_comparable(left) and156                    _is_comparable(right)):157                return None158            return comparator_func(left, right)159 160    def visit_current(self, node, value):161        return value162 163    def visit_expref(self, node, value):164        return _Expression(node['children'][0], self)165 166    def visit_function_expression(self, node, value):167        resolved_args = []168        for child in node['children']:169            current = self.visit(child, value)170            resolved_args.append(current)171        return self._functions.call_function(node['value'], resolved_args)172 173    def visit_filter_projection(self, node, value):174        base = self.visit(node['children'][0], value)175        if not isinstance(base, list):176            return None177        comparator_node = node['children'][2]178        collected = []179        for element in base:180            if self._is_true(self.visit(comparator_node, element)):181                current = self.visit(node['children'][1], element)182                if current is not None:183                    collected.append(current)184        return collected185 186    def visit_flatten(self, node, value):187        base = self.visit(node['children'][0], value)188        if not isinstance(base, list):189            # Can't flatten the object if it's not a list.190            return None191        merged_list = []192        for element in base:193            if isinstance(element, list):194                merged_list.extend(element)195            else:196                merged_list.append(element)197        return merged_list198 199    def visit_identity(self, node, value):200        return value201 202    def visit_index(self, node, value):203        # Even though we can index strings, we don't204        # want to support that.205        if not isinstance(value, list):206            return None207        try:208            return value[node['value']]209        except IndexError:210            return None211 212    def visit_index_expression(self, node, value):213        result = value214        for node in node['children']:215            result = self.visit(node, result)216        return result217 218    def visit_slice(self, node, value):219        if not isinstance(value, list):220            return None221        s = slice(*node['children'])222        return value[s]223 224    def visit_key_val_pair(self, node, value):225        return self.visit(node['children'][0], value)226 227    def visit_literal(self, node, value):228        return node['value']229 230    def visit_multi_select_dict(self, node, value):231        if value is None:232            return None233        collected = self._dict_cls()234        for child in node['children']:235            collected[child['value']] = self.visit(child, value)236        return collected237 238    def visit_multi_select_list(self, node, value):239        if value is None:240            return None241        collected = []242        for child in node['children']:243            collected.append(self.visit(child, value))244        return collected245 246    def visit_or_expression(self, node, value):247        matched = self.visit(node['children'][0], value)248        if self._is_false(matched):249            matched = self.visit(node['children'][1], value)250        return matched251 252    def visit_and_expression(self, node, value):253        matched = self.visit(node['children'][0], value)254        if self._is_false(matched):255            return matched256        return self.visit(node['children'][1], value)257 258    def visit_not_expression(self, node, value):259        original_result = self.visit(node['children'][0], value)260        if _is_actual_number(original_result) and original_result == 0:261            # Special case for 0, !0 should be false, not true.262            # 0 is not a special cased integer in jmespath.263            return False264        return not original_result265 266    def visit_pipe(self, node, value):267        result = value268        for node in node['children']:269            result = self.visit(node, result)270        return result271 272    def visit_projection(self, node, value):273        base = self.visit(node['children'][0], value)274        if not isinstance(base, list):275            return None276        collected = []277        for element in base:278            current = self.visit(node['children'][1], element)279            if current is not None:280                collected.append(current)281        return collected282 283    def visit_value_projection(self, node, value):284        base = self.visit(node['children'][0], value)285        try:286            base = base.values()287        except AttributeError:288            return None289        collected = []290        for element in base:291            current = self.visit(node['children'][1], element)292            if current is not None:293                collected.append(current)294        return collected295 296    def _is_false(self, value):297        # This looks weird, but we're explicitly using equality checks298        # because the truth/false values are different between299        # python and jmespath.300        return (value == '' or value == [] or value == {} or value is None or301                value is False)302 303    def _is_true(self, value):304        return not self._is_false(value)305 306 307class GraphvizVisitor(Visitor):308    def __init__(self):309        super(GraphvizVisitor, self).__init__()310        self._lines = []311        self._count = 1312 313    def visit(self, node, *args, **kwargs):314        self._lines.append('digraph AST {')315        current = '%s%s' % (node['type'], self._count)316        self._count += 1317        self._visit(node, current)318        self._lines.append('}')319        return '\n'.join(self._lines)320 321    def _visit(self, node, current):322        self._lines.append('%s [label="%s(%s)"]' % (323            current, node['type'], node.get('value', '')))324        for child in node.get('children', []):325            child_name = '%s%s' % (child['type'], self._count)326            self._count += 1327            self._lines.append('  %s -> %s' % (current, child_name))328            self._visit(child, child_name)329 
codekingpro/portable-devtools · Team Ai