docs: def install_dir(pkgpath, install_path, register_func, delete_after_install=False):
      """Install plugin from specified directory.
  
      install_path and register_func are same as :func:`install_plugin`.
      :param delete_after_install: Delete pkgpath after install (used in :func:`install_from_zip`).
      """
      logger.debug("%s is a directory, attempting to validate", pkgpath)
      plugin = register_func(pkgpath)
      logger.debug("%s looks good, copying to %s", pkgpath, install_path)
      try:
          copy_tree(pkgpath, os.path.join(install_path, plugin.name))
          if delete_after_install:
              logger.debug("deleting %s", pkgpath)
              shutil.rmtree(pkgpath)
          pkgpath = os.path.join(install_path, plugin.name)
      except (OSError, CTError) as exc:
          # TODO: handle package name exists (upgrade? overwrite?)
          logger.debug(str(exc), exc_info=True)
          raise exceptions.PluginAlreadyInstalled(plugin.name)
  
      return install_deps(pkgpath)