Skip to content

Commit

Permalink
docs: abbreviate type aliases in API (#207)
Browse files Browse the repository at this point in the history
* chore: simplify abbreviate_signature.py
* chore: rename 'hack' scripts for conf.py
* docs: improve RelativisticBreitWignerBuilder docstring
* docs: use autodoc_typehints_format='short'
* fix: match type_to_xref signature
* fix: correctly set TwoBodyKinematicVariableSet validators
  • Loading branch information
redeboer authored Jan 18, 2022
1 parent 2db5669 commit 964f255
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 82 deletions.
File renamed without changes.
108 changes: 108 additions & 0 deletions docs/_relink_references.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# 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 = {
"sp.Expr": "sympy.core.expr.Expr",
"sp.Symbol": "sympy.core.symbol.Symbol",
"typing_extensions.Protocol": "typing.Protocol",
}
__REF_TYPE_SUBSTITUTIONS = {
"None": "obj",
"ParameterValue": "obj",
"ampform.dynamics.builder.BuilderReturnType": "obj",
"symplot.RangeDefinition": "obj",
"symplot.Slider": "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
61 changes: 0 additions & 61 deletions docs/abbreviate_signature.py

This file was deleted.

15 changes: 11 additions & 4 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,11 @@ def fetch_logo(url: str, output_path: str) -> None:

# -- Generate API ------------------------------------------------------------
sys.path.insert(0, os.path.abspath("."))
import extend_docstrings # noqa: E402
from abbreviate_signature import abbreviate_signature # noqa: E402
from _extend_docstrings import insert_math # noqa: E402
from _relink_references import relink_references # noqa: E402

abbreviate_signature()
extend_docstrings.insert_math()
relink_references()
insert_math()

shutil.rmtree("api", ignore_errors=True)
subprocess.call(
Expand Down Expand Up @@ -153,6 +153,13 @@ def fetch_logo(url: str, output_path: str) -> None:
]
),
}
autodoc_type_aliases = {
"BuilderReturnType": "ampform.dynamics.builder.BuilderReturnType",
"ParameterValue": "ampform.helicity.ParameterValue",
"RangeDefinition": "symplot.RangeDefinition",
"Slider": "symplot.Slider",
}
autodoc_typehints_format = "short"
AUTODOC_INSERT_SIGNATURE_LINEBREAKS = False
graphviz_output_format = "svg"
html_copy_source = True # needed for download notebook button
Expand Down
30 changes: 16 additions & 14 deletions src/ampform/dynamics/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ class TwoBodyKinematicVariableSet:
amplitude model.
"""

incoming_state_mass: sp.Symbol = attr.ib(instance_of(sp.Symbol))
outgoing_state_mass1: sp.Symbol = attr.ib(instance_of(sp.Symbol))
outgoing_state_mass2: sp.Symbol = attr.ib(instance_of(sp.Symbol))
helicity_theta: sp.Symbol = attr.ib(instance_of(sp.Symbol))
helicity_phi: sp.Symbol = attr.ib(instance_of(sp.Symbol))
incoming_state_mass: sp.Symbol = attr.ib(validator=instance_of(sp.Symbol))
outgoing_state_mass1: sp.Symbol = attr.ib(validator=instance_of(sp.Symbol))
outgoing_state_mass2: sp.Symbol = attr.ib(validator=instance_of(sp.Symbol))
helicity_theta: sp.Symbol = attr.ib(validator=instance_of(sp.Symbol))
helicity_phi: sp.Symbol = attr.ib(validator=instance_of(sp.Symbol))
angular_momentum: Optional[int] = attr.ib(default=None)


Expand Down Expand Up @@ -66,21 +66,21 @@ class ResonanceDynamicsBuilder(Protocol):

def __call__(
self, resonance: Particle, variable_pool: TwoBodyKinematicVariableSet
) -> BuilderReturnType:
) -> "BuilderReturnType":
"""Formulate a dynamics `~sympy.core.expr.Expr` for this resonance."""
...


def create_non_dynamic(
resonance: Particle, variable_pool: TwoBodyKinematicVariableSet
) -> BuilderReturnType:
) -> "BuilderReturnType":
# pylint: disable=unused-argument
return (1, {})


def create_non_dynamic_with_ff(
resonance: Particle, variable_pool: TwoBodyKinematicVariableSet
) -> BuilderReturnType:
) -> "BuilderReturnType":
"""Generate (only) a Blatt-Weisskopf form factor for a two-body decay.
Returns the `~sympy.functions.elementary.miscellaneous.sqrt` of a
Expand Down Expand Up @@ -109,15 +109,17 @@ def create_non_dynamic_with_ff(


class RelativisticBreitWignerBuilder:
"""Factory for building a `.relativistic_breit_wigner_with_ff`.
"""Factory for building relativistic Breit-Wigner expressions.
The :meth:`__call__` of this builder complies with the
`.ResonanceDynamicsBuilder`, so instances of this class can be used in
:meth:`.set_dynamics`.
Args:
form_factor: Formulate a relativistic Breit-Wigner function with form
factor, using :func:`.relativistic_breit_wigner_with_ff`.
factor, using :func:`.relativistic_breit_wigner_with_ff`. If set to
`False`, :meth:`__call__` builds a
:func:`.relativistic_breit_wigner` (_without_ form factor).
phsp_factor: A class that complies with the
`.PhaseSpaceFactorProtocol`. Defaults to `.PhaseSpaceFactor`.
"""
Expand All @@ -134,16 +136,16 @@ def __init__(

def __call__(
self, resonance: Particle, variable_pool: TwoBodyKinematicVariableSet
) -> BuilderReturnType:
"""Build an relativistic Breit-Wigner expression."""
) -> "BuilderReturnType":
"""Formulate a relativistic Breit-Wigner for this resonance."""
if self.__with_form_factor:
return self.__formulate_with_form_factor(resonance, variable_pool)
return self.__formulate(resonance, variable_pool)

@staticmethod
def __formulate(
resonance: Particle, variable_pool: TwoBodyKinematicVariableSet
) -> BuilderReturnType:
) -> "BuilderReturnType":
inv_mass = variable_pool.incoming_state_mass
res_mass = sp.Symbol(f"m_{resonance.name}")
res_width = sp.Symbol(f"Gamma_{resonance.name}")
Expand All @@ -160,7 +162,7 @@ def __formulate(

def __formulate_with_form_factor(
self, resonance: Particle, variable_pool: TwoBodyKinematicVariableSet
) -> BuilderReturnType:
) -> "BuilderReturnType":
if variable_pool.angular_momentum is None:
raise ValueError(
"Angular momentum is not defined but is required in the"
Expand Down
8 changes: 5 additions & 3 deletions src/symplot/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,12 @@


Slider = Union[FloatSlider, IntSlider]
"""Allowed :doc:`ipywidgets <ipywidgets:index>` slider types."""
RangeDefinition = Union[
Tuple[float, float],
Tuple[float, float, Union[float, int]],
]
"""Types of range definitions used in :meth:`.set_ranges`."""


class SliderKwargs(abc.Mapping):
Expand Down Expand Up @@ -113,7 +115,7 @@ def _verify_arguments(
f'Slider "{name}" is not a valid ipywidgets slider'
)

def __getitem__(self, key: Union[str, sp.Symbol]) -> Slider:
def __getitem__(self, key: Union[str, sp.Symbol]) -> "Slider":
"""Get slider by symbol, symbol name, or argument name."""
if isinstance(key, sp.Symbol):
key = key.name
Expand Down Expand Up @@ -179,7 +181,7 @@ def set_values(self, *args: Dict[str, float], **kwargs: float) -> None:
continue

def set_ranges( # noqa: R701
self, *args: Dict[str, RangeDefinition], **kwargs: RangeDefinition
self, *args: Dict[str, RangeDefinition], **kwargs: "RangeDefinition"
) -> None:
"""Set min, max and (optionally) the nr of steps for each slider.
Expand Down Expand Up @@ -290,7 +292,7 @@ def prepare_sliders(
return lambdified_expression, sliders


def create_slider(symbol: sp.Symbol) -> Slider:
def create_slider(symbol: sp.Symbol) -> "Slider":
r"""Create an `int` or `float` slider, depending on Symbol assumptions.
The description for the slider is rendered as LaTeX from the
Expand Down

0 comments on commit 964f255

Please sign in to comment.