from ..interface import Contract, ContractNotRespected
from ..syntax import(add_contract, W, contract_expression, O, S, ZeroOrMore,
Group, add_keyword, Keyword)
from .compositions import or_contract
[docs]class Tuple(Contract):
def __init__(self, length=None, elements=None, where=None):
Contract.__init__(self, where)
self.length = length
self.elements = elements
assert elements is None or isinstance(elements, list)
if elements:
for e in elements:
assert isinstance(e, Contract)
[docs] def check_contract(self, context, value, silent):
if not isinstance(value, tuple):
error = 'Expected a tuple, got %r.' % value.__class__.__name__
raise ContractNotRespected(contract=self, error=error,
value=value, context=context)
if self.length is not None:
self.length._check_contract(context, len(value), silent)
if self.elements is not None:
if len(value) != len(self.elements):
error = ('Expected a tuple of length '
'%s, got %r of length %s.' %
(len(self.elements), value, len(value)))
raise ContractNotRespected(contract=self, error=error,
value=value, context=context)
for i in range(len(value)):
self.elements[i]._check_contract(context, value[i], silent)
def __repr__(self):
return 'Tuple(%r,%r)' % (self.length, self.elements)
def __str__(self):
s = 'tuple'
if self.length is not None:
s += '[%s]' % self.length
def rep(x):
from .compositions import And
if isinstance(x, And):
return "(%s)" % x
else:
return "%s" % x
if self.elements is not None:
s += '(%s)' % ",".join(rep(x) for x in self.elements)
return s
[docs] @staticmethod
def parse_action(s, loc, tokens):
where = W(s, loc)
length = tokens.get('length', [None])[0]
# elements = tokens.get('elements', [None])[0]
if 'elements' in tokens:
elements = list(tokens['elements'])
else:
elements = None
assert elements is None or length is None
assert length is None or isinstance(length, Contract), ("Wrong type %r"
% length)
if elements:
for e in elements:
assert isinstance(e, Contract), ("Wrong type %s (%r)"
% (type(e), e))
return Tuple(length, elements, where=where)
# if you use contract instead of simple_contract, it will be matched as And
inside = (S('(') - contract_expression - S(')')) | or_contract
inside.setName('Any contract for tuple elements (use parenthesis for AND)')
elements = (Group(S('(') - inside - ZeroOrMore(S(',')
- inside) - S(')'))('elements'))
elements.setName('Tuple elements contract.')
length = Group(S('[') - contract_expression - S(']'))('length')
length.setName('Tuple length contract.')
tuple_contract = Keyword('tuple') - O(length | elements)
tuple_contract.setName('tuple contract')
add_contract(tuple_contract.setParseAction(Tuple.parse_action))
add_keyword('tuple')