Run the engine (that should have been configured first)
        
        if the `inputs` are given without name it should be the inputs of the
        first block, ig `named_inputs` are used it may be the inputs of any
        block.
        
        .. note:: Either `inputs` or `named_inputs` should be provided, not both
        
        :param inputs: the data to give as input to the first block
        :param named_inputs: named input data should match with
            :func:`needed_inputs` result.

def play(self, *inputs, **named_inputs):
        """ Run the engine (that should have been configured first)
        
        if the `inputs` are given without name it should be the inputs of the
        first block, ig `named_inputs` are used it may be the inputs of any
        block.
        
        .. note:: Either `inputs` or `named_inputs` should be provided, not both
        
        :param inputs: the data to give as input to the first block
        :param named_inputs: named input data should match with
            :func:`needed_inputs` result.
        """
        self._logger.info("\n\n\t\t\t ** ============= play engine ============= ** \n")
        #
        # create data structure for results and metaresults
        results = OrderedDict()
        self.meta = PlayMeta("engine")
        ### manage inputs
        if len(inputs) and len(named_inputs):
            raise ValueError("Either `inputs` or `named_inputs` should be provided, not both !")
        # default input name (so also the default last_output_name)
        if len(inputs):
            # prepare the input data
            first_in_names = self.in_name or [Engine.DEFAULT_IN_NAME]
            if len(inputs) != len(first_in_names):
                raise ValueError("%d inputs are needed for first block, but %d given" % (len(first_in_names), len(inputs)))
            # inputs are store in results dict (then there is no special run for the first block)
            for input_num, in_name in enumerate(first_in_names):
                results[in_name] = inputs[input_num]
        else:
            results.update(named_inputs)
        #
        ## validate
        self.validate(results.keys())
        #
        ### run the blocks
        last_output_name = Engine.DEFAULT_IN_NAME
        for block in self:
            # continue if block is not selected (note: if require the validate should have faild before)
            if not len(block.selected()):
                continue
            # prepare block ipouts
            in_names = block.in_name or [last_output_name]
            # ^ note: if the block has no named input then the last block output is used
            inputs = [results[name] for name in in_names]
            # run the block
            try:
                results.update(block.play(*inputs))
                # ^ note: le validate par rapport au type est fait dans le run du block
            finally:
                # store metadata
                self.meta.append(block.meta)
            last_output_name = block.out_name
        return results