Source code for contracts.library.compositions

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')