Find documentation directories in a package using ``manifest.yaml``.

    Parameters
    ----------
    package_dir : `str`
        Directory of an EUPS package.
    skippedNames : `list` of `str`, optional
        List of package or module names to skip when creating links.

    Returns
    -------
    doc_dirs : namedtuple
        Attributes of the namedtuple are:

        - ``package_dirs`` (`dict`). Keys are package names (for example,
          ``'afw'``). Values are absolute directory paths to the package's
          documentation directory inside the package's ``doc`` directory. If
          there is no package-level documentation the dictionary will be empty.

        - ``modules_dirs`` (`dict`). Keys are module names (for example,
          ``'lsst.afw.table'``). Values are absolute directory paths to the
          module's directory inside the package's ``doc`` directory. If a
          package has no modules the returned dictionary will be empty.

        - ``static_doc_dirs`` (`dict`). Keys are directory names relative to
          the ``_static`` directory. Values are absolute directory paths to
          the static documentation directory in the package. If there
          isn't a declared ``_static`` directory, this dictionary is empty.

    Raises
    ------
    NoPackageDocs
       Raised when the ``manifest.yaml`` file cannot be found in a package.

    Notes
    -----
    Stack packages have documentation in subdirectories of their `doc`
    directory. The ``manifest.yaml`` file declares what these directories are
    so that they can be symlinked into the root project.

    There are three types of documentation directories:

    1. Package doc directories contain documentation for the EUPS package
       aspect. This is optional.
    2. Module doc directories contain documentation for a Python package
       aspect. These are optional.
    3. Static doc directories are root directories inside the package's
       ``doc/_static/`` directory. These are optional.

    These are declared in a package's ``doc/manifest.yaml`` file. For example:

    .. code-block:: yaml

       package: "afw"
       modules:
         - "lsst.afw.image"
         - "lsst.afw.geom"
       statics:
         - "_static/afw"

    This YAML declares *module* documentation directories:

    - ``afw/doc/lsst.afw.image/``
    - ``afw/doc/lsst.afw.geom/``

    It also declares a *package* documentation directory:

    - ``afw/doc/afw``

    And a static documentaton directory:

    - ``afw/doc/_static/afw``

def find_package_docs(package_dir, skippedNames=None):
    """Find documentation directories in a package using ``manifest.yaml``.

    Parameters
    ----------
    package_dir : `str`
        Directory of an EUPS package.
    skippedNames : `list` of `str`, optional
        List of package or module names to skip when creating links.

    Returns
    -------
    doc_dirs : namedtuple
        Attributes of the namedtuple are:

        - ``package_dirs`` (`dict`). Keys are package names (for example,
          ``'afw'``). Values are absolute directory paths to the package's
          documentation directory inside the package's ``doc`` directory. If
          there is no package-level documentation the dictionary will be empty.

        - ``modules_dirs`` (`dict`). Keys are module names (for example,
          ``'lsst.afw.table'``). Values are absolute directory paths to the
          module's directory inside the package's ``doc`` directory. If a
          package has no modules the returned dictionary will be empty.

        - ``static_doc_dirs`` (`dict`). Keys are directory names relative to
          the ``_static`` directory. Values are absolute directory paths to
          the static documentation directory in the package. If there
          isn't a declared ``_static`` directory, this dictionary is empty.

    Raises
    ------
    NoPackageDocs
       Raised when the ``manifest.yaml`` file cannot be found in a package.

    Notes
    -----
    Stack packages have documentation in subdirectories of their `doc`
    directory. The ``manifest.yaml`` file declares what these directories are
    so that they can be symlinked into the root project.

    There are three types of documentation directories:

    1. Package doc directories contain documentation for the EUPS package
       aspect. This is optional.
    2. Module doc directories contain documentation for a Python package
       aspect. These are optional.
    3. Static doc directories are root directories inside the package's
       ``doc/_static/`` directory. These are optional.

    These are declared in a package's ``doc/manifest.yaml`` file. For example:

    .. code-block:: yaml

       package: "afw"
       modules:
         - "lsst.afw.image"
         - "lsst.afw.geom"
       statics:
         - "_static/afw"

    This YAML declares *module* documentation directories:

    - ``afw/doc/lsst.afw.image/``
    - ``afw/doc/lsst.afw.geom/``

    It also declares a *package* documentation directory:

    - ``afw/doc/afw``

    And a static documentaton directory:

    - ``afw/doc/_static/afw``
    """
    logger = logging.getLogger(__name__)

    if skippedNames is None:
        skippedNames = []

    doc_dir = os.path.join(package_dir, 'doc')
    modules_yaml_path = os.path.join(doc_dir, 'manifest.yaml')

    if not os.path.exists(modules_yaml_path):
        raise NoPackageDocs(
            'Manifest YAML not found: {0}'.format(modules_yaml_path))

    with open(modules_yaml_path) as f:
        manifest_data = yaml.safe_load(f)

    module_dirs = {}
    package_dirs = {}
    static_dirs = {}

    if 'modules' in manifest_data:
        for module_name in manifest_data['modules']:
            if module_name in skippedNames:
                logger.debug('Skipping module {0}'.format(module_name))
                continue
            module_dir = os.path.join(doc_dir, module_name)

            # validate that the module's documentation directory does exist
            if not os.path.isdir(module_dir):
                message = 'module doc dir not found: {0}'.format(module_dir)
                logger.warning(message)
                continue

            module_dirs[module_name] = module_dir
            logger.debug('Found module doc dir {0}'.format(module_dir))

    if 'package' in manifest_data:
        package_name = manifest_data['package']

        full_package_dir = os.path.join(doc_dir, package_name)

        # validate the directory exists
        if os.path.isdir(full_package_dir) \
                and package_name not in skippedNames:
            package_dirs[package_name] = full_package_dir
            logger.debug('Found package doc dir {0}'.format(full_package_dir))
        else:
            logger.warning('package doc dir excluded or not found: {0}'.format(
                full_package_dir))

    if 'statics' in manifest_data:
        for static_dirname in manifest_data['statics']:
            full_static_dir = os.path.join(doc_dir, static_dirname)

            # validate the directory exists
            if not os.path.isdir(full_static_dir):
                message = '_static doc dir not found: {0}'.format(
                    full_static_dir)
                logger.warning(message)
                continue

            # Make a relative path to `_static` that's used as the
            # link source in the root docproject's _static/ directory
            relative_static_dir = os.path.relpath(
                full_static_dir,
                os.path.join(doc_dir, '_static'))

            static_dirs[relative_static_dir] = full_static_dir
            logger.debug('Found _static doc dir: {0}'.format(full_static_dir))

    Dirs = namedtuple('Dirs', ['module_dirs', 'package_dirs', 'static_dirs'])
    return Dirs(module_dirs=module_dirs,
                package_dirs=package_dirs,
                static_dirs=static_dirs)