diff --git a/docs/_relink_references.py b/docs/_relink_references.py new file mode 100644 index 00000000..75dc1b09 --- /dev/null +++ b/docs/_relink_references.py @@ -0,0 +1,111 @@ +# pylint: disable=import-error, import-outside-toplevel +# pyright: reportMissingImports=false +"""Abbreviated the annotations generated by sphinx-autodoc. + +It's not necessary to generate the full path of type hints, because they are +rendered as clickable links. + +See also https://github.com/sphinx-doc/sphinx/issues/5868. +""" + +from typing import List + +import sphinx.domains.python +from docutils import nodes +from sphinx.addnodes import pending_xref +from sphinx.environment import BuildEnvironment + +__TARGET_SUBSTITUTIONS = { + "HelicityAdapter": "ampform.kinematics.HelicityAdapter", + "ParameterValue": "tensorwaves.interface.ParameterValue", + "a set-like object providing a view on D's items": "typing.ItemsView", + "a set-like object providing a view on D's keys": "typing.KeysView", + "an object providing a view on D's values": "typing.ValuesView", + "sp.Expr": "sympy.core.expr.Expr", + "sp.Symbol": "sympy.core.symbol.Symbol", +} +__REF_TYPE_SUBSTITUTIONS = { + "None": "obj", + "tensorwaves.interface.InputType": "obj", + "tensorwaves.interface.OutputType": "obj", + "tensorwaves.interface.ParameterValue": "obj", +} + + +try: # Sphinx >=4.4.0 + # https://github.com/sphinx-doc/sphinx/blob/v4.4.0/sphinx/domains/python.py#L110-L133 + from sphinx.addnodes import pending_xref_condition + from sphinx.domains.python import parse_reftarget + + def _new_type_to_xref( + target: str, + env: BuildEnvironment = None, + suppress_prefix: bool = False, + ) -> pending_xref: + """Convert a type string to a cross reference node.""" + reftype, target, title, refspecific = parse_reftarget( + target, suppress_prefix + ) + target = __TARGET_SUBSTITUTIONS.get(target, target) + reftype = __REF_TYPE_SUBSTITUTIONS.get(target, reftype) + + assert env is not None + return pending_xref( + "", + *__create_nodes(env, title), + refdomain="py", + reftype=reftype, + reftarget=target, + refspecific=refspecific, + **__get_env_kwargs(env), + ) + +except ImportError: # Sphinx <4.4.0 + # https://github.com/sphinx-doc/sphinx/blob/v4.3.2/sphinx/domains/python.py#L83-L107 + def _new_type_to_xref( + target: str, + env: BuildEnvironment = None, + suppress_prefix: bool = False, + ) -> pending_xref: + # pylint: disable=unused-argument + """Convert a type string to a cross reference node.""" + if target == "None": + reftype = "obj" + else: + reftype = "class" + + target = __TARGET_SUBSTITUTIONS.get(target, target) + reftype = __REF_TYPE_SUBSTITUTIONS.get(target, reftype) + + assert env is not None + return pending_xref( + "", + *__create_nodes(env, target), + refdomain="py", + reftype=reftype, + reftarget=target, + **__get_env_kwargs(env), + ) + + +def __get_env_kwargs(env: BuildEnvironment) -> dict: + if env: + return { + "py:module": env.ref_context.get("py:module"), + "py:class": env.ref_context.get("py:class"), + } + return {} + + +def __create_nodes(env: BuildEnvironment, title: str) -> List[nodes.Node]: + short_name = title.split(".")[-1] + if env.config.python_use_unqualified_type_names: + return [ + pending_xref_condition("", short_name, condition="resolved"), + pending_xref_condition("", title, condition="*"), + ] + return [nodes.Text(short_name)] + + +def relink_references() -> None: + sphinx.domains.python.type_to_xref = _new_type_to_xref diff --git a/docs/abbreviate_signature.py b/docs/abbreviate_signature.py deleted file mode 100644 index 300ee48e..00000000 --- a/docs/abbreviate_signature.py +++ /dev/null @@ -1,73 +0,0 @@ -# cspell:ignore docutils -# pylint: disable=import-error -# pyright: reportMissingImports=false -"""Abbreviated the annotations generated by sphinx-autodoc. - -It's not necessary to generate the full path of type hints, because they are -rendered as clickable links. - -See also https://github.com/sphinx-doc/sphinx/issues/5868. -""" - -import sphinx.domains.python -from docutils import nodes -from sphinx import addnodes -from sphinx.environment import BuildEnvironment - - -def replace_link(text: str) -> str: - replacements = { - "HelicityAdapter": "ampform.kinematics.HelicityAdapter", - "ParameterValue": "tensorwaves.interface.ParameterValue", - "a set-like object providing a view on D's items": "typing.ItemsView", - "a set-like object providing a view on D's keys": "typing.KeysView", - "an object providing a view on D's values": "typing.ValuesView", - "sp.Expr": "sympy.core.expr.Expr", - "sp.Symbol": "sympy.core.symbol.Symbol", - } - for old, new in replacements.items(): - if text == old: - return new - return text - - -def new_type_to_xref( - text: str, env: BuildEnvironment = None -) -> addnodes.pending_xref: - """Convert a type string to a cross reference node.""" - if text == "None": - reftype = "obj" - else: - reftype = "class" - - if env: - kwargs = { - "py:module": env.ref_context.get("py:module"), - "py:class": env.ref_context.get("py:class"), - } - else: - kwargs = {} - - text = replace_link(text) - short_text = text.split(".")[-1] - - numpy_types = { - "numpy.typing.ArrayLike", - "numpy.typing.DTypeLike", - } - if text in numpy_types: - text = "numpy.typing" - reftype = "mod" - - return addnodes.pending_xref( - "", - nodes.Text(short_text), - refdomain="py", - reftype=reftype, - reftarget=text, - **kwargs, - ) - - -def abbreviate_signature() -> None: - sphinx.domains.python.type_to_xref = new_type_to_xref # type: ignore[assignment] diff --git a/docs/conf.py b/docs/conf.py index a8979cda..39f948ba 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -55,9 +55,9 @@ def fetch_logo(url: str, output_path: str) -> None: # -- Generate API ------------------------------------------------------------ sys.path.insert(0, os.path.abspath(".")) -from abbreviate_signature import abbreviate_signature # noqa: E402 +from _relink_references import relink_references # noqa: E402 -abbreviate_signature() +relink_references() shutil.rmtree("api", ignore_errors=True) subprocess.call( " ".join( @@ -129,6 +129,11 @@ def fetch_logo(url: str, output_path: str) -> None: ), } autodoc_member_order = "bysource" +autodoc_type_aliases = { + "InputType": "tensorwaves.interface.InputType", + "OutputType": "tensorwaves.interface.OutputType", + "ParameterValue": "tensorwaves.interface.ParameterValue", +} AUTODOC_INSERT_SIGNATURE_LINEBREAKS = False graphviz_output_format = "svg" html_copy_source = True # needed for download notebook button