docs: def autorun_filters(filters, doc, search_dirs, verbose):
      """
      :param filters: list of str
      :param doc: panflute.Doc
      :param search_dirs: list of str
      :param verbose: bool
      :return: panflute.Doc
      """
      def remove_py(s):
              return s[:-3] if s.endswith('.py') else s
  
      filter_paths = []
      for filter_ in filters:
          filter_exp = p.normpath(p.expanduser(p.expandvars(filter_)))
  
          if filter_exp == remove_py(p.basename(filter_exp)).lstrip('.'):
              # import .foo  # is not supported
              module = True
              mod_path = filter_exp.replace('.', p.sep)
              path_postfixes = (p.join(mod_path, '__init__.py'), mod_path + '.py')
          else:
              module = False
              # allow with and without .py ending
              path_postfixes = (remove_py(filter_exp) + '.py',)
  
          for path, path_postf in [(path, path_postf)
                                   for path in search_dirs
                                   for path_postf in path_postfixes]:
              if p.isabs(path_postf):
                  filter_path = path_postf
              else:
                  filter_path = p.abspath(p.normpath(p.join(path, path_postf)))
  
              if p.isfile(filter_path):
                  if verbose:
                      debug("panflute: filter <{}> found in {}".format(filter_, filter_path))
  
                  if module and not (path in reduced_sys_path):
                      extra_dir = p.abspath(path)
                      # `path` already doesn't contain `.`, `..`, env vars or `~`
                  else:
                      extra_dir = None
                  module_ = filter_exp if module else filter_path
  
                  filter_paths.append((filter_, filter_path, module_, extra_dir))
                  break
              elif p.isabs(path_postf):
                  if verbose:
                      debug("          filter <{}> NOT found in {}".format(filter_, filter_path))
                  raise Exception("filter not found: " + filter_)
              elif verbose:
                  debug("          filter <{}> NOT found in {}".format(filter_, filter_path))
          else:
              raise Exception("filter not found: " + filter_)
  
      for filter_, filter_path, module_, extra_dir in filter_paths:
          if verbose:
              debug("panflute: running filter <{}>".format(filter_))
          with ContextImport(module_, extra_dir) as module:
              try:
                  module.main(doc)
              except Exception as e:
                  debug("Failed to run filter: " + filter_)
                  if not hasattr(module, 'main'):
                      debug(' - Possible cause: filter lacks a main() function')
                  debug('Filter code:')
                  debug('-' * 64)
                  with open(filter_path) as fp:
                      debug(fp.read())
                  debug('-' * 64)
                  raise Exception(e)
          if verbose:
              debug("panflute: filter <{}> completed".format(filter_))
  
      return doc