from ..interface import Contract, ContractNotRespected, add_prefix
from ..pyparsing_utils import myOperatorPrecedence
from ..syntax import ParsingTmp, W, opAssoc, simple_contract
from .extensions import Extension
from .suggester import create_suggester
NOT_GLYPH = '!'
AND_GLYPH = ','
OR_GLYPH = '|'
[docs]class Logical(object):
def __init__(self, glyph, precedence):
self.glyph = glyph
self.precedence = precedence
def __str__(self):
s = self.glyph.join(self._convert(x) for x in self.clauses)
return s
def _convert(self, x):
if isinstance(x, Logical) and x.precedence < self.precedence:
return '(%s)' % x
return '%s' % x
[docs]class OR(Logical, Contract):
def __init__(self, clauses, where=None):
assert isinstance(clauses, list)
assert len(clauses) >= 2
Contract.__init__(self, where)
Logical.__init__(self, OR_GLYPH, 1)
self.clauses = clauses
def _check_quick(self, context, value):
""" Returns True if this checks out. """
# first make a quick pass
for c in self.clauses:
try:
# try with fake context
c._check_contract(context.copy(), value, silent=True)
# if ok, do with main context
c._check_contract(context, value, silent=True)
return True
except ContractNotRespected as e:
pass
return False
[docs] def check_contract(self, context, value, silent):
orig = context.copy()
if self._check_quick(context, value):
return
else:
if silent:
msg = '(Error description suppressed.)'
raise ContractNotRespected(contract=self, error=msg,
value=value, context=context)
# otherwise need to do it again with detailed error messages
self.get_error(orig, value)
[docs] def get_error(self, context, value):
""" This assumes that we are going to fail """
exceptions = []
for c in self.clauses:
try:
# try with fake context
c._check_contract(context.copy(), value, silent=False)
# if ok, do with main context
c._check_contract(context, value, silent=False)
assert False, "We should not be here."
except ContractNotRespected as e:
exceptions.append((c, e))
else:
msg = self._format_exceptions(exceptions)
raise ContractNotRespected(contract=self, error=msg,
value=value, context=context)
def _format_exceptions(self, exceptions):
msg = ('Could not satisfy any of the %d clauses in %s.'
% (len(self.clauses), self))
for i, ex in enumerate(exceptions):
c, e = ex
msg += '\n ---- Clause #%d: %s\n' % (i + 1, c)
msg += add_prefix('%s' % e, ' | ')
msg += '\n ------- (end clauses) -------'
return msg
def __repr__(self):
s = 'OR(%r)' % self.clauses
return s
[docs] @staticmethod
def parse_action(string, location, tokens):
l = list(tokens[0])
clauses = [l.pop(0)]
while l:
glyph = l.pop(0) # @UnusedVariable
assert glyph == OR_GLYPH
operand = l.pop(0)
clauses.append(operand)
where = W(string, location)
return OR(clauses, where=where)
[docs]class And(Logical, Contract):
def __init__(self, clauses, where=None):
assert isinstance(clauses, list)
assert len(clauses) >= 2, clauses
Contract.__init__(self, where)
Logical.__init__(self, AND_GLYPH, 2)
self.clauses = clauses
[docs] def check_contract(self, context, value, silent):
for c in self.clauses:
c._check_contract(context, value, silent)
def __repr__(self):
s = 'And(%r)' % self.clauses
return s
[docs] @staticmethod
def parse_action(string, location, tokens):
l = list(tokens[0])
clauses = [l.pop(0)]
while l:
glyph = l.pop(0) # @UnusedVariable
assert glyph == AND_GLYPH
operand = l.pop(0)
clauses.append(operand)
where = W(string, location)
return And(clauses, where=where)
[docs]class Not(Logical, Contract):
def __init__(self, clauses, where=None):
assert isinstance(clauses, list)
assert len(clauses) == 1, clauses
Contract.__init__(self, where)
Logical.__init__(self, NOT_GLYPH, 3)
self.clauses = clauses
[docs] def check_contract(self, context, value, silent):
clause = self.clauses[0]
try:
clause._check_contract(context, value, silent)
except ContractNotRespected:
pass
else:
msg = "Shouldn't have satisfied the clause %s." % clause
raise ContractNotRespected(contract=self, error=msg,
value=value, context=context)
[docs] @staticmethod
def parse_action(string, location, tokens):
l = list(tokens[0])
assert l.pop(0) == NOT_GLYPH
where = W(string, location)
return Not(l, where=where)
def __repr__(self):
s = 'Not(%r)' % self.clauses
return s
def __str__(self):
return self.glyph + self._convert(self.clauses[0])
suggester = create_suggester(get_options=lambda: ParsingTmp.keywords +
list(Extension.registrar.keys()))
baseExpr = simple_contract | suggester
baseExpr.setName('Simple contract (recovering)')
op = myOperatorPrecedence
# op = operatorPrecedence
composite_contract = op(baseExpr, [
(NOT_GLYPH, 1, opAssoc.RIGHT, Not.parse_action),
(AND_GLYPH, 2, opAssoc.LEFT, And.parse_action),
(OR_GLYPH, 2, opAssoc.LEFT, OR.parse_action),
])
composite_contract.setName('NOT/OR/AND contract')
or_contract = op(baseExpr, [
(OR_GLYPH, 2, opAssoc.LEFT, OR.parse_action),
])
or_contract.setName('OR contract')