-
Notifications
You must be signed in to change notification settings - Fork 0
Description
User request
Nitpick flags Literal annotation values as missing py:class
Describe the bug:
When a value is present in a type annotation as Literal, Sphinx treats the value as a py:class. With nitpick enabled, values like Literal[True] fail because True is not a class. This blocks builds using -n -W.
How to reproduce:
import typing
@typing.overload
def foo(x: "typing.Literal[True]") -> int: ...
@typing.overload
def foo(x: "typing.Literal[False]") -> str: ...
def foo(x: bool):
"""a func"""
return 1 if x else "foo"A failing example project: https://github.com/sirosen/repro/tree/master/sphinxdoc/literal (run ./doc.sh).
Expected behavior:
Literal[True] (or any literal value) should be present in the type annotation but should not trigger nitpick warnings.
Environment:
- OS: Linux
- Python: 3.8, 3.9
- Sphinx: 4.1.2
- Extensions: autodoc
Research specification (by Emerson Gray)
Summary:
- Literals are values (True/False/None, numbers, strings, enum members) and should not be cross-referenced as classes/objects under nitpicky mode. Current Python domain parsing wraps tokens from
Literal[...]into pending xrefs.
Proposed changes:
-
Modify
sphinx/domains/python.pyin_parse_annotationand itsunparsehelper to carry anin_literalflag when traversingtyping.Literal[...](andtyping_extensions.Literal[...]). Whenin_literalis true, emit plain literal text nodes for the arguments (e.g., booleans, numbers, strings, enum members) and do not createpending_xrefnodes for them.- Detect
Literalbase via AST (ast.Name(id='Literal')orast.Attribute(..., attr='Literal')). - Handle Python 3.8+ (
ast.Constant) and <3.8 (ast.NameConstant) appropriately. Preserve punctuation and ellipsis handling.
- Detect
-
Extend
PyXrefMixin.make_xrefs(same file) to suppress xref creation for tokens insideLiteral[...]when parsing typed docfields (e.g.,:type a: Literal[True, 1, "x", None]). Maintain xrefs for actual type names (Literal,Union,Annotated,bool, etc.).- Use a simple bracket depth state machine activated when encountering
Literal[or qualifiedtyping.Literal[/typing_extensions.Literal[.
- Use a simple bracket depth state machine activated when encountering
Compatibility:
- Works with
typing.Literalandtyping_extensions.Literalwithout import-time resolution. - Handles nested generics (
Union[Literal[True], bool]) andAnnotated[Literal[...], ...]. - Known limitation: aliases like
from typing import Literal as Lwon’t be detected asLiteral.
Tests to add:
- New tests under
tests/test_domain_py.pyand a dedicated root attests/roots/test-domain-py-literal/withnitpicky = True. Verify that:- Pending xrefs are created for type names (e.g.,
Literal,Union,Annotated,bool). - No xrefs or nitpicky warnings are created for literal values inside
Literal[...](True, 1, "x", None,SomeEnum.VALUE). - Include docfield case:
:type a: Literal["A", "B"].
- Pending xrefs are created for type names (e.g.,
Observed failure & reproduction (current behavior):
- Build a minimal project with nitpicky enabled and signatures using
Literal[...]. - Observed warnings before fix include (representative examples):
WARNING: py:class reference target not found: TrueWARNING: py:class reference target not found: 1WARNING: py:class reference target not found: 'x'WARNING: py:obj reference target not found: NoneWARNING: py:class reference target not found: SomeEnum.VALUE
These originate from unresolvedpending_xrefnodes created for the literal tokens.
Acceptance criteria:
- Under nitpicky mode, builds with
Literal[...]-containing annotations do not emit missing-reference warnings for literal values, while correctly cross-referencing actual types.