docs: 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