def condition(action_input=[], action_output=[]):
    """
    Decorator condition specifies the required input data and output space.
    Both parameters are lists of tuples: (port, #tokens consumed/produced)
    Optionally, the port spec can be a port only, meaning #tokens is 1.
    Return value is an ActionResult object

    FIXME:
    - Modify ActionResult to specify how many tokens were read/written from/to each port
      E.g. ActionResult.tokens_consumed/produced are dicts: {'port1':4, 'port2':1, ...}
      Since reading is done in the wrapper, tokens_consumed is fixed and given by action_input.
      The action fills in tokens_produced and the wrapper uses that info when writing to ports.
    - We can keep the @condition syntax by making the change in the normalize step below.
    """
    #
    # Normalize argument list (fill in a default repeat of 1 if not stated)
    #
    action_input = [p if isinstance(p, (list, tuple)) else (p, 1) for p in action_input]
    action_output = [p if isinstance(p, (list, tuple)) else (p, 1) for p in action_output]
    contract_output = tuple(n for _, n in action_output)
    tokens_produced = sum(contract_output)
    tokens_consumed = sum([n for _, n in action_input])

    def wrap(action_method):

        @functools.wraps(action_method)
        def condition_wrapper(self):
            #
            # Check if input ports have enough tokens. Note that all([]) evaluates to True
            #
            input_ok = all(
                [self.inports[portname].available_tokens() >= repeat for (portname, repeat) in action_input]
            )
            #
            # Check if output port have enough free token slots
            #
            output_ok = all(
                [self.outports[portname].available_tokens() >= repeat for (portname, repeat) in action_output]
            )

            if not input_ok or not output_ok:
                _log.debug("%s.%s not runnable (%s, %s)" % (self.name, action_method.__name__, input_ok, output_ok))
                return ActionResult(did_fire=False)
            #
            # Build the arguments for the action from the input port(s)
            #
            args = []
            ex = {}
            for (portname, repeat) in action_input:
                port = self.inports[portname]
                tokenlist = []
                for i in range(repeat):
                    token = port.peek_token()
                    is_exception = isinstance(token, ExceptionToken)
                    if is_exception:
                        ex.setdefault(portname, []).append(i)
                    tokenlist.append(token if is_exception else token.value)
                args.append(tokenlist if len(tokenlist) > 1 else tokenlist[0])

            #
            # Check for exceptional conditions
            #
            if ex:
                action_result = self.exception_handler(action_method, args, {'exceptions': ex})
            else:
                #
                # Perform the action (N.B. the method may be wrapped in a guard)
                #
                action_result = action_method(self, *args)

            valid_production = False
            if action_result.did_fire and (len(contract_output) == len(action_result.production)):
                valid_production = True
                for repeat, prod in zip(contract_output, action_result.production):
                    if repeat > 1 and len(prod) != repeat:
                        valid_production = False
                        break

            if action_result.did_fire and valid_production:
                #
                # Commit to the read from the FIFOs
                #
                for (portname, _) in action_input:
                    self.inports[portname].commit_peek_as_read()
                #
                # Write the results from the action to the output port(s)
                #
                for (portname, repeat), retval in zip(action_output, action_result.production):
                    port = self.outports[portname]
                    for data in retval if repeat > 1 else [retval]:
                        port.write_token(data if isinstance(data, Token) else Token(data))
                #
                # Bookkeeping
                #
                action_result.tokens_consumed = tokens_consumed
                action_result.tokens_produced = tokens_produced
            else:
                #
                # Rewind the read from the FIFOs
                #
                for (portname, _) in action_input:
                    self.inports[portname].peek_rewind()

            if action_result.did_fire and not valid_production:
                action = (self._type, action_method.__name__) % "%s.%s"
                raise Exception("%s invalid production %s, expected %s" % (action, str(action_result.production), str(tuple(action_output))))

            return action_result
        condition_wrapper.action_input = action_input
        condition_wrapper.action_output = action_output
        return condition_wrapper
    return wrap
dataset/ETHPy150Open EricssonResearch/calvin-base/calvin/actor/actor.py/condition/swapped operands of `%`