docs: def find_executable(self):
          '''Find an executable node, which means nodes that has not been completed
          and has no input dependency.'''
          if 'DAG' in env.config['SOS_DEBUG'] or 'ALL' in env.config['SOS_DEBUG']:
              env.log_to_file('DAG', 'find_executable')
          for node in self.nodes():
              # if it has not been executed
              if node._status is None:
                  with_dependency = False
                  for edge in self.in_edges(node):
                      if edge[0]._status != 'completed':
                          with_dependency = True
                          break
                  if not with_dependency:
                      return node
          # if no node could be found, let use try pending ones
          pending_jobs = [
              x for x in self.nodes() if x._status == 'signature_pending'
          ]
          if pending_jobs:
              try:
                  notifier = ActivityNotifier(
                      f'Waiting for {len(pending_jobs)} pending job{"s: e.g." if len(pending_jobs) > 1 else ":"} output {short_repr(pending_jobs[0]._signature[0])} with signature file {pending_jobs[0]._signature[1] + "_"}. You can manually remove this lock file if you are certain that no other process is working on the output.'
                  )
                  while True:
                      for node in pending_jobs:
                          # if it has not been executed
                          lock = fasteners.InterProcessLock(node._signature[1] +
                                                            '_')
                          if lock.acquire(blocking=False):
                              lock.release()
                              node._status = None
                              return node
                      time.sleep(0.1)
              except Exception as e:
                  env.logger.error(e)
              finally:
                  notifier.stop()
          return None