Fixing tests

This commit is contained in:
Matthew Stevens 2019-04-12 10:13:36 -04:00 committed by Garrett Heel
parent 9450328527
commit 6303d07bac
2 changed files with 67 additions and 74 deletions

View File

@ -2,7 +2,6 @@ from __future__ import unicode_literals
import re import re
import six import six
import re import re
import enum
from collections import deque from collections import deque
from collections import namedtuple from collections import namedtuple
@ -199,46 +198,47 @@ class ConditionExpressionParser:
op = self._make_op_condition(node) op = self._make_op_condition(node)
return op return op
class Kind(enum.Enum): class Kind:
"""Defines types of nodes in the syntax tree.""" """Enum defining types of nodes in the syntax tree."""
# Condition nodes # Condition nodes
# --------------- # ---------------
OR = enum.auto() OR = 'OR'
AND = enum.auto() AND = 'AND'
NOT = enum.auto() NOT = 'NOT'
PARENTHESES = enum.auto() PARENTHESES = 'PARENTHESES'
FUNCTION = enum.auto() FUNCTION = 'FUNCTION'
BETWEEN = enum.auto() BETWEEN = 'BETWEEN'
IN = enum.auto() IN = 'IN'
COMPARISON = enum.auto() COMPARISON = 'COMPARISON'
# Operand nodes # Operand nodes
# ------------- # -------------
EXPRESSION_ATTRIBUTE_VALUE = enum.auto() EXPRESSION_ATTRIBUTE_VALUE = 'EXPRESSION_ATTRIBUTE_VALUE'
PATH = enum.auto() PATH = 'PATH'
# Literal nodes # Literal nodes
# -------------- # --------------
LITERAL = enum.auto() LITERAL = 'LITERAL'
class Nonterminal(enum.Enum): class Nonterminal:
"""Defines nonterminals for defining productions.""" """Enum defining nonterminals for productions."""
CONDITION = enum.auto()
OPERAND = enum.auto() CONDITION = 'CONDITION'
COMPARATOR = enum.auto() OPERAND = 'OPERAND'
FUNCTION_NAME = enum.auto() COMPARATOR = 'COMPARATOR'
IDENTIFIER = enum.auto() FUNCTION_NAME = 'FUNCTION_NAME'
AND = enum.auto() IDENTIFIER = 'IDENTIFIER'
OR = enum.auto() AND = 'AND'
NOT = enum.auto() OR = 'OR'
BETWEEN = enum.auto() NOT = 'NOT'
IN = enum.auto() BETWEEN = 'BETWEEN'
COMMA = enum.auto() IN = 'IN'
LEFT_PAREN = enum.auto() COMMA = 'COMMA'
RIGHT_PAREN = enum.auto() LEFT_PAREN = 'LEFT_PAREN'
WHITESPACE = enum.auto() RIGHT_PAREN = 'RIGHT_PAREN'
WHITESPACE = 'WHITESPACE'
Node = namedtuple('Node', ['nonterminal', 'kind', 'text', 'value', 'children']) Node = namedtuple('Node', ['nonterminal', 'kind', 'text', 'value', 'children'])
@ -286,7 +286,7 @@ class ConditionExpressionParser:
if match: if match:
match_text = match.group() match_text = match.group()
break break
else: else: # pragma: no cover
raise ValueError("Cannot parse condition starting at: " + raise ValueError("Cannot parse condition starting at: " +
remaining_expression) remaining_expression)
@ -387,7 +387,7 @@ class ConditionExpressionParser:
children=[]) children=[])
elif name.startswith('['): elif name.startswith('['):
# e.g. [123] # e.g. [123]
if not name.endswith(']'): if not name.endswith(']'): # pragma: no cover
raise ValueError("Bad path element %s" % name) raise ValueError("Bad path element %s" % name)
return self.Node( return self.Node(
nonterminal=self.Nonterminal.IDENTIFIER, nonterminal=self.Nonterminal.IDENTIFIER,
@ -642,7 +642,7 @@ class ConditionExpressionParser:
"Unmatched ) at", nodes) "Unmatched ) at", nodes)
close_paren = nodes.popleft() close_paren = nodes.popleft()
children = self._apply_booleans(output) children = self._apply_booleans(output)
all_children = [left_paren, *children, close_paren] all_children = [left_paren] + list(children) + [close_paren]
return deque([ return deque([
self.Node( self.Node(
nonterminal=self.Nonterminal.CONDITION, nonterminal=self.Nonterminal.CONDITION,
@ -650,7 +650,7 @@ class ConditionExpressionParser:
text=" ".join([t.text for t in all_children]), text=" ".join([t.text for t in all_children]),
value=None, value=None,
children=list(children), children=list(children),
), *nodes]) )] + list(nodes))
else: else:
output.append(nodes.popleft()) output.append(nodes.popleft())
@ -747,11 +747,12 @@ class ConditionExpressionParser:
return AttributeValue(node.value) return AttributeValue(node.value)
elif node.kind == self.Kind.FUNCTION: elif node.kind == self.Kind.FUNCTION:
# size() # size()
function_node, *arguments = node.children function_node = node.children[0]
arguments = node.children[1:]
function_name = function_node.value function_name = function_node.value
arguments = [self._make_operand(arg) for arg in arguments] arguments = [self._make_operand(arg) for arg in arguments]
return FUNC_CLASS[function_name](*arguments) return FUNC_CLASS[function_name](*arguments)
else: else: # pragma: no cover
raise ValueError("Unknown operand: %r" % node) raise ValueError("Unknown operand: %r" % node)
@ -768,12 +769,13 @@ class ConditionExpressionParser:
self._make_op_condition(rhs)) self._make_op_condition(rhs))
elif node.kind == self.Kind.NOT: elif node.kind == self.Kind.NOT:
child, = node.children child, = node.children
return OpNot(self._make_op_condition(child), None) return OpNot(self._make_op_condition(child))
elif node.kind == self.Kind.PARENTHESES: elif node.kind == self.Kind.PARENTHESES:
child, = node.children child, = node.children
return self._make_op_condition(child) return self._make_op_condition(child)
elif node.kind == self.Kind.FUNCTION: elif node.kind == self.Kind.FUNCTION:
function_node, *arguments = node.children function_node = node.children[0]
arguments = node.children[1:]
function_name = function_node.value function_name = function_node.value
arguments = [self._make_operand(arg) for arg in arguments] arguments = [self._make_operand(arg) for arg in arguments]
return FUNC_CLASS[function_name](*arguments) return FUNC_CLASS[function_name](*arguments)
@ -784,24 +786,25 @@ class ConditionExpressionParser:
self._make_operand(low), self._make_operand(low),
self._make_operand(high)) self._make_operand(high))
elif node.kind == self.Kind.IN: elif node.kind == self.Kind.IN:
query, *possible_values = node.children query = node.children[0]
possible_values = node.children[1:]
query = self._make_operand(query) query = self._make_operand(query)
possible_values = [self._make_operand(v) for v in possible_values] possible_values = [self._make_operand(v) for v in possible_values]
return FuncIn(query, *possible_values) return FuncIn(query, *possible_values)
elif node.kind == self.Kind.COMPARISON: elif node.kind == self.Kind.COMPARISON:
lhs, comparator, rhs = node.children lhs, comparator, rhs = node.children
return OP_CLASS[comparator.value]( return COMPARATOR_CLASS[comparator.value](
self._make_operand(lhs), self._make_operand(lhs),
self._make_operand(rhs)) self._make_operand(rhs))
else: else: # pragma: no cover
raise ValueError("Unknown expression node kind %r" % node.kind) raise ValueError("Unknown expression node kind %r" % node.kind)
def _print_debug(self, nodes): def _print_debug(self, nodes): # pragma: no cover
print('ROOT') print('ROOT')
for node in nodes: for node in nodes:
self._print_node_recursive(node, depth=1) self._print_node_recursive(node, depth=1)
def _print_node_recursive(self, node, depth=0): def _print_node_recursive(self, node, depth=0): # pragma: no cover
if len(node.children) > 0: if len(node.children) > 0:
print(' ' * depth, node.nonterminal, node.kind) print(' ' * depth, node.nonterminal, node.kind)
for child in node.children: for child in node.children:
@ -922,6 +925,9 @@ class OpDefault(Op):
class OpNot(Op): class OpNot(Op):
OP = 'NOT' OP = 'NOT'
def __init__(self, lhs):
super(OpNot, self).__init__(lhs, None)
def expr(self, item): def expr(self, item):
lhs = self.lhs.expr(item) lhs = self.lhs.expr(item)
return not lhs return not lhs
@ -1002,15 +1008,6 @@ class OpOr(Op):
return lhs or rhs return lhs or rhs
class OpIn(Op):
OP = 'IN'
def expr(self, item):
lhs = self.lhs.expr(item)
rhs = self.rhs.expr(item)
return lhs in rhs
class Func(object): class Func(object):
""" """
Base class for a FilterExpression function Base class for a FilterExpression function
@ -1034,14 +1031,14 @@ class FuncAttrExists(Func):
def __init__(self, attribute): def __init__(self, attribute):
self.attr = attribute self.attr = attribute
super().__init__(attribute) super(FuncAttrExists, self).__init__(attribute)
def expr(self, item): def expr(self, item):
return self.attr.get_type(item) is not None return self.attr.get_type(item) is not None
def FuncAttrNotExists(attribute): def FuncAttrNotExists(attribute):
return OpNot(FuncAttrExists(attribute), None) return OpNot(FuncAttrExists(attribute))
class FuncAttrType(Func): class FuncAttrType(Func):
@ -1050,7 +1047,7 @@ class FuncAttrType(Func):
def __init__(self, attribute, _type): def __init__(self, attribute, _type):
self.attr = attribute self.attr = attribute
self.type = _type self.type = _type
super().__init__(attribute, _type) super(FuncAttrType, self).__init__(attribute, _type)
def expr(self, item): def expr(self, item):
return self.attr.get_type(item) == self.type.expr(item) return self.attr.get_type(item) == self.type.expr(item)
@ -1062,7 +1059,7 @@ class FuncBeginsWith(Func):
def __init__(self, attribute, substr): def __init__(self, attribute, substr):
self.attr = attribute self.attr = attribute
self.substr = substr self.substr = substr
super().__init__(attribute, substr) super(FuncBeginsWith, self).__init__(attribute, substr)
def expr(self, item): def expr(self, item):
if self.attr.get_type(item) != 'S': if self.attr.get_type(item) != 'S':
@ -1078,7 +1075,7 @@ class FuncContains(Func):
def __init__(self, attribute, operand): def __init__(self, attribute, operand):
self.attr = attribute self.attr = attribute
self.operand = operand self.operand = operand
super().__init__(attribute, operand) super(FuncContains, self).__init__(attribute, operand)
def expr(self, item): def expr(self, item):
if self.attr.get_type(item) in ('S', 'SS', 'NS', 'BS', 'L'): if self.attr.get_type(item) in ('S', 'SS', 'NS', 'BS', 'L'):
@ -1090,7 +1087,7 @@ class FuncContains(Func):
def FuncNotContains(attribute, operand): def FuncNotContains(attribute, operand):
return OpNot(FuncContains(attribute, operand), None) return OpNot(FuncContains(attribute, operand))
class FuncSize(Func): class FuncSize(Func):
@ -1098,7 +1095,7 @@ class FuncSize(Func):
def __init__(self, attribute): def __init__(self, attribute):
self.attr = attribute self.attr = attribute
super().__init__(attribute) super(FuncSize, self).__init__(attribute)
def expr(self, item): def expr(self, item):
if self.attr.get_type(item) is None: if self.attr.get_type(item) is None:
@ -1116,7 +1113,7 @@ class FuncBetween(Func):
self.attr = attribute self.attr = attribute
self.start = start self.start = start
self.end = end self.end = end
super().__init__(attribute, start, end) super(FuncBetween, self).__init__(attribute, start, end)
def expr(self, item): def expr(self, item):
return self.start.expr(item) <= self.attr.expr(item) <= self.end.expr(item) return self.start.expr(item) <= self.attr.expr(item) <= self.end.expr(item)
@ -1128,7 +1125,7 @@ class FuncIn(Func):
def __init__(self, attribute, *possible_values): def __init__(self, attribute, *possible_values):
self.attr = attribute self.attr = attribute
self.possible_values = possible_values self.possible_values = possible_values
super().__init__(attribute, *possible_values) super(FuncIn, self).__init__(attribute, *possible_values)
def expr(self, item): def expr(self, item):
for possible_value in self.possible_values: for possible_value in self.possible_values:
@ -1138,11 +1135,7 @@ class FuncIn(Func):
return False return False
OP_CLASS = { COMPARATOR_CLASS = {
'NOT': OpNot,
'AND': OpAnd,
'OR': OpOr,
'IN': OpIn,
'<': OpLessThan, '<': OpLessThan,
'>': OpGreaterThan, '>': OpGreaterThan,
'<=': OpLessThanOrEqual, '<=': OpLessThanOrEqual,

View File

@ -6,6 +6,7 @@ import decimal
import json import json
import re import re
import uuid import uuid
import six
import boto3 import boto3
from moto.compat import OrderedDict from moto.compat import OrderedDict
@ -89,7 +90,7 @@ class DynamoType(object):
Returns DynamoType or None. Returns DynamoType or None.
""" """
if isinstance(key, str) and self.is_map() and key in self.value: if isinstance(key, six.string_types) and self.is_map() and key in self.value:
return DynamoType(self.value[key]) return DynamoType(self.value[key])
if isinstance(key, int) and self.is_list(): if isinstance(key, int) and self.is_list():
@ -994,7 +995,6 @@ class DynamoDBBackend(BaseBackend):
dynamo_types = [DynamoType(value) for value in comparison_values] dynamo_types = [DynamoType(value) for value in comparison_values]
scan_filters[key] = (comparison_operator, dynamo_types) scan_filters[key] = (comparison_operator, dynamo_types)
filter_expression = get_filter_expression(filter_expression, expr_names, expr_values) filter_expression = get_filter_expression(filter_expression, expr_names, expr_values)
return table.scan(scan_filters, limit, exclusive_start_key, filter_expression, index_name) return table.scan(scan_filters, limit, exclusive_start_key, filter_expression, index_name)
@ -1024,12 +1024,12 @@ class DynamoDBBackend(BaseBackend):
if not get_expected(expected).expr(item): if not get_expected(expected).expr(item):
raise ValueError('The conditional request failed') raise ValueError('The conditional request failed')
condition_op = get_filter_expression( condition_op = get_filter_expression(
condition_expression, condition_expression,
expression_attribute_names, expression_attribute_names,
expression_attribute_values) expression_attribute_values)
if not condition_op.expr(current): if not condition_op.expr(item):
raise ValueError('The conditional request failed') raise ValueError('The conditional request failed')
# Update does not fail on new items, so create one # Update does not fail on new items, so create one
if item is None: if item is None: