docs: def execute(self, inputs=None, output=None, load_targets=False):
          """
          Run this step, recursively running or loading inputs.
          Used in bin/run_step.py which is run by drake.
          Args:
              inputs: collection of steps that should be loaded
              output: step that should be dumped after it is run
              load_targets (boolean): load all steps which are targets.
                  This argument is not used by run_step.py because target
                  does not get serialized. But it can be useful for
                  running steps directly.
          """
          if self == output:
              if os.path.exists(self._dump_dirname):
                  shutil.rmtree(self._dump_dirname)
              if os.path.exists(self._target_filename):
                  os.remove(self._target_filename)
              os.makedirs(self._dump_dirname)
  
          if inputs is None:
              inputs = []
  
          if not hasattr(self, 'result'):
              if self in inputs or (load_targets and self.target):
                  logging.info('Loading\n%s' % util.indent(str(self)))
                  self.load()
              else:
                  for i in self.inputs:
                      i.execute(inputs=inputs, output=output,
                                load_targets=load_targets)
  
                  args = merge_results(self.inputs)
                  logging.info('Running\n%s' % util.indent(str(self)))
                  self.result = self.run(*args.args, **args.kwargs)
  
          if self == output:
              logging.info('Dumping\n%s' % util.indent(str(self)))
              self.dump()
              util.touch(self._target_filename)