class RuleBasedStateMachine(GenericStateMachine): """A RuleBasedStateMachine gives you a more structured way to define state machines. The idea is that a state machine carries a bunch of types of data divided into Bundles, and has a set of rules which may read data from bundles (or just from normal strategies) and push data onto bundles. At any given point a random applicable rule will be executed. """ _rules_per_class = {} _invariants_per_class = {} _base_rules_per_class = {} def __init__(self): if not self.rules(): raise InvalidDefinition(u'Type %s defines no rules' % ( type(self).__name__, )) self.bundles = {} self.name_counter = 1 self.names_to_values = {} self.__stream = CUnicodeIO() self.__printer = RepresentationPrinter(self.__stream) def __pretty(self, value): self.__stream.seek(0) self.__stream.truncate(0) self.__printer.output_width = 0 self.__printer.buffer_width = 0 self.__printer.buffer.clear() self.__printer.pretty(value) self.__printer.flush() return self.__stream.getvalue() def __repr__(self): return u'%s(%s)' % ( type(self).__name__, nicerepr(self.bundles), ) def upcoming_name(self): return u'v%d' % (self.name_counter,) def new_name(self): result = self.upcoming_name() self.name_counter += 1 return result def bundle(self, name): return self.bundles.setdefault(name, []) @classmethod def rules(cls): try: return cls._rules_per_class[cls] except KeyError: pass for k, v in inspect.getmembers(cls): r = getattr(v, RULE_MARKER, None) if r is not None: cls.define_rule( r.targets, r.function, r.arguments, r.precondition, ) cls._rules_per_class[cls] = cls._base_rules_per_class.pop(cls, []) return cls._rules_per_class[cls] @classmethod def invariants(cls): try: return cls._invariants_per_class[cls] except KeyError: pass target = [] for k, v in inspect.getmembers(cls): i = getattr(v, INVARIANT_MARKER, None) if i is not None: target.append(i) cls._invariants_per_class[cls] = target return cls._invariants_per_class[cls] @classmethod def define_rule(cls, targets, function, arguments, precondition=None): converted_arguments = {} for k, v in arguments.items(): converted_arguments[k] = v if cls in cls._rules_per_class: target = cls._rules_per_class[cls] else: target = cls._base_rules_per_class.setdefault(cls, []) return target.append( Rule( targets, function, converted_arguments, precondition, ) ) def steps(self): strategies = [] for rule in self.rules(): converted_arguments = {} valid = True if rule.precondition and not rule.precondition(self): continue for k, v in sorted(rule.arguments.items()): if isinstance(v, Bundle): bundle = self.bundle(v.name) if not bundle: valid = False break converted_arguments[k] = v if valid: strategies.append(TupleStrategy(( just(rule), FixedKeysDictStrategy(converted_arguments) ), tuple)) if not strategies: raise InvalidDefinition( u'No progress can be made from state %r' % (self,) ) for name, bundle in self.bundles.items(): if len(bundle) > 1: strategies.append( builds( ShuffleBundle, just(name), lists(integers(0, len(bundle) - 1)))) return one_of(strategies) def print_step(self, step): if isinstance(step, ShuffleBundle): return rule, data = step data_repr = {} for k, v in data.items(): data_repr[k] = self.__pretty(v) self.step_count = getattr(self, u'step_count', 0) + 1 report(u'Step #%d: %s%s(%s)' % ( self.step_count, u'%s = ' % (self.upcoming_name(),) if rule.targets else u'', rule.function.__name__, u', '.join(u'%s=%s' % kv for kv in data_repr.items()) )) def execute_step(self, step): if isinstance(step, ShuffleBundle): bundle = self.bundle(step.bundle) for i in step.swaps: bundle.insert(i, bundle.pop()) return rule, data = step data = dict(data) result = rule.function(self, **data) if rule.targets: name = self.new_name() self.names_to_values[name] = result self.__printer.singleton_pprinters.setdefault( id(result), lambda obj, p, cycle: p.text(name), ) for target in rule.targets: self.bundle(target).append(VarReference(name)) def check_invariants(self): for invar in self.invariants(): if invar.precondition and not invar.precondition(self): continue invar.function(self)
class RuleBasedStateMachine(GenericStateMachine): """A RuleBasedStateMachine gives you a more structured way to define state machines. The idea is that a state machine carries a bunch of types of data divided into Bundles, and has a set of rules which may read data from bundles (or just from normal strategies) and push data onto bundles. At any given point a random applicable rule will be executed. """ _rules_per_class = {} # type: Dict[type, List[classmethod]] _invariants_per_class = {} # type: Dict[type, List[classmethod]] _base_rules_per_class = {} # type: Dict[type, List[classmethod]] _initializers_per_class = {} # type: Dict[type, List[classmethod]] _base_initializers_per_class = {} # type: Dict[type, List[classmethod]] def __init__(self): if not self.rules(): raise InvalidDefinition(u'Type %s defines no rules' % (type(self).__name__, )) self.bundles = {} # type: Dict[Text, list] self.name_counter = 1 self.names_to_values = {} # type: Dict[Text, Any] self.__stream = CUnicodeIO() self.__printer = RepresentationPrinter(self.__stream) self._initialize_rules_to_run = copy(self.initialize_rules()) def __pretty(self, value): if isinstance(value, VarReference): return value.name self.__stream.seek(0) self.__stream.truncate(0) self.__printer.output_width = 0 self.__printer.buffer_width = 0 self.__printer.buffer.clear() self.__printer.pretty(value) self.__printer.flush() return self.__stream.getvalue() def __repr__(self): return u'%s(%s)' % ( type(self).__name__, nicerepr(self.bundles), ) def upcoming_name(self): return u'v%d' % (self.name_counter, ) def new_name(self): result = self.upcoming_name() self.name_counter += 1 return result def bundle(self, name): return self.bundles.setdefault(name, []) @classmethod def initialize_rules(cls): try: return cls._initializers_per_class[cls] except KeyError: pass for k, v in inspect.getmembers(cls): r = getattr(v, INITIALIZE_RULE_MARKER, None) if r is not None: cls.define_initialize_rule( r.targets, r.function, r.arguments, r.precondition, ) cls._initializers_per_class[cls] = \ cls._base_initializers_per_class.pop(cls, []) return cls._initializers_per_class[cls] @classmethod def rules(cls): try: return cls._rules_per_class[cls] except KeyError: pass for k, v in inspect.getmembers(cls): r = getattr(v, RULE_MARKER, None) if r is not None: cls.define_rule( r.targets, r.function, r.arguments, r.precondition, ) cls._rules_per_class[cls] = cls._base_rules_per_class.pop(cls, []) return cls._rules_per_class[cls] @classmethod def invariants(cls): try: return cls._invariants_per_class[cls] except KeyError: pass target = [] for k, v in inspect.getmembers(cls): i = getattr(v, INVARIANT_MARKER, None) if i is not None: target.append(i) cls._invariants_per_class[cls] = target return cls._invariants_per_class[cls] @classmethod def define_initialize_rule(cls, targets, function, arguments, precondition=None): converted_arguments = {} for k, v in arguments.items(): converted_arguments[k] = v if cls in cls._initializers_per_class: target = cls._initializers_per_class[cls] else: target = cls._base_initializers_per_class.setdefault(cls, []) return target.append( Rule( targets, function, converted_arguments, precondition, )) @classmethod def define_rule(cls, targets, function, arguments, precondition=None): converted_arguments = {} for k, v in arguments.items(): converted_arguments[k] = v if cls in cls._rules_per_class: target = cls._rules_per_class[cls] else: target = cls._base_rules_per_class.setdefault(cls, []) return target.append( Rule( targets, function, converted_arguments, precondition, )) def steps(self): # Pick initialize rules first if self._initialize_rules_to_run: return one_of([ tuples(just(rule), fixed_dictionaries(rule.arguments)) for rule in self._initialize_rules_to_run ]) # All initialize rules has been run once, go with the regular rules strategies = [] for rule in self.rules(): converted_arguments = {} valid = True if rule.precondition and not rule.precondition(self): continue for k, v in sorted(rule.arguments.items()): if isinstance(v, Bundle): bundle = self.bundle(v.name) if not bundle: valid = False break v = BundleReferenceStrategy(v.name) converted_arguments[k] = v if valid: strategies.append( tuples(just(rule), fixed_dictionaries(converted_arguments))) if not strategies: raise InvalidDefinition(u'No progress can be made from state %r' % (self, )) return one_of(strategies) def print_start(self): report(u'state = %s()' % (self.__class__.__name__, )) def print_end(self): report(u'state.teardown()') def print_step(self, step): rule, data = step data_repr = {} for k, v in data.items(): data_repr[k] = self.__pretty(v) self.step_count = getattr(self, u'step_count', 0) + 1 report( u'%sstate.%s(%s)' % (u'%s = ' % (self.upcoming_name(), ) if rule.targets else u'', rule.function.__name__, u', '.join(u'%s=%s' % kv for kv in data_repr.items()))) def execute_step(self, step): rule, data = step data = dict(data) for k, v in list(data.items()): if isinstance(v, VarReference): data[k] = self.names_to_values[v.name] result = rule.function(self, **data) if rule.targets: name = self.new_name() self.names_to_values[name] = result self.__printer.singleton_pprinters.setdefault( id(result), lambda obj, p, cycle: p.text(name)) for target in rule.targets: self.bundle(target).append(VarReference(name)) if self._initialize_rules_to_run: self._initialize_rules_to_run.remove(rule) def check_invariants(self): for invar in self.invariants(): if invar.precondition and not invar.precondition(self): continue invar.function(self)
class RuleBasedStateMachine(GenericStateMachine): """A RuleBasedStateMachine gives you a more structured way to define state machines. The idea is that a state machine carries a bunch of types of data divided into Bundles, and has a set of rules which may read data from bundles (or just from normal strategies) and push data onto bundles. At any given point a random applicable rule will be executed. """ _rules_per_class = {} # type: Dict[type, List[classmethod]] _invariants_per_class = {} # type: Dict[type, List[classmethod]] _base_rules_per_class = {} # type: Dict[type, List[classmethod]] _initializers_per_class = {} # type: Dict[type, List[classmethod]] _base_initializers_per_class = {} # type: Dict[type, List[classmethod]] def __init__(self): if not self.rules(): raise InvalidDefinition( u"Type %s defines no rules" % (type(self).__name__,) ) self.bundles = {} # type: Dict[Text, list] self.name_counter = 1 self.names_to_values = {} # type: Dict[Text, Any] self.__stream = CUnicodeIO() self.__printer = RepresentationPrinter(self.__stream) self._initialize_rules_to_run = copy(self.initialize_rules()) self.__rules_strategy = RuleStrategy(self) def __pretty(self, value): if isinstance(value, VarReference): return value.name self.__stream.seek(0) self.__stream.truncate(0) self.__printer.output_width = 0 self.__printer.buffer_width = 0 self.__printer.buffer.clear() self.__printer.pretty(value) self.__printer.flush() return self.__stream.getvalue() def __repr__(self): return u"%s(%s)" % (type(self).__name__, nicerepr(self.bundles)) def upcoming_name(self): return u"v%d" % (self.name_counter,) def last_names(self, n): assert self.name_counter > n count = self.name_counter return [u"v%d" % (i,) for i in hrange(count - n, count)] def new_name(self): result = self.upcoming_name() self.name_counter += 1 return result def bundle(self, name): return self.bundles.setdefault(name, []) @classmethod def initialize_rules(cls): try: return cls._initializers_per_class[cls] except KeyError: pass for _, v in inspect.getmembers(cls): r = getattr(v, INITIALIZE_RULE_MARKER, None) if r is not None: cls.define_initialize_rule( r.targets, r.function, r.arguments, r.precondition ) cls._initializers_per_class[cls] = cls._base_initializers_per_class.pop(cls, []) return cls._initializers_per_class[cls] @classmethod def rules(cls): try: return cls._rules_per_class[cls] except KeyError: pass for _, v in inspect.getmembers(cls): r = getattr(v, RULE_MARKER, None) if r is not None: cls.define_rule(r.targets, r.function, r.arguments, r.precondition) cls._rules_per_class[cls] = cls._base_rules_per_class.pop(cls, []) return cls._rules_per_class[cls] @classmethod def invariants(cls): try: return cls._invariants_per_class[cls] except KeyError: pass target = [] for _, v in inspect.getmembers(cls): i = getattr(v, INVARIANT_MARKER, None) if i is not None: target.append(i) cls._invariants_per_class[cls] = target return cls._invariants_per_class[cls] @classmethod def define_initialize_rule(cls, targets, function, arguments, precondition=None): converted_arguments = {} for k, v in arguments.items(): converted_arguments[k] = v if cls in cls._initializers_per_class: target = cls._initializers_per_class[cls] else: target = cls._base_initializers_per_class.setdefault(cls, []) return target.append(Rule(targets, function, converted_arguments, precondition)) @classmethod def define_rule(cls, targets, function, arguments, precondition=None): converted_arguments = {} for k, v in arguments.items(): converted_arguments[k] = v if cls in cls._rules_per_class: target = cls._rules_per_class[cls] else: target = cls._base_rules_per_class.setdefault(cls, []) return target.append(Rule(targets, function, converted_arguments, precondition)) def steps(self): # Pick initialize rules first if self._initialize_rules_to_run: return st.one_of( [ st.tuples(st.just(rule), st.fixed_dictionaries(rule.arguments)) for rule in self._initialize_rules_to_run ] ) return self.__rules_strategy def print_start(self): report(u"state = %s()" % (self.__class__.__name__,)) def print_end(self): report(u"state.teardown()") def print_step(self, step, result): rule, data = step data_repr = {} for k, v in data.items(): data_repr[k] = self.__pretty(v) self.step_count = getattr(self, u"step_count", 0) + 1 # If the step has target bundles, and the result is a MultipleResults # then we want to assign to multiple variables. if isinstance(result, MultipleResults): n_output_vars = len(result.values) else: n_output_vars = 1 output_assignment = ( u"%s = " % (", ".join(self.last_names(n_output_vars)),) if rule.targets and n_output_vars >= 1 else u"" ) report( u"%sstate.%s(%s)" % ( output_assignment, rule.function.__name__, u", ".join(u"%s=%s" % kv for kv in data_repr.items()), ) ) def _add_result_to_targets(self, targets, result): name = self.new_name() self.__printer.singleton_pprinters.setdefault( id(result), lambda obj, p, cycle: p.text(name) ) self.names_to_values[name] = result for target in targets: self.bundle(target).append(VarReference(name)) def execute_step(self, step): rule, data = step data = dict(data) for k, v in list(data.items()): if isinstance(v, VarReference): data[k] = self.names_to_values[v.name] result = rule.function(self, **data) if rule.targets: if isinstance(result, MultipleResults): for single_result in result.values: self._add_result_to_targets(rule.targets, single_result) else: self._add_result_to_targets(rule.targets, result) if self._initialize_rules_to_run: self._initialize_rules_to_run.remove(rule) return result def check_invariants(self): for invar in self.invariants(): if invar.precondition and not invar.precondition(self): continue invar.function(self)
class RuleBasedStateMachine(GenericStateMachine): """A RuleBasedStateMachine gives you a more structured way to define state machines. The idea is that a state machine carries a bunch of types of data divided into Bundles, and has a set of rules which may read data from bundles (or just from normal strategies) and push data onto bundles. At any given point a random applicable rule will be executed. """ _rules_per_class = {} _base_rules_per_class = {} def __init__(self): if not self.rules(): raise InvalidDefinition(u'Type %s defines no rules' % ( type(self).__name__, )) self.bundles = {} self.name_counter = 1 self.names_to_values = {} self.__stream = CUnicodeIO() self.__printer = RepresentationPrinter(self.__stream) def __pretty(self, value): self.__stream.seek(0) self.__stream.truncate(0) self.__printer.output_width = 0 self.__printer.buffer_width = 0 self.__printer.buffer.clear() self.__printer.pretty(value) self.__printer.flush() return self.__stream.getvalue() def __repr__(self): return u'%s(%s)' % ( type(self).__name__, nicerepr(self.bundles), ) def upcoming_name(self): return u'v%d' % (self.name_counter,) def new_name(self): result = self.upcoming_name() self.name_counter += 1 return result def bundle(self, name): return self.bundles.setdefault(name, []) @classmethod def rules(cls): try: return cls._rules_per_class[cls] except KeyError: pass for k, v in inspect.getmembers(cls): r = getattr(v, RULE_MARKER, None) while r is not None: cls.define_rule( r.targets, r.function, r.arguments, r.precondition, r.parent_rule ) r = r.parent_rule cls._rules_per_class[cls] = cls._base_rules_per_class.pop(cls, []) return cls._rules_per_class[cls] @classmethod def define_rule(cls, targets, function, arguments, precondition=None, parent_rule=None): converted_arguments = {} for k, v in arguments.items(): converted_arguments[k] = v if cls in cls._rules_per_class: target = cls._rules_per_class[cls] else: target = cls._base_rules_per_class.setdefault(cls, []) return target.append( Rule( targets, function, converted_arguments, precondition, parent_rule ) ) def steps(self): strategies = [] for rule in self.rules(): converted_arguments = {} valid = True if rule.precondition is not None and not rule.precondition(self): continue for k, v in sorted(rule.arguments.items()): if isinstance(v, Bundle): bundle = self.bundle(v.name) if not bundle: valid = False break converted_arguments[k] = v if valid: strategies.append(TupleStrategy(( just(rule), FixedKeysDictStrategy(converted_arguments) ), tuple)) if not strategies: raise InvalidDefinition( u'No progress can be made from state %r' % (self,) ) for name, bundle in self.bundles.items(): if len(bundle) > 1: strategies.append( builds( ShuffleBundle, just(name), lists(integers(0, len(bundle) - 1)))) return one_of(strategies) def print_step(self, step): if isinstance(step, ShuffleBundle): return rule, data = step data_repr = {} for k, v in data.items(): data_repr[k] = self.__pretty(v) self.step_count = getattr(self, u'step_count', 0) + 1 report(u'Step #%d: %s%s(%s)' % ( self.step_count, u'%s = ' % (self.upcoming_name(),) if rule.targets else u'', rule.function.__name__, u', '.join(u'%s=%s' % kv for kv in data_repr.items()) )) def execute_step(self, step): if isinstance(step, ShuffleBundle): bundle = self.bundle(step.bundle) for i in step.swaps: bundle.insert(i, bundle.pop()) return rule, data = step data = dict(data) result = rule.function(self, **data) if rule.targets: name = self.new_name() self.names_to_values[name] = result self.__printer.singleton_pprinters.setdefault( id(result), lambda obj, p, cycle: p.text(name), ) for target in rule.targets: self.bundle(target).append(VarReference(name))
class RuleBasedStateMachine(GenericStateMachine): """A RuleBasedStateMachine gives you a more structured way to define state machines. The idea is that a state machine carries a bunch of types of data divided into Bundles, and has a set of rules which may read data from bundles (or just from normal strategies) and push data onto bundles. At any given point a random applicable rule will be executed. """ _rules_per_class = {} # type: Dict[type, List[classmethod]] _invariants_per_class = {} # type: Dict[type, List[classmethod]] _base_rules_per_class = {} # type: Dict[type, List[classmethod]] _initializers_per_class = {} # type: Dict[type, List[classmethod]] _base_initializers_per_class = {} # type: Dict[type, List[classmethod]] def __init__(self): if not self.rules(): raise InvalidDefinition(u'Type %s defines no rules' % ( type(self).__name__, )) self.bundles = {} # type: Dict[Text, list] self.name_counter = 1 self.names_to_values = {} # type: Dict[Text, Any] self.__stream = CUnicodeIO() self.__printer = RepresentationPrinter(self.__stream) self._initialize_rules_to_run = copy(self.initialize_rules()) self.__rules_strategy = RuleStrategy(self) def __pretty(self, value): if isinstance(value, VarReference): return value.name self.__stream.seek(0) self.__stream.truncate(0) self.__printer.output_width = 0 self.__printer.buffer_width = 0 self.__printer.buffer.clear() self.__printer.pretty(value) self.__printer.flush() return self.__stream.getvalue() def __repr__(self): return u'%s(%s)' % ( type(self).__name__, nicerepr(self.bundles), ) def upcoming_name(self): return u'v%d' % (self.name_counter,) def new_name(self): result = self.upcoming_name() self.name_counter += 1 return result def bundle(self, name): return self.bundles.setdefault(name, []) @classmethod def initialize_rules(cls): try: return cls._initializers_per_class[cls] except KeyError: pass for k, v in inspect.getmembers(cls): r = getattr(v, INITIALIZE_RULE_MARKER, None) if r is not None: cls.define_initialize_rule( r.targets, r.function, r.arguments, r.precondition, ) cls._initializers_per_class[cls] = \ cls._base_initializers_per_class.pop(cls, []) return cls._initializers_per_class[cls] @classmethod def rules(cls): try: return cls._rules_per_class[cls] except KeyError: pass for k, v in inspect.getmembers(cls): r = getattr(v, RULE_MARKER, None) if r is not None: cls.define_rule( r.targets, r.function, r.arguments, r.precondition, ) cls._rules_per_class[cls] = cls._base_rules_per_class.pop(cls, []) return cls._rules_per_class[cls] @classmethod def invariants(cls): try: return cls._invariants_per_class[cls] except KeyError: pass target = [] for k, v in inspect.getmembers(cls): i = getattr(v, INVARIANT_MARKER, None) if i is not None: target.append(i) cls._invariants_per_class[cls] = target return cls._invariants_per_class[cls] @classmethod def define_initialize_rule( cls, targets, function, arguments, precondition=None): converted_arguments = {} for k, v in arguments.items(): converted_arguments[k] = v if cls in cls._initializers_per_class: target = cls._initializers_per_class[cls] else: target = cls._base_initializers_per_class.setdefault(cls, []) return target.append( Rule( targets, function, converted_arguments, precondition, ) ) @classmethod def define_rule(cls, targets, function, arguments, precondition=None): converted_arguments = {} for k, v in arguments.items(): converted_arguments[k] = v if cls in cls._rules_per_class: target = cls._rules_per_class[cls] else: target = cls._base_rules_per_class.setdefault(cls, []) return target.append( Rule( targets, function, converted_arguments, precondition, ) ) def steps(self): # Pick initialize rules first if self._initialize_rules_to_run: return one_of([ tuples(just(rule), fixed_dictionaries(rule.arguments)) for rule in self._initialize_rules_to_run ]) return self.__rules_strategy def print_start(self): report(u'state = %s()' % (self.__class__.__name__,)) def print_end(self): report(u'state.teardown()') def print_step(self, step): rule, data = step data_repr = {} for k, v in data.items(): data_repr[k] = self.__pretty(v) self.step_count = getattr(self, u'step_count', 0) + 1 report(u'%sstate.%s(%s)' % ( u'%s = ' % (self.upcoming_name(),) if rule.targets else u'', rule.function.__name__, u', '.join(u'%s=%s' % kv for kv in data_repr.items()) )) def execute_step(self, step): rule, data = step data = dict(data) for k, v in list(data.items()): if isinstance(v, VarReference): data[k] = self.names_to_values[v.name] result = rule.function(self, **data) if rule.targets: name = self.new_name() self.names_to_values[name] = result self.__printer.singleton_pprinters.setdefault( id(result), lambda obj, p, cycle: p.text(name) ) for target in rule.targets: self.bundle(target).append(VarReference(name)) if self._initialize_rules_to_run: self._initialize_rules_to_run.remove(rule) def check_invariants(self): for invar in self.invariants(): if invar.precondition and not invar.precondition(self): continue invar.function(self)