Skip to content

Commit

Permalink
Apply qualname_overrides in more circumstances (#191)
Browse files Browse the repository at this point in the history
  • Loading branch information
flying-sheep authored Jan 16, 2025
1 parent 684834c commit 7578770
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 2 deletions.
2 changes: 0 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
{
"python.analysis.typeCheckingMode": "strict",
"python.testing.pytestArgs": ["-vv", "--color=yes"],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true,
"python.terminal.activateEnvironment": false,
"[python]": {
"editor.defaultFormatter": "charliermarsh.ruff",
"editor.formatOnSave": true,
Expand Down
23 changes: 23 additions & 0 deletions src/scanpydoc/elegant_typehints/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,10 @@ def x() -> Tuple[int, float]:
from collections.abc import Callable

from sphinx.config import Config
from docutils.nodes import TextElement, reference
from sphinx.addnodes import pending_xref
from sphinx.application import Sphinx
from sphinx.environment import BuildEnvironment


__all__ = [
Expand Down Expand Up @@ -113,6 +116,24 @@ class PickleableCallable:
__call__ = property(lambda self: self.func)


# https://www.sphinx-doc.org/en/master/extdev/event_callbacks.html#event-missing-reference
def _last_resolve(
app: Sphinx,
env: BuildEnvironment,
node: pending_xref,
contnode: TextElement,
) -> reference | None:
if "sphinx.ext.intersphinx" not in app.extensions:
return None

from sphinx.ext.intersphinx import resolve_reference_detect_inventory

if (qualname := qualname_overrides.get(node["reftarget"])) is None:
return None
node["reftarget"] = qualname
return resolve_reference_detect_inventory(env, node, contnode)


@_setup_sig
def setup(app: Sphinx) -> dict[str, Any]:
"""Patches :mod:`sphinx_autodoc_typehints` for a more elegant display."""
Expand All @@ -123,6 +144,8 @@ def setup(app: Sphinx) -> dict[str, Any]:
app.add_config_value("qualname_overrides", default={}, rebuild="html")
app.add_config_value("annotate_defaults", default=True, rebuild="html")
app.connect("config-inited", _init_vars)
# Add 1 to priority to run after sphinx.ext.intersphinx
app.connect("missing-reference", _last_resolve, priority=501)

from ._formatting import typehints_formatter

Expand Down
42 changes: 42 additions & 0 deletions tests/test_elegant_typehints.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@

import pytest

from scanpydoc.elegant_typehints import _last_resolve, qualname_overrides
from scanpydoc.elegant_typehints._formatting import typehints_formatter


if TYPE_CHECKING:
from types import ModuleType
from typing import Protocol
from collections.abc import Generator

from sphinx.application import Sphinx

Expand All @@ -32,6 +34,12 @@ def __call__( # noqa: D102
NONE_RTYPE = ":rtype: :sphinx_autodoc_typehints_type:`\\:py\\:obj\\:\\`None\\``"


@pytest.fixture(autouse=True)
def _reset_qualname_overrides() -> Generator[None, None, None]:
yield
qualname_overrides.clear()


@pytest.fixture
def testmod(make_module: Callable[[str, str], ModuleType]) -> ModuleType:
return make_module(
Expand Down Expand Up @@ -240,6 +248,40 @@ def fn_test(m: object) -> None: # pragma: no cover
]


def test_resolve(app: Sphinx) -> None:
"""Test that qualname_overrides affects _last_resolve as expected."""
from docutils.nodes import TextElement, reference
from sphinx.addnodes import pending_xref
from sphinx.ext.intersphinx import InventoryAdapter

app.setup_extension("sphinx.ext.intersphinx")

# Inventory contains documented name
InventoryAdapter(app.env).main_inventory["py:class"] = {
"test.Class": ("TestProj", "1", "https://x.com", "Class"),
}
# Node contains name from code
node = pending_xref(refdomain="py", reftarget="testmod.Class", reftype="class")

resolved = _last_resolve(app, app.env, node, TextElement())
assert isinstance(resolved, reference)
assert resolved["refuri"] == "https://x.com"
assert resolved["reftitle"] == "(in TestProj v1)"


@pytest.mark.parametrize("qualname", ["testmod.Class", "nonexistent.Class"])
def test_resolve_failure(app: Sphinx, qualname: str) -> None:
from docutils.nodes import TextElement
from sphinx.addnodes import pending_xref

app.setup_extension("sphinx.ext.intersphinx")
node = pending_xref(refdomain="py", reftarget=qualname, reftype="class")

resolved = _last_resolve(app, app.env, node, TextElement())
assert resolved is None
assert node["reftarget"] == qualname_overrides.get(qualname, qualname)


# These guys aren’t listed as classes in Python’s intersphinx index:
@pytest.mark.parametrize(
"annotation",
Expand Down

0 comments on commit 7578770

Please sign in to comment.