From f1d1fc0e097ffd363a618c23f273fa4580a62b53 Mon Sep 17 00:00:00 2001 From: grayson-helmholz <107720976+grayson-helmholz@users.noreply.github.com> Date: Fri, 11 Oct 2024 17:57:14 +0200 Subject: [PATCH] MAINT: drop Python 3.7 & 3.8 support due to EOL (#291) --- .constraints/py3.7.txt | 199 ------------------ .constraints/py3.8.txt | 198 ----------------- docs/conf.py | 1 + pyproject.toml | 24 +-- src/qrules/__init__.py | 5 +- src/qrules/argument_handling.py | 22 +- src/qrules/combinatorics.py | 14 +- src/qrules/conservation_rules.py | 101 +++++---- src/qrules/io/_dot.py | 4 +- src/qrules/particle.py | 14 +- src/qrules/quantum_numbers.py | 9 +- src/qrules/settings.py | 4 +- src/qrules/solving.py | 9 +- src/qrules/system_control.py | 6 +- src/qrules/topology.py | 32 +-- src/qrules/transition.py | 9 +- tests/conftest.py | 7 +- .../conservation_rules/test_clebsch_gordan.py | 4 +- tests/unit/conservation_rules/test_spin.py | 14 +- tests/unit/test_particle.py | 7 +- tests/unit/test_system_control.py | 7 +- 21 files changed, 119 insertions(+), 571 deletions(-) delete mode 100644 .constraints/py3.7.txt delete mode 100644 .constraints/py3.8.txt diff --git a/.constraints/py3.7.txt b/.constraints/py3.7.txt deleted file mode 100644 index 180c4aa5..00000000 --- a/.constraints/py3.7.txt +++ /dev/null @@ -1,199 +0,0 @@ -# This file was autogenerated by uv via the following command: -# uv pip compile pyproject.toml -o .constraints/py3.7.txt --all-extras --no-annotate --python-version=3.7 --no-emit-package setuptools -accessible-pygments==0.0.4 -aiofiles==22.1.0 -aiosqlite==0.19.0 -alabaster==0.7.13 -anyio==3.7.1 -argon2-cffi==23.1.0 -argon2-cffi-bindings==21.2.0 -arrow==1.2.3 -attrs==24.2.0 -babel==2.14.0 -backcall==0.2.0 -beautifulsoup4==4.12.3 -bleach==6.0.0 -cached-property==1.5.2 -cachetools==5.5.0 -cattrs==23.1.2 -certifi==2024.8.30 -cffi==1.15.1 -cfgv==3.3.1 -chardet==5.2.0 -charset-normalizer==3.3.2 -click==8.1.7 -colorama==0.4.6 -comm==0.1.4 -coverage==7.2.7 -debugpy==1.7.0 -decorator==5.1.1 -defusedxml==0.7.1 -distlib==0.3.8 -docstring-to-markdown==0.15 -docutils==0.17.1 -entrypoints==0.4 -exceptiongroup==1.2.2 -execnet==2.0.2 -fastjsonschema==2.20.0 -filelock==3.12.2 -fqdn==1.5.1 -gitdb==4.0.11 -gitpython==3.1.43 -gprof2dot==2022.7.29 -graphviz==0.20.1 -greenlet==3.1.0 -hepunits==2.3.3 -identify==2.5.24 -idna==3.8 -imagesize==1.4.1 -importlib-metadata==6.7.0 -importlib-resources==5.12.0 -iniconfig==2.0.0 -ipykernel==6.16.2 -ipython==7.34.0 -ipython-genutils==0.2.0 -ipywidgets==8.1.5 -isoduration==20.11.0 -jedi==0.18.2 -jinja2==3.1.4 -json5==0.9.16 -jsonpointer==3.0.0 -jsonschema==4.17.3 -jupyter-cache==0.5.0 -jupyter-client==7.4.9 -jupyter-core==4.12.0 -jupyter-events==0.6.3 -jupyter-lsp==1.5.1 -jupyter-server==1.24.0 -jupyter-server-fileid==0.9.3 -jupyter-server-mathjax==0.2.6 -jupyter-server-ydoc==0.8.0 -jupyter-ydoc==0.2.5 -jupyterlab==3.6.8 -jupyterlab-code-formatter==3.0.2 -jupyterlab-git==0.44.0 -jupyterlab-lsp==3.10.2 -jupyterlab-myst==1.2.0 -jupyterlab-pygments==0.2.2 -jupyterlab-server==2.24.0 -jupyterlab-widgets==3.0.13 -latexcodec==3.0.0 -livereload==2.7.0 -lsprotocol==2023.0.1 -markdown-it-py==2.2.0 -markupsafe==2.1.5 -matplotlib-inline==0.1.6 -mdit-py-plugins==0.3.5 -mdurl==0.1.2 -mistune==3.0.2 -mypy==1.4.1 -mypy-extensions==1.0.0 -myst-nb==0.17.2 -myst-parser==0.18.1 -nbclassic==1.1.0 -nbclient==0.5.13 -nbconvert==7.6.0 -nbdime==3.2.1 -nbformat==5.8.0 -nbmake==1.2.1 -nest-asyncio==1.6.0 -nodeenv==1.9.1 -notebook==6.5.7 -notebook-shim==0.2.4 -packaging==24.0 -pandocfilters==1.5.1 -parso==0.8.4 -particle==0.23.1 -pexpect==4.9.0 -pickleshare==0.7.5 -pkgutil-resolve-name==1.3.10 -platformdirs==4.0.0 -pluggy==1.2.0 -pre-commit==2.21.0 -prometheus-client==0.17.1 -prompt-toolkit==3.0.47 -psutil==6.0.0 -ptyprocess==0.7.0 -pybtex==0.24.0 -pybtex-docutils==1.0.3 -pycparser==2.21 -pydantic==1.10.18 -pydata-sphinx-theme==0.13.3 -pydot==1.4.2 -pygments==2.17.2 -pyparsing==3.1.4 -pyproject-api==1.5.3 -pyrsistent==0.19.3 -pytest==7.4.4 -pytest-cov==4.1.0 -pytest-profiling==1.7.0 -pytest-xdist==3.5.0 -python-constraint==1.4.0 -python-dateutil==2.9.0.post0 -python-json-logger==2.0.7 -python-lsp-jsonrpc==1.0.0 -python-lsp-ruff==1.6.0 -python-lsp-server==1.7.4 -pytoolconfig==1.3.0 -pytz==2024.1 -pyyaml==6.0.1 -pyzmq==26.2.0 -requests==2.31.0 -rfc3339-validator==0.1.4 -rfc3986-validator==0.1.1 -rope==1.9.0 -ruff==0.1.15 -send2trash==1.8.3 -six==1.16.0 -smmap==5.0.1 -sniffio==1.3.1 -snowballstemmer==2.2.0 -soupsieve==2.4.1 -sphinx==5.3.0 -sphinx-api-relink==0.0.9 -sphinx-autobuild==2021.3.14 -sphinx-book-theme==1.0.1 -sphinx-codeautolink==0.15.2 -sphinx-comments==0.0.3 -sphinx-copybutton==0.5.2 -sphinx-design==0.4.1 -sphinx-hep-pdgref==0.2.0 -sphinx-pybtex-etal-style==0.0.2 -sphinx-thebe==0.2.1 -sphinx-togglebutton==0.3.2 -sphinxcontrib-applehelp==1.0.2 -sphinxcontrib-bibtex==2.6.2 -sphinxcontrib-devhelp==1.0.2 -sphinxcontrib-htmlhelp==2.0.0 -sphinxcontrib-jsmath==1.0.1 -sphinxcontrib-qthelp==1.0.3 -sphinxcontrib-serializinghtml==1.1.5 -sqlalchemy==1.4.54 -tabulate==0.9.0 -terminado==0.17.1 -tinycss2==1.2.1 -tomli==2.0.1 -tornado==6.2 -tox==4.8.0 -tqdm==4.66.5 -traitlets==5.9.0 -typed-ast==1.5.5 -types-pyyaml==6.0.12.12 -types-setuptools==69.0.0.0 -typing-extensions==4.7.1 -ujson==5.7.0 -uri-template==1.3.0 -urllib3==2.0.7 -virtualenv==20.26.4 -wcwidth==0.2.13 -webcolors==1.13 -webencodings==0.5.1 -websocket-client==1.6.1 -wheel==0.42.0 -widgetsnbextension==4.0.13 -y-py==0.6.2 -ypy-websocket==0.8.4 -zipp==3.15.0 - -# The following packages were excluded from the output: -# setuptools diff --git a/.constraints/py3.8.txt b/.constraints/py3.8.txt deleted file mode 100644 index e5a2358c..00000000 --- a/.constraints/py3.8.txt +++ /dev/null @@ -1,198 +0,0 @@ -# This file was autogenerated by uv via the following command: -# uv pip compile pyproject.toml -o .constraints/py3.8.txt --all-extras --no-annotate --python-version=3.8 --no-emit-package setuptools -accessible-pygments==0.0.4 -alabaster==0.7.13 -anyio==4.4.0 -argon2-cffi==23.1.0 -argon2-cffi-bindings==21.2.0 -arrow==1.3.0 -asttokens==2.4.1 -async-lru==2.0.4 -attrs==24.2.0 -babel==2.16.0 -backcall==0.2.0 -beautifulsoup4==4.12.3 -bleach==6.1.0 -cachetools==5.5.0 -cattrs==24.1.0 -certifi==2024.8.30 -cffi==1.17.1 -cfgv==3.4.0 -chardet==5.2.0 -charset-normalizer==3.3.2 -click==8.1.7 -colorama==0.4.6 -comm==0.2.2 -coverage==7.6.1 -debugpy==1.8.5 -decorator==5.1.1 -defusedxml==0.7.1 -distlib==0.3.8 -docstring-to-markdown==0.15 -docutils==0.17.1 -exceptiongroup==1.2.2 -execnet==2.1.1 -executing==2.1.0 -fastjsonschema==2.20.0 -filelock==3.16.0 -fqdn==1.5.1 -gitdb==4.0.11 -gitpython==3.1.43 -gprof2dot==2024.6.6 -graphviz==0.20.3 -greenlet==3.1.0 -h11==0.14.0 -hepunits==2.3.4 -httpcore==1.0.5 -httpx==0.27.2 -identify==2.6.0 -idna==3.8 -imagesize==1.4.1 -importlib-metadata==8.4.0 -importlib-resources==6.4.5 -iniconfig==2.0.0 -ipykernel==6.29.5 -ipython==8.12.3 -ipywidgets==8.1.5 -isoduration==20.11.0 -jedi==0.19.1 -jinja2==3.1.4 -json5==0.9.25 -jsonpointer==3.0.0 -jsonschema==4.23.0 -jsonschema-specifications==2023.12.1 -jupyter-cache==0.6.1 -jupyter-client==8.6.2 -jupyter-core==5.7.2 -jupyter-events==0.10.0 -jupyter-lsp==2.2.5 -jupyter-server==2.14.2 -jupyter-server-mathjax==0.2.6 -jupyter-server-terminals==0.5.3 -jupyterlab==4.2.5 -jupyterlab-code-formatter==3.0.2 -jupyterlab-git==0.50.1 -jupyterlab-lsp==5.1.0 -jupyterlab-myst==2.4.2 -jupyterlab-pygments==0.3.0 -jupyterlab-server==2.27.3 -jupyterlab-widgets==3.0.13 -latexcodec==3.0.0 -livereload==2.7.0 -lsprotocol==2023.0.1 -markdown-it-py==2.2.0 -markupsafe==2.1.5 -matplotlib-inline==0.1.7 -mdit-py-plugins==0.3.5 -mdurl==0.1.2 -mistune==3.0.2 -mypy==1.11.2 -mypy-extensions==1.0.0 -myst-nb==0.17.2 -myst-parser==0.18.1 -nbclient==0.6.8 -nbconvert==7.16.4 -nbdime==4.0.2 -nbformat==5.10.4 -nbmake==1.5.4 -nest-asyncio==1.6.0 -nodeenv==1.9.1 -notebook-shim==0.2.4 -overrides==7.7.0 -packaging==24.1 -pandocfilters==1.5.1 -parso==0.8.4 -particle==0.25.0 -pexpect==4.9.0 -pickleshare==0.7.5 -pkgutil-resolve-name==1.3.10 -platformdirs==4.3.2 -pluggy==1.5.0 -pre-commit==3.5.0 -prometheus-client==0.20.0 -prompt-toolkit==3.0.47 -psutil==6.0.0 -ptyprocess==0.7.0 -pure-eval==0.2.3 -pybtex==0.24.0 -pybtex-docutils==1.0.3 -pycparser==2.22 -pydata-sphinx-theme==0.14.4 -pydot==1.4.2 -pygments==2.18.0 -pyparsing==3.1.4 -pyproject-api==1.7.1 -pytest==8.3.3 -pytest-cov==5.0.0 -pytest-profiling==1.7.0 -pytest-xdist==3.6.1 -python-constraint2==2.0.0b5 -python-dateutil==2.9.0.post0 -python-json-logger==2.0.7 -python-lsp-jsonrpc==1.1.2 -python-lsp-ruff==2.2.2 -python-lsp-server==1.12.0 -pytoolconfig==1.3.1 -pytz==2024.1 -pyyaml==6.0.2 -pyzmq==26.2.0 -referencing==0.35.1 -requests==2.32.3 -rfc3339-validator==0.1.4 -rfc3986-validator==0.1.1 -rope==1.13.0 -rpds-py==0.20.0 -ruff==0.6.4 -send2trash==1.8.3 -six==1.16.0 -smmap==5.0.1 -sniffio==1.3.1 -snowballstemmer==2.2.0 -soupsieve==2.6 -sphinx==5.3.0 -sphinx-api-relink==0.0.9 -sphinx-autobuild==2021.3.14 -sphinx-book-theme==1.0.1 -sphinx-codeautolink==0.15.2 -sphinx-comments==0.0.3 -sphinx-copybutton==0.5.2 -sphinx-design==0.5.0 -sphinx-hep-pdgref==0.2.0 -sphinx-pybtex-etal-style==0.0.2 -sphinx-thebe==0.3.1 -sphinx-togglebutton==0.3.2 -sphinxcontrib-applehelp==1.0.4 -sphinxcontrib-bibtex==2.6.2 -sphinxcontrib-devhelp==1.0.2 -sphinxcontrib-htmlhelp==2.0.1 -sphinxcontrib-jsmath==1.0.1 -sphinxcontrib-qthelp==1.0.3 -sphinxcontrib-serializinghtml==1.1.5 -sqlalchemy==2.0.34 -stack-data==0.6.3 -tabulate==0.9.0 -terminado==0.18.1 -tinycss2==1.3.0 -tomli==2.0.1 -tornado==6.4.1 -tox==4.18.1 -tqdm==4.66.5 -traitlets==5.14.3 -types-python-dateutil==2.9.0.20240906 -types-pyyaml==6.0.12.20240808 -types-setuptools==74.1.0.20240907 -typing-extensions==4.12.2 -ujson==5.10.0 -uri-template==1.3.0 -urllib3==2.2.2 -virtualenv==20.26.4 -wcwidth==0.2.13 -webcolors==24.8.0 -webencodings==0.5.1 -websocket-client==1.8.0 -wheel==0.44.0 -widgetsnbextension==4.0.13 -zipp==3.20.1 - -# The following packages were excluded from the output: -# setuptools diff --git a/docs/conf.py b/docs/conf.py index 971f053c..d395ae1e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -49,6 +49,7 @@ "qrules.topology.NodeType": "typing.TypeVar", "SpinFormalism": ("obj", "qrules.transition.SpinFormalism"), "StateDefinition": ("obj", "qrules.combinatorics.StateDefinition"), + "StateTransition": ("obj", "qrules.transition.StateTransition"), "typing.Literal[-1, 1]": "typing.Literal", } api_target_types: dict[str, str | tuple[str, str]] = { diff --git a/pyproject.toml b/pyproject.toml index ebca45f6..a2fe9749 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,8 +18,6 @@ classifiers = [ "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python", "Topic :: Scientific/Engineering :: Physics", @@ -31,10 +29,8 @@ dependencies = [ "attrs >=20.1.0", # on_setattr and https://www.attrs.org/en/stable/api.html#next-gen "jsonschema", "particle", + "python-constraint2", "tqdm >=4.24.0", # autonotebook - 'python-constraint2; python_version >="3.8.0"', - 'python-constraint; python_version <"3.8.0"', - 'typing-extensions; python_version <"3.8.0"', # Literal, Protocol ] description = "Rule-based particle reaction problem solver on a quantum number level" dynamic = ["version"] @@ -50,7 +46,7 @@ keywords = [ license = {file = "LICENSE"} maintainers = [{email = "compwa-admin@ep1.rub.de"}] name = "qrules" -requires-python = ">=3.7" +requires-python = ">=3.9" [project.optional-dependencies] all = [ @@ -69,33 +65,33 @@ doc = [ "Sphinx >=3", "myst-nb", # nb_ configuration prefixes "qrules[viz]", + "sphinx-api-relink >=0.0.4", "sphinx-book-theme", "sphinx-codeautolink[ipython]", "sphinx-comments", "sphinx-copybutton", "sphinx-design", "sphinx-hep-pdgref", + "sphinx-pybtex-etal-style", "sphinx-thebe", "sphinx-togglebutton", "sphinxcontrib-bibtex >=2", - 'sphinx-api-relink >=0.0.4', - 'sphinx-pybtex-etal-style', ] jupyter = [ "ipywidgets", "jupyterlab", "jupyterlab-code-formatter >=3.0.0", + "jupyterlab-git", "jupyterlab-lsp", + "jupyterlab-myst", + "python-lsp-ruff", "python-lsp-server[rope]", - 'jupyterlab-git', - 'jupyterlab-myst', - 'python-lsp-ruff', ] sty = [ "mypy >=0.730", # attrs and error code support "pre-commit >=1.4.0", "qrules[types]", - 'ruff', + "ruff", ] test = [ "ipython", @@ -105,16 +101,14 @@ test = [ "pytest-cov", "pytest-profiling", "pytest-xdist", - 'importlib-metadata; python_version <"3.8.0"', - 'nbmake <1.3; python_version=="3.7.*"', ] types = [ "ipython", "pydot <2", # problem with pydot.graph_from_dot_data "pytest", + "sphinx-api-relink >=0.0.4", "types-PyYAML", "types-setuptools", - 'sphinx-api-relink >=0.0.4', ] viz = [ "graphviz", diff --git a/src/qrules/__init__.py b/src/qrules/__init__.py index 9c291320..9abd1717 100644 --- a/src/qrules/__init__.py +++ b/src/qrules/__init__.py @@ -18,7 +18,7 @@ from __future__ import annotations from itertools import product -from typing import Iterable, Sequence +from typing import TYPE_CHECKING import attrs @@ -63,6 +63,9 @@ StateTransitionManager, ) +if TYPE_CHECKING: + from collections.abc import Iterable, Sequence + def check_reaction_violations( # noqa: C901, PLR0917 initial_state: StateDefinition | Sequence[StateDefinition], diff --git a/src/qrules/argument_handling.py b/src/qrules/argument_handling.py index fcc214b6..95a19176 100644 --- a/src/qrules/argument_handling.py +++ b/src/qrules/argument_handling.py @@ -8,18 +8,7 @@ from __future__ import annotations import inspect -from typing import ( - Any, - Callable, - Dict, - Generic, - List, - Sequence, - Tuple, - Type, - TypeVar, - Union, -) +from typing import TYPE_CHECKING, Any, Callable, Generic, TypeVar, Union import attrs @@ -30,13 +19,16 @@ ) from qrules.quantum_numbers import EdgeQuantumNumber, NodeQuantumNumber, Parity +if TYPE_CHECKING: + from collections.abc import Sequence + Scalar = Union[int, float] Rule = Union[GraphElementRule, EdgeQNConservationRule, ConservationRule] _ElementType = TypeVar("_ElementType") -GraphElementPropertyMap = Dict[Type[_ElementType], Scalar] +GraphElementPropertyMap = dict[type[_ElementType], Scalar] GraphEdgePropertyMap = GraphElementPropertyMap[EdgeQuantumNumber] """Type alias for a graph edge property map.""" GraphNodePropertyMap = GraphElementPropertyMap[NodeQuantumNumber] @@ -52,7 +44,7 @@ def _is_optional(field_type: type | None) -> bool: def _is_sequence_type(input_type: type) -> bool: origin = getattr(input_type, "__origin__", None) - return origin in {list, tuple, List, Tuple} + return origin in {list, tuple} def _is_edge_quantum_number(qn_type: Any) -> bool: @@ -291,7 +283,7 @@ def _resolve_argument_type_hints(rule: Rule) -> list: >>> _resolve_argument_type_hints(gellmann_nishijima) [] >>> _resolve_argument_type_hints(MassConservation(width_factor=1.0)) - [typing.List[qrules.conservation_rules.MassEdgeInput], typing.List[qrules.conservation_rules.MassEdgeInput]] + [list[qrules.conservation_rules.MassEdgeInput], list[qrules.conservation_rules.MassEdgeInput]] """ func_signature = inspect.signature(rule) if not func_signature.return_annotation: diff --git a/src/qrules/combinatorics.py b/src/qrules/combinatorics.py index 858787e2..f87cf23f 100644 --- a/src/qrules/combinatorics.py +++ b/src/qrules/combinatorics.py @@ -9,17 +9,9 @@ import itertools from collections import OrderedDict +from collections.abc import Iterable, Mapping, Sequence from copy import deepcopy -from typing import ( - TYPE_CHECKING, - Any, - Callable, - Iterable, - Mapping, - Sequence, - Tuple, - Union, -) +from typing import TYPE_CHECKING, Any, Callable, Union from qrules.particle import ParticleWithSpin from qrules.quantum_numbers import InteractionProperties, arange @@ -29,7 +21,7 @@ from qrules.particle import ParticleCollection -StateWithSpins = Tuple[str, Sequence[float]] +StateWithSpins = tuple[str, Sequence[float]] StateDefinition = Union[str, StateWithSpins] """Particle name, optionally with a list of spin projections.""" InitialFacts = MutableTransition[ParticleWithSpin, InteractionProperties] diff --git a/src/qrules/conservation_rules.py b/src/qrules/conservation_rules.py index 1863a331..0ddc8017 100644 --- a/src/qrules/conservation_rules.py +++ b/src/qrules/conservation_rules.py @@ -46,11 +46,10 @@ """ import operator -import sys from copy import deepcopy from functools import reduce from textwrap import dedent -from typing import Any, Callable, List, Optional, Set, Tuple, Type, Union +from typing import Any, Callable, Optional, Protocol, Union from attrs import define, field, frozen from attrs.converters import optional @@ -59,11 +58,6 @@ from qrules.quantum_numbers import NodeQuantumNumbers as NodeQN from qrules.quantum_numbers import arange -if sys.version_info >= (3, 8): - from typing import Protocol -else: - from typing_extensions import Protocol - def _is_boson(spin_magnitude: float) -> bool: return abs(spin_magnitude % 1) < 0.01 @@ -76,21 +70,22 @@ def _is_particle_antiparticle_pair(pid1: int, pid2: int) -> bool: class GraphElementRule(Protocol): - def __call__(self, __qns: Any) -> bool: ... + def __call__(self, qns: Any, /) -> bool: ... class EdgeQNConservationRule(Protocol): def __call__( - self, __ingoing_edge_qns: List[Any], __outgoing_edge_qns: List[Any] + self, ingoing_edge_qns: list[Any], outgoing_edge_qns: list[Any], / ) -> bool: ... class ConservationRule(Protocol): def __call__( self, - __ingoing_edge_qns: List[Any], - __outgoing_edge_qns: List[Any], - __node_qns: Any, + ingoing_edge_qns: list[Any], + outgoing_edge_qns: list[Any], + node_qns: Any, + /, ) -> bool: ... @@ -114,9 +109,9 @@ def additive_quantum_number_rule( def decorator(rule_class: Any) -> EdgeQNConservationRule: def new_call( - self: Type[EdgeQNConservationRule], # noqa: ARG001 - ingoing_edge_qns: List[quantum_number], # type: ignore[valid-type] - outgoing_edge_qns: List[quantum_number], # type: ignore[valid-type] + self: type[EdgeQNConservationRule], # noqa: ARG001 + ingoing_edge_qns: list[quantum_number], # type: ignore[valid-type] + outgoing_edge_qns: list[quantum_number], # type: ignore[valid-type] ) -> bool: return sum(ingoing_edge_qns) == sum(outgoing_edge_qns) @@ -174,8 +169,8 @@ class BottomnessConservation(EdgeQNConservationRule): def parity_conservation( - ingoing_edge_qns: List[EdgeQN.parity], - outgoing_edge_qns: List[EdgeQN.parity], + ingoing_edge_qns: list[EdgeQN.parity], + outgoing_edge_qns: list[EdgeQN.parity], l_magnitude: NodeQN.l_magnitude, ) -> bool: r"""Implement :math:`P_{in} = P_{out} \cdot (-1)^L`.""" @@ -196,8 +191,8 @@ class HelicityParityEdgeInput: def parity_conservation_helicity( - ingoing_edge_qns: List[HelicityParityEdgeInput], - outgoing_edge_qns: List[HelicityParityEdgeInput], + ingoing_edge_qns: list[HelicityParityEdgeInput], + outgoing_edge_qns: list[HelicityParityEdgeInput], parity_prefactor: NodeQN.parity_prefactor, ) -> bool: r"""Implements parity conservation for helicity formalism. @@ -245,8 +240,8 @@ class CParityNodeInput: def c_parity_conservation( - ingoing_edge_qns: List[CParityEdgeInput], - outgoing_edge_qns: List[CParityEdgeInput], + ingoing_edge_qns: list[CParityEdgeInput], + outgoing_edge_qns: list[CParityEdgeInput], interaction_node_qns: CParityNodeInput, ) -> bool: """Check for :math:`C`-parity conservation. @@ -255,7 +250,7 @@ def c_parity_conservation( """ def _get_c_parity_multiparticle( - part_qns: List[CParityEdgeInput], interaction_qns: CParityNodeInput + part_qns: list[CParityEdgeInput], interaction_qns: CParityNodeInput ) -> Optional[int]: c_parities_part = [x.c_parity.value for x in part_qns if x.c_parity] # if all states have C parity defined, then just multiply them @@ -302,8 +297,8 @@ class GParityNodeInput: def g_parity_conservation( # noqa: C901 - ingoing_edge_qns: List[GParityEdgeInput], - outgoing_edge_qns: List[GParityEdgeInput], + ingoing_edge_qns: list[GParityEdgeInput], + outgoing_edge_qns: list[GParityEdgeInput], interaction_qns: GParityNodeInput, ) -> bool: """Check for :math:`G`-parity conservation. @@ -313,7 +308,7 @@ def g_parity_conservation( # noqa: C901 def check_multistate_g_parity( isospin: EdgeQN.isospin_magnitude, - double_state_qns: Tuple[GParityEdgeInput, GParityEdgeInput], + double_state_qns: tuple[GParityEdgeInput, GParityEdgeInput], ) -> Optional[int]: if _is_particle_antiparticle_pair( double_state_qns[0].pid, double_state_qns[1].pid @@ -330,7 +325,7 @@ def check_multistate_g_parity( def check_g_parity_isobar( single_state: GParityEdgeInput, - couple_state: Tuple[GParityEdgeInput, GParityEdgeInput], + couple_state: tuple[GParityEdgeInput, GParityEdgeInput], ) -> bool: couple_state_g_parity = check_multistate_g_parity( single_state.isospin_magnitude, @@ -384,8 +379,8 @@ class IdenticalParticleSymmetryOutEdgeInput: def identical_particle_symmetrization( - ingoing_parities: List[EdgeQN.parity], - outgoing_edge_qns: List[IdenticalParticleSymmetryOutEdgeInput], + ingoing_parities: list[EdgeQN.parity], + outgoing_edge_qns: list[IdenticalParticleSymmetryOutEdgeInput], ) -> bool: """Verifies multi particle state symmetrization for identical particles. @@ -401,7 +396,7 @@ def identical_particle_symmetrization( """ def _check_particles_identical( - particles: List[IdenticalParticleSymmetryOutEdgeInput], + particles: list[IdenticalParticleSymmetryOutEdgeInput], ) -> bool: """Check if pids and spins match.""" if len(particles) == 1: @@ -480,19 +475,19 @@ def ls_spin_validity(spin_input: SpinNodeInput) -> bool: def _check_magnitude( - in_part: List[float], - out_part: List[float], + in_part: list[float], + out_part: list[float], interaction_qns: Optional[Union[SpinMagnitudeNodeInput, SpinNodeInput]], ) -> bool: - def couple_mags(j_1: float, j_2: float) -> List[float]: + def couple_mags(j_1: float, j_2: float) -> list[float]: return [ x / 2.0 for x in range(int(2 * abs(j_1 - j_2)), int(2 * (j_1 + j_2 + 1)), 2) ] def couple_magnitudes( - magnitudes: List[float], + magnitudes: list[float], interaction_qns: Optional[Union[SpinMagnitudeNodeInput, SpinNodeInput]], - ) -> Set[float]: + ) -> set[float]: if len(magnitudes) == 1: return set(magnitudes) @@ -522,8 +517,8 @@ def couple_magnitudes( def _check_spin_couplings( - in_part: List[_Spin], - out_part: List[_Spin], + in_part: list[_Spin], + out_part: list[_Spin], interaction_qns: Optional[SpinNodeInput], ) -> bool: in_tot_spins = __calculate_total_spins(in_part, interaction_qns) @@ -533,9 +528,9 @@ def _check_spin_couplings( def __calculate_total_spins( - spins: List[_Spin], + spins: list[_Spin], interaction_qns: Optional[SpinNodeInput] = None, -) -> Set[_Spin]: +) -> set[_Spin]: total_spins = set() if len(spins) == 1: return set(spins) @@ -552,9 +547,9 @@ def __calculate_total_spins( return total_spins -def __create_coupled_spins(spins: List[_Spin]) -> Set[_Spin]: +def __create_coupled_spins(spins: list[_Spin]) -> set[_Spin]: """Creates all combinations of coupled spins.""" - spins_daughters_coupled: Set[_Spin] = set() + spins_daughters_coupled: set[_Spin] = set() spin_list = deepcopy(spins) while spin_list: if spins_daughters_coupled: @@ -570,7 +565,7 @@ def __create_coupled_spins(spins: List[_Spin]) -> Set[_Spin]: return spins_daughters_coupled -def __spin_couplings(spin1: _Spin, spin2: _Spin) -> Set[_Spin]: +def __spin_couplings(spin1: _Spin, spin2: _Spin) -> set[_Spin]: r"""Implement the coupling of two spins. :math:`|S_1 - S_2| \leq S \leq |S_1 + S_2|` and :math:`M_1 + M_2 = M` @@ -613,8 +608,8 @@ def isospin_validity(isospin: IsoSpinEdgeInput) -> bool: def isospin_conservation( - ingoing_isospins: List[IsoSpinEdgeInput], - outgoing_isospins: List[IsoSpinEdgeInput], + ingoing_isospins: list[IsoSpinEdgeInput], + outgoing_isospins: list[IsoSpinEdgeInput], ) -> bool: r"""Check for isospin conservation. @@ -651,8 +646,8 @@ def spin_validity(spin: SpinEdgeInput) -> bool: def spin_conservation( - ingoing_spins: List[SpinEdgeInput], - outgoing_spins: List[SpinEdgeInput], + ingoing_spins: list[SpinEdgeInput], + outgoing_spins: list[SpinEdgeInput], interaction_qns: SpinNodeInput, ) -> bool: r"""Check for spin conservation. @@ -692,8 +687,8 @@ def spin_conservation( def spin_magnitude_conservation( - ingoing_spin_magnitudes: List[EdgeQN.spin_magnitude], - outgoing_spin_magnitudes: List[EdgeQN.spin_magnitude], + ingoing_spin_magnitudes: list[EdgeQN.spin_magnitude], + outgoing_spin_magnitudes: list[EdgeQN.spin_magnitude], interaction_qns: SpinMagnitudeNodeInput, ) -> bool: r"""Check for spin conservation. @@ -729,8 +724,8 @@ def spin_magnitude_conservation( def clebsch_gordan_helicity_to_canonical( - ingoing_spins: List[SpinEdgeInput], - outgoing_spins: List[SpinEdgeInput], + ingoing_spins: list[SpinEdgeInput], + outgoing_spins: list[SpinEdgeInput], interaction_qns: SpinNodeInput, ) -> bool: """Implement Clebsch-Gordan checks. @@ -775,8 +770,8 @@ def clebsch_gordan_helicity_to_canonical( def helicity_conservation( - ingoing_spin_mags: List[EdgeQN.spin_magnitude], - outgoing_helicities: List[EdgeQN.spin_projection], + ingoing_spin_mags: list[EdgeQN.spin_magnitude], + outgoing_helicities: list[EdgeQN.spin_projection], ) -> bool: r"""Implementation of helicity conservation. @@ -881,8 +876,8 @@ def __init__(self, width_factor: float) -> None: def __call__( self, - ingoing_edge_qns: List[MassEdgeInput], - outgoing_edge_qns: List[MassEdgeInput], + ingoing_edge_qns: list[MassEdgeInput], + outgoing_edge_qns: list[MassEdgeInput], ) -> bool: r"""Implements mass conservation. diff --git a/src/qrules/io/_dot.py b/src/qrules/io/_dot.py index e9ffd723..1ac16b17 100644 --- a/src/qrules/io/_dot.py +++ b/src/qrules/io/_dot.py @@ -12,7 +12,7 @@ from functools import singledispatch from inspect import isfunction from numbers import Number -from typing import TYPE_CHECKING, Any, Iterable, cast +from typing import TYPE_CHECKING, Any, cast import attrs from attrs import Attribute, define, field @@ -25,6 +25,8 @@ from qrules.transition import ProblemSet, ReactionInfo, State if TYPE_CHECKING: + from collections.abc import Iterable + from qrules.argument_handling import Rule _LOGGER = logging.getLogger(__name__) diff --git a/src/qrules/particle.py b/src/qrules/particle.py index 899b5449..55040e3f 100644 --- a/src/qrules/particle.py +++ b/src/qrules/particle.py @@ -18,15 +18,7 @@ from difflib import get_close_matches from functools import total_ordering from math import copysign -from typing import ( - TYPE_CHECKING, - Any, - Callable, - Iterable, - Iterator, - SupportsFloat, - Tuple, -) +from typing import TYPE_CHECKING, Any, Callable, SupportsFloat import attrs from attrs import field, frozen @@ -41,6 +33,8 @@ else: from typing import Self if TYPE_CHECKING: + from collections.abc import Iterable, Iterator + from IPython.lib.pretty import PrettyPrinter from particle import Particle as PdgDatabase from particle.particle import enums @@ -242,7 +236,7 @@ def _get_name_root(name: str) -> str: return re.sub(r"[\*\+\-~\d']", "", name_root) -ParticleWithSpin = Tuple[Particle, float] +ParticleWithSpin = tuple[Particle, float] """A particle and its spin projection.""" diff --git a/src/qrules/quantum_numbers.py b/src/qrules/quantum_numbers.py index a1a4f63f..ba0d6129 100644 --- a/src/qrules/quantum_numbers.py +++ b/src/qrules/quantum_numbers.py @@ -8,20 +8,17 @@ from __future__ import annotations -import sys from decimal import Decimal from fractions import Fraction from functools import total_ordering -from typing import Any, Generator, NewType, Union +from typing import TYPE_CHECKING, Any, Literal, NewType, Union from attrs import field, frozen from qrules._implementers import implement_pretty_repr -if sys.version_info < (3, 8): - from typing_extensions import Literal -else: - from typing import Literal +if TYPE_CHECKING: + from collections.abc import Generator def _to_parity(value: int) -> Literal[-1, 1]: diff --git a/src/qrules/settings.py b/src/qrules/settings.py index 1bf920d1..6ff0111b 100644 --- a/src/qrules/settings.py +++ b/src/qrules/settings.py @@ -13,7 +13,7 @@ from copy import deepcopy from enum import Enum, auto from os.path import dirname, join, realpath -from typing import TYPE_CHECKING, Any, Callable, Iterable +from typing import TYPE_CHECKING, Any, Callable from qrules.conservation_rules import ( BaryonNumberConservation, @@ -49,6 +49,8 @@ from qrules.solving import EdgeSettings, NodeSettings if TYPE_CHECKING: + from collections.abc import Iterable + from qrules.particle import Particle, ParticleCollection from qrules.transition import SpinFormalism diff --git a/src/qrules/solving.py b/src/qrules/solving.py index 8897ad06..77bec661 100644 --- a/src/qrules/solving.py +++ b/src/qrules/solving.py @@ -14,7 +14,7 @@ from abc import ABC, abstractmethod from collections import defaultdict from copy import copy -from typing import Any, Callable, Generic, Iterable, Tuple, Type, TypeVar +from typing import TYPE_CHECKING, Any, Callable, Generic, TypeVar import attrs from attrs import define, field, frozen @@ -37,6 +37,9 @@ ) from qrules.topology import MutableTransition, Topology +if TYPE_CHECKING: + from collections.abc import Iterable + _LOGGER = logging.getLogger(__name__) @@ -383,8 +386,8 @@ def _create_variable_containers( ) -_EdgeVariableInfo = Tuple[int, Type[EdgeQuantumNumber]] -_NodeVariableInfo = Tuple[int, Type[NodeQuantumNumber]] +_EdgeVariableInfo = tuple[int, type[EdgeQuantumNumber]] +_NodeVariableInfo = tuple[int, type[NodeQuantumNumber]] def _create_variable_string( diff --git a/src/qrules/system_control.py b/src/qrules/system_control.py index 0dfe4f63..00f43cc9 100644 --- a/src/qrules/system_control.py +++ b/src/qrules/system_control.py @@ -5,7 +5,7 @@ import logging import operator from abc import ABC, abstractmethod -from typing import TYPE_CHECKING, Callable, Dict, Iterable, List, Tuple +from typing import TYPE_CHECKING, Callable import attrs @@ -22,13 +22,15 @@ from qrules.topology import MutableTransition if TYPE_CHECKING: + from collections.abc import Iterable + from qrules.particle import Particle, ParticleCollection, ParticleWithSpin _LOGGER = logging.getLogger(__name__) Strength = float -GraphSettingsGroups = Dict[Strength, List[Tuple[MutableTransition, GraphSettings]]] +GraphSettingsGroups = dict[Strength, list[tuple[MutableTransition, GraphSettings]]] def create_edge_properties( diff --git a/src/qrules/topology.py b/src/qrules/topology.py index f0b4c817..56437568 100644 --- a/src/qrules/topology.py +++ b/src/qrules/topology.py @@ -19,25 +19,10 @@ import copy import itertools import logging -import sys from abc import ABC, abstractmethod from collections import abc from functools import total_ordering -from typing import ( - TYPE_CHECKING, - Any, - Callable, - Generic, - ItemsView, - Iterable, - Iterator, - KeysView, - Mapping, - Sequence, - TypeVar, - ValuesView, - overload, -) +from typing import TYPE_CHECKING, Any, Callable, Generic, Protocol, TypeVar, overload import attrs from attrs import define, field, frozen @@ -45,12 +30,17 @@ from qrules._implementers import implement_pretty_repr -if sys.version_info >= (3, 8): - from typing import Protocol -else: - from typing_extensions import Protocol - if TYPE_CHECKING: + from collections.abc import ( + ItemsView, + Iterable, + Iterator, + KeysView, + Mapping, + Sequence, + ValuesView, + ) + from IPython.lib.pretty import PrettyPrinter _LOGGER = logging.getLogger(__name__) diff --git a/src/qrules/transition.py b/src/qrules/transition.py index 1b2939c6..a163f9b4 100644 --- a/src/qrules/transition.py +++ b/src/qrules/transition.py @@ -4,13 +4,12 @@ import logging import re -import sys import warnings from collections import defaultdict from copy import copy, deepcopy from enum import Enum, auto from multiprocessing import Pool -from typing import Iterable, Sequence, overload +from typing import TYPE_CHECKING, Literal, overload import attrs from attrs import define, field, frozen @@ -75,10 +74,8 @@ create_n_body_topology, ) -if sys.version_info >= (3, 8): - from typing import Literal -else: - from typing_extensions import Literal +if TYPE_CHECKING: + from collections.abc import Iterable, Sequence _LOGGER = logging.getLogger(__name__) diff --git a/tests/conftest.py b/tests/conftest.py index 56334410..da08d137 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,4 @@ -import sys +from importlib.metadata import version import pytest @@ -6,11 +6,6 @@ from qrules.particle import ParticleCollection from qrules.settings import NumberOfThreads -if sys.version_info < (3, 8): - from importlib_metadata import version -else: - from importlib.metadata import version - # Ensure consistent test coverage when running pytest multithreaded # https://github.com/ComPWA/qrules/issues/11 NumberOfThreads.set(1) diff --git a/tests/unit/conservation_rules/test_clebsch_gordan.py b/tests/unit/conservation_rules/test_clebsch_gordan.py index b9192b21..9594e46b 100644 --- a/tests/unit/conservation_rules/test_clebsch_gordan.py +++ b/tests/unit/conservation_rules/test_clebsch_gordan.py @@ -1,5 +1,3 @@ -from typing import List, Tuple - import pytest from qrules.conservation_rules import ( @@ -13,7 +11,7 @@ from qrules.particle import Spin from tests.unit.conservation_rules.test_spin import __create_two_body_decay_spin_data -_SpinRuleInputType = Tuple[List[SpinEdgeInput], List[SpinEdgeInput], SpinNodeInput] +_SpinRuleInputType = tuple[list[SpinEdgeInput], list[SpinEdgeInput], SpinNodeInput] @pytest.mark.parametrize( diff --git a/tests/unit/conservation_rules/test_spin.py b/tests/unit/conservation_rules/test_spin.py index 921f1a56..6e937b21 100644 --- a/tests/unit/conservation_rules/test_spin.py +++ b/tests/unit/conservation_rules/test_spin.py @@ -1,7 +1,5 @@ from __future__ import annotations -from typing import List, Tuple - import pytest from qrules.conservation_rules import ( @@ -13,14 +11,14 @@ from qrules.particle import Spin from qrules.quantum_numbers import EdgeQuantumNumbers -_SpinMagnitudeRuleInputType = Tuple[ - List[EdgeQuantumNumbers.spin_magnitude], - List[EdgeQuantumNumbers.spin_magnitude], +_SpinMagnitudeRuleInputType = tuple[ + list[EdgeQuantumNumbers.spin_magnitude], + list[EdgeQuantumNumbers.spin_magnitude], SpinNodeInput, ] -_SpinRuleInputType = Tuple[ - List[SpinEdgeInput], - List[SpinEdgeInput], +_SpinRuleInputType = tuple[ + list[SpinEdgeInput], + list[SpinEdgeInput], SpinNodeInput, ] diff --git a/tests/unit/test_particle.py b/tests/unit/test_particle.py index d85138ca..0b980ead 100644 --- a/tests/unit/test_particle.py +++ b/tests/unit/test_particle.py @@ -1,8 +1,8 @@ from __future__ import annotations import logging -import sys from copy import deepcopy +from importlib.metadata import version import pytest from attrs.exceptions import FrozenInstanceError @@ -22,11 +22,6 @@ Parity, # noqa: F401 # pyright: ignore[reportUnusedImport] ) -if sys.version_info < (3, 8): - from importlib_metadata import version -else: - from importlib.metadata import version - class TestParticle: @pytest.mark.parametrize("repr_method", [repr, pretty]) diff --git a/tests/unit/test_system_control.py b/tests/unit/test_system_control.py index 7e6433af..7ad803f8 100644 --- a/tests/unit/test_system_control.py +++ b/tests/unit/test_system_control.py @@ -1,7 +1,7 @@ from __future__ import annotations -import sys from copy import deepcopy +from importlib.metadata import version import attrs import pytest @@ -26,11 +26,6 @@ ) from qrules.topology import Edge, MutableTransition, Topology -if sys.version_info < (3, 8): - from importlib_metadata import version -else: - from importlib.metadata import version - @pytest.mark.parametrize( (