def rule( *, targets: Union[Sequence[Bundle[Ex]], _OmittedArgument] = (), target: Optional[Bundle[Ex]] = None, **kwargs: SearchStrategy, ) -> Union[_RuleWrapper[Ex], Callable[[Callable[..., None]], Callable[..., None]]]: """Decorator for RuleBasedStateMachine. Any Bundle present in ``target`` or ``targets`` will define where the end result of this function should go. If both are empty then the end result will be discarded. ``target`` must be a Bundle, or if the result should go to multiple bundles you can pass a tuple of them as the ``targets`` argument. It is invalid to use both arguments for a single rule. If the result should go to exactly one of several bundles, define a separate rule for each case. kwargs then define the arguments that will be passed to the function invocation. If their value is a Bundle, or if it is ``consumes(b)`` where ``b`` is a Bundle, then values that have previously been produced for that bundle will be provided. If ``consumes`` is used, the value will also be removed from the bundle. Any other kwargs should be strategies and values from them will be provided. """ converted_targets = _convert_targets(targets, target) for k, v in kwargs.items(): check_strategy(v, name=k) def accept(f): if getattr(f, INVARIANT_MARKER, None): raise InvalidDefinition( "A function cannot be used for both a rule and an invariant.", Settings.default, ) existing_rule = getattr(f, RULE_MARKER, None) existing_initialize_rule = getattr(f, INITIALIZE_RULE_MARKER, None) if existing_rule is not None or existing_initialize_rule is not None: raise InvalidDefinition( "A function cannot be used for two distinct rules. ", Settings.default) preconditions = getattr(f, PRECONDITIONS_MARKER, ()) rule = Rule( targets=converted_targets, arguments=kwargs, function=f, preconditions=preconditions, ) @proxies(f) def rule_wrapper(*args, **kwargs): return f(*args, **kwargs) setattr(rule_wrapper, RULE_MARKER, rule) return rule_wrapper return accept
def initialize( *, targets: Union[Sequence[Bundle[Ex]], _OmittedArgument] = (), target: Optional[Bundle[Ex]] = None, **kwargs: SearchStrategy, ) -> Union[_RuleWrapper[Ex], Callable[[Callable[..., None]], Callable[..., None]]]: """Decorator for RuleBasedStateMachine. An initialize decorator behaves like a rule, but all ``@initialize()`` decorated methods will be called before any ``@rule()`` decorated methods, in an arbitrary order. Each ``@initialize()`` method will be called exactly once per run, unless one raises an exception - after which only the ``.teardown()`` method will be run. ``@initialize()`` methods may not have preconditions. """ converted_targets = _convert_targets(targets, target) for k, v in kwargs.items(): check_strategy(v, name=k) def accept(f): if getattr(f, INVARIANT_MARKER, None): raise InvalidDefinition( "A function cannot be used for both a rule and an invariant.", Settings.default, ) existing_rule = getattr(f, RULE_MARKER, None) existing_initialize_rule = getattr(f, INITIALIZE_RULE_MARKER, None) if existing_rule is not None or existing_initialize_rule is not None: raise InvalidDefinition( "A function cannot be used for two distinct rules. ", Settings.default) preconditions = getattr(f, PRECONDITIONS_MARKER, ()) if preconditions: raise InvalidDefinition( "An initialization rule cannot have a precondition. ", Settings.default) rule = Rule( targets=converted_targets, arguments=kwargs, function=f, preconditions=preconditions, ) @proxies(f) def rule_wrapper(*args, **kwargs): return f(*args, **kwargs) setattr(rule_wrapper, INITIALIZE_RULE_MARKER, rule) return rule_wrapper return accept