Sphinx nit-picky mode but only for links I explicitly wrote

Viewed 351

I tried turning on Sphinx's nit-picky mode (-n) to catch any broken links I might have accidentally made. However, it spews out errors for all the places where I've documented types. In some cases I've described types semantically (e.g. "3D array"), but it does it even for types extracted from type hints (even with intersphinx set up to pull Python types). For example, for this module

from typing import Callable

def foo(x: Callable[..., int]):
    pass

I get the error docstring of myproj.foo:: WARNING: py:class reference target not found: Callable[..., int]. That's with only sphinx.ext.autodoc and sphinx.ext.intersphinx extensions and a freshly-generated conf.py.

Is there some way to prevent Sphinx from trying to generate links for type information, or at least stop it complaining when they don't exist while still telling me about bad links in my hand-written documentation?

I'm using Sphinx 3.0.3.

2 Answers

Perhaps nitpick_ignore will do what you want? In your conf.py, something like this:

nitpick_ignore = [
    ("py:class", "Callable"),
]

I'm not sure of the exact values in the tuple that should be used, but I got the idea from this issue and a linked commit.

I had success solving a similar problem by writing a custom sphinx transform. I only wanted warnings for cross-references to my own package's python documentation. The following can be saved as a python file and added to extensions in conf.py once it is on the python path.

from sphinx import addnodes
from sphinx.errors import NoUri
from sphinx.transforms.post_transforms import SphinxPostTransform
from sphinx.util import logging

logger = logging.getLogger(__name__)

class MyLinkWarner(SphinxPostTransform):
    """
    Warns about broken cross-reference links, but only for my_package_name. 
    This is very similar to the sphinx option ``nitpicky=True`` (see
    :py:class:`sphinx.transforms.post_transforms.ReferencesResolver`), but there
    is no way to restrict that option to a specific package.
    """

    # this transform needs to happen before ReferencesResolver
    default_priority = 5

    def run(self):
        for node in self.document.traverse(addnodes.pending_xref):
            target = node["reftarget"]

            if target.startswith("my_package_name."):
                found_ref = False

                with suppress(NoUri, KeyError):
                    # let the domain try to resolve the reference
                    found_ref = self.env.domains[node["refdomain"]].resolve_xref(
                        self.env,
                        node.get("refdoc", self.env.docname),
                        self.app.builder,
                        node["reftype"],
                        target,
                        node,
                        nodes.TextElement("", ""),
                    )

                # warn if resolve_xref did not return or raised
                if not found_ref:
                    logger.warning(
                        f"API link {target} is broken.", location=node, type="ref"
                    )


def setup(app):
    app.add_post_transform(MyLinkWarner)
Related