Skip to content

Commit

Permalink
Fix type_annotation_transforms for Sphinx >= 7.3 (#359)
Browse files Browse the repository at this point in the history
The monkey patching of `type_to_xref` was broken by the refactor in Sphinx 7.3.
  • Loading branch information
jbms authored Jul 3, 2024
1 parent 5fc98aa commit 6a933e6
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 19 deletions.
11 changes: 8 additions & 3 deletions sphinx_immaterial/apidoc/python/type_annotation_transforms.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@
)

import docutils.nodes
import sphinx
import sphinx.application
import sphinx.domains.python
import sphinx.environment
import sphinx.util.logging
from sphinx import version_info

# `ast.unparse` added in Python 3.9
if sys.version_info >= (3, 9):
Expand Down Expand Up @@ -354,15 +354,18 @@ def _builder_inited(app: sphinx.application.Sphinx):


def _monkey_patch_python_domain_to_transform_xref_titles():
orig_type_to_xref = sphinx.domains.python.type_to_xref
if sphinx.version_info >= (7, 3):
orig_type_to_xref = sphinx.domains.python._annotations.type_to_xref
else:
orig_type_to_xref = sphinx.domains.python.type_to_xref

def type_to_xref(
target: str,
env: sphinx.environment.BuildEnvironment,
*args,
suppress_prefix: bool = False,
) -> sphinx.addnodes.pending_xref:
if version_info < (7, 2):
if sphinx.version_info < (7, 2):
# suppress_prefix may not have been used like a kwarg before v7.2.0 as
# there was only 3 params for type_to_xref() prior to v7.2.0
if args:
Expand All @@ -389,6 +392,8 @@ def type_to_xref(
node.append(docutils.nodes.Text(new_target))
return node

if sphinx.version_info >= (7, 3):
sphinx.domains.python._annotations.type_to_xref = type_to_xref # type: ignore[assignment]
sphinx.domains.python.type_to_xref = type_to_xref # type: ignore[assignment]


Expand Down
57 changes: 41 additions & 16 deletions tests/python_transform_type_annotations_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import docutils.nodes
import sphinx
import sphinx.application
import sphinx.domains.python

if sphinx.version_info < (7, 2):
Expand Down Expand Up @@ -31,6 +32,31 @@ def make(extra_conf: str = "", **kwargs):
yield make


def get_parsed_annotation_as_text(
annotation: str, app: sphinx.application.Sphinx
) -> str:
if sphinx.version_info >= (7, 3):
assert (
sphinx.domains.python._annotations._parse_annotation # type: ignore[attr-defined]
is sphinx.domains.python._parse_annotation
)

assert (
sphinx.domains.python._object._parse_annotation # type: ignore[attr-defined]
is sphinx.domains.python._parse_annotation
)

assert (
sphinx.domains.python._annotations.type_to_xref # type: ignore[attr-defined]
is sphinx.domains.python.type_to_xref
)

parsed_annotations = sphinx.domains.python._parse_annotation(annotation, app.env)
parent = docutils.nodes.TextElement("", "")
parent.extend(parsed_annotations)
return parent.astext()


def test_transform_type_annotations_pep604(theme_make_app):
app = theme_make_app(
confoverrides=dict(),
Expand All @@ -40,20 +66,19 @@ def test_transform_type_annotations_pep604(theme_make_app):
("Union[int, float]", "int | float"),
("Literal[1, 2, None]", "1 | 2 | None"),
]:
parent = docutils.nodes.TextElement("", "")
assert get_parsed_annotation_as_text(annotation, app) == expected_text

parsed_annotations = sphinx.domains.python._parse_annotation(
annotation, app.env
)
if sphinx.version_info >= (7, 3):
as_text = "".join([n.astext() for n in parsed_annotations])
og_parsed = sphinx.domains.python._annotations._parse_annotation( # type: ignore[module-not-found,attr-defined]
annotation, app.env
)
assert as_text == "".join([n.astext() for n in og_parsed])
re_exported = sphinx.domains.python._object._parse_annotation( # type: ignore[module-not-found,attr-defined]
annotation, app.env
)
assert as_text == "".join([n.astext() for n in re_exported])
parent.extend(parsed_annotations)
assert parent.astext() == expected_text

def test_python_module_names_to_strip_from_xrefs(theme_make_app):
app = theme_make_app(
confoverrides=dict(
python_module_names_to_strip_from_xrefs=["tensorstore", "collections.abc"]
),
)

for annotation, expected_text in [
("tensorstore.Dim", "Dim"),
("collections.abc.Sequence", "Sequence"),
("collections.abc.def.Sequence", "def.Sequence"),
]:
assert get_parsed_annotation_as_text(annotation, app) == expected_text

0 comments on commit 6a933e6

Please sign in to comment.