Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor!: generalize STG to MutableTransition #156

Merged
merged 34 commits into from
Feb 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
37f1b94
refactor: make MutableTopology public
redeboer Feb 21, 2022
fe2adc2
refactor: remove kw_only from MutableTopology
redeboer Feb 21, 2022
113df7a
chore: simplify (over)defined_assert functions
redeboer Feb 21, 2022
accc555
chore: rewrite StateTransitionGraph with attrs
redeboer Feb 21, 2022
b350fe1
ci: disable pylint line-too-long
redeboer Feb 21, 2022
2a1a261
refactor: remove get_edge/node_props()
redeboer Feb 21, 2022
6e5240a
refactor: remove StateTransitionGraph.evolve()
redeboer Feb 21, 2022
7b00d5c
refactor: make NodeType of StateTransitionGraph generic
redeboer Feb 21, 2022
9d1a3fb
refactor: rename STG to MutableTransition
redeboer Feb 21, 2022
fee55c3
feat: define (frozen) Transition
redeboer Feb 21, 2022
f050ace
refactor: rename edge/node_props to states/interactions
redeboer Feb 21, 2022
0bcf9c4
refactor: rename GraphSettings attrs to states/interactions
redeboer Feb 21, 2022
ba6e385
refactor: rename QuantumNumberSolution attrs to states/interactions
redeboer Feb 21, 2022
6c2f1a2
feat: define Transition Protocol and simplify _dot
redeboer Feb 21, 2022
1e9c2c7
fix: add default values for MutableTransition
redeboer Feb 21, 2022
17af25f
refactor: remove GraphSettings, GraphElementProperties, QuantumNumber…
redeboer Feb 21, 2022
a45c2f9
feat: implement initial_states etc in FrozenTransition
redeboer Feb 21, 2022
fb68b72
refactor: remove StateTransition class
redeboer Feb 21, 2022
f0cd48d
refactor: remove ReactionInfo.from/to_graphs()
redeboer Feb 21, 2022
4e32818
docs: improve API rendering with autodoc_type_aliases
redeboer Feb 21, 2022
b5a4524
refactor: write Transition as ABC
redeboer Feb 21, 2022
2e5be8b
refactor: change initial_states etc into mixin methods
redeboer Feb 21, 2022
4c51903
fix: return NewNodeType in FrozenTransition convert()
redeboer Feb 21, 2022
195151e
ci: do not fast-fail test jobs
redeboer Feb 21, 2022
140843a
fix: support generic Transition class for py3.6
redeboer Feb 21, 2022
071ddbe
fix: ignore logo.svg in sphinx-autobuild
redeboer Feb 21, 2022
eae98c9
refactor: hide TypeVars from topology API
redeboer Feb 21, 2022
cc412aa
docs: hide dict methods from API
redeboer Feb 21, 2022
c5fe2c7
fix: improve argument type hints Topology
redeboer Feb 21, 2022
cf9efab
fix: improve argument type hints MutableTopology
redeboer Feb 21, 2022
c6ccd6f
refactor: move organize_edge_ids to MutableTopology
redeboer Feb 21, 2022
38d7750
refactor: sort output of create_isobar_topologies
redeboer Feb 21, 2022
faecd1c
ci: ignore tmp files in sphinx-autobuild
redeboer Feb 21, 2022
9d8e0b2
docs: improve docstrings of topology module
redeboer Feb 21, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ ignore =
W503
extend-select =
TI100
per-file-ignores =
# casts with generics
src/qrules/topology.py:E731
rst-roles =
attr
cite
Expand All @@ -44,6 +47,8 @@ rst-roles =
mod
ref
rst-directives =
autolink-preface
automethod
deprecated
envvar
exception
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/ci-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ jobs:
name: Unit tests
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os:
- macos-11
Expand Down
2 changes: 2 additions & 0 deletions .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@ ignore-patterns=
disable=
duplicate-code, # https://github.com/PyCQA/pylint/issues/214
invalid-unary-operand-type, # conflicts with attrs.field
line-too-long, # automatically fixed with black
logging-fstring-interpolation,
missing-class-docstring, # pydocstyle
missing-function-docstring, # pydocstyle
missing-module-docstring, # pydocstyle
no-member, # conflicts with attrs.field
no-name-in-module, # already checked by mypy
not-an-iterable, # conflicts with attrs.field
not-callable, # conflicts with attrs.field
redefined-builtin, # flake8-built
Expand Down
1 change: 1 addition & 0 deletions docs/.gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
*.doctree
*.inv
*build/
_images/*
api/

!_static/*
Expand Down
130 changes: 130 additions & 0 deletions docs/_extend_docstrings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# flake8: noqa
# pylint: disable=import-error,import-outside-toplevel,invalid-name,protected-access
# pyright: reportMissingImports=false
"""Extend docstrings of the API.

This small script is used by ``conf.py`` to dynamically modify docstrings.
"""

import inspect
import logging
import textwrap
from typing import Callable, Dict, Optional, Type, Union

import qrules

logging.getLogger().setLevel(logging.ERROR)


def extend_docstrings() -> None:
script_name = __file__.rsplit("/", maxsplit=1)[-1]
script_name = ".".join(script_name.split(".")[:-1])
definitions = dict(globals())
for name, definition in definitions.items():
module = inspect.getmodule(definition)
if module is None:
continue
if module.__name__ not in {"__main__", script_name}:
continue
if not inspect.isfunction(definition):
continue
if not name.startswith("extend_"):
continue
if name == "extend_docstrings":
continue
function_arguments = inspect.signature(definition).parameters
if len(function_arguments):
raise ValueError(
f"Local function {name} should not have a signature"
)
definition()


def extend_create_isobar_topologies() -> None:
from qrules.topology import create_isobar_topologies

topologies = qrules.topology.create_isobar_topologies(4)
dot_renderings = map(
lambda t: qrules.io.asdot(t, render_resonance_id=True),
topologies,
)
images = [_graphviz_to_image(dot, indent=6) for dot in dot_renderings]
_append_to_docstring(
create_isobar_topologies,
f"""

.. panels::
:body: text-center
{images[0]}

---
{images[1]}
""",
)


def extend_create_n_body_topology() -> None:
from qrules.topology import create_n_body_topology

topology = create_n_body_topology(
number_of_initial_states=2,
number_of_final_states=5,
)
dot = qrules.io.asdot(topology, render_initial_state_id=True)
_append_to_docstring(
create_n_body_topology,
_graphviz_to_image(dot, indent=4),
)


def extend_Topology() -> None:
from qrules.topology import Topology, create_isobar_topologies

topologies = create_isobar_topologies(number_of_final_states=3)
dot = qrules.io.asdot(
topologies[0],
render_initial_state_id=True,
render_resonance_id=True,
)
_append_to_docstring(
Topology,
_graphviz_to_image(dot, indent=4),
)


def _append_to_docstring(
class_type: Union[Callable, Type], appended_text: str
) -> None:
assert class_type.__doc__ is not None
class_type.__doc__ += appended_text


_GRAPHVIZ_COUNTER = 0
_IMAGE_DIR = "_images"


def _graphviz_to_image( # pylint: disable=too-many-arguments
dot: str,
options: Optional[Dict[str, str]] = None,
format: str = "svg",
indent: int = 0,
caption: str = "",
label: str = "",
) -> str:
import graphviz # type: ignore[import]

if options is None:
options = {}
global _GRAPHVIZ_COUNTER # pylint: disable=global-statement
output_file = f"graphviz_{_GRAPHVIZ_COUNTER}"
_GRAPHVIZ_COUNTER += 1
graphviz.Source(dot).render(f"{_IMAGE_DIR}/{output_file}", format=format)
restructuredtext = "\n"
if label:
restructuredtext += f".. _{label}:\n"
restructuredtext += f".. figure:: /{_IMAGE_DIR}/{output_file}.{format}\n"
for option, value in options.items():
restructuredtext += f" :{option}: {value}\n"
if caption:
restructuredtext += f"\n {caption}\n"
return textwrap.indent(restructuredtext, indent * " ")
102 changes: 83 additions & 19 deletions docs/_relink_references.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,51 @@
See also https://github.com/sphinx-doc/sphinx/issues/5868.
"""

from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, List

if TYPE_CHECKING:
from docutils import nodes
from sphinx.addnodes import pending_xref
from sphinx.environment import BuildEnvironment


__TARGET_SUBSTITUTIONS = {
"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",
"typing_extensions.Protocol": "typing.Protocol",
"typing_extensions.TypeAlias": "typing.TypeAlias",
}
__REF_TYPE_SUBSTITUTIONS = {
"None": "obj",
"qrules.combinatorics.InitialFacts": "obj",
"qrules.quantum_numbers.EdgeQuantumNumbers.baryon_number": "obj",
"qrules.quantum_numbers.EdgeQuantumNumbers.bottomness": "obj",
"qrules.quantum_numbers.EdgeQuantumNumbers.c_parity": "obj",
"qrules.quantum_numbers.EdgeQuantumNumbers.charge": "obj",
"qrules.quantum_numbers.EdgeQuantumNumbers.charmness": "obj",
"qrules.quantum_numbers.EdgeQuantumNumbers.electron_lepton_number": "obj",
"qrules.quantum_numbers.EdgeQuantumNumbers.g_parity": "obj",
"qrules.quantum_numbers.EdgeQuantumNumbers.isospin_magnitude": "obj",
"qrules.quantum_numbers.EdgeQuantumNumbers.isospin_projection": "obj",
"qrules.quantum_numbers.EdgeQuantumNumbers.mass": "obj",
"qrules.quantum_numbers.EdgeQuantumNumbers.muon_lepton_number": "obj",
"qrules.quantum_numbers.EdgeQuantumNumbers.parity": "obj",
"qrules.quantum_numbers.EdgeQuantumNumbers.pid": "obj",
"qrules.quantum_numbers.EdgeQuantumNumbers.spin_magnitude": "obj",
"qrules.quantum_numbers.EdgeQuantumNumbers.spin_projection": "obj",
"qrules.quantum_numbers.EdgeQuantumNumbers.strangeness": "obj",
"qrules.quantum_numbers.EdgeQuantumNumbers.tau_lepton_number": "obj",
"qrules.quantum_numbers.EdgeQuantumNumbers.topness": "obj",
"qrules.quantum_numbers.EdgeQuantumNumbers.width": "obj",
"qrules.quantum_numbers.NodeQuantumNumbers.l_magnitude": "obj",
"qrules.quantum_numbers.NodeQuantumNumbers.l_projection": "obj",
"qrules.quantum_numbers.NodeQuantumNumbers.parity_prefactor": "obj",
"qrules.quantum_numbers.NodeQuantumNumbers.s_magnitude": "obj",
"qrules.quantum_numbers.NodeQuantumNumbers.s_projection": "obj",
"qrules.solving.GraphElementProperties": "obj",
"qrules.solving.GraphSettings": "obj",
"qrules.transition.StateTransition": "obj",
"typing.TypeAlias": "obj",
}


Expand All @@ -31,34 +61,68 @@ def _new_type_to_xref(
env: "BuildEnvironment" = None,
suppress_prefix: bool = False,
) -> "pending_xref":
if env:
kwargs = {
"py:module": env.ref_context.get("py:module"),
"py:class": env.ref_context.get("py:class"),
}
else:
kwargs = {}
import sphinx
from sphinx.addnodes import pending_xref

target = __TARGET_SUBSTITUTIONS.get(target, target)
reftype = __REF_TYPE_SUBSTITUTIONS.get(target, "class")
if suppress_prefix:
short_text = target.split(".")[-1]
else:
short_text = target
if sphinx.version_info >= (4, 4):
# https://github.com/sphinx-doc/sphinx/blob/v4.4.0/sphinx/domains/python.py#L110-L133
from sphinx.domains.python import ( # type: ignore[attr-defined]
parse_reftarget,
)

from docutils.nodes import Text
from sphinx.addnodes import pending_xref
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),
)

# Sphinx <4.4.0
# https://github.com/sphinx-doc/sphinx/blob/v4.3.2/sphinx/domains/python.py#L83-L107
target = __TARGET_SUBSTITUTIONS.get(target, target)
reftype = __REF_TYPE_SUBSTITUTIONS.get(target, "class")
assert env is not None
return pending_xref(
"",
Text(short_text),
*__create_nodes(env, target),
refdomain="py",
reftype=reftype,
reftarget=target,
**kwargs,
**__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]":
from docutils import nodes
from sphinx.addnodes import pending_xref_condition

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:
import sphinx.domains.python

Expand Down
34 changes: 24 additions & 10 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,12 @@ def fetch_logo(url: str, output_path: str) -> None:

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

extend_docstrings()
relink_references()

shutil.rmtree("api", ignore_errors=True)
subprocess.call(
" ".join(
Expand Down Expand Up @@ -164,17 +167,29 @@ def fetch_logo(url: str, output_path: str) -> None:
# General sphinx settings
add_module_names = False
autodoc_default_options = {
"exclude-members": ", ".join(
[
"items",
"keys",
"values",
]
),
"members": True,
"undoc-members": True,
"show-inheritance": True,
"special-members": ", ".join(
[
"__call__",
"__getitem__",
]
),
}
autodoc_member_order = "bysource"
autodoc_type_aliases = {
"GraphElementProperties": "qrules.solving.GraphElementProperties",
"GraphSettings": "qrules.solving.GraphSettings",
"InitialFacts": "qrules.combinatorics.InitialFacts",
"StateTransition": "qrules.transition.StateTransition",
}
autodoc_typehints_format = "short"
codeautolink_concat_default = True
AUTODOC_INSERT_SIGNATURE_LINEBREAKS = True
Expand Down Expand Up @@ -220,15 +235,14 @@ def fetch_logo(url: str, output_path: str) -> None:
default_role = "py:obj"
primary_domain = "py"
nitpicky = True # warn if cross-references are missing
nitpick_ignore = [
("py:class", "EdgeType"),
("py:class", "NoneType"),
("py:class", "StateTransitionGraph"),
("py:class", "ValueType"),
("py:class", "json.encoder.JSONEncoder"),
("py:class", "typing_extensions.Protocol"),
("py:obj", "qrules.topology._K"),
("py:obj", "qrules.topology._V"),
nitpick_ignore_regex = [
(r"py:(class|obj)", "json.encoder.JSONEncoder"),
(r"py:(class|obj)", r"(qrules\.topology\.)?EdgeType"),
(r"py:(class|obj)", r"(qrules\.topology\.)?KT"),
(r"py:(class|obj)", r"(qrules\.topology\.)?NewEdgeType"),
(r"py:(class|obj)", r"(qrules\.topology\.)?NewNodeType"),
(r"py:(class|obj)", r"(qrules\.topology\.)?NodeType"),
(r"py:(class|obj)", r"(qrules\.topology\.)?VT"),
]


Expand Down
Loading