Description
Describe the bug
This is somewhat related to a number of issues in #11991, but is wider than just autodoc.
It also affects sphinx-autodoc2
users as TypeVars are created as py:data
rather than py:class
.
When creating an xref from an argument annotation, this is called:
sphinx/sphinx/domains/python/_annotations.py
Lines 47 to 53 in 758dc81
This means that if you write code like:
T = int | str
class Foo:
def __init__(t: T): ...
then the python domain expects T
to be a class, but it is categorized as data:
WARNING: py:class reference target not found: foo.T [ref.class]
One possible solution is to widen the reftype in parse_reftarget
to "obj"
in every case, not just for the typing module:
def parse_reftarget(
reftarget: str, suppress_prefix: bool = False
) -> tuple[str, str, str, bool]:
"""Parse a type string and return (reftype, reftarget, title, refspecific flag)"""
refspecific = False
if reftarget.startswith('.'):
reftarget = reftarget[1:]
title = reftarget
refspecific = True
elif reftarget.startswith('~'):
reftarget = reftarget[1:]
title = reftarget.split('.')[-1]
elif suppress_prefix:
title = reftarget.split('.')[-1]
elif reftarget.startswith('typing.'):
title = reftarget[7:]
else:
title = reftarget
return 'obj', reftarget, title, refspecific
This would fix it for my application, but would break applications that create an attribute with the same name as a builtin, so would probably need a new configuration variable.
Another possible solution would be to get sphinx-autodoc2
to make all TypeVars or unions reftype "class" rather than "data", although that may also be wrong.
I hope you don't mind me tagging you @chrisjsewell but maybe as author of sphinx-autodoc2 you may have an opinion?
How to Reproduce
index
=====
.. py:module:: foo
.. py:data:: T
:value: TypeVar(...)
.. py:class:: Foo(t: T)
# conf.py
nitpicky = True
Environment Information
Platform: linux; (Linux-4.18.0-513.24.1.el8_9.x86_64-x86_64-with-glibc2.28)
Python version: 3.11.5 (main, Sep 22 2023, 15:34:29) [GCC 8.5.0 20210514 (Red Hat 8.5.0-20)])
Python implementation: CPython
Sphinx version: 8.2.0+/1ab62c9b0
Docutils version: 0.21.2
Jinja2 version: 3.1.5
Pygments version: 2.19.1
Sphinx extensions
[]
Additional context
I am working around this problem with the following in conf.py:
from sphinx import addnodes, application, environment
from sphinx.ext import intersphinx
# A custom handler for TypeVars and Unions
def missing_reference_handler(
app: application.Sphinx,
env: environment.BuildEnvironment,
node: addnodes.pending_xref,
contnode,
):
target = node["reftarget"]
if "." in target and node["reftype"] == "class":
# Try again as `obj` so we pick up Unions, TypeVars and other things
if target.startswith("ophyd_async"):
# Pick it up from our domain
domain = env.domains[node["refdomain"]]
refdoc = node.get("refdoc")
return domain.resolve_xref(
env, refdoc, app.builder, "obj", target, node, contnode
)
else:
# pass it to intersphinx with the right type
node["reftype"] = "obj"
return intersphinx.missing_reference(app, env, node, contnode)
def setup(app: application.Sphinx):
app.connect("missing-reference", missing_reference_handler)