From f6c2da54b74a0a1968389b00c5963366055b7247 Mon Sep 17 00:00:00 2001 From: Maxim V4S Date: Thu, 24 Oct 2024 16:53:36 +0300 Subject: [PATCH 1/4] ci: update ruff --- poetry.lock | 46 ++++++++++++++++++++++++++-------------------- pyproject.toml | 2 +- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/poetry.lock b/poetry.lock index ac4f2a8..74a3953 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. [[package]] name = "annotated-types" @@ -1658,28 +1658,29 @@ idna2008 = ["idna"] [[package]] name = "ruff" -version = "0.4.10" +version = "0.7.0" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.4.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:5c2c4d0859305ac5a16310eec40e4e9a9dec5dcdfbe92697acd99624e8638dac"}, - {file = "ruff-0.4.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a79489607d1495685cdd911a323a35871abfb7a95d4f98fc6f85e799227ac46e"}, - {file = "ruff-0.4.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1dd1681dfa90a41b8376a61af05cc4dc5ff32c8f14f5fe20dba9ff5deb80cd6"}, - {file = "ruff-0.4.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c75c53bb79d71310dc79fb69eb4902fba804a81f374bc86a9b117a8d077a1784"}, - {file = "ruff-0.4.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18238c80ee3d9100d3535d8eb15a59c4a0753b45cc55f8bf38f38d6a597b9739"}, - {file = "ruff-0.4.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:d8f71885bce242da344989cae08e263de29752f094233f932d4f5cfb4ef36a81"}, - {file = "ruff-0.4.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:330421543bd3222cdfec481e8ff3460e8702ed1e58b494cf9d9e4bf90db52b9d"}, - {file = "ruff-0.4.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9e9b6fb3a37b772628415b00c4fc892f97954275394ed611056a4b8a2631365e"}, - {file = "ruff-0.4.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f54c481b39a762d48f64d97351048e842861c6662d63ec599f67d515cb417f6"}, - {file = "ruff-0.4.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:67fe086b433b965c22de0b4259ddfe6fa541c95bf418499bedb9ad5fb8d1c631"}, - {file = "ruff-0.4.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:acfaaab59543382085f9eb51f8e87bac26bf96b164839955f244d07125a982ef"}, - {file = "ruff-0.4.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:3cea07079962b2941244191569cf3a05541477286f5cafea638cd3aa94b56815"}, - {file = "ruff-0.4.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:338a64ef0748f8c3a80d7f05785930f7965d71ca260904a9321d13be24b79695"}, - {file = "ruff-0.4.10-py3-none-win32.whl", hash = "sha256:ffe3cd2f89cb54561c62e5fa20e8f182c0a444934bf430515a4b422f1ab7b7ca"}, - {file = "ruff-0.4.10-py3-none-win_amd64.whl", hash = "sha256:67f67cef43c55ffc8cc59e8e0b97e9e60b4837c8f21e8ab5ffd5d66e196e25f7"}, - {file = "ruff-0.4.10-py3-none-win_arm64.whl", hash = "sha256:dd1fcee327c20addac7916ca4e2653fbbf2e8388d8a6477ce5b4e986b68ae6c0"}, - {file = "ruff-0.4.10.tar.gz", hash = "sha256:3aa4f2bc388a30d346c56524f7cacca85945ba124945fe489952aadb6b5cd804"}, + {file = "ruff-0.7.0-py3-none-linux_armv6l.whl", hash = "sha256:0cdf20c2b6ff98e37df47b2b0bd3a34aaa155f59a11182c1303cce79be715628"}, + {file = "ruff-0.7.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:496494d350c7fdeb36ca4ef1c9f21d80d182423718782222c29b3e72b3512737"}, + {file = "ruff-0.7.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:214b88498684e20b6b2b8852c01d50f0651f3cc6118dfa113b4def9f14faaf06"}, + {file = "ruff-0.7.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:630fce3fefe9844e91ea5bbf7ceadab4f9981f42b704fae011bb8efcaf5d84be"}, + {file = "ruff-0.7.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:211d877674e9373d4bb0f1c80f97a0201c61bcd1e9d045b6e9726adc42c156aa"}, + {file = "ruff-0.7.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:194d6c46c98c73949a106425ed40a576f52291c12bc21399eb8f13a0f7073495"}, + {file = "ruff-0.7.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:82c2579b82b9973a110fab281860403b397c08c403de92de19568f32f7178598"}, + {file = "ruff-0.7.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9af971fe85dcd5eaed8f585ddbc6bdbe8c217fb8fcf510ea6bca5bdfff56040e"}, + {file = "ruff-0.7.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b641c7f16939b7d24b7bfc0be4102c56562a18281f84f635604e8a6989948914"}, + {file = "ruff-0.7.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d71672336e46b34e0c90a790afeac8a31954fd42872c1f6adaea1dff76fd44f9"}, + {file = "ruff-0.7.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ab7d98c7eed355166f367597e513a6c82408df4181a937628dbec79abb2a1fe4"}, + {file = "ruff-0.7.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1eb54986f770f49edb14f71d33312d79e00e629a57387382200b1ef12d6a4ef9"}, + {file = "ruff-0.7.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:dc452ba6f2bb9cf8726a84aa877061a2462afe9ae0ea1d411c53d226661c601d"}, + {file = "ruff-0.7.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:4b406c2dce5be9bad59f2de26139a86017a517e6bcd2688da515481c05a2cb11"}, + {file = "ruff-0.7.0-py3-none-win32.whl", hash = "sha256:f6c968509f767776f524a8430426539587d5ec5c662f6addb6aa25bc2e8195ec"}, + {file = "ruff-0.7.0-py3-none-win_amd64.whl", hash = "sha256:ff4aabfbaaba880e85d394603b9e75d32b0693152e16fa659a3064a85df7fce2"}, + {file = "ruff-0.7.0-py3-none-win_arm64.whl", hash = "sha256:10842f69c245e78d6adec7e1db0a7d9ddc2fff0621d730e61657b64fa36f207e"}, + {file = "ruff-0.7.0.tar.gz", hash = "sha256:47a86360cf62d9cd53ebfb0b5eb0e882193fc191c6d717e8bef4462bc3b9ea2b"}, ] [[package]] @@ -1704,6 +1705,11 @@ files = [ {file = "scikit_learn-1.5.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f60021ec1574e56632be2a36b946f8143bf4e5e6af4a06d85281adc22938e0dd"}, {file = "scikit_learn-1.5.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:394397841449853c2290a32050382edaec3da89e35b3e03d6cc966aebc6a8ae6"}, {file = "scikit_learn-1.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:57cc1786cfd6bd118220a92ede80270132aa353647684efa385a74244a41e3b1"}, + {file = "scikit_learn-1.5.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9a702e2de732bbb20d3bad29ebd77fc05a6b427dc49964300340e4c9328b3f5"}, + {file = "scikit_learn-1.5.2-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:b0768ad641981f5d3a198430a1d31c3e044ed2e8a6f22166b4d546a5116d7908"}, + {file = "scikit_learn-1.5.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:178ddd0a5cb0044464fc1bfc4cca5b1833bfc7bb022d70b05db8530da4bb3dd3"}, + {file = "scikit_learn-1.5.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7284ade780084d94505632241bf78c44ab3b6f1e8ccab3d2af58e0e950f9c12"}, + {file = "scikit_learn-1.5.2-cp313-cp313-win_amd64.whl", hash = "sha256:b7b0f9a0b1040830d38c39b91b3a44e1b643f4b36e36567b80b7c6bd2202a27f"}, {file = "scikit_learn-1.5.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:757c7d514ddb00ae249832fe87100d9c73c6ea91423802872d9e74970a0e40b9"}, {file = "scikit_learn-1.5.2-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:52788f48b5d8bca5c0736c175fa6bdaab2ef00a8f536cda698db61bd89c551c1"}, {file = "scikit_learn-1.5.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:643964678f4b5fbdc95cbf8aec638acc7aa70f5f79ee2cdad1eec3df4ba6ead8"}, @@ -1942,4 +1948,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "c3bba5f3bc0017510d807298169ff48e6b1c9eb309bea21f422343b0399e21fa" +content-hash = "b4cf5a8ccc5879691bceac055d64b7b297a7332ad54ec0713757ce5562b2da50" diff --git a/pyproject.toml b/pyproject.toml index e3a15e0..0b468c9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,7 @@ setuptools = "^75.1.0" # it's required for qm -> [tool.poetry.group.dev.dependencies] -ruff = "^0.4.8" +ruff = "^0.7.0" mypy = "^1.10.0" poethepoet = "^0.27.0" From 1b523a78e09e2e250016b2b8546ca93e13cff28f Mon Sep 17 00:00:00 2001 From: Maxim V4S Date: Thu, 24 Oct 2024 16:53:54 +0300 Subject: [PATCH 2/4] refactor: apply new lint rules --- pyproject.toml | 13 +- qualibrate/models/execution_history.py | 5 +- qualibrate/models/run_summary/base.py | 11 +- .../orchestration/basic_orchestrator.py | 3 +- .../qualibration_orchestrator.py | 17 +- qualibrate/parameters.py | 18 +- qualibrate/q_runnnable.py | 32 ++-- qualibrate/qualibration_graph.py | 53 +++--- qualibrate/qualibration_library.py | 54 +++--- qualibrate/qualibration_node.py | 165 ++++++++---------- qualibrate/storage/local_storage_manager.py | 3 +- qualibrate/utils/exceptions.py | 2 +- qualibrate/utils/logger_m.py | 3 +- qualibrate/utils/parameters.py | 7 +- qualibrate/utils/singleton.py | 4 +- qualibrate/utils/types_parsing.py | 21 +-- .../test_basic_orchestrator.py | 2 +- .../test_qualibration_graph/test_graph.py | 3 +- .../test_run_qualibration_node.py | 3 +- .../test_node_outcomes.py | 9 +- .../test_basic_orchestrator.py | 2 +- .../test_execution_parameters.py | 13 +- .../test_node_and_graph_parameters.py | 6 +- .../test_parameters/test_target_parameter.py | 7 +- .../test_qualibration_graph/test_graph.py | 3 +- .../test_graph_export.py | 3 +- .../unit/test_qualibration_node/test_node.py | 2 +- .../test_qualibration_node/test_node_copy.py | 8 +- 28 files changed, 237 insertions(+), 235 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 0b468c9..e2cd71a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,7 +33,18 @@ target-version = "py39" exclude = ["calibrations"] [tool.ruff.lint] -extend-select = ["I"] +select = [ + "E", # pycodestyle + "F", # Pyflakes + "UP", # pyupgrade + "B", # flake8-bugbear + "SIM", # flake8-simplify + "I", # isort +] + +[tool.ruff.lint.pycodestyle] +max-line-length = 80 +max-doc-length = 80 [tool.mypy] python_version = "3.9" diff --git a/qualibrate/models/execution_history.py b/qualibrate/models/execution_history.py index a7afeb4..17f18f2 100644 --- a/qualibrate/models/execution_history.py +++ b/qualibrate/models/execution_history.py @@ -1,5 +1,6 @@ +from collections.abc import Sequence from datetime import datetime -from typing import Dict, Optional, Sequence +from typing import Optional from pydantic import BaseModel, ConfigDict, Field, computed_field @@ -23,7 +24,7 @@ class ExecutionHistoryItem(BaseModel): run_end: datetime parameters: NodeParameters error: Optional[RunError] = None - outcomes: Dict[TargetType, Outcome] = Field(default_factory=dict) + outcomes: dict[TargetType, Outcome] = Field(default_factory=dict) @computed_field def run_duration(self) -> float: diff --git a/qualibrate/models/run_summary/base.py b/qualibrate/models/run_summary/base.py index 8fdd13f..107df73 100644 --- a/qualibrate/models/run_summary/base.py +++ b/qualibrate/models/run_summary/base.py @@ -1,5 +1,6 @@ +from collections.abc import Mapping, Sequence from datetime import datetime -from typing import Any, Dict, List, Mapping, Optional, Sequence +from typing import Any, Optional from pydantic import BaseModel, Field, computed_field, field_serializer @@ -17,13 +18,13 @@ class BaseRunSummary(BaseModel): created_at: datetime completed_at: datetime parameters: Optional[RunnableParameters] = None - outcomes: Dict[TargetType, Outcome] + outcomes: dict[TargetType, Outcome] error: Optional[RunError] = None initial_targets: Sequence[TargetType] = Field(default_factory=list) - successful_targets: List[TargetType] = Field(default_factory=list) - failed_targets: List[TargetType] = Field(default_factory=list) - dropped_targets: Optional[List[TargetType]] = None + successful_targets: list[TargetType] = Field(default_factory=list) + failed_targets: list[TargetType] = Field(default_factory=list) + dropped_targets: Optional[list[TargetType]] = None state_updates: Mapping[str, Any] = Field(default_factory=dict) @computed_field diff --git a/qualibrate/orchestration/basic_orchestrator.py b/qualibrate/orchestration/basic_orchestrator.py index c5ab584..6637d8d 100644 --- a/qualibrate/orchestration/basic_orchestrator.py +++ b/qualibrate/orchestration/basic_orchestrator.py @@ -1,7 +1,8 @@ import traceback +from collections.abc import Sequence from datetime import datetime from queue import Queue -from typing import Any, Generic, Optional, Sequence +from typing import Any, Generic, Optional import networkx as nx diff --git a/qualibrate/orchestration/qualibration_orchestrator.py b/qualibrate/orchestration/qualibration_orchestrator.py index 1e8de78..1910f21 100644 --- a/qualibrate/orchestration/qualibration_orchestrator.py +++ b/qualibrate/orchestration/qualibration_orchestrator.py @@ -1,5 +1,6 @@ from abc import ABC, abstractmethod -from typing import Any, Dict, Generic, List, Mapping, Optional, Sequence +from collections.abc import Mapping, Sequence +from typing import Any, Generic, Optional from pydantic import create_model @@ -16,9 +17,6 @@ __all__ = ["QualibrationOrchestrator"] -# NodeType = TypeVar("NodeType", bound=QualibrationNode[NodeParameters]) - - class QualibrationOrchestrator(ABC, Generic[NodeTypeVar]): """ Abstract base class for orchestrating the execution of nodes in a @@ -29,7 +27,8 @@ class QualibrationOrchestrator(ABC, Generic[NodeTypeVar]): clean up resources, and serialize the orchestrator's current state. Args: - **parameters (Any): Keyword arguments for initializing the orchestrator parameters. + **parameters: Keyword arguments for initializing the orchestrator + parameters. """ def __init__(self, **parameters: Any): @@ -43,11 +42,11 @@ def __init__(self, **parameters: Any): }, ) self._parameters = self.parameters_class() - self.initial_targets: Optional[List[Any]] = None - self.targets: Optional[List[Any]] = None - self._execution_history: List[ExecutionHistoryItem] = [] + self.initial_targets: Optional[list[Any]] = None + self.targets: Optional[list[Any]] = None + self._execution_history: list[ExecutionHistoryItem] = [] self._active_node: Optional[NodeTypeVar] = None - self.final_outcomes: Dict[Any, Outcome] = {} + self.final_outcomes: dict[Any, Outcome] = {} @property def active_node(self) -> Optional[NodeTypeVar]: diff --git a/qualibrate/parameters.py b/qualibrate/parameters.py index 60a9313..e165cda 100644 --- a/qualibrate/parameters.py +++ b/qualibrate/parameters.py @@ -1,14 +1,14 @@ import sys +from collections.abc import Mapping, Sequence from typing import ( Any, ClassVar, - List, - Mapping, Optional, - Sequence, cast, ) +from pydantic import BaseModel, Field, model_validator + from qualibrate.utils.logger_m import logger from qualibrate.utils.naming import get_full_class_path from qualibrate.utils.parameters import recursive_properties_solver @@ -20,7 +20,6 @@ else: from typing_extensions import Self -from pydantic import BaseModel, Field, model_validator __all__ = [ "ExecutionParameters", @@ -75,13 +74,13 @@ def targets_exists_if_specified(self) -> Self: return self @property - def targets(self) -> Optional[List[TargetType]]: + def targets(self) -> Optional[list[TargetType]]: if ( self.targets_name is None or self.targets_name not in self.model_fields ): return None - return cast(List[TargetType], getattr(self, self.targets_name)) + return cast(list[TargetType], getattr(self, self.targets_name)) @targets.setter def targets(self, new_targets: Sequence[TargetType]) -> None: @@ -89,7 +88,8 @@ def targets(self, new_targets: Sequence[TargetType]) -> None: return if self.targets_name not in self.model_fields: raise ValueError( - f"Targets name ({self.targets_name}) specified but field does not exist" + f"Targets name ({self.targets_name}) specified but field does " + "not exist" ) if not isinstance(new_targets, Sequence): raise ValueError(f"Targets must be an iterable of {TargetType}") @@ -134,7 +134,7 @@ class NodesParameters(RunnableParameters): class GraphParameters(RunnableParameters, TargetParameter): targets_name: ClassVar[Optional[str]] = "qubits" - qubits: List[TargetType] = Field(default_factory=list) + qubits: list[TargetType] = Field(default_factory=list) @classmethod def serialize( @@ -161,7 +161,7 @@ def serialize(cls, **kwargs: Any) -> Mapping[str, Any]: for k, v in serialized.items() if k not in ("parameters", "nodes") } - exclude_targets = kwargs.get("exclude_targets", None) + exclude_targets = kwargs.get("exclude_targets") exclude_parameters_targets = ( exclude_targets if exclude_targets is not None else False ) diff --git a/qualibrate/q_runnnable.py b/qualibrate/q_runnnable.py index 1ff9200..ca8ae3b 100644 --- a/qualibrate/q_runnnable.py +++ b/qualibrate/q_runnnable.py @@ -1,15 +1,12 @@ from abc import ABC, abstractmethod +from collections.abc import Mapping from contextvars import ContextVar from copy import copy from pathlib import Path from typing import ( Any, - Dict, Generic, - Mapping, Optional, - Tuple, - Type, TypeVar, cast, ) @@ -45,10 +42,10 @@ class QRunnable(ABC, Generic[CreateParametersType, RunParametersType]): Abstract base class representing a runnable task. Args: - name (str): The name of the runnable. - parameters (CreateParametersType): Parameters to initialize the runnable. - description (Optional[str]): Description of the runnable. - modes (Optional[RunModes]): Optional run modes for the runnable. + name: The name of the runnable. + parameters: Parameters to initialize the runnable. + description: Description of the runnable. + modes: Optional run modes for the runnable. """ modes = RunModes() @@ -74,7 +71,7 @@ def __init__( self._state_updates: dict[str, Any] = {} - self.outcomes: Dict[TargetType, Outcome] = {} + self.outcomes: dict[TargetType, Outcome] = {} self.run_summary: Optional[BaseRunSummary] = None def cleanup(self) -> None: @@ -88,7 +85,7 @@ def cleanup(self) -> None: @staticmethod def build_parameters_class_from_instance( parameters: CreateParametersType, - ) -> Type[CreateParametersType]: + ) -> type[CreateParametersType]: """ Builds a parameter class from a given instance. @@ -96,7 +93,7 @@ def build_parameters_class_from_instance( parameters (CreateParametersType): The parameters instance. Returns: - Type[CreateParametersType]: A new parameter class type. + A new parameter class type. """ fields = { name: copy(field) for name, field in parameters.model_fields.items() @@ -113,7 +110,7 @@ def build_parameters_class_from_instance( ) if hasattr(parameters, "targets_name"): model.targets_name = parameters.targets_name - return cast(Type[CreateParametersType], model) + return cast(type[CreateParametersType], model) @classmethod def get_run_modes(cls, modes: Optional[RunModes] = None) -> RunModes: @@ -121,7 +118,8 @@ def get_run_modes(cls, modes: Optional[RunModes] = None) -> RunModes: Determines the run modes for the QRunnable. If modes are provided, they are returned. If no modes are provided, the context run modes are returned. - If no context run modes are provided, the default run modes are returned. + If no context run modes are provided, the default run modes are + returned. Args: modes (Optional[RunModes]): Run modes, if provided. @@ -184,7 +182,7 @@ def stop(self, **kwargs: Any) -> bool: @abstractmethod def scan_folder_for_instances( cls, path: Path - ) -> Dict[str, "QRunnable[CreateParametersType, RunParametersType]"]: + ) -> dict[str, "QRunnable[CreateParametersType, RunParametersType]"]: """ Scans a folder for runnable instances. @@ -192,14 +190,14 @@ def scan_folder_for_instances( path (Path): The folder path to scan. Returns: - Dict[str, QRunnable]: A dictionary of runnable instances. + dict[str, QRunnable]: A dictionary of runnable instances. """ pass @abstractmethod def run( self, **passed_parameters: Any - ) -> Tuple[ + ) -> tuple[ "QRunnable[CreateParametersType, RunParametersType]", BaseRunSummary, ]: @@ -210,7 +208,7 @@ def run( **passed_parameters (Any): Parameters to run the runnable. Returns: - Tuple[QRunnable, BaseRunSummary]: The executed runnable and summary. + tuple[QRunnable, BaseRunSummary]: The executed runnable and summary. """ pass diff --git a/qualibrate/qualibration_graph.py b/qualibrate/qualibration_graph.py index 469522a..e9820b6 100644 --- a/qualibrate/qualibration_graph.py +++ b/qualibrate/qualibration_graph.py @@ -1,16 +1,12 @@ import traceback +from collections.abc import Mapping, Sequence from datetime import datetime from pathlib import Path from typing import ( TYPE_CHECKING, Any, - Dict, Generic, - Mapping, Optional, - Sequence, - Tuple, - Type, TypeVar, cast, ) @@ -72,7 +68,7 @@ class QualibrationGraph( graph. nodes (Mapping[str, QualibrationNode]): A mapping of node names to nodes. - connectivity (Sequence[Tuple[str, str]]): Adjacency list representing + connectivity (Sequence[tuple[str, str]]): Adjacency list representing node connectivity as pairs of node names. orchestrator (Optional[QualibrationOrchestrator]): Orchestrator for the graph. Defaults to None. @@ -92,7 +88,7 @@ def __init__( name: str, parameters: GraphCreateParametersType, nodes: Mapping[str, NodeTypeVar], - connectivity: Sequence[Tuple[str, str]], + connectivity: Sequence[tuple[str, str]], orchestrator: Optional["QualibrationOrchestrator[NodeTypeVar]"] = None, description: Optional[str] = None, *, @@ -101,7 +97,7 @@ def __init__( super().__init__(name, parameters, description=description, modes=modes) self._nodes = self._validate_nodes_names_mapping(nodes) self._connectivity = connectivity - self._graph: "nx.DiGraph[NodeTypeVar]" = nx.DiGraph() + self._graph: nx.DiGraph[NodeTypeVar] = nx.DiGraph() self._orchestrator = orchestrator self._initial_targets: Sequence[TargetType] = [] self._add_nodes_and_connections() @@ -131,7 +127,7 @@ def _add_nodes_and_connections(self) -> None: f"not add connection ({v_name}, {x_name}) because node " f'with name "{issued_node_name}" has not been registered. ' f"Available node names: {tuple(self._nodes.keys())}" - ) + ) from ex if not self._graph.has_edge(v, x): self._graph.add_edge(v, x) @@ -166,7 +162,7 @@ def _validate_nodes_names_mapping( # TODO: logic commonly same with node so need to move to @classmethod - def scan_folder_for_instances(cls, path: Path) -> Dict[str, QGraphBaseType]: + def scan_folder_for_instances(cls, path: Path) -> dict[str, QGraphBaseType]: """ Scans a folder for graph instances and returns them. @@ -177,10 +173,10 @@ def scan_folder_for_instances(cls, path: Path) -> Dict[str, QGraphBaseType]: path (Path): The folder to scan for graph files. Returns: - Dict[str, QGraphBaseType]: A dictionary of graph names to their + dict[str, QGraphBaseType]: A dictionary of graph names to their corresponding instances. """ - graphs: Dict[str, QGraphBaseType] = {} + graphs: dict[str, QGraphBaseType] = {} try: if run_modes_ctx.get() is not None: logger.error( @@ -206,7 +202,7 @@ def scan_folder_for_instances(cls, path: Path) -> Dict[str, QGraphBaseType]: @classmethod def scan_graph_file( - cls, file: Path, graphs: Dict[str, QGraphBaseType] + cls, file: Path, graphs: dict[str, QGraphBaseType] ) -> None: """ Scans a graph file and adds its instance to the given dictionary. @@ -216,7 +212,7 @@ def scan_graph_file( Args: file (Path): The file to scan for a graph. - graphs (Dict[str, QGraphBaseType]): The dictionary to add valid + graphs (dict[str, QGraphBaseType]): The dictionary to add valid graphs to. Raises: @@ -236,7 +232,7 @@ def scan_graph_file( def add_graph( cls, graph: "QualibrationGraph[NodeTypeVar]", - graphs: Dict[str, QGraphBaseType], + graphs: dict[str, QGraphBaseType], ) -> None: """ Adds a graph to the library dictionary. @@ -246,7 +242,7 @@ def add_graph( Args: graph (QualibrationGraph): The graph to add. - graphs (Dict[str, QGraphBaseType]): The dictionary to store the + graphs (dict[str, QGraphBaseType]): The dictionary to store the graph. """ if graph.name in graphs: @@ -302,7 +298,7 @@ def _get_all_nodes_parameters( If parameters are missing for any node, it assigns default values. Args: - nodes_parameters (Mapping[str, Any]): Dictionary containing + nodes_parameters (Mapping[str, Any]): dictionary containing parameters for nodes, keyed by node names. Returns: @@ -314,7 +310,7 @@ def _get_all_nodes_parameters( ].annotation return { name: nodes_parameters.get(name, {}) - for name in cast(NodesParameters, nodes_class).model_fields.keys() + for name in cast(NodesParameters, nodes_class).model_fields } def _orchestrator_or_error(self) -> "QualibrationOrchestrator[NodeTypeVar]": @@ -416,7 +412,7 @@ def _post_run( def run( self, **passed_parameters: Any - ) -> Tuple["QualibrationGraph[NodeTypeVar]", BaseRunSummary]: + ) -> tuple["QualibrationGraph[NodeTypeVar]", BaseRunSummary]: """ Runs the graph using the given parameters. @@ -428,7 +424,7 @@ def run( execution. Should include the `nodes` key. Returns: - Tuple[QualibrationGraph, BaseRunSummary]: The graph and a summary of + tuple[QualibrationGraph, BaseRunSummary]: The graph and a summary of the execution, including outcomes and other details. Raises: @@ -493,7 +489,7 @@ def _add_node_by_name(self, node_name: str) -> NodeTypeVar: self._graph.add_node(node, **self.__class__._node_init_args) return node - def _build_parameters_class(self) -> Type[GraphRunParametersType]: + def _build_parameters_class(self) -> type[GraphRunParametersType]: """ Builds a class for the parameters used in running the graph. @@ -502,7 +498,7 @@ def _build_parameters_class(self) -> Type[GraphRunParametersType]: graph's execution. Returns: - Type[GraphRunParametersType]: The parameter class to be used for + The parameter class to be used for execution. """ nodes_parameters_class = create_model( @@ -540,7 +536,7 @@ def serialize(self, **kwargs: Any) -> Mapping[str, Any]: data = dict(super().serialize()) cytoscape = bool(kwargs.get("cytoscape", False)) parameters = self.full_parameters_class.serialize(**kwargs) - nx_data: Dict[str, Any] = dict( + nx_data: dict[str, Any] = dict( self.nx_graph_export(node_names_only=True) ) data.update( @@ -591,9 +587,7 @@ def nx_graph_export( for key in ("multigraph", "directed", "graph"): data.pop(key) if node_names_only: - for i, (node, adjacency) in enumerate( - zip(data["nodes"], data["adjacency"]) - ): + for node, adjacency in zip(data["nodes"], data["adjacency"]): node["id"] = node["id"].name for adj in adjacency: adj["id"] = adj["id"].name @@ -654,13 +648,12 @@ def stop(self, **kwargs: Any) -> bool: bool: True if successful in stopping execution, False otherwise. """ logger.debug(f"Stop graph {self.name}") - stop_node: Optional[bool] = kwargs.get("stop_graph_node", None) + stop_node: Optional[bool] = kwargs.get("stop_graph_node") node_stop = True orchestrator = self._orchestrator if orchestrator is None: return False - if stop_node: - if node := orchestrator.active_node: - node_stop = node.stop() + if stop_node and (node := orchestrator.active_node): + node_stop = node.stop() orchestrator.stop() return node_stop diff --git a/qualibrate/qualibration_library.py b/qualibrate/qualibration_library.py index bf9cb61..9147e3b 100644 --- a/qualibrate/qualibration_library.py +++ b/qualibrate/qualibration_library.py @@ -1,6 +1,7 @@ +from collections.abc import Mapping from importlib.util import find_spec from pathlib import Path -from typing import Any, Dict, Generic, Mapping, Optional, cast +from typing import Any, Generic, Optional, cast from qualibrate.models.run_summary.graph import GraphRunSummary from qualibrate.models.run_summary.node import NodeRunSummary @@ -17,7 +18,8 @@ class QualibrationLibrary(Generic[NodeTypeVar]): """ - Manages a collection of Qualibration nodes and graphs for calibration purposes. + Manages a collection of Qualibration nodes and graphs for calibration + purposes. This class provides functionality to load, manage, and run nodes and graphs from a given library folder. It supports scanning the folder to identify @@ -25,9 +27,9 @@ class QualibrationLibrary(Generic[NodeTypeVar]): managing an active instance of the library. Args: - library_folder (Optional[Path]): The folder containing the calibration - nodes and graphs. Defaults to None. - set_active (bool): Whether to set this instance as the active library. + library_folder: The folder containing the calibration nodes and graphs. + Defaults to None. + set_active: Whether to set this instance as the active library. Defaults to True. Side Effects: @@ -40,8 +42,8 @@ class QualibrationLibrary(Generic[NodeTypeVar]): def __init__( self, library_folder: Optional[Path] = None, set_active: bool = True ): - self.nodes: Dict[str, NodeTypeVar] = {} - self.graphs: Dict[str, QualibrationGraph[NodeTypeVar]] = {} + self.nodes: dict[str, NodeTypeVar] = {} + self.graphs: dict[str, QualibrationGraph[NodeTypeVar]] = {} self._library_folder = library_folder if set_active: @@ -64,11 +66,11 @@ def _scan(self) -> None: logger.warning("Can't rescan library without specified folder.") return self.nodes = cast( - Dict[str, NodeTypeVar], + dict[str, NodeTypeVar], QualibrationNode.scan_folder_for_instances(self._library_folder), ) self.graphs = cast( - Dict[str, QualibrationGraph[NodeTypeVar]], + dict[str, QualibrationGraph[NodeTypeVar]], QualibrationGraph.scan_folder_for_instances(self._library_folder), ) @@ -94,13 +96,13 @@ def get_active_library( configurations are used to create the library. Args: - library_folder (Optional[Path]): Path to the folder containing the - library resources. Defaults to None. - create (bool): Whether to create a new instance if none exists. - Defaults to True. + library_folder: Path to the folder containing the library resources. + Defaults to None. + create: Whether to create a new instance if none exists. Defaults + to True. Returns: - QualibrationLibrary: The active library instance. + The active library instance. Raises: RuntimeError: If no library is instantiated and `create` is False, @@ -136,7 +138,7 @@ def serialize(self) -> Mapping[str, Any]: contains library folder path. Returns: - Mapping[str, Any]: A dictionary containing serialized data. + A dictionary containing serialized data. """ return { "__class__": ( @@ -156,7 +158,7 @@ def get_nodes(self) -> Mapping[str, NodeTypeVar]: Returns all nodes available in the library. Returns: - Mapping[str, QualibrationNode]: Dictionary of nodes keyed by their names. + Dictionary of nodes keyed by their names. """ return self.nodes @@ -165,7 +167,7 @@ def get_graphs(self) -> Mapping[str, QualibrationGraph[NodeTypeVar]]: Returns all graphs available in the library. Returns: - Mapping[str, QualibrationGraph]: Dictionary of graphs keyed by their names. + Dictionary of graphs keyed by their names. """ return self.graphs @@ -179,14 +181,15 @@ def run_node( `input_parameters` and returns a summary of the run. Args: - node_name (str): The name of the node to run. - input_parameters (NodeParameters): The parameters to use for the run. + node_name: The name of the node to run. + input_parameters: The parameters to use for the run. Returns: - NodeRunSummary: Summary of the node run containing outcomes and details. + Summary of the node run containing outcomes and details. Raises: - KeyError: If the specified `node_name` does not exist in the library. + KeyError: If the specified `node_name` does not exist in the + library. """ node = self.nodes[node_name] return cast(NodeRunSummary, node.run(**input_parameters.model_dump())) @@ -201,14 +204,15 @@ def run_graph( `input_parameters` and returns a summary of the graph's execution. Args: - graph_name (str): The name of the graph to run. - input_parameters (ExecutionParameters): The parameters for executing the graph. + graph_name: The name of the graph to run. + input_parameters: The parameters for executing the graph. Returns: - GraphRunSummary: Summary of the graph execution containing outcomes and details. + Summary of the graph execution containing outcomes and details. Raises: - KeyError: If the specified `graph_name` does not exist in the library. + KeyError: If the specified `graph_name` does not exist in the + library. """ graph = self.graphs[graph_name] return cast( diff --git a/qualibrate/qualibration_node.py b/qualibrate/qualibration_node.py index 7a1555e..eb61d77 100644 --- a/qualibrate/qualibration_node.py +++ b/qualibrate/qualibration_node.py @@ -1,5 +1,6 @@ import traceback from collections import UserDict, UserList +from collections.abc import Generator, Sequence from contextlib import contextmanager from contextvars import ContextVar from copy import copy @@ -9,12 +10,7 @@ from pathlib import Path from typing import ( Any, - Dict, - Generator, Optional, - Sequence, - Tuple, - Type, TypeVar, cast, ) @@ -56,7 +52,7 @@ ParametersType = TypeVar("ParametersType", bound=NodeParameters) # TODO: use node parameters type instead of Any -external_parameters_ctx: ContextVar[Optional[Tuple[str, Any]]] = ContextVar( +external_parameters_ctx: ContextVar[Optional[tuple[str, Any]]] = ContextVar( "external_parameters", default=None ) last_executed_node_ctx: ContextVar[Optional["QualibrationNode[Any]"]] = ( @@ -73,14 +69,13 @@ class QualibrationNode( and modes. Args: - name (str): The name of the node. - parameters (Optional[NodeCreateParametersType]): Parameters - passed to the node for its initialization. Defaults to None. - description (Optional[str]): A description of the node. + name: The name of the node. + parameters: Parameters passed to the node for its initialization. Defaults to None. - parameters_class (Optional[Type[NodeCreateParametersType]]): - Class used for node parameters validation. Defaults to None. - modes (Optional[RunModes]): Execution modes. Defaults to None. + description: A description of the node. Defaults to None. + parameters_class: Class used for node parameters validation. Defaults + to None. + modes: Execution modes. Defaults to None. Raises: StopInspection: Raised if the node is instantiated in inspection mode. @@ -96,14 +91,14 @@ def __init__( parameters: Optional[ParametersType] = None, description: Optional[str] = None, *, - parameters_class: Optional[Type[ParametersType]] = None, + parameters_class: Optional[type[ParametersType]] = None, modes: Optional[RunModes] = None, ): logger.info(f"Creating node {name}") parameters = self.__class__._validate_passed_parameters_options( name, parameters, parameters_class ) - super(QualibrationNode, self).__init__( + super().__init__( name, parameters, description=description, @@ -132,7 +127,7 @@ def _validate_passed_parameters_options( cls, name: str, parameters: Optional[ParametersType], - parameters_class: Optional[Type[ParametersType]], + parameters_class: Optional[type[ParametersType]], ) -> ParametersType: """ Validates passed parameters and parameters class. If parameters @@ -142,14 +137,12 @@ def _validate_passed_parameters_options( used. Args: - name (str): The name of the node. - parameters (Optional[NodeCreateParametersType]): Parameters for the - node. - parameters_class (Optional[Type[NodeCreateParametersType]]): - Parameters class. + name: The name of the node. + parameters: Parameters for the node. + parameters_class: Parameters class. Returns: - NodeCreateParametersType: Validated parameters. + Validated parameters. Raises: ValueError: If parameters class instantiation fails. @@ -200,7 +193,7 @@ def __copy__(self) -> "QualibrationNode[ParametersType]": consistency. Returns: - QualibrationNode: A copy of the node. + A copy of the node. """ modes = self.modes.model_copy(update={"inspection": False}) instance = self.__class__( @@ -221,11 +214,11 @@ def copy( the original. Args: - name (Optional[str]): A new name for the node. Defaults to None. - node_parameters (Any): Additional parameters for the copied node. + name: A new name for the node. Defaults to None. + node_parameters: Additional parameters for the copied node. Returns: - QualibrationNode: A copied node with the new parameters and name. + A copied node with the new parameters and name. Raises: ValueError: If the name provided is not a string. @@ -284,7 +277,7 @@ def snapshot_idx(self) -> Optional[int]: the associated storage, if any. Returns: - Optional[int]: Snapshot index or None. + Snapshot index or None. """ if self.storage_manager is None: return None @@ -339,14 +332,14 @@ def _post_run( (if any), state changes, and outcomes for each target. Args: - last_executed_node (QualibrationNode): The node that was last executed. - created_at (datetime): The timestamp when the run started. - initial_targets (Sequence[TargetType]): Targets at the start of the run. - parameters (NodeParameters): Parameters used in the run. - run_error (Optional[RunError]): Details of any error that occurred. + last_executed_node: The node that was last executed. + created_at: The timestamp when the run started. + initial_targets: Targets at the start of the run. + parameters: Parameters used in the run. + run_error: Details of any error that occurred. Returns: - NodeRunSummary: A summary object containing execution details. + A summary object containing execution details. """ outcomes = last_executed_node.outcomes if self.parameters is not None and (targets := self.parameters.targets): @@ -383,7 +376,7 @@ def _post_run( def run( self, interactive: bool = True, **passed_parameters: Any - ) -> Tuple["QualibrationNode[ParametersType]", BaseRunSummary]: + ) -> tuple["QualibrationNode[ParametersType]", BaseRunSummary]: """ Runs the node with given parameters, potentially interactively. @@ -393,14 +386,13 @@ def run( status. Args: - interactive (bool): Whether the node should be run interactively. - **passed_parameters (Any): Additional parameters to pass when + interactive: Whether the node should be run interactively. + **passed_parameters: Additional parameters to pass when running the node. Returns: - Tuple[QualibrationNode, BaseRunSummary]: The executed node and - a summary of the run including outcomes, errors, and execution - details. + The executed node and a summary of the run including outcomes, + errors, and execution details. Raises: RuntimeError: Raised if the node filepath is not provided, or @@ -474,8 +466,8 @@ def run_node_file(self, node_filepath: Path) -> None: the original matplotlib backend is restored. Args: - node_filepath (Path): Path to the file that contains the node's - execution logic. + node_filepath: Path to the file that contains the node's execution + logic. """ mpl_backend = matplotlib.get_backend() # Appending dir with nodes can cause issues with relative imports @@ -496,11 +488,11 @@ def stop(self, **kwargs: Any) -> bool: not present, it will return False. Args: - **kwargs (Any): Additional keyword arguments that might be used - for stopping the node. + **kwargs: Additional keyword arguments that might be used for + stopping the node. Returns: - bool: True if the node is successfully stopped, False otherwise. + True if the node is successfully stopped, False otherwise. """ logger.debug(f"Stop node {self.name}") if find_spec("qm") is None: @@ -536,11 +528,12 @@ def record_state_updates( to record these changes. Args: - interactive_only (bool): Whether to only record in interactive - mode. Defaults to True. + interactive_only: Whether to only record in interactive mode. + Defaults to True. Yields: - None: Allows wrapped operations to execute while recording state updates. + None: Allows wrapped operations to execute while recording state + updates. """ if not self.modes.interactive and interactive_only: yield @@ -552,7 +545,7 @@ def record_state_updates( from quam.core import ( QuamBase, QuamComponent, - QuamDict, + Quamdict, QuamList, QuamRoot, ) @@ -564,9 +557,9 @@ def record_state_updates( QuamBase, QuamComponent, QuamRoot, - QuamDict, + Quamdict, ) - quam_classes_sequences = (QuamList, QuamDict) + quam_classes_sequences = (QuamList, Quamdict) cls_setattr_funcs = { cls: cls.__dict__["__setattr__"] @@ -580,28 +573,24 @@ def record_state_updates( } try: for cls in cls_setattr_funcs: - setattr( - cls, - "__setattr__", - partialmethod(_record_state_update_getattr, node=self), + cls.__setattr__ = partialmethod( + _record_state_update_getattr, node=self ) for cls in cls_setitem_funcs: - setattr( - cls, - "__setitem__", - partialmethod(_record_state_update_getitem, node=self), + cls.__setitem__ = partialmethod( + _record_state_update_getitem, node=self ) yield finally: for cls, setattr_func in cls_setattr_funcs.items(): - setattr(cls, "__setattr__", setattr_func) + cls.__setattr__ = setattr_func for cls, setitem_func in cls_setitem_funcs.items(): - setattr(cls, "__setitem__", setitem_func) + cls.__setitem__ = setitem_func @classmethod def scan_folder_for_instances( cls, path: Path - ) -> Dict[str, QRunnable[ParametersType, NodeRunParametersType]]: + ) -> dict[str, QRunnable[ParametersType, NodeRunParametersType]]: """ Scans a directory for node instances and returns them. @@ -610,13 +599,12 @@ def scan_folder_for_instances( executing the nodes during scanning. Args: - path (Path): The directory to scan for node files. + path: The directory to scan for node files. Returns: - Dict[str, QualibrationNode]: A dictionary of node names to their - corresponding node instances. + A dictionary of node names to their corresponding node instances. """ - nodes: Dict[str, QRunnable[ParametersType, NodeRunParametersType]] = {} + nodes: dict[str, QRunnable[ParametersType, NodeRunParametersType]] = {} if run_modes_ctx.get() is not None: logger.error( "Run modes context is already set to %s", @@ -643,7 +631,7 @@ def scan_folder_for_instances( def scan_node_file( cls, file: Path, - nodes: Dict[str, QRunnable[ParametersType, NodeRunParametersType]], + nodes: dict[str, QRunnable[ParametersType, NodeRunParametersType]], ) -> None: """ Scans a node file and adds its instance to the provided dictionary. @@ -653,9 +641,8 @@ def scan_node_file( the given nodes dictionary for further processing. Args: - file (Path): The node file to scan. - nodes (Dict[str, QualibrationNode]): Dictionary to add valid - nodes to. + file: The node file to scan. + nodes: dictionary to add valid nodes to. Raises: StopInspection: Used to stop execution once inspection completes. @@ -674,7 +661,7 @@ def scan_node_file( def add_node( cls, node: "QualibrationNode[ParametersType]", - nodes: Dict[str, QRunnable[ParametersType, NodeRunParametersType]], + nodes: dict[str, QRunnable[ParametersType, NodeRunParametersType]], ) -> None: """ Adds a node instance to the node dictionary. @@ -683,8 +670,8 @@ def add_node( this method overwrites the existing entry with a warning. Args: - node (QualibrationNode): The node instance to add. - nodes (Dict[str, QualibrationNode]): Dictionary to store nodes. + node: The node instance to add. + nodes: dictionary to store nodes. """ if node.name in nodes: logger.warning( @@ -710,13 +697,12 @@ def _record_state_update( `_state_updates` dictionary. Args: - node (Optional[QualibrationNode]): The node where the state update - will be recorded. If None, no action is performed. - reference (str): The reference key to identify the updated attribute - or item. - attr (str): The name of the attribute or item key that is updated. - old (Any): The old value of the attribute or item before the update. - val (Any): The new value of the attribute or item. + node: The node where the state update will be recorded. If None, no + action is performed. + reference: The reference key to identify the updated attribute or item. + attr: The name of the attribute or item key that is updated. + old: The old value of the attribute or item before the update. + val: The new value of the attribute or item. """ if node is None: return @@ -744,11 +730,11 @@ def _record_state_update_getattr( For details see `_record_state_update`. Args: - quam_obj (GetRefProtocol): The Quam object whose attribute is updated. - attr (str): The name of the attribute being updated. - val (Any, optional): The new value of the attribute. Defaults to None. - node (Optional[QualibrationNode], optional): The node where the state - update will be recorded. Defaults to None. + quam_obj: The Quam object whose attribute is updated. + attr: The name of the attribute being updated. + val: The new value of the attribute. Defaults to None. + node: The node where the state update will be recorded. Defaults to + None. """ _record_state_update( node, quam_obj.get_reference(attr), attr, getattr(quam_obj, attr), val @@ -767,12 +753,11 @@ def _record_state_update_getitem( For details see `_record_state_update`. Args: - quam_obj (GetRefGetItemProtocol): The Quam object whose item is being - updated. - attr (str): The key/index of the item being updated. - val (Any, optional): The new value of the item. Defaults to None. - node (Optional[QualibrationNode], optional): The node where the state - update will be recorded. Defaults to None. + quam_obj: The Quam object whose item is being updated. + attr: The key/index of the item being updated. + val: The new value of the item. Defaults to None. + node: The node where the state update will be recorded. Defaults to + None. """ _record_state_update( node, quam_obj.get_reference(attr), attr, quam_obj[attr], val diff --git a/qualibrate/storage/local_storage_manager.py b/qualibrate/storage/local_storage_manager.py index a233137..392b7c9 100644 --- a/qualibrate/storage/local_storage_manager.py +++ b/qualibrate/storage/local_storage_manager.py @@ -57,7 +57,8 @@ def save(self, node: NodeTypeVar) -> None: - Saves the node's results and machine state in the root data folder. - Updates the `snapshot_idx` to reflect the newly saved node state. - - Optionally saves the machine state to the active path if specified. + - Optionally saves the machine state to the active path if + specified. Raises: AssertionError: If `self.data_handler.path` is not of type `Path`. diff --git a/qualibrate/utils/exceptions.py b/qualibrate/utils/exceptions.py index 7a3a45c..459ed96 100644 --- a/qualibrate/utils/exceptions.py +++ b/qualibrate/utils/exceptions.py @@ -13,5 +13,5 @@ def __init__( *args: Any, instance: QRunnable[CreateParametersType, RunParametersType], ): - super(StopInspection, self).__init__(*args, instance) + super().__init__(*args, instance) self.instance = instance diff --git a/qualibrate/utils/logger_m.py b/qualibrate/utils/logger_m.py index 9c6dcf9..2e95e1c 100644 --- a/qualibrate/utils/logger_m.py +++ b/qualibrate/utils/logger_m.py @@ -1,8 +1,9 @@ import logging +from collections.abc import Mapping from logging.handlers import RotatingFileHandler from pathlib import Path from types import TracebackType -from typing import Mapping, Optional, Union +from typing import Optional, Union try: from qualibrate_app.config import get_config_path, get_settings diff --git a/qualibrate/utils/parameters.py b/qualibrate/utils/parameters.py index 744e2f1..9758ccd 100644 --- a/qualibrate/utils/parameters.py +++ b/qualibrate/utils/parameters.py @@ -1,11 +1,12 @@ -from typing import Any, Dict, Mapping +from collections.abc import Mapping, MutableMapping +from typing import Any from jsonpointer import resolve_pointer def recursive_properties_solver( - properties: Dict[str, Any], schema: Mapping[str, Any] -) -> Dict[str, Any]: + properties: MutableMapping[str, Any], schema: Mapping[str, Any] +) -> MutableMapping[str, Any]: for name, structure in properties.items(): if ( "allOf" in structure diff --git a/qualibrate/utils/singleton.py b/qualibrate/utils/singleton.py index 77612d1..b3e79c1 100644 --- a/qualibrate/utils/singleton.py +++ b/qualibrate/utils/singleton.py @@ -10,7 +10,5 @@ class Singleton(type, Generic[T]): def __call__(cls, *args: Any, **kwargs: Any) -> T: if cls not in cls._instances: - cls._instances[cls] = super(Singleton, cls).__call__( - *args, **kwargs - ) + cls._instances[cls] = super().__call__(*args, **kwargs) return cls._instances[cls] diff --git a/qualibrate/utils/types_parsing.py b/qualibrate/utils/types_parsing.py index e4b16a4..31385f9 100644 --- a/qualibrate/utils/types_parsing.py +++ b/qualibrate/utils/types_parsing.py @@ -1,19 +1,20 @@ # duplication of qualibrate_runner/core/types_parsing.py import sys +from collections.abc import Mapping +from typing import Any, Optional, Union if sys.version_info >= (3, 10): from types import NoneType else: NoneType = type(None) -from typing import Any, Dict, List, Mapping, Optional, Type, Union NOT_NONE_BASIC_TYPES = Union[bool, int, float, str] BASIC_TYPES = Union[NOT_NONE_BASIC_TYPES, NoneType] -LIST_TYPES = Union[List[bool], List[int], List[float], List[str]] +LIST_TYPES = Union[list[bool], list[int], list[float], list[str]] VALUE_TYPES_WITHOUT_REC = Union[BASIC_TYPES, LIST_TYPES] INPUT_CONVERSION_TYPE = Union[ - VALUE_TYPES_WITHOUT_REC, Dict[str, "INPUT_CONVERSION_TYPE"] + VALUE_TYPES_WITHOUT_REC, dict[str, "INPUT_CONVERSION_TYPE"] ] @@ -91,7 +92,7 @@ def parse_none(value: VALUE_TYPES_WITHOUT_REC) -> VALUE_TYPES_WITHOUT_REC: NoneType: parse_none, } -STR_TO_TYPE: Mapping[str, Type[BASIC_TYPES]] = { +STR_TO_TYPE: Mapping[str, type[BASIC_TYPES]] = { "integer": int, "number": float, "boolean": bool, @@ -101,7 +102,7 @@ def parse_none(value: VALUE_TYPES_WITHOUT_REC) -> VALUE_TYPES_WITHOUT_REC: def parse_typed_list( - value: List[Any], item_type: Type[BASIC_TYPES] + value: list[Any], item_type: type[BASIC_TYPES] ) -> LIST_TYPES: if len(value) == 0: return value @@ -114,9 +115,9 @@ def parse_typed_list( def parse_list( value: VALUE_TYPES_WITHOUT_REC, - item_type: Optional[Type[BASIC_TYPES]], + item_type: Optional[type[BASIC_TYPES]], ) -> VALUE_TYPES_WITHOUT_REC: - if isinstance(value, List): + if isinstance(value, list): if item_type is None: return value return parse_typed_list(value, item_type) @@ -142,7 +143,7 @@ def types_conversion(value: Any, expected_type: Mapping[str, Any]) -> Any: new[k] = v return new if "anyOf" in expected_type: - # suppose that only `Type | None` is possible + # suppose that only `type | None` is possible none = parse_none(value) if none is None: return None @@ -152,13 +153,13 @@ def types_conversion(value: Any, expected_type: Mapping[str, Any]) -> Any: if "type" in expected_type: if expected_type.get("type") == "array": # array - item_type: Optional[Type[BASIC_TYPES]] = ( + item_type: Optional[type[BASIC_TYPES]] = ( STR_TO_TYPE.get(expected_type["items"]["type"]) if "items" in expected_type else None ) return parse_list(value, item_type) - if expected_type.get("type") in STR_TO_TYPE.keys(): + if expected_type.get("type") in STR_TO_TYPE: expected = STR_TO_TYPE[expected_type["type"]] parser = BASIC_PARSERS[expected] return parser(value) diff --git a/tests/integration/test_orchestration/test_basic_orchestrator.py b/tests/integration/test_orchestration/test_basic_orchestrator.py index 4bdf430..3bf3a58 100644 --- a/tests/integration/test_orchestration/test_basic_orchestrator.py +++ b/tests/integration/test_orchestration/test_basic_orchestrator.py @@ -1,5 +1,5 @@ +from collections.abc import Generator from pathlib import Path -from typing import Generator import pytest from pydantic import Field diff --git a/tests/integration/test_qualibration_graph/test_graph.py b/tests/integration/test_qualibration_graph/test_graph.py index 801f279..7ae9a69 100644 --- a/tests/integration/test_qualibration_graph/test_graph.py +++ b/tests/integration/test_qualibration_graph/test_graph.py @@ -1,5 +1,6 @@ +from collections.abc import Generator, Sequence from pathlib import Path -from typing import Any, Generator, Sequence +from typing import Any import pytest from pydantic import Field diff --git a/tests/integration/test_qualibration_library/test_run_qualibration_node.py b/tests/integration/test_qualibration_library/test_run_qualibration_node.py index 2595b30..7b9b51a 100644 --- a/tests/integration/test_qualibration_library/test_run_qualibration_node.py +++ b/tests/integration/test_qualibration_library/test_run_qualibration_node.py @@ -16,5 +16,6 @@ def test_run_calibration_node_from_library(tmp_path, mocker): "qualibrate.storage.local_storage_manager.LocalStorageManager.save" ) node = library.nodes["basic_node"] - # TODO: Mock save method or need to use non-default path for exporting results + # TODO: Mock save method or need to use non-default path for exporting + # results library.run_node("basic_node", node.parameters_class()) diff --git a/tests/integration/test_qualibration_node/test_node_outcomes.py b/tests/integration/test_qualibration_node/test_node_outcomes.py index 0c65e42..cfbd443 100644 --- a/tests/integration/test_qualibration_node/test_node_outcomes.py +++ b/tests/integration/test_qualibration_node/test_node_outcomes.py @@ -1,6 +1,8 @@ import importlib from pathlib import Path +import pytest + from qualibrate.models.outcome import Outcome from qualibrate.qualibration_node import QualibrationNode from qualibrate.utils.exceptions import StopInspection @@ -9,12 +11,11 @@ def test_node_outcomes(): try: QualibrationNode.modes.inspection = True - try: + with pytest.raises(StopInspection): importlib.import_module( - "tests.integration.example_calibration_scripts.1_node_with_partial_outcomes" + "tests.integration.example_calibration_scripts." + "1_node_with_partial_outcomes" ) - except StopInspection: - pass nodes = {} QualibrationNode.scan_node_file( Path(__file__) diff --git a/tests/unit/test_orchestration/test_basic_orchestrator.py b/tests/unit/test_orchestration/test_basic_orchestrator.py index 4f990a0..7b2944e 100644 --- a/tests/unit/test_orchestration/test_basic_orchestrator.py +++ b/tests/unit/test_orchestration/test_basic_orchestrator.py @@ -68,7 +68,7 @@ def test_nx_graph_raises_error_if_no_graph(self): orchestrator = BasicOrchestrator() orchestrator._graph = None with pytest.raises(ValueError, match="Graph is not specified"): - orchestrator.nx_graph + orchestrator.nx_graph # noqa: B018 def test_nx_graph_returns_correct_value(self, mocker): orchestrator = BasicOrchestrator() diff --git a/tests/unit/test_parameters/test_execution_parameters.py b/tests/unit/test_parameters/test_execution_parameters.py index 540b819..246668b 100644 --- a/tests/unit/test_parameters/test_execution_parameters.py +++ b/tests/unit/test_parameters/test_execution_parameters.py @@ -1,4 +1,4 @@ -from typing import List, Optional +from typing import Optional import pytest from pydantic import Field @@ -14,21 +14,21 @@ class Node1(NodeParameters): - qubits: Optional[List[TargetType]] = Field( + qubits: Optional[list[TargetType]] = Field( default_factory=lambda: ["a", "b", "c"] ) int_value: int = 1 class Node2(NodeParameters): - qubits: Optional[List[TargetType]] = Field( + qubits: Optional[list[TargetType]] = Field( default_factory=lambda: ["d", "e", "f"] ) float_value: float = 2.0 class Graph(GraphParameters): - qubits: List[TargetType] = Field(default_factory=lambda: ["1", "2", "3"]) + qubits: list[TargetType] = Field(default_factory=lambda: ["1", "2", "3"]) str_value: str = "test" @@ -200,6 +200,9 @@ def test_serialize_none_parameters_class(self, mocker): with pytest.raises( RuntimeError, - match="Graph parameters class should be subclass of qualibrate.parameters.GraphParameters", + match=( + "Graph parameters class should be subclass of " + "qualibrate.parameters.GraphParameters" + ), ): ExecutionParameters.serialize() diff --git a/tests/unit/test_parameters/test_node_and_graph_parameters.py b/tests/unit/test_parameters/test_node_and_graph_parameters.py index 4c0ae0b..9e21899 100644 --- a/tests/unit/test_parameters/test_node_and_graph_parameters.py +++ b/tests/unit/test_parameters/test_node_and_graph_parameters.py @@ -1,4 +1,4 @@ -from typing import List, Optional +from typing import Optional import pytest @@ -7,11 +7,11 @@ class TestCreateParameters: class SampleNodeParameters(NodeParameters): - qubits: Optional[List[str]] = None + qubits: Optional[list[str]] = None other_param: str = "test" class SampleGraphParameters(GraphParameters): - qubits: Optional[List[str]] = None + qubits: Optional[list[str]] = None other_param: str = "test" def test_node_targets_name(self): diff --git a/tests/unit/test_parameters/test_target_parameter.py b/tests/unit/test_parameters/test_target_parameter.py index 5fde4c4..05aae03 100644 --- a/tests/unit/test_parameters/test_target_parameter.py +++ b/tests/unit/test_parameters/test_target_parameter.py @@ -1,4 +1,4 @@ -from typing import ClassVar, List, Optional +from typing import ClassVar, Optional import pytest from pydantic import ValidationError @@ -10,7 +10,7 @@ class TestTargetParameter: class SampleTargetParameter(TargetParameter): targets_name: ClassVar[Optional[str]] = "test_targets" - test_targets: Optional[List[TargetType]] = None + test_targets: Optional[list[TargetType]] = None other_field: Optional[str] = None def test_prepare_targets_with_targets_name(self): @@ -43,7 +43,8 @@ def test_prepare_targets_with_both_fields(self, mocker): assert instance.test_targets == ["1", "2", "3"] assert instance.targets == ["1", "2", "3"] logger_mock.warning.assert_called_once_with( - "You specified `targets` and `test_targets` (marked as targets name) fields. `test_targets` will be ignored.", + "You specified `targets` and `test_targets` (marked as targets " + "name) fields. `test_targets` will be ignored.", ) def test_prepare_targets_without_targets_name(self): diff --git a/tests/unit/test_qualibration_graph/test_graph.py b/tests/unit/test_qualibration_graph/test_graph.py index 6052701..dcd8450 100644 --- a/tests/unit/test_qualibration_graph/test_graph.py +++ b/tests/unit/test_qualibration_graph/test_graph.py @@ -130,7 +130,8 @@ def test_validate_nodes_names_mapping_with_conflicting_name( assert result["new_name"] == "copied_instance" node.copy.assert_called_once_with("new_name") mock_logger.warning.assert_called_once_with( - "copied_instance has to be copied due to conflicting name (new_name)" + "copied_instance has to be copied due to conflicting name " + "(new_name)" ) def test_add_node_by_name( diff --git a/tests/unit/test_qualibration_graph/test_graph_export.py b/tests/unit/test_qualibration_graph/test_graph_export.py index 628c90a..42a8b91 100644 --- a/tests/unit/test_qualibration_graph/test_graph_export.py +++ b/tests/unit/test_qualibration_graph/test_graph_export.py @@ -1,5 +1,6 @@ +from collections.abc import Generator, Sequence from pathlib import Path -from typing import Any, Generator, Sequence +from typing import Any import pytest from pydantic import Field diff --git a/tests/unit/test_qualibration_node/test_node.py b/tests/unit/test_qualibration_node/test_node.py index d0b444c..e73c1c5 100644 --- a/tests/unit/test_qualibration_node/test_node.py +++ b/tests/unit/test_qualibration_node/test_node.py @@ -178,7 +178,7 @@ def test__validate_passed_parameters_options_with_none(self, mocker): "a", __doc__="str", __base__=params, __module__="module" ) - def test__validate_passed_parameters_options_parameters_class_instantiation_failure( + def test__validate_passed_parameters_options_parameters_class_instantiation_failure( # noqa: E501 self, mock_logger ): parameters_class = MagicMock( diff --git a/tests/unit/test_qualibration_node/test_node_copy.py b/tests/unit/test_qualibration_node/test_node_copy.py index d841cda..7e2e405 100644 --- a/tests/unit/test_qualibration_node/test_node_copy.py +++ b/tests/unit/test_qualibration_node/test_node_copy.py @@ -1,5 +1,3 @@ -from typing import Type - import pytest from pydantic import Field @@ -8,7 +6,7 @@ @pytest.fixture -def params_with_req() -> Type[NodeParameters]: +def params_with_req() -> type[NodeParameters]: class Parameters(NodeParameters): qubits: list[str] = Field(default_factory=list) req_str_param: str @@ -20,7 +18,7 @@ class Parameters(NodeParameters): @pytest.fixture def node_with_req_param( - params_with_req: Type[NodeParameters], + params_with_req: type[NodeParameters], ) -> QualibrationNode: yield QualibrationNode( "node_name", params_with_req(req_str_param="a"), "node description" @@ -54,7 +52,7 @@ def test_copy_without_parameters_instance( def test_copy_with_parameters_instance( - params_with_req: Type[NodeParameters], node_with_req_param: QualibrationNode + params_with_req: type[NodeParameters], node_with_req_param: QualibrationNode ): node = node_with_req_param node.modes.external = False From feaa7a7cb43ed6dd69ebbf288347dc10ece9258d Mon Sep 17 00:00:00 2001 From: Maxim V4S Date: Thu, 24 Oct 2024 16:56:00 +0300 Subject: [PATCH 3/4] tests: mock save method on run node from library --- .../test_run_qualibration_node.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/integration/test_qualibration_library/test_run_qualibration_node.py b/tests/integration/test_qualibration_library/test_run_qualibration_node.py index 7b9b51a..402d0ec 100644 --- a/tests/integration/test_qualibration_library/test_run_qualibration_node.py +++ b/tests/integration/test_qualibration_library/test_run_qualibration_node.py @@ -12,10 +12,7 @@ def test_library_scan(tmp_path): def test_run_calibration_node_from_library(tmp_path, mocker): folder = Path(__file__).parents[1] / "example_calibration_scripts" library = QualibrationLibrary(library_folder=folder) - mocker.patch( - "qualibrate.storage.local_storage_manager.LocalStorageManager.save" - ) + mocked = mocker.patch("qualibrate.qualibration_node.QualibrationNode.save") node = library.nodes["basic_node"] - # TODO: Mock save method or need to use non-default path for exporting - # results library.run_node("basic_node", node.parameters_class()) + mocked.assert_called_once() From 8e43f1d8eafc6b521791f6a1237db9aa59a67ae9 Mon Sep 17 00:00:00 2001 From: Maxim V4S Date: Fri, 25 Oct 2024 10:01:59 +0300 Subject: [PATCH 4/4] tests: fix typo after refactor --- qualibrate/qualibration_node.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/qualibrate/qualibration_node.py b/qualibrate/qualibration_node.py index eb61d77..a3fe6c9 100644 --- a/qualibrate/qualibration_node.py +++ b/qualibrate/qualibration_node.py @@ -545,11 +545,12 @@ def record_state_updates( from quam.core import ( QuamBase, QuamComponent, - Quamdict, + QuamDict, QuamList, QuamRoot, ) - except ImportError: + except ImportError as ex: + print(ex) yield return @@ -557,9 +558,9 @@ def record_state_updates( QuamBase, QuamComponent, QuamRoot, - Quamdict, + QuamDict, ) - quam_classes_sequences = (QuamList, Quamdict) + quam_classes_sequences = (QuamList, QuamDict) cls_setattr_funcs = { cls: cls.__dict__["__setattr__"]