From 4b863f86a799d0a0e7c2c46ca57d6c6e196eb3e6 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Mon, 19 Oct 2026 11:54:47 +0200 Subject: [PATCH 01/20] Complete Keithley test annotations and replace deprecated snapshot argument Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tests/drivers/test_keithley_26xx.py | 44 ++++++++++++++++++----------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/tests/drivers/test_keithley_26xx.py b/tests/drivers/test_keithley_26xx.py index 9a22d1f16be..0ddfd44b6d1 100644 --- a/tests/drivers/test_keithley_26xx.py +++ b/tests/drivers/test_keithley_26xx.py @@ -13,11 +13,13 @@ ) if TYPE_CHECKING: + from collections.abc import Generator + from qcodes.instrument_drivers.Keithley import Keithley2600Channel @pytest.fixture(scope="function", name="driver") -def _make_driver(): +def _make_driver() -> "Generator[Keithley2614B, None, None]": driver = Keithley2614B( "Keithley_2600", address="GPIB::1::INSTR", pyvisa_sim_file="Keithley_2600.yaml" ) @@ -27,14 +29,16 @@ def _make_driver(): @pytest.fixture(scope="function", name="smus") -def _make_smus(driver): +def _make_smus( + driver: Keithley2614B, +) -> "Generator[tuple[Keithley2600Channel, ...], None, None]": smu_names = {"smua", "smub"} assert smu_names == set(driver.submodules.keys()) yield tuple(getattr(driver, smu_name) for smu_name in smu_names) -def test_idn(driver) -> None: +def test_idn(driver: Keithley2614B) -> None: assert { "firmware": "3.0.0", "model": "2601B", @@ -43,7 +47,7 @@ def test_idn(driver) -> None: } == driver.IDN() -def test_smu_channels_and_their_parameters(driver) -> None: +def test_smu_channels_and_their_parameters(driver: Keithley2614B) -> None: assert {"smua", "smub"} == set(driver.submodules.keys()) for smu_name in ("smua", "smub"): @@ -154,11 +158,11 @@ def test_smu_channels_and_their_parameters(driver) -> None: def test_snapshot_updates_every_parameter( - driver, caplog: pytest.LogCaptureFixture + driver: Keithley2614B, caplog: pytest.LogCaptureFixture ) -> None: # a query missing from the sim file waits for the VISA timeout and is only logged with caplog.at_level(logging.WARNING): - driver.snapshot(update=True) + driver.snapshot(update="All") assert not [ record.getMessage() @@ -167,7 +171,9 @@ def test_snapshot_updates_every_parameter( ] -def test_setting_source_voltage_range_disables_autorange(smus) -> None: +def test_setting_source_voltage_range_disables_autorange( + smus: "tuple[Keithley2600Channel, ...]", +) -> None: for smu in smus: smu.source_autorange_v_enabled(True) assert smu.source_autorange_v_enabled() is True @@ -176,7 +182,9 @@ def test_setting_source_voltage_range_disables_autorange(smus) -> None: assert smu.source_autorange_v_enabled() is False -def test_setting_measure_voltage_range_disables_autorange(smus) -> None: +def test_setting_measure_voltage_range_disables_autorange( + smus: "tuple[Keithley2600Channel, ...]", +) -> None: for smu in smus: smu.measure_autorange_v_enabled(True) assert smu.measure_autorange_v_enabled() is True @@ -185,7 +193,9 @@ def test_setting_measure_voltage_range_disables_autorange(smus) -> None: assert smu.measure_autorange_v_enabled() is False -def test_setting_source_current_range_disables_autorange(smus) -> None: +def test_setting_source_current_range_disables_autorange( + smus: "tuple[Keithley2600Channel, ...]", +) -> None: for smu in smus: smu.source_autorange_i_enabled(True) assert smu.source_autorange_i_enabled() is True @@ -194,7 +204,9 @@ def test_setting_source_current_range_disables_autorange(smus) -> None: assert smu.source_autorange_i_enabled() is False -def test_setting_measure_current_range_disables_autorange(smus) -> None: +def test_setting_measure_current_range_disables_autorange( + smus: "tuple[Keithley2600Channel, ...]", +) -> None: for smu in smus: smu.measure_autorange_i_enabled(True) assert smu.measure_autorange_i_enabled() is True @@ -203,7 +215,7 @@ def test_setting_measure_current_range_disables_autorange(smus) -> None: assert smu.measure_autorange_i_enabled() is False -def test_fast_sweep_parameters(smus) -> None: +def test_fast_sweep_parameters(smus: "tuple[Keithley2600Channel, ...]") -> None: for smu in smus: smu = cast("Keithley2600Channel", smu) @@ -235,7 +247,7 @@ def test_fast_sweep_parameters(smus) -> None: np.testing.assert_array_almost_equal(setpoints, expected_setpoints) -def test_fastsweep(driver) -> None: +def test_fastsweep(driver: Keithley2614B) -> None: smu = driver.smua # Configure the fastsweep using LinSweep @@ -252,7 +264,7 @@ def test_fastsweep(driver) -> None: np.testing.assert_array_equal(result, fake_data) -def test_fastsweep_2d_parameters(driver) -> None: +def test_fastsweep_2d_parameters(driver: Keithley2614B) -> None: """Test 2D fastsweep configuration with inner and outer sweeps.""" smua = driver.smua smub = driver.smub @@ -286,7 +298,7 @@ def test_fastsweep_2d_parameters(driver) -> None: assert smua.fastsweep.setpoints[1] is smua.fastsweep_inner_setpoints -def test_fastsweep_2d_execution(driver) -> None: +def test_fastsweep_2d_execution(driver: Keithley2614B) -> None: """Test 2D fastsweep returns correctly shaped data.""" smua = driver.smua smub = driver.smub @@ -311,7 +323,7 @@ def test_fastsweep_2d_execution(driver) -> None: np.testing.assert_array_equal(result.flatten(), fake_data) -def test_fastsweep_channel_detection(driver) -> None: +def test_fastsweep_channel_detection(driver: Keithley2614B) -> None: """Test that channels are correctly detected from LinSweep parameters.""" smua = driver.smua smub = driver.smub @@ -336,7 +348,7 @@ def test_fastsweep_channel_detection(driver) -> None: assert result.shape == (5, 10) -def test_fastsweep_reconfigure_creates_fresh_setpoints(driver) -> None: +def test_fastsweep_reconfigure_creates_fresh_setpoints(driver: Keithley2614B) -> None: """Test that calling setup_fastsweep twice creates fresh setpoint parameters. Regression test: previously, setpoint parameters were reused and their From 9e38d9d21257cf2621ee5ab604ccf26a08d99e11 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Mon, 19 Oct 2026 13:10:19 +0200 Subject: [PATCH 02/20] Add type annotations to shared dataset and delegate fixtures Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tests/dataset/conftest.py | 85 +++++++++++++++++++++++----------- tests/dataset/dond/conftest.py | 29 +++++++----- tests/delegate/conftest.py | 18 ++++--- 3 files changed, 88 insertions(+), 44 deletions(-) diff --git a/tests/dataset/conftest.py b/tests/dataset/conftest.py index f8014325341..c2a7c37eee4 100644 --- a/tests/dataset/conftest.py +++ b/tests/dataset/conftest.py @@ -39,6 +39,7 @@ from qcodes.dataset.data_set_protocol import DataSetProtocol from qcodes.dataset.experiment_container import Experiment + from qcodes.dataset.sqlite.connection import AtomicConnection @pytest.fixture(scope="function", name="non_created_db") @@ -52,7 +53,9 @@ def _make_non_created_db(tmp_path: Path) -> None: @pytest.fixture(scope="function") -def empty_temp_db_connection(tmp_path): +def empty_temp_db_connection( + tmp_path: Path, +) -> Generator[AtomicConnection, None, None]: """ Yield connection to an empty temporary DB file. """ @@ -65,7 +68,9 @@ def empty_temp_db_connection(tmp_path): @pytest.fixture(scope="function") -def two_empty_temp_db_connections(tmp_path): +def two_empty_temp_db_connections( + tmp_path: Path, +) -> Generator[tuple[AtomicConnection, AtomicConnection], None, None]: """ Yield connections to two empty files. Meant for use with the test_database_extract_runs @@ -108,7 +113,7 @@ def temporarily_copied_DB(filepath: str, **kwargs): @pytest.fixture(name="scalar_dataset") -def _make_scalar_dataset(dataset): +def _make_scalar_dataset(dataset: DataSet) -> Generator[DataSet, None, None]: n_params = 3 n_rows = 10**3 params_indep = [ @@ -138,7 +143,9 @@ def _make_scalar_dataset(dataset): @pytest.fixture( name="scalar_datasets_parameterized", params=((3, 10**3), (5, 10**3), (10, 50)) ) -def _make_scalar_datasets_parameterized(dataset, request: FixtureRequest): +def _make_scalar_datasets_parameterized( + dataset: DataSet, request: FixtureRequest +) -> Generator[DataSet, None, None]: n_params = request.param[0] n_rows = request.param[1] params_indep = [ @@ -166,7 +173,7 @@ def _make_scalar_datasets_parameterized(dataset, request: FixtureRequest): @pytest.fixture -def scalar_dataset_with_nulls(dataset): +def scalar_dataset_with_nulls(dataset: DataSet) -> Generator[DataSet, None, None]: """ A very simple dataset. A scalar is varied, and two parameters are measured one by one @@ -186,7 +193,9 @@ def scalar_dataset_with_nulls(dataset): @pytest.fixture(scope="function", params=["array", "numeric"]) -def array_dataset(experiment, request: FixtureRequest): +def array_dataset( + experiment: Experiment, request: FixtureRequest +) -> Generator[DataSetProtocol, None, None]: meas = Measurement() param = ArraySetPointParam() meas.register_parameter(param, paramtype=request.param) @@ -206,7 +215,9 @@ def array_dataset(experiment, request: FixtureRequest): @pytest.fixture(scope="function", params=["array", "numeric"]) -def array_dataset_with_nulls(experiment, request: FixtureRequest): +def array_dataset_with_nulls( + experiment: Experiment, request: FixtureRequest +) -> Generator[DataSetProtocol, None, None]: """ A dataset where two arrays are measured, one as a function of two other (setpoint) arrays, the other as a function of just one @@ -235,7 +246,9 @@ def array_dataset_with_nulls(experiment, request: FixtureRequest): @pytest.fixture(scope="function", params=["array", "numeric"]) -def multi_dataset(experiment, request: FixtureRequest): +def multi_dataset( + experiment: Experiment, request: FixtureRequest +) -> Generator[DataSetProtocol, None, None]: meas = Measurement() param = Multi2DSetPointParam() @@ -264,7 +277,9 @@ class MeasurementOptions(StrEnum): scope="function", params=[MeasurementOptions.SHAPE_KNOWN, MeasurementOptions.SHAPE_UNKNOWN], ) -def different_setpoint_dataset(experiment, request: FixtureRequest): +def different_setpoint_dataset( + experiment: Experiment, request: FixtureRequest +) -> Generator[DataSetProtocol, None, None]: meas = Measurement() param = Multi2DSetPointParam2Sizes() @@ -328,7 +343,9 @@ def two_params_partial_2d_dataset( @pytest.fixture(scope="function") -def array_in_scalar_dataset(experiment): +def array_in_scalar_dataset( + experiment: Experiment, +) -> Generator[DataSetProtocol, None, None]: meas = Measurement() scalar_param = Parameter("scalarparam", set_cmd=None) param = ArraySetPointParam() @@ -349,7 +366,9 @@ def array_in_scalar_dataset(experiment): @pytest.fixture(scope="function") -def varlen_array_in_scalar_dataset(experiment): +def varlen_array_in_scalar_dataset( + experiment: Experiment, +) -> Generator[DataSetProtocol, None, None]: meas = Measurement() scalar_param = Parameter("scalarparam", set_cmd=None) param = ArraySetPointParam() @@ -371,7 +390,9 @@ def varlen_array_in_scalar_dataset(experiment): @pytest.fixture(scope="function") -def array_in_scalar_dataset_unrolled(experiment): +def array_in_scalar_dataset_unrolled( + experiment: Experiment, +) -> Generator[DataSetProtocol, None, None]: """ This fixture yields a dataset where an array-valued parameter is registered as a 'numeric' type and has an additional single-valued setpoint. We @@ -398,7 +419,9 @@ def array_in_scalar_dataset_unrolled(experiment): @pytest.fixture(scope="function", params=["array", "numeric"]) -def array_in_str_dataset(experiment, request: FixtureRequest): +def array_in_str_dataset( + experiment: Experiment, request: FixtureRequest +) -> Generator[DataSetProtocol, None, None]: meas = Measurement() scalar_param = Parameter("textparam", set_cmd=None) param = ArraySetPointParam() @@ -431,7 +454,7 @@ def some_paramspecbases() -> tuple[ @pytest.fixture -def some_paramspecs(): +def some_paramspecs() -> dict[int, dict[str, ParamSpec]]: """ Some different paramspecs for testing. The idea is that we just add a new group of paramspecs as the need arises @@ -481,7 +504,7 @@ def some_paramspecs(): @pytest.fixture -def some_interdeps(): +def some_interdeps() -> list[InterDependencies_]: """ Some different InterDependencies_ objects for testing """ @@ -510,21 +533,21 @@ def some_interdeps(): @pytest.fixture(name="DAC") # scope is "function" per default -def _make_dac(): +def _make_dac() -> Generator[DummyInstrument, None, None]: dac = DummyInstrument("dummy_dac", gates=["ch1", "ch2"]) yield dac dac.close() @pytest.fixture(name="DAC3D") # scope is "function" per default -def _make_dac_3d(): +def _make_dac_3d() -> Generator[DummyInstrument, None, None]: dac = DummyInstrument("dummy_dac", gates=["ch1", "ch2", "ch3"]) yield dac dac.close() @pytest.fixture(name="DAC_with_metadata") # scope is "function" per default -def _make_dac_with_metadata(): +def _make_dac_with_metadata() -> Generator[DummyInstrument, None, None]: dac = DummyInstrument( "dummy_dac", gates=["ch1", "ch2"], metadata={"dac": "metadata"} ) @@ -533,21 +556,21 @@ def _make_dac_with_metadata(): @pytest.fixture(name="DMM") -def _make_dmm(): +def _make_dmm() -> Generator[DummyInstrument, None, None]: dmm = DummyInstrument("dummy_dmm", gates=["v1", "v2"]) yield dmm dmm.close() @pytest.fixture -def channel_array_instrument(): +def channel_array_instrument() -> Generator[DummyChannelInstrument, None, None]: channelarrayinstrument = DummyChannelInstrument("dummy_channel_inst") yield channelarrayinstrument channelarrayinstrument.close() @pytest.fixture -def complex_num_instrument(): +def complex_num_instrument() -> Generator[DummyInstrument, None, None]: class MyParam(Parameter): def get_raw(self): assert self.instrument is not None @@ -642,7 +665,7 @@ def get_raw(self): @pytest.fixture -def SpectrumAnalyzer(): +def SpectrumAnalyzer() -> Generator[DummyInstrument, None, None]: """ Yields a DummyInstrument that holds ArrayParameters returning different types @@ -723,7 +746,9 @@ def get_raw(self): @pytest.fixture -def meas_with_registered_param(experiment, DAC, DMM): +def meas_with_registered_param( + experiment: Experiment, DAC: DummyInstrument, DMM: DummyInstrument +) -> Generator[Measurement, None, None]: meas = Measurement() meas.register_parameter(DAC.ch1) meas.register_parameter(DMM.v1, setpoints=[DAC.ch1]) @@ -731,7 +756,9 @@ def meas_with_registered_param(experiment, DAC, DMM): @pytest.fixture -def meas_with_registered_param_2d(experiment, DAC, DMM): +def meas_with_registered_param_2d( + experiment: Experiment, DAC: DummyInstrument, DMM: DummyInstrument +) -> Generator[Measurement, None, None]: meas = Measurement() meas.register_parameter(DAC.ch1) meas.register_parameter(DAC.ch2) @@ -740,7 +767,9 @@ def meas_with_registered_param_2d(experiment, DAC, DMM): @pytest.fixture -def meas_with_registered_param_3d(experiment, DAC3D, DMM): +def meas_with_registered_param_3d( + experiment: Experiment, DAC3D: DummyInstrument, DMM: DummyInstrument +) -> Generator[Measurement, None, None]: meas = Measurement() meas.register_parameter(DAC3D.ch1) meas.register_parameter(DAC3D.ch2) @@ -750,7 +779,11 @@ def meas_with_registered_param_3d(experiment, DAC3D, DMM): @pytest.fixture(name="meas_with_registered_param_complex") -def _make_meas_with_registered_param_complex(experiment, DAC, complex_num_instrument): +def _make_meas_with_registered_param_complex( + experiment: Experiment, + DAC: DummyInstrument, + complex_num_instrument: DummyInstrument, +) -> Generator[Measurement, None, None]: meas = Measurement() meas.register_parameter(DAC.ch1) meas.register_parameter(complex_num_instrument.complex_num, setpoints=[DAC.ch1]) diff --git a/tests/dataset/dond/conftest.py b/tests/dataset/dond/conftest.py index 947b5e3610d..e95d3e8628f 100644 --- a/tests/dataset/dond/conftest.py +++ b/tests/dataset/dond/conftest.py @@ -1,12 +1,19 @@ +from typing import TYPE_CHECKING + import matplotlib.pyplot as plt import pytest from qcodes import config, validators from qcodes.parameters import Parameter +if TYPE_CHECKING: + from collections.abc import Generator + + from _pytest._py.path import LocalPath + @pytest.fixture(autouse=True) -def set_tmp_output_dir(tmpdir): +def set_tmp_output_dir(tmpdir: "LocalPath") -> "Generator[None, None, None]": old_config = config.user.mainfolder try: config.user.mainfolder = str(tmpdir) @@ -16,25 +23,25 @@ def set_tmp_output_dir(tmpdir): @pytest.fixture() -def plot_close(): +def plot_close() -> "Generator[None, None, None]": yield plt.close("all") @pytest.fixture() -def _param(): +def _param() -> Parameter[int]: p = Parameter("simple_parameter", set_cmd=None, get_cmd=lambda: 1) return p @pytest.fixture() -def _param_2(): +def _param_2() -> Parameter[int]: p = Parameter("simple_parameter_2", set_cmd=None, get_cmd=lambda: 2) return p @pytest.fixture() -def _param_complex(): +def _param_complex() -> Parameter[complex]: p = Parameter( "simple_complex_parameter", set_cmd=None, @@ -45,7 +52,7 @@ def _param_complex(): @pytest.fixture() -def _param_complex_2(): +def _param_complex_2() -> Parameter[complex]: p = Parameter( "simple_complex_parameter_2", set_cmd=None, @@ -56,18 +63,18 @@ def _param_complex_2(): @pytest.fixture() -def _param_set(): +def _param_set() -> Parameter[float | None]: p = Parameter("simple_setter_parameter", set_cmd=None, get_cmd=None) return p @pytest.fixture() -def _param_set_2(): +def _param_set_2() -> Parameter[float | None]: p = Parameter("simple_setter_parameter_2", set_cmd=None, get_cmd=None) return p -def _param_func(_p): +def _param_func(_p: Parameter[int]) -> Parameter[int]: """ A private utility function. """ @@ -78,7 +85,7 @@ def _param_func(_p): @pytest.fixture() -def _param_callable(_param): +def _param_callable(_param: Parameter[int]) -> Parameter[int]: return _param_func(_param) @@ -88,5 +95,5 @@ def test_param_callable(_param_callable) -> None: @pytest.fixture() -def _string_callable(): +def _string_callable() -> str: return "Call" diff --git a/tests/delegate/conftest.py b/tests/delegate/conftest.py index 38868839ae0..a0d6d6de25b 100644 --- a/tests/delegate/conftest.py +++ b/tests/delegate/conftest.py @@ -1,32 +1,36 @@ import os import pathlib +from typing import TYPE_CHECKING import pytest from qcodes.instrument_drivers.mock_instruments import MockDAC, MockField, MockLockin from qcodes.station import Station +if TYPE_CHECKING: + from qcodes.instrument.delegate import InstrumentGroup + PARENT_DIR = pathlib.Path(__file__).parent.absolute() @pytest.fixture(scope="function") -def dac(): +def dac() -> MockDAC: return MockDAC("dac", num_channels=3) @pytest.fixture(scope="function") -def field_x(): +def field_x() -> MockField: return MockField("field_x") @pytest.fixture(scope="function") -def lockin(): +def lockin() -> MockLockin: _lockin = MockLockin(name="lockin") return _lockin @pytest.fixture(scope="function") -def station(dac, lockin, field_x): +def station(dac: MockDAC, lockin: MockLockin, field_x: MockField) -> Station: _station = Station() _station.add_component(dac) _station.add_component(lockin) @@ -35,12 +39,12 @@ def station(dac, lockin, field_x): @pytest.fixture() -def chip_config(): +def chip_config() -> str: return os.path.join(PARENT_DIR, "data/chip.yml") @pytest.fixture() -def chip(station, chip_config): +def chip(station: Station, chip_config: str) -> "InstrumentGroup": if hasattr(station, "MockChip_123"): return station.MockChip_123 @@ -50,5 +54,5 @@ def chip(station, chip_config): @pytest.fixture() -def chip_config_typo(): +def chip_config_typo() -> str: return os.path.join(PARENT_DIR, "data/chip_typo.yml") From f56757c4ec0d2f022b7959896a30ec9e45faf139 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Mon, 19 Oct 2026 14:07:26 +0200 Subject: [PATCH 03/20] Add type annotations to dataset tests Annotate dataset test fixtures, helpers, and test signatures while preserving intentional dynamic typing boundaries and constructor return annotations. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tests/dataset/conftest.py | 45 ++- tests/dataset/dond/conftest.py | 2 +- tests/dataset/dond/test_do0d.py | 71 +++-- tests/dataset/dond/test_do1d.py | 84 ++++-- tests/dataset/dond/test_do2d.py | 86 ++++-- tests/dataset/dond/test_dond.py | 269 +++++++++++++----- .../dond/test_dond_keyboard_interrupts.py | 13 +- tests/dataset/helper_functions.py | 11 +- .../test_measurement_context_manager.py | 222 +++++++++++---- .../dataset/measurement/test_register_name.py | 6 +- .../test_self_registering_parameters.py | 20 +- .../measurement/test_self_unpacking.py | 15 +- tests/dataset/measurement/test_shapes.py | 16 +- tests/dataset/test__get_data_from_ds.py | 37 ++- tests/dataset/test_concurrent_datasets.py | 21 +- tests/dataset/test_converters.py | 38 ++- tests/dataset/test_data_set_cache.py | 196 +++++++------ .../test_database_creation_and_upgrading.py | 7 +- tests/dataset/test_database_extract_runs.py | 82 ++++-- tests/dataset/test_datasaver.py | 19 +- tests/dataset/test_dataset_basic.py | 113 +++++--- tests/dataset/test_dataset_export.py | 2 +- tests/dataset/test_dataset_in_mem_import.py | 39 ++- tests/dataset/test_dataset_in_memory.py | 112 ++++++-- tests/dataset/test_dataset_in_memory_bacis.py | 20 +- tests/dataset/test_dataset_loading.py | 16 +- tests/dataset/test_dependencies.py | 50 +++- tests/dataset/test_descriptions.py | 34 ++- tests/dataset/test_detect_shape.py | 56 +++- tests/dataset/test_experiment_container.py | 40 +-- tests/dataset/test_export_info.py | 2 +- tests/dataset/test_fix_functions.py | 2 +- tests/dataset/test_guids.py | 22 +- tests/dataset/test_links.py | 4 +- tests/dataset/test_measurement_extensions.py | 137 ++++++--- tests/dataset/test_metadata.py | 12 +- tests/dataset/test_nested_measurements.py | 30 +- ...st_parameter_with_setpoints_has_control.py | 4 +- tests/dataset/test_paramspec.py | 39 ++- tests/dataset/test_plotting.py | 25 +- tests/dataset/test_raw_data_storage.py | 4 +- tests/dataset/test_snapshot.py | 21 +- tests/dataset/test_sqlite_base.py | 84 +++--- tests/dataset/test_sqlite_connection.py | 22 +- tests/dataset/test_string_data.py | 21 +- tests/dataset/test_subscribing.py | 48 +++- 46 files changed, 1577 insertions(+), 642 deletions(-) diff --git a/tests/dataset/conftest.py b/tests/dataset/conftest.py index c2a7c37eee4..f075014c49a 100644 --- a/tests/dataset/conftest.py +++ b/tests/dataset/conftest.py @@ -33,6 +33,11 @@ ) from qcodes.validators import Arrays, ComplexNumbers, Numbers +if TYPE_CHECKING: + from numpy.typing import NDArray + + from qcodes.instrument import InstrumentBase + if TYPE_CHECKING: from collections.abc import Generator, Iterator from pathlib import Path @@ -88,7 +93,9 @@ def two_empty_temp_db_connections( @contextmanager -def temporarily_copied_DB(filepath: str, **kwargs): +def temporarily_copied_DB( + filepath: str, **kwargs +) -> Generator[AtomicConnection, None, None]: """ Make a temporary copy of a db-file and delete it after use. Meant to be used together with the old version database fixtures, lest we change the @@ -572,12 +579,12 @@ def channel_array_instrument() -> Generator[DummyChannelInstrument, None, None]: @pytest.fixture def complex_num_instrument() -> Generator[DummyInstrument, None, None]: class MyParam(Parameter): - def get_raw(self): + def get_raw(self) -> complex: assert self.instrument is not None return self.instrument.setpoint() + 1j * self.instrument.setpoint() class RealPartParam(Parameter): - def get_raw(self): + def get_raw(self) -> float: assert self.instrument is not None return self.instrument.complex_setpoint().real @@ -672,7 +679,12 @@ def SpectrumAnalyzer() -> Generator[DummyInstrument, None, None]: """ class BaseSpectrum(ArrayParameter): - def __init__(self, name, instrument, **kwargs): + def __init__( + self, + name: str, + instrument: InstrumentBase, + **kwargs, + ): super().__init__( name=name, shape=(1,), # this attribute should be removed @@ -688,7 +700,7 @@ def __init__(self, name, instrument, **kwargs): self.stop = 2e6 self._rng = np.random.default_rng() - def get_data(self): + def get_data(self) -> NDArray[np.float64]: # This is how it should be: the setpoints are generated at the # time we get. But that will of course not work with the old Loop self.setpoints = (tuple(np.linspace(self.start, self.stop, self.npts)),) @@ -696,11 +708,16 @@ def get_data(self): return self._rng.standard_normal(self.npts) class Spectrum(BaseSpectrum): - def get_raw(self): + def get_raw(self) -> NDArray[np.float64]: return super().get_data() class MultiDimSpectrum(ArrayParameter): - def __init__(self, name, instrument, **kwargs): + def __init__( + self, + name: str, + instrument: InstrumentBase, + **kwargs, + ): self.start = 0 self.stop = 2e6 self.npts = (100, 50, 20) @@ -722,16 +739,16 @@ def __init__(self, name, instrument, **kwargs): ) self._rng = np.random.default_rng() - def get_raw(self): + def get_raw(self) -> NDArray[np.float64]: return self._rng.standard_normal(self.npts) class ListSpectrum(BaseSpectrum): - def get_raw(self): + def get_raw(self) -> list[np.float64]: output = super().get_data() return list(output) class TupleSpectrum(BaseSpectrum): - def get_raw(self): + def get_raw(self) -> tuple[np.float64, ...]: output = super().get_data() return tuple(output) @@ -800,11 +817,15 @@ def _make_dummy_instrument() -> Iterator[DummyChannelInstrument]: class ArrayshapedParam(Parameter): - def __init__(self, *args, **kwargs): + def __init__( + self, + *args: str, + **kwargs, + ): super().__init__(*args, **kwargs) self._rng = np.random.default_rng() - def get_raw(self): + def get_raw(self) -> NDArray[np.float64]: assert isinstance(self.vals, Arrays) assert self.vals.shape is not None shape = self.vals.shape diff --git a/tests/dataset/dond/conftest.py b/tests/dataset/dond/conftest.py index e95d3e8628f..5e368610bed 100644 --- a/tests/dataset/dond/conftest.py +++ b/tests/dataset/dond/conftest.py @@ -89,7 +89,7 @@ def _param_callable(_param: Parameter[int]) -> Parameter[int]: return _param_func(_param) -def test_param_callable(_param_callable) -> None: +def test_param_callable(_param_callable: Parameter[int]) -> None: _param_modified = _param_callable assert _param_modified.get() == 2 diff --git a/tests/dataset/dond/test_do0d.py b/tests/dataset/dond/test_do0d.py index a7e61e12f9e..35c5d5b48f3 100644 --- a/tests/dataset/dond/test_do0d.py +++ b/tests/dataset/dond/test_do0d.py @@ -1,3 +1,5 @@ +from typing import TYPE_CHECKING + import hypothesis.strategies as hst import matplotlib.axes import numpy as np @@ -10,18 +12,27 @@ from qcodes.dataset.data_set import DataSet from qcodes.instrument_drivers.mock_instruments import ( ArraySetPointParam, + DummyChannelInstrument, Multi2DSetPointParam, Multi2DSetPointParam2Sizes, MultiSetPointParam, ) from tests.dataset.conftest import ArrayshapedParam +if TYPE_CHECKING: + from pathlib import Path + + from qcodes.dataset.experiment_container import Experiment + from qcodes.parameters import Parameter + @pytest.mark.usefixtures("plot_close", "experiment") @pytest.mark.parametrize("period", [None, 1]) @pytest.mark.parametrize("plot", [None, True, False]) @pytest.mark.parametrize("plot_config", [True, None, False]) -def test_do0d_with_real_parameter(period, plot, plot_config) -> None: +def test_do0d_with_real_parameter( + period: float | None, plot: bool | None, plot_config: bool | None +) -> None: arrayparam = ArraySetPointParam(name="arrayparam") if plot_config is not None: @@ -39,7 +50,9 @@ def test_do0d_with_real_parameter(period, plot, plot_config) -> None: @pytest.mark.parametrize( "period, plot", [(None, True), (None, False), (1, True), (1, False)] ) -def test_do0d_with_complex_parameter(_param_complex, period, plot) -> None: +def test_do0d_with_complex_parameter( + _param_complex: "Parameter[complex]", period: float | None, plot: bool +) -> None: do0d(_param_complex, write_period=period, do_plot=plot) @@ -47,7 +60,9 @@ def test_do0d_with_complex_parameter(_param_complex, period, plot) -> None: @pytest.mark.parametrize( "period, plot", [(None, True), (None, False), (1, True), (1, False)] ) -def test_do0d_with_a_callable(_param_callable, period, plot) -> None: +def test_do0d_with_a_callable( + _param_callable: "Parameter[int]", period: float | None, plot: bool +) -> None: do0d(_param_callable, write_period=period, do_plot=plot) @@ -55,7 +70,12 @@ def test_do0d_with_a_callable(_param_callable, period, plot) -> None: @pytest.mark.parametrize( "period, plot", [(None, True), (None, False), (1, True), (1, False)] ) -def test_do0d_with_2_parameters(_param, _param_complex, period, plot) -> None: +def test_do0d_with_2_parameters( + _param: "Parameter[int]", + _param_complex: "Parameter[complex]", + period: float | None, + plot: bool, +) -> None: do0d(_param, _param_complex, write_period=period, do_plot=plot) @@ -64,31 +84,36 @@ def test_do0d_with_2_parameters(_param, _param_complex, period, plot) -> None: "period, plot", [(None, True), (None, False), (1, True), (1, False)] ) def test_do0d_with_parameter_and_a_callable( - _param_complex, _param_callable, period, plot + _param_complex: "Parameter[complex]", + _param_callable: "Parameter[int]", + period: float | None, + plot: bool, ) -> None: do0d(_param_callable, _param_complex, write_period=period, do_plot=plot) @pytest.mark.usefixtures("plot_close", "experiment") -def test_do0d_output_type_real_parameter(_param) -> None: +def test_do0d_output_type_real_parameter(_param: "Parameter[int]") -> None: data = do0d(_param) assert isinstance(data[0], DataSet) is True @pytest.mark.usefixtures("plot_close", "experiment") -def test_do0d_output_type_complex_parameter(_param_complex) -> None: +def test_do0d_output_type_complex_parameter( + _param_complex: "Parameter[complex]", +) -> None: data_complex = do0d(_param_complex) assert isinstance(data_complex[0], DataSet) is True @pytest.mark.usefixtures("plot_close", "experiment") -def test_do0d_output_type_callable(_param_callable) -> None: +def test_do0d_output_type_callable(_param_callable: "Parameter[int]") -> None: data_func = do0d(_param_callable) assert isinstance(data_func[0], DataSet) is True @pytest.mark.usefixtures("plot_close", "experiment") -def test_do0d_output_data(_param) -> None: +def test_do0d_output_data(_param: "Parameter[int]") -> None: exp = do0d(_param) data = exp[0] assert data.description.interdeps.names == (_param.name,) @@ -97,7 +122,7 @@ def test_do0d_output_data(_param) -> None: @pytest.mark.usefixtures("experiment") -def test_do0d_export_to_netcdf(_param, tmp_path) -> None: +def test_do0d_export_to_netcdf(_param: "Parameter[int]", tmp_path: "Path") -> None: dataset = do0d(_param, do_plot=False)[0] dataset.export(export_type="netcdf", path=tmp_path) @@ -111,7 +136,7 @@ def test_do0d_export_to_netcdf(_param, tmp_path) -> None: @pytest.mark.usefixtures("experiment") -def test_do0d_array_export_to_netcdf(tmp_path) -> None: +def test_do0d_array_export_to_netcdf(tmp_path: "Path") -> None: param = ArrayshapedParam( name="paramwitharrayval", vals=validators.Arrays(shape=(10,)) ) @@ -140,7 +165,13 @@ def test_do0d_array_export_to_netcdf(tmp_path) -> None: suppress_health_check=(HealthCheck.function_scoped_fixture,), ) def test_do0d_verify_shape( - _param, _param_complex, multiparamtype, dummyinstrument, n_points_pws + _param: "Parameter[int]", + _param_complex: "Parameter[complex]", + multiparamtype: type[ + MultiSetPointParam | Multi2DSetPointParam | Multi2DSetPointParam2Sizes + ], + dummyinstrument: DummyChannelInstrument, + n_points_pws: int, ) -> None: arrayparam = ArraySetPointParam(name="arrayparam") multiparam = multiparamtype(name="multiparam") @@ -182,7 +213,9 @@ def test_do0d_parameter_with_array_vals() -> None: assert results[0].description.shapes == expected_shapes -def test_do0d_explicit_experiment(_param, experiment) -> None: +def test_do0d_explicit_experiment( + _param: "Parameter[int]", experiment: "Experiment" +) -> None: experiment_2 = new_experiment("new-exp", "no-sample") data1 = do0d(_param, do_plot=False, exp=experiment) @@ -195,13 +228,15 @@ def test_do0d_explicit_experiment(_param, experiment) -> None: @pytest.mark.usefixtures("experiment") -def test_do0d_explicit_name(_param) -> None: +def test_do0d_explicit_name(_param: "Parameter[int]") -> None: data1 = do0d(_param, do_plot=False, measurement_name="my measurement") assert data1[0].name == "my measurement" @pytest.mark.usefixtures("experiment") -def test_do0d_parameter_with_setpoints_2d(dummyinstrument) -> None: +def test_do0d_parameter_with_setpoints_2d( + dummyinstrument: DummyChannelInstrument, +) -> None: dummyinstrument.A.dummy_start(0) dummyinstrument.A.dummy_stop(10) dummyinstrument.A.dummy_n_points(10) @@ -218,7 +253,11 @@ def test_do0d_parameter_with_setpoints_2d(dummyinstrument) -> None: @pytest.mark.usefixtures("plot_close", "experiment") -def test_dond_0d_output_type(_param, _param_complex, _param_callable) -> None: +def test_dond_0d_output_type( + _param: "Parameter[int]", + _param_complex: "Parameter[complex]", + _param_callable: "Parameter[int]", +) -> None: data_1 = do0d(_param) assert isinstance(data_1[0], DataSet) is True diff --git a/tests/dataset/dond/test_do1d.py b/tests/dataset/dond/test_do1d.py index 2c19c4f43fc..342c73874be 100644 --- a/tests/dataset/dond/test_do1d.py +++ b/tests/dataset/dond/test_do1d.py @@ -1,4 +1,5 @@ import logging +from typing import TYPE_CHECKING import hypothesis.strategies as hst import matplotlib.axes @@ -13,6 +14,7 @@ from qcodes.dataset.data_set import DataSet from qcodes.instrument_drivers.mock_instruments import ( ArraySetPointParam, + DummyChannelInstrument, Multi2DSetPointParam, Multi2DSetPointParam2Sizes, MultiSetPointParam, @@ -20,10 +22,15 @@ from qcodes.parameters import Parameter from tests.dataset.conftest import ArrayshapedParam +if TYPE_CHECKING: + from qcodes.dataset.experiment_container import Experiment + @pytest.mark.usefixtures("plot_close", "experiment") @pytest.mark.parametrize("delay", [0, 0.1, 1]) -def test_do1d_with_real_parameter(_param_set, _param, delay) -> None: +def test_do1d_with_real_parameter( + _param_set: Parameter[float | None], _param: Parameter[int], delay: float +) -> None: start = 0 stop = 1 num_points = 1 @@ -34,7 +41,12 @@ def test_do1d_with_real_parameter(_param_set, _param, delay) -> None: @pytest.mark.usefixtures("plot_close", "experiment") @pytest.mark.parametrize("plot", [None, True, False]) @pytest.mark.parametrize("plot_config", [None, True, False]) -def test_do1d_plot(_param_set, _param, plot, plot_config) -> None: +def test_do1d_plot( + _param_set: Parameter[float | None], + _param: Parameter[int], + plot: bool | None, + plot_config: bool | None, +) -> None: if plot_config is not None: config.dataset.dond_plot = plot_config @@ -52,7 +64,11 @@ def test_do1d_plot(_param_set, _param, plot, plot_config) -> None: @pytest.mark.usefixtures("plot_close", "experiment") @pytest.mark.parametrize("delay", [0, 0.1, 1]) -def test_do1d_with_complex_parameter(_param_set, _param_complex, delay) -> None: +def test_do1d_with_complex_parameter( + _param_set: Parameter[float | None], + _param_complex: Parameter[complex], + delay: float, +) -> None: start = 0 stop = 1 num_points = 1 @@ -62,7 +78,12 @@ def test_do1d_with_complex_parameter(_param_set, _param_complex, delay) -> None: @pytest.mark.usefixtures("plot_close", "experiment") @pytest.mark.parametrize("delay", [0, 0.1, 1]) -def test_do1d_with_2_parameter(_param_set, _param, _param_complex, delay) -> None: +def test_do1d_with_2_parameter( + _param_set: Parameter[float | None], + _param: Parameter[int], + _param_complex: Parameter[complex], + delay: float, +) -> None: start = 0 stop = 1 num_points = 1 @@ -72,7 +93,9 @@ def test_do1d_with_2_parameter(_param_set, _param, _param_complex, delay) -> Non @pytest.mark.usefixtures("plot_close", "experiment") @pytest.mark.parametrize("delay", [0, 0.1, 1]) -def test_do1d_output_type_real_parameter(_param_set, _param, delay) -> None: +def test_do1d_output_type_real_parameter( + _param_set: Parameter[float | None], _param: Parameter[int], delay: float +) -> None: start = 0 stop = 1 num_points = 1 @@ -82,7 +105,9 @@ def test_do1d_output_type_real_parameter(_param_set, _param, delay) -> None: @pytest.mark.usefixtures("plot_close", "experiment") -def test_do1d_output_data(_param, _param_set) -> None: +def test_do1d_output_data( + _param: Parameter[int], _param_set: Parameter[float | None] +) -> None: start = 0 stop = 1 num_points = 5 @@ -113,13 +138,15 @@ def test_do1d_output_data(_param, _param_set) -> None: suppress_health_check=(HealthCheck.function_scoped_fixture,), ) def test_do1d_verify_shape( - _param, - _param_complex, - _param_set, - multiparamtype, - dummyinstrument, - num_points, - n_points_pws, + _param: Parameter[int], + _param_complex: Parameter[complex], + _param_set: Parameter[float | None], + multiparamtype: type[ + MultiSetPointParam | Multi2DSetPointParam | Multi2DSetPointParam2Sizes + ], + dummyinstrument: DummyChannelInstrument, + num_points: int, + n_points_pws: int, ) -> None: arrayparam = ArraySetPointParam(name="arrayparam") multiparam = multiparamtype(name="multiparam") @@ -156,7 +183,7 @@ def test_do1d_verify_shape( @pytest.mark.usefixtures("experiment") -def test_do1d_parameter_with_array_vals(_param_set) -> None: +def test_do1d_parameter_with_array_vals(_param_set: Parameter[float | None]) -> None: param = ArrayshapedParam( name="paramwitharrayval", vals=validators.Arrays(shape=(10,)) ) @@ -179,7 +206,11 @@ def test_do1d_parameter_with_array_vals(_param_set) -> None: assert param_data.shape == expected_shapes[name] -def test_do1d_explicit_experiment(_param_set, _param, experiment) -> None: +def test_do1d_explicit_experiment( + _param_set: Parameter[float | None], + _param: Parameter[int], + experiment: "Experiment", +) -> None: start = 0 stop = 1 num_points = 5 @@ -215,7 +246,9 @@ def test_do1d_explicit_experiment(_param_set, _param, experiment) -> None: @pytest.mark.usefixtures("experiment") -def test_do1d_explicit_name(_param_set, _param) -> None: +def test_do1d_explicit_name( + _param_set: Parameter[float | None], _param: Parameter[int] +) -> None: start = 0 stop = 1 num_points = 5 @@ -235,7 +268,11 @@ def test_do1d_explicit_name(_param_set, _param) -> None: @pytest.mark.usefixtures("plot_close", "experiment") -def test_do1d_additional_setpoints(_param, _param_complex, _param_set) -> None: +def test_do1d_additional_setpoints( + _param: Parameter[int], + _param_complex: Parameter[complex], + _param_set: Parameter[float | None], +) -> None: additional_setpoints = [ Parameter(f"additional_setter_parameter_{i}", set_cmd=None, get_cmd=None) for i in range(2) @@ -271,7 +308,10 @@ def test_do1d_additional_setpoints(_param, _param_complex, _param_set) -> None: @given(num_points_p1=hst.integers(min_value=1, max_value=10)) @pytest.mark.usefixtures("experiment") def test_do1d_additional_setpoints_shape( - _param, _param_complex, _param_set, num_points_p1 + _param: Parameter[int], + _param_complex: Parameter[complex], + _param_set: Parameter[float | None], + num_points_p1: int, ) -> None: arrayparam = ArraySetPointParam(name="arrayparam") array_shape = arrayparam.shape @@ -307,13 +347,17 @@ def test_do1d_additional_setpoints_shape( @pytest.mark.usefixtures("plot_close", "experiment") -def test_do1d_break_condition(caplog: LogCaptureFixture, _param_set, _param) -> None: +def test_do1d_break_condition( + caplog: LogCaptureFixture, + _param_set: Parameter[float | None], + _param: Parameter[int], +) -> None: start = 0 stop = 1 num_points = 5 delay = 0 - def break_condition(): + def break_condition() -> bool: return True data = do1d( diff --git a/tests/dataset/dond/test_do2d.py b/tests/dataset/dond/test_do2d.py index 3f1d8578ef5..6202deb81c3 100644 --- a/tests/dataset/dond/test_do2d.py +++ b/tests/dataset/dond/test_do2d.py @@ -2,6 +2,8 @@ These are the basic black box tests for the doNd functions. """ +from typing import TYPE_CHECKING + import hypothesis.strategies as hst import matplotlib.axes import matplotlib.pyplot as plt @@ -14,18 +16,29 @@ from qcodes.dataset.data_set import DataSet from qcodes.instrument_drivers.mock_instruments import ( ArraySetPointParam, + DummyChannelInstrument, Multi2DSetPointParam, Multi2DSetPointParam2Sizes, MultiSetPointParam, ) from qcodes.parameters import Parameter +if TYPE_CHECKING: + from qcodes.dataset.experiment_container import Experiment + @pytest.mark.usefixtures("plot_close", "experiment") @pytest.mark.parametrize( "sweep, columns", [(False, False), (False, True), (True, False), (True, True)] ) -def test_do2d(_param, _param_complex, _param_set, _param_set_2, sweep, columns) -> None: +def test_do2d( + _param: Parameter[int], + _param_complex: Parameter[complex], + _param_set: Parameter[float | None], + _param_set_2: Parameter[float | None], + sweep: bool, + columns: bool, +) -> None: start_p1 = 0 stop_p1 = 1 num_points_p1 = 1 @@ -57,7 +70,13 @@ def test_do2d(_param, _param_complex, _param_set, _param_set_2, sweep, columns) @pytest.mark.usefixtures("plot_close", "experiment") @pytest.mark.parametrize("plot", [None, True, False]) @pytest.mark.parametrize("plot_config", [None, True, False]) -def test_do2d_plot(_param_set, _param_set_2, _param, plot, plot_config) -> None: +def test_do2d_plot( + _param_set: Parameter[float | None], + _param_set_2: Parameter[float | None], + _param: Parameter[int], + plot: bool | None, + plot_config: bool | None, +) -> None: if plot_config is not None: config.dataset.dond_plot = plot_config @@ -94,7 +113,12 @@ def test_do2d_plot(_param_set, _param_set_2, _param, plot, plot_config) -> None: @pytest.mark.usefixtures("plot_close", "experiment") -def test_do2d_output_type(_param, _param_complex, _param_set, _param_set_2) -> None: +def test_do2d_output_type( + _param: Parameter[int], + _param_complex: Parameter[complex], + _param_set: Parameter[float | None], + _param_set_2: Parameter[float | None], +) -> None: start_p1 = 0 stop_p1 = 0.5 num_points_p1 = 1 @@ -123,7 +147,12 @@ def test_do2d_output_type(_param, _param_complex, _param_set, _param_set_2) -> N @pytest.mark.usefixtures("plot_close", "experiment") -def test_do2d_output_data(_param, _param_complex, _param_set, _param_set_2) -> None: +def test_do2d_output_data( + _param: Parameter[int], + _param_complex: Parameter[complex], + _param_set: Parameter[float | None], + _param_set_2: Parameter[float | None], +) -> None: start_p1 = 0 stop_p1 = 0.5 num_points_p1 = 5 @@ -201,17 +230,19 @@ def test_do2d_output_data(_param, _param_complex, _param_set, _param_set_2) -> N suppress_health_check=(HealthCheck.function_scoped_fixture,), ) def test_do2d_verify_shape( - _param, - _param_complex, - _param_set, - _param_set_2, - multiparamtype, - dummyinstrument, - sweep, - columns, - num_points_p1, - num_points_p2, - n_points_pws, + _param: Parameter[int], + _param_complex: Parameter[complex], + _param_set: Parameter[float | None], + _param_set_2: Parameter[float | None], + multiparamtype: type[ + MultiSetPointParam | Multi2DSetPointParam | Multi2DSetPointParam2Sizes + ], + dummyinstrument: DummyChannelInstrument, + sweep: bool, + columns: bool, + num_points_p1: int, + num_points_p2: int, + n_points_pws: int, ) -> None: arrayparam = ArraySetPointParam(name="arrayparam") multiparam = multiparamtype(name="multiparam") @@ -280,7 +311,10 @@ def test_do2d_verify_shape( @pytest.mark.usefixtures("plot_close", "experiment") def test_do2d_additional_setpoints( - _param, _param_complex, _param_set, _param_set_2 + _param: Parameter[int], + _param_complex: Parameter[complex], + _param_set: Parameter[float | None], + _param_set_2: Parameter[float | None], ) -> None: additional_setpoints = [ Parameter(f"additional_setter_parameter_{i}", set_cmd=None, get_cmd=None) @@ -326,7 +360,12 @@ def test_do2d_additional_setpoints( @settings(deadline=None, suppress_health_check=(HealthCheck.function_scoped_fixture,)) @pytest.mark.usefixtures("experiment") def test_do2d_additional_setpoints_shape( - _param, _param_complex, _param_set, _param_set_2, num_points_p1, num_points_p2 + _param: Parameter[int], + _param_complex: Parameter[complex], + _param_set: Parameter[float | None], + _param_set_2: Parameter[float | None], + num_points_p1: int, + num_points_p2: int, ) -> None: arrayparam = ArraySetPointParam(name="arrayparam") array_shape = arrayparam.shape @@ -372,7 +411,12 @@ def test_do2d_additional_setpoints_shape( assert results[0].description.shapes == expected_shapes -def test_do2d_explicit_experiment(_param_set, _param_set_2, _param, experiment) -> None: +def test_do2d_explicit_experiment( + _param_set: Parameter[float | None], + _param_set_2: Parameter[float | None], + _param: Parameter[int], + experiment: "Experiment", +) -> None: start_p1 = 0 stop_p1 = 0.5 num_points_p1 = 5 @@ -436,7 +480,11 @@ def test_do2d_explicit_experiment(_param_set, _param_set_2, _param, experiment) @pytest.mark.usefixtures("experiment") -def test_do2d_explicit_name(_param_set, _param_set_2, _param) -> None: +def test_do2d_explicit_name( + _param_set: Parameter[float | None], + _param_set_2: Parameter[float | None], + _param: Parameter[int], +) -> None: start_p1 = 0 stop_p1 = 0.5 num_points_p1 = 5 diff --git a/tests/dataset/dond/test_dond.py b/tests/dataset/dond/test_dond.py index d79beef6a79..6e1de2e46de 100644 --- a/tests/dataset/dond/test_dond.py +++ b/tests/dataset/dond/test_dond.py @@ -5,7 +5,7 @@ import logging import re from functools import partial -from typing import assert_type +from typing import TYPE_CHECKING, assert_type import hypothesis.strategies as hst import matplotlib.axes @@ -31,6 +31,7 @@ from qcodes.dataset.dond.do_nd import _Sweeper from qcodes.instrument_drivers.mock_instruments import ( ArraySetPointParam, + DummyChannelInstrument, Multi2DSetPointParam, Multi2DSetPointParam2Sizes, MultiSetPointParam, @@ -44,20 +45,25 @@ from qcodes.validators import Arrays, Ints from tests.dataset.conftest import ArrayshapedParam +if TYPE_CHECKING: + from pytest_mock import MockerFixture + + from qcodes.dataset.experiment_container import Experiment + class TrackingParameter(Parameter): """Parameter that keeps track of number of get and set operations""" - def __init__(self, *args, **kwargs): + def __init__(self, *args: str, **kwargs): self.set_count = 0 self.get_count = 0 super().__init__(*args, **kwargs) - def set_raw(self, value): + def set_raw(self, value: object) -> None: self.set_count += 1 self.cache._set_from_raw_value(value) - def get_raw(self): + def get_raw(self) -> object: self.get_count += 1 return self.cache.raw_value @@ -71,16 +77,16 @@ class GetReturnsCountParameter(Parameter): Allows you to set a value but returns the get count rather than the value""" - def __init__(self, *args, **kwargs): + def __init__(self, *args: str, **kwargs): self.set_count = 0 self.get_count = 0 super().__init__(*args, **kwargs) - def set_raw(self, value): + def set_raw(self, value: object) -> None: self.set_count += 1 self.cache._set_from_raw_value(value) - def get_raw(self): + def get_raw(self) -> int: self.get_count += 1 return self.get_count @@ -89,7 +95,7 @@ def reset_count(self) -> None: self.set_count = 0 -def test_linear_sweep_get_setpoints(_param) -> None: +def test_linear_sweep_get_setpoints(_param: Parameter[int]) -> None: start = 0 stop = 1 num_points = 5 @@ -104,7 +110,12 @@ def test_linear_sweep_get_setpoints(_param) -> None: @pytest.mark.usefixtures("experiment") @pytest.mark.parametrize("cache_config", [True, False]) @pytest.mark.parametrize("cache_setting", [True, False, None]) -def test_cache_config(_param, _param_2, cache_config, cache_setting) -> None: +def test_cache_config( + _param: Parameter[int], + _param_2: Parameter[int], + cache_config: bool, + cache_setting: bool | None, +) -> None: qc.config.dataset.in_memory_cache = cache_config start = 0 stop = 1 @@ -121,7 +132,9 @@ def test_cache_config(_param, _param_2, cache_config, cache_setting) -> None: assert ds.cache.live is None -def test_linear_sweep_properties(_param, _param_complex) -> None: +def test_linear_sweep_properties( + _param: Parameter[int], _param_complex: Parameter[complex] +) -> None: start = 0 stop = 1 num_points = 5 @@ -137,7 +150,9 @@ def test_linear_sweep_properties(_param, _param_complex) -> None: assert sweep_2.delay == 0 -def test_linear_sweep_parameter_class(_param, _param_complex) -> None: +def test_linear_sweep_parameter_class( + _param: Parameter[int], _param_complex: Parameter[complex] +) -> None: start = 0 stop = 1 num_points = 5 @@ -153,7 +168,7 @@ def test_linear_sweep_parameter_class(_param, _param_complex) -> None: assert isinstance(sweep_3.param, ParameterBase) -def test_log_sweep_get_setpoints(_param) -> None: +def test_log_sweep_get_setpoints(_param: Parameter[int]) -> None: start = 0 stop = 1 num_points = 5 @@ -165,7 +180,9 @@ def test_log_sweep_get_setpoints(_param) -> None: ) -def test_log_sweep_properties(_param, _param_complex) -> None: +def test_log_sweep_properties( + _param: Parameter[int], _param_complex: Parameter[complex] +) -> None: start = 0 stop = 1 num_points = 5 @@ -181,7 +198,9 @@ def test_log_sweep_properties(_param, _param_complex) -> None: assert sweep_2.delay == 0 -def test_log_sweep_parameter_class(_param, _param_complex) -> None: +def test_log_sweep_parameter_class( + _param: Parameter[int], _param_complex: Parameter[complex] +) -> None: start = 0 stop = 1 num_points = 5 @@ -197,7 +216,7 @@ def test_log_sweep_parameter_class(_param, _param_complex) -> None: assert isinstance(sweep_3.param, ParameterBase) -def test_array_sweep_get_setpoints(_param) -> None: +def test_array_sweep_get_setpoints(_param: Parameter[int]) -> None: array = np.linspace(0, 1, 5) delay = 1 sweep = ArraySweep(_param, array, delay) @@ -210,7 +229,7 @@ def test_array_sweep_get_setpoints(_param) -> None: np.testing.assert_array_equal(sweep2.get_setpoints(), np.array(array2)) -def test_array_sweep_properties(_param) -> None: +def test_array_sweep_properties(_param: Parameter[int]) -> None: array = np.linspace(0, 1, 5) delay = 1 sweep = ArraySweep(_param, array, delay) @@ -224,7 +243,9 @@ def test_array_sweep_properties(_param) -> None: assert sweep_2.delay == 0 -def test_array_sweep_parameter_class(_param, _param_complex) -> None: +def test_array_sweep_parameter_class( + _param: Parameter[int], _param_complex: Parameter[complex] +) -> None: array = np.linspace(0, 1, 5) sweep = ArraySweep(_param, array) @@ -238,7 +259,9 @@ def test_array_sweep_parameter_class(_param, _param_complex) -> None: assert isinstance(sweep_3.param, ParameterBase) -def test_dond_explicit_exp_meas_sample(_param, experiment) -> None: +def test_dond_explicit_exp_meas_sample( + _param: Parameter[int], experiment: "Experiment" +) -> None: experiment_2 = new_experiment("new-exp", "no-sample") data1 = dond(_param, do_plot=False, exp=experiment) @@ -263,7 +286,7 @@ def test_dond_explicit_exp_meas_sample(_param, experiment) -> None: def test_dond_multi_datasets_explicit_exp_meas_sample( - _param, _param_complex, experiment + _param: Parameter[int], _param_complex: Parameter[complex], experiment: "Experiment" ) -> None: experiment_2 = new_experiment("new-exp", "no-sample") @@ -295,7 +318,7 @@ def test_dond_multi_datasets_explicit_exp_meas_sample( def test_dond_multi_datasets_explicit_meas_names( - _param, _param_complex, experiment + _param: Parameter[int], _param_complex: Parameter[complex], experiment: "Experiment" ) -> None: data1 = dond( [_param], @@ -310,7 +333,9 @@ def test_dond_multi_datasets_explicit_meas_names( assert datasets1[1].name == "bar" -def test_dond_multi_datasets_meas_names_len_mismatch(_param, experiment) -> None: +def test_dond_multi_datasets_meas_names_len_mismatch( + _param: Parameter[int], experiment: "Experiment" +) -> None: with pytest.raises( ValueError, match=re.escape("Got 2 measurement names but should create 1 dataset(s)."), @@ -335,7 +360,13 @@ def test_dond_multi_datasets_meas_names_len_mismatch(_param, experiment) -> None suppress_health_check=(HealthCheck.function_scoped_fixture,), ) def test_dond_0d_verify_shape( - _param, _param_complex, multiparamtype, dummyinstrument, n_points_pws + _param: Parameter[int], + _param_complex: Parameter[complex], + multiparamtype: type[ + MultiSetPointParam | Multi2DSetPointParam | Multi2DSetPointParam2Sizes + ], + dummyinstrument: DummyChannelInstrument, + n_points_pws: int, ) -> None: arrayparam = ArraySetPointParam(name="arrayparam") multiparam = multiparamtype(name="multiparam") @@ -395,7 +426,7 @@ def complex_get_cmd() -> np.ndarray: @pytest.mark.usefixtures("plot_close", "experiment") -def test_dond_0d_output_data(_param) -> None: +def test_dond_0d_output_data(_param: Parameter[int]) -> None: exp = dond(_param) data = exp[0] assert isinstance(data, DataSetProtocol) @@ -420,7 +451,9 @@ def test_dond_0d_parameter_with_array_vals() -> None: @pytest.mark.parametrize("period", [None, 1]) @pytest.mark.parametrize("plot", [None, True, False]) @pytest.mark.parametrize("plot_config", [None, True, False]) -def test_dond_0d_with_real_parameter(period, plot, plot_config) -> None: +def test_dond_0d_with_real_parameter( + period: float | None, plot: bool | None, plot_config: bool | None +) -> None: arrayparam = ArraySetPointParam(name="arrayparam") if plot_config is not None: @@ -435,7 +468,9 @@ def test_dond_0d_with_real_parameter(period, plot, plot_config) -> None: @pytest.mark.usefixtures("plot_close", "experiment") -def test_dond_0d_parameter_with_setpoints_2d(dummyinstrument) -> None: +def test_dond_0d_parameter_with_setpoints_2d( + dummyinstrument: DummyChannelInstrument, +) -> None: dummyinstrument.A.dummy_start(0) dummyinstrument.A.dummy_stop(10) dummyinstrument.A.dummy_n_points(10) @@ -452,7 +487,7 @@ def test_dond_0d_parameter_with_setpoints_2d(dummyinstrument) -> None: @pytest.mark.usefixtures("experiment") -def test_dond_1d_parameter_with_array_vals(_param_set) -> None: +def test_dond_1d_parameter_with_array_vals(_param_set: Parameter[float | None]) -> None: param = ArrayshapedParam( name="paramwitharrayval", vals=validators.Arrays(shape=(10,)) ) @@ -490,13 +525,15 @@ def test_dond_1d_parameter_with_array_vals(_param_set) -> None: suppress_health_check=(HealthCheck.function_scoped_fixture,), ) def test_dond_1d_verify_shape( - _param, - _param_complex, - _param_set, - multiparamtype, - dummyinstrument, - num_points, - n_points_pws, + _param: Parameter[int], + _param_complex: Parameter[complex], + _param_set: Parameter[float | None], + multiparamtype: type[ + MultiSetPointParam | Multi2DSetPointParam | Multi2DSetPointParam2Sizes + ], + dummyinstrument: DummyChannelInstrument, + num_points: int, + n_points_pws: int, ) -> None: arrayparam = ArraySetPointParam(name="arrayparam") multiparam = multiparamtype(name="multiparam") @@ -533,7 +570,11 @@ def test_dond_1d_verify_shape( @pytest.mark.usefixtures("plot_close", "experiment") -def test_dond_1d_additional_setpoints(_param, _param_complex, _param_set) -> None: +def test_dond_1d_additional_setpoints( + _param: Parameter[int], + _param_complex: Parameter[complex], + _param_set: Parameter[float | None], +) -> None: additional_setpoints = [ Parameter(f"additional_setter_parameter_{i}", set_cmd=None, get_cmd=None) for i in range(2) @@ -560,7 +601,9 @@ def test_dond_1d_additional_setpoints(_param, _param_complex, _param_set) -> Non @settings(suppress_health_check=(HealthCheck.function_scoped_fixture,)) @given(num_points_p1=hst.integers(min_value=1, max_value=5)) @pytest.mark.usefixtures("experiment") -def test_dond_1d_additional_setpoints_shape(_param, _param_set, num_points_p1) -> None: +def test_dond_1d_additional_setpoints_shape( + _param: Parameter[int], _param_set: Parameter[float | None], num_points_p1: int +) -> None: arrayparam = ArraySetPointParam(name="arrayparam") array_shape = arrayparam.shape additional_setpoints = [ @@ -595,7 +638,12 @@ def test_dond_1d_additional_setpoints_shape(_param, _param_set, num_points_p1) - @pytest.mark.usefixtures("plot_close", "experiment") @pytest.mark.parametrize("plot", [None, True, False]) @pytest.mark.parametrize("plot_config", [None, True, False]) -def test_dond_1d_plot(_param_set, _param, plot, plot_config) -> None: +def test_dond_1d_plot( + _param_set: Parameter[float | None], + _param: Parameter[int], + plot: bool | None, + plot_config: bool | None, +) -> None: if plot_config is not None: config.dataset.dond_plot = plot_config @@ -610,7 +658,11 @@ def test_dond_1d_plot(_param_set, _param, plot, plot_config) -> None: @pytest.mark.usefixtures("plot_close", "experiment") -def test_dond_1d_output_data(_param, _param_complex, _param_set) -> None: +def test_dond_1d_output_data( + _param: Parameter[int], + _param_complex: Parameter[complex], + _param_set: Parameter[float | None], +) -> None: sweep_1 = LinSweep(_param_set, 0, 0.5, 5, 0) exp_1 = dond(sweep_1, _param, _param_complex) @@ -641,7 +693,11 @@ def test_dond_1d_output_data(_param, _param_complex, _param_set) -> None: @pytest.mark.usefixtures("plot_close", "experiment") -def test_dond_1d_output_type(_param, _param_complex, _param_set) -> None: +def test_dond_1d_output_type( + _param: Parameter[int], + _param_complex: Parameter[complex], + _param_set: Parameter[float | None], +) -> None: sweep_1 = LinSweep(_param_set, 0, 0.5, 2, 0) data_1 = dond(sweep_1, _param, _param_complex) @@ -664,15 +720,17 @@ def test_dond_1d_output_type(_param, _param_complex, _param_set) -> None: suppress_health_check=(HealthCheck.function_scoped_fixture,), ) def test_dond_2d_verify_shape( - _param, - _param_complex, - _param_set, - _param_set_2, - multiparamtype, - dummyinstrument, - num_points_p1, - num_points_p2, - n_points_pws, + _param: Parameter[int], + _param_complex: Parameter[complex], + _param_set: Parameter[float | None], + _param_set_2: Parameter[float | None], + multiparamtype: type[ + MultiSetPointParam | Multi2DSetPointParam | Multi2DSetPointParam2Sizes + ], + dummyinstrument: DummyChannelInstrument, + num_points_p1: int, + num_points_p2: int, + n_points_pws: int, ) -> None: arrayparam = ArraySetPointParam(name="arrayparam") multiparam = multiparamtype(name="multiparam") @@ -733,7 +791,10 @@ def test_dond_2d_verify_shape( @pytest.mark.usefixtures("plot_close", "experiment") def test_dond_2d_additional_setpoints( - _param, _param_complex, _param_set, _param_set_2 + _param: Parameter[int], + _param_complex: Parameter[complex], + _param_set: Parameter[float | None], + _param_set_2: Parameter[float | None], ) -> None: additional_setpoints = [ Parameter(f"additional_setter_parameter_{i}", set_cmd=None, get_cmd=None) @@ -776,7 +837,12 @@ def test_dond_2d_additional_setpoints( @settings(deadline=None, suppress_health_check=(HealthCheck.function_scoped_fixture,)) @pytest.mark.usefixtures("experiment") def test_dond_2d_additional_setpoints_shape( - _param, _param_complex, _param_set, _param_set_2, num_points_p1, num_points_p2 + _param: Parameter[int], + _param_complex: Parameter[complex], + _param_set: Parameter[float | None], + _param_set_2: Parameter[float | None], + num_points_p1: int, + num_points_p2: int, ) -> None: arrayparam = ArraySetPointParam(name="arrayparam") array_shape = arrayparam.shape @@ -821,7 +887,13 @@ def test_dond_2d_additional_setpoints_shape( @pytest.mark.usefixtures("plot_close", "experiment") @pytest.mark.parametrize("plot", [None, True, False]) @pytest.mark.parametrize("plot_config", [None, True, False]) -def test_dond_2d_plot(_param_set, _param_set_2, _param, plot, plot_config) -> None: +def test_dond_2d_plot( + _param_set: Parameter[float | None], + _param_set_2: Parameter[float | None], + _param: Parameter[int], + plot: bool | None, + plot_config: bool | None, +) -> None: if plot_config is not None: config.dataset.dond_plot = plot_config @@ -838,7 +910,12 @@ def test_dond_2d_plot(_param_set, _param_set_2, _param, plot, plot_config) -> No @pytest.mark.usefixtures("plot_close", "experiment") -def test_dond_2d_output_type(_param, _param_complex, _param_set, _param_set_2) -> None: +def test_dond_2d_output_type( + _param: Parameter[int], + _param_complex: Parameter[complex], + _param_set: Parameter[float | None], + _param_set_2: Parameter[float | None], +) -> None: sweep_1 = LinSweep(_param_set, 0, 0.5, 2, 0) sweep_2 = LinSweep(_param_set_2, 0.5, 1, 2, 0) @@ -847,7 +924,12 @@ def test_dond_2d_output_type(_param, _param_complex, _param_set, _param_set_2) - @pytest.mark.usefixtures("plot_close", "experiment") -def test_dond_2d_output_data(_param, _param_complex, _param_set, _param_set_2) -> None: +def test_dond_2d_output_data( + _param: Parameter[int], + _param_complex: Parameter[complex], + _param_set: Parameter[float | None], + _param_set_2: Parameter[float | None], +) -> None: sweep_1 = LinSweep(_param_set, 0, 0.5, 5, 0) sweep_2 = LinSweep(_param_set_2, 0.5, 1, 5, 0) exp_2 = dond(sweep_1, sweep_2, _param, _param_complex) @@ -891,7 +973,10 @@ def test_dond_2d_output_data(_param, _param_complex, _param_set, _param_set_2) - @pytest.mark.usefixtures("plot_close", "experiment") def test_dond_2d_multi_datasets_output_type( - _param, _param_complex, _param_set, _param_set_2 + _param: Parameter[int], + _param_complex: Parameter[complex], + _param_set: Parameter[float | None], + _param_set_2: Parameter[float | None], ) -> None: sweep_1 = LinSweep(_param_set, 0, 0.5, 2, 0) sweep_2 = LinSweep(_param_set_2, 0.5, 1, 2, 0) @@ -905,7 +990,11 @@ def test_dond_2d_multi_datasets_output_type( @pytest.mark.usefixtures("plot_close", "experiment") def test_dond_2d_multi_datasets_multi_exp( - _param, _param_complex, _param_set, _param_set_2, request: FixtureRequest + _param: Parameter[int], + _param_complex: Parameter[complex], + _param_set: Parameter[float | None], + _param_set_2: Parameter[float | None], + request: FixtureRequest, ) -> None: exp1 = new_experiment("test-experiment-1", sample_name="test-sample-1") exp2 = new_experiment("test-experiment-2", sample_name="test-sample-2") @@ -928,7 +1017,11 @@ def test_dond_2d_multi_datasets_multi_exp( @pytest.mark.usefixtures("plot_close", "experiment") def test_dond_2d_multi_datasets_multi_exp_inconsistent_raises( - _param, _param_complex, _param_set, _param_set_2, request: FixtureRequest + _param: Parameter[int], + _param_complex: Parameter[complex], + _param_set: Parameter[float | None], + _param_set_2: Parameter[float | None], + request: FixtureRequest, ) -> None: exp1 = new_experiment("test-experiment-1", sample_name="test-sample-1") exp2 = new_experiment("test-experiment-2", sample_name="test-sample-2") @@ -951,7 +1044,12 @@ def test_dond_2d_multi_datasets_multi_exp_inconsistent_raises( @pytest.mark.parametrize("plot", [None, True, False]) @pytest.mark.parametrize("plot_config", [None, True, False]) def test_dond_2d_multiple_datasets_plot( - _param_set, _param_set_2, _param, _param_2, plot, plot_config + _param_set: Parameter[float | None], + _param_set_2: Parameter[float | None], + _param: Parameter[int], + _param_2: Parameter[int], + plot: bool | None, + plot_config: bool | None, ) -> None: if plot_config is not None: config.dataset.dond_plot = plot_config @@ -979,12 +1077,12 @@ def test_dond_2d_multiple_datasets_plot( @pytest.mark.usefixtures("plot_close", "experiment") def test_dond_2d_multi_datasets_with_callable_output_data( - _param, - _param_2, - _param_complex, - _param_complex_2, - _param_set, - _param_set_2, + _param: Parameter[int], + _param_2: Parameter[int], + _param_complex: Parameter[complex], + _param_complex_2: Parameter[complex], + _param_set: Parameter[float | None], + _param_set_2: Parameter[float | None], _string_callable, ) -> None: sweep_1 = LinSweep(_param_set, 0, 0.5, 5, 0) @@ -1077,7 +1175,12 @@ def test_dond_2d_multi_datasets_with_callable_output_data( @pytest.mark.usefixtures("plot_close", "experiment") -def test_dond_together_sweep(_param_set, _param_set_2, _param, _param_2) -> None: +def test_dond_together_sweep( + _param_set: Parameter[float | None], + _param_set_2: Parameter[float | None], + _param: Parameter[int], + _param_2: Parameter[int], +) -> None: sweep_1 = LinSweep(_param_set, 0, 1, 10, 0) sweep_2 = LinSweep(_param_set_2, 1, 2, 10, 0) @@ -1105,7 +1208,11 @@ def test_dond_together_sweep(_param_set, _param_set_2, _param, _param_2) -> None @pytest.mark.usefixtures("plot_close", "experiment") -def test_dond_together_sweep_sweeper(_param_set, _param_set_2, _param) -> None: +def test_dond_together_sweep_sweeper( + _param_set: Parameter[float | None], + _param_set_2: Parameter[float | None], + _param: Parameter[int], +) -> None: sweep_len = 10 delay_1 = 0.1 @@ -1400,7 +1507,7 @@ def test_dond_together_sweep_sweeper_combined_lists() -> None: n_points_1=hst.integers(min_value=1, max_value=500), n_points_2=hst.integers(min_value=1, max_value=500), ) -def test_together_sweep_validation(n_points_1, n_points_2) -> None: +def test_together_sweep_validation(n_points_1: int, n_points_2: int) -> None: a = ManualParameter("a", initial_value=0) b = ManualParameter("b", initial_value=0) sweepA = LinSweep(a, 0, 3, n_points_1) @@ -1531,7 +1638,9 @@ def test_dond_together_sweep_sweeper_wrong_mp_in_dataset_dependencies() -> None: @pytest.mark.usefixtures("plot_close", "experiment") -def test_dond_together_sweep_parameter_with_setpoints(dummyinstrument) -> None: +def test_dond_together_sweep_parameter_with_setpoints( + dummyinstrument: DummyChannelInstrument, +) -> None: outer_shape = 10 inner_shape = 15 @@ -1584,7 +1693,7 @@ def test_dond_together_sweep_parameter_with_setpoints(dummyinstrument) -> None: @pytest.mark.usefixtures("plot_close", "experiment") def test_dond_together_sweep_parameter_with_setpoints_explicit_mapping( - dummyinstrument, + dummyinstrument: DummyChannelInstrument, ) -> None: outer_shape = 10 inner_shape = 15 @@ -1640,7 +1749,7 @@ def test_dond_together_sweep_parameter_with_setpoints_explicit_mapping( @pytest.mark.usefixtures("plot_close", "experiment") def test_dond_together_sweep_parameter_with_setpoints_explicit_mapping_and_callable( - dummyinstrument, + dummyinstrument: DummyChannelInstrument, ) -> None: outer_shape = 10 inner_shape = 15 @@ -1696,7 +1805,11 @@ def test_dond_together_sweep_parameter_with_setpoints_explicit_mapping_and_calla @pytest.mark.usefixtures("plot_close", "experiment") -def test_dond_sweeper_combinations(_param_set, _param_set_2, _param) -> None: +def test_dond_sweeper_combinations( + _param_set: Parameter[float | None], + _param_set_2: Parameter[float | None], + _param: Parameter[int], +) -> None: outer_shape = 10 inner_shape = 15 @@ -1763,7 +1876,7 @@ def test_error_measured_grouped_and_not_grouped() -> None: @pytest.mark.usefixtures("plot_close", "experiment") -def test_post_action(mocker) -> None: +def test_post_action(mocker: "MockerFixture") -> None: param_1 = ManualParameter("param_1", initial_value=0.0) param_2 = ManualParameter("param_2", initial_value=0.0) @@ -1797,7 +1910,11 @@ def test_default_log_info(caplog: LogCaptureFixture) -> None: @pytest.mark.usefixtures("plot_close", "experiment") -def test_dond_get_after_set(_param_set, _param_set_2, _param) -> None: +def test_dond_get_after_set( + _param_set: Parameter[float | None], + _param_set_2: Parameter[float | None], + _param: Parameter[int], +) -> None: n_points = 10 a = TrackingParameter("a", initial_value=0) @@ -1830,7 +1947,11 @@ def test_dond_get_after_set(_param_set, _param_set_2, _param) -> None: @pytest.mark.usefixtures("plot_close", "experiment") -def test_dond_get_after_set_stores_get_value(_param_set, _param_set_2, _param) -> None: +def test_dond_get_after_set_stores_get_value( + _param_set: Parameter[float | None], + _param_set_2: Parameter[float | None], + _param: Parameter[int], +) -> None: n_points = 11 a = GetReturnsCountParameter("a", initial_value=0) @@ -1858,7 +1979,9 @@ def test_dond_get_after_set_stores_get_value(_param_set, _param_set_2, _param) - @pytest.mark.usefixtures("plot_close", "experiment") -def test_dond_return_type(_param_set, _param) -> None: +def test_dond_return_type( + _param_set: Parameter[float | None], _param: Parameter[int] +) -> None: n_points = 11 # test that with squeeze=False we get MultiAxesTupleListWithDataSet as the return type diff --git a/tests/dataset/dond/test_dond_keyboard_interrupts.py b/tests/dataset/dond/test_dond_keyboard_interrupts.py index 146a19a31df..91345a134c2 100644 --- a/tests/dataset/dond/test_dond_keyboard_interrupts.py +++ b/tests/dataset/dond/test_dond_keyboard_interrupts.py @@ -1,9 +1,14 @@ +from typing import TYPE_CHECKING + import pytest from qcodes.dataset.dond.do_nd_utils import BreakConditionInterrupt, catch_interrupts +if TYPE_CHECKING: + from collections.abc import Iterator + -def test_catch_interrupts(): +def test_catch_interrupts() -> None: # Test normal execution (no interrupt) with catch_interrupts() as get_interrupt: assert get_interrupt() is None @@ -41,7 +46,7 @@ def test_catch_interrupts(): assert isinstance(get_interrupt(), BreakConditionInterrupt) -def test_catch_interrupts_in_loops(): +def test_catch_interrupts_in_loops() -> None: # Test interruption in a simple loop loop_count = 0 with pytest.raises(KeyboardInterrupt): # noqa: PT012 @@ -68,8 +73,8 @@ def test_catch_interrupts_in_loops(): assert inner_count == 5 -def test_catch_interrupts_simulated_sweeps(): - def simulated_sweep(interrupt_at=None): +def test_catch_interrupts_simulated_sweeps() -> None: + def simulated_sweep(interrupt_at: int | None = None) -> "Iterator[int]": for i in range(5): with catch_interrupts(): if i == interrupt_at: diff --git a/tests/dataset/helper_functions.py b/tests/dataset/helper_functions.py index a4c2ad83749..9c4f20b0caf 100644 --- a/tests/dataset/helper_functions.py +++ b/tests/dataset/helper_functions.py @@ -8,6 +8,9 @@ import pandas as pd from numpy.testing import assert_array_equal +if TYPE_CHECKING: + from numpy.typing import NDArray + if TYPE_CHECKING: from collections.abc import Collection, Hashable, Mapping, Sequence @@ -70,8 +73,8 @@ def verify_data_dict_for_single_param( datadict: dict[str, np.ndarray], names: Sequence[str], shapes: Sequence[tuple[int, ...]], - values, -): + values: Sequence[NDArray[np.generic]], +) -> None: # check that there are no unexpected elements in the dict key_names = list(datadict.keys()) assert set(key_names) == set(names) @@ -89,8 +92,8 @@ def verify_dataframe_for_single_param( dataframe: pd.DataFrame, names: Sequence[str], shapes: Sequence[tuple[int, ...]], - values, -): + values: Sequence[NDArray[np.generic]], +) -> None: # check that the dataframe has the same elements as index and columns pandas_index_names = list(dataframe.index.names) pandas_column_names = list(dataframe) diff --git a/tests/dataset/measurement/test_measurement_context_manager.py b/tests/dataset/measurement/test_measurement_context_manager.py index bac839bb628..d42fdaaa2cb 100644 --- a/tests/dataset/measurement/test_measurement_context_manager.py +++ b/tests/dataset/measurement/test_measurement_context_manager.py @@ -7,7 +7,7 @@ import traceback from functools import reduce from time import sleep -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, Literal import hypothesis.strategies as hst import numpy as np @@ -41,10 +41,18 @@ from tests.common import retry_until_does_not_throw if TYPE_CHECKING: - from qcodes.instrument_drivers.mock_instruments import DummyInstrument + from pytest import TempPathFactory + from pytest_mock import MockerFixture + + from qcodes.instrument_drivers.mock_instruments import ( + DummyChannelInstrument, + DummyInstrument, + ) -def test_log_messages(caplog: LogCaptureFixture, meas_with_registered_param) -> None: +def test_log_messages( + caplog: LogCaptureFixture, meas_with_registered_param: Measurement +) -> None: caplog.set_level(logging.INFO) with meas_with_registered_param.run(): @@ -56,7 +64,7 @@ def test_log_messages(caplog: LogCaptureFixture, meas_with_registered_param) -> def test_log_includes_extra_info( - caplog: LogCaptureFixture, meas_with_registered_param + caplog: LogCaptureFixture, meas_with_registered_param: Measurement ) -> None: caplog.set_level(logging.INFO) meas_with_registered_param._extra_log_info = "some extra info" @@ -67,7 +75,9 @@ def test_log_includes_extra_info( @pytest.mark.usefixtures("experiment") -def test_register_parameter_arg_types(DAC, DMM): +def test_register_parameter_arg_types( + DAC: DummyInstrument, DMM: DummyInstrument +) -> None: """Test basis and setpoints argument types.""" meas = Measurement() meas.register_parameter(DAC.ch1) @@ -91,7 +101,7 @@ def test_register_parameter_arg_types(DAC, DMM): meas.register_parameter(DMM.v1, setpoints=(DAC.ch1, 3)) # type: ignore[arg-type] -def test_register_parameter_numbers(DAC, DMM) -> None: +def test_register_parameter_numbers(DAC: DummyInstrument, DMM: DummyInstrument) -> None: """ Test the registration of scalar QCoDeS parameters """ @@ -157,7 +167,7 @@ def test_register_parameter_numbers(DAC, DMM) -> None: meas.register_parameter(DMM.v1, setpoints=(DAC.ch2,)) -def test_register_custom_parameter(DAC) -> None: +def test_register_custom_parameter(DAC: DummyInstrument) -> None: """ Test the registration of custom parameters """ @@ -246,7 +256,7 @@ def test_register_delegate_parameters_with_late_source() -> None: assert meas.parameters["x"].type == "numeric" -def test_register_delegate_parameters_with_late_source_chain(): +def test_register_delegate_parameters_with_late_source_chain() -> None: x_param = Parameter("x", set_cmd=None, get_cmd=None) complex_param = Parameter( @@ -268,7 +278,7 @@ def test_register_delegate_parameters_with_late_source_chain(): assert meas.parameters["x"].type == "numeric" -def test_unregister_parameter(DAC, DMM) -> None: +def test_unregister_parameter(DAC: DummyInstrument, DMM: DummyInstrument) -> None: """ Test the unregistering of parameters. """ @@ -310,7 +320,7 @@ def test_unregister_parameter(DAC, DMM) -> None: @pytest.mark.usefixtures("experiment") @pytest.mark.parametrize("bg_writing", [True, False]) -def test_mixing_array_and_numeric(DAC, bg_writing) -> None: +def test_mixing_array_and_numeric(DAC: DummyInstrument, bg_writing: bool) -> None: """ Test that mixing array and numeric types is okay """ @@ -326,7 +336,9 @@ def test_mixing_array_and_numeric(DAC, bg_writing) -> None: ) -def test_measurement_name_default(experiment, DAC, DMM) -> None: +def test_measurement_name_default( + experiment: Experiment, DAC: DummyInstrument, DMM: DummyInstrument +) -> None: fmt = experiment.format_string exp_id = experiment.exp_id @@ -347,7 +359,9 @@ def test_measurement_name_default(experiment, DAC, DMM) -> None: assert ds.name == default_name -def test_measurement_name_changed_via_attribute(experiment, DAC, DMM) -> None: +def test_measurement_name_changed_via_attribute( + experiment: Experiment, DAC: DummyInstrument, DMM: DummyInstrument +) -> None: fmt = experiment.format_string exp_id = experiment.exp_id @@ -368,7 +382,9 @@ def test_measurement_name_changed_via_attribute(experiment, DAC, DMM) -> None: assert ds.name == name -def test_measurement_name_set_as_argument(experiment, DAC, DMM) -> None: +def test_measurement_name_set_as_argument( + experiment: Experiment, DAC: DummyInstrument, DMM: DummyInstrument +) -> None: fmt = experiment.format_string exp_id = experiment.exp_id @@ -391,7 +407,7 @@ def test_measurement_name_set_as_argument(experiment, DAC, DMM) -> None: @settings(deadline=None) @given(wp=hst.one_of(hst.integers(), hst.floats(allow_nan=False), hst.text())) @pytest.mark.usefixtures("empty_temp_db") -def test_setting_write_period(wp) -> None: +def test_setting_write_period(wp: float | str) -> None: new_experiment("firstexp", sample_name="no sample") meas = Measurement() meas.register_custom_parameter(name="dummy") @@ -413,7 +429,7 @@ def test_setting_write_period(wp) -> None: @settings(deadline=None) @given(wp=hst.one_of(hst.integers(), hst.floats(allow_nan=False), hst.text())) @pytest.mark.usefixtures("experiment") -def test_setting_write_period_from_config(wp) -> None: +def test_setting_write_period_from_config(wp: float | str) -> None: qc.config.dataset.write_period = wp if isinstance(wp, str) or wp < 1e-3: @@ -429,7 +445,7 @@ def test_setting_write_period_from_config(wp) -> None: @pytest.mark.parametrize("write_in_background", [True, False]) @pytest.mark.usefixtures("experiment") -def test_setting_write_in_background_from_config(write_in_background) -> None: +def test_setting_write_in_background_from_config(write_in_background: bool) -> None: qc.config.dataset.write_in_background = write_in_background meas = Measurement() @@ -441,7 +457,7 @@ def test_setting_write_in_background_from_config(write_in_background) -> None: @pytest.mark.usefixtures("experiment") -def test_method_chaining(DAC) -> None: +def test_method_chaining(DAC: DummyInstrument) -> None: ( Measurement() .register_parameter(DAC.ch1) @@ -455,11 +471,11 @@ def test_method_chaining(DAC) -> None: @pytest.mark.usefixtures("experiment") @settings(deadline=None, suppress_health_check=(HealthCheck.function_scoped_fixture,)) @given(words=hst.lists(elements=hst.text(), min_size=4, max_size=10)) -def test_enter_and_exit_actions(DAC, words) -> None: +def test_enter_and_exit_actions(DAC: DummyInstrument, words: list[str]) -> None: # we use a list to check that the functions executed # in the correct order - def action(lst, word): + def action(lst: list[str], word: str) -> None: lst.append(word) meas = Measurement() @@ -501,7 +517,11 @@ def test_subscriptions( automatically after the write_period passes after a add_result call. """ - def collect_all_results(results, length, state): + def collect_all_results( + results: list[tuple[float, ...]], + length: int, + state: dict[int, list[tuple[float, ...]]], + ) -> None: """ Updates the *state* to contain all the *results* acquired during the experiment run @@ -513,7 +533,9 @@ def collect_all_results(results, length, state): # the same length. state[length] = results - def collect_values_larger_than_7(results, length, state): + def collect_values_larger_than_7( + results: list[tuple[float, ...]], length: int, state: list[float] + ) -> None: """ Appends to the *state* only the values from *results* that are larger than 7 @@ -606,7 +628,7 @@ def assert_states_updated_from_callbacks( def test_subscribers_called_at_exiting_context_if_queue_is_not_empty( - experiment, DAC + experiment: Experiment, DAC: DummyInstrument ) -> None: """ Upon quitting the "run()" context, verify that in case the queue is @@ -615,7 +637,9 @@ def test_subscribers_called_at_exiting_context_if_queue_is_not_empty( to a number that is larger than the number of value written to the dataset. """ - def collect_x_vals(results, length, state): + def collect_x_vals( + results: list[tuple[float]], length: int, state: list[float] + ) -> None: """ Collects first elements of results tuples in *state* """ @@ -652,16 +676,22 @@ def collect_x_vals(results, length, state): @pytest.mark.serial @pytest.mark.flaky(reruns=5) -def test_subscribers_called_for_all_data_points(experiment, DAC, DMM) -> None: +def test_subscribers_called_for_all_data_points( + experiment: Experiment, DAC: DummyInstrument, DMM: DummyInstrument +) -> None: N = random.randint(2000, 3000) - def sub_get_x_vals(results, length, state): + def sub_get_x_vals( + results: list[tuple[float, float]], length: int, state: list[float] + ) -> None: """ A list of all x values """ state += [res[0] for res in results] - def sub_get_y_vals(results, length, state): + def sub_get_y_vals( + results: list[tuple[float, float]], length: int, state: list[float] + ) -> None: """ A list of all y values """ @@ -702,7 +732,13 @@ def sub_get_y_vals(results, length, state): get_values=hst.lists(elements=hst.floats(), min_size=20, max_size=20), ) def test_datasaver_scalars( - experiment, DAC, DMM, set_values, get_values, breakpoint, write_period + experiment: Experiment, + DAC: DummyInstrument, + DMM: DummyInstrument, + set_values: list[float], + get_values: list[float], + breakpoint: int, + write_period: float, ) -> None: no_of_runs = len(experiment) @@ -811,7 +847,7 @@ class SomeMeasurementException(Exception): @settings(max_examples=10, deadline=None) @given(N=hst.integers(min_value=2, max_value=500)) @pytest.mark.usefixtures("empty_temp_db") -def test_datasaver_arrays_lists_tuples(bg_writing, N) -> None: +def test_datasaver_arrays_lists_tuples(bg_writing: bool, N: int) -> None: new_experiment("firstexp", sample_name="no sample") meas = Measurement() @@ -894,7 +930,7 @@ def test_datasaver_arrays_lists_tuples(bg_writing, N) -> None: @settings(max_examples=10, deadline=None) @given(N=hst.integers(min_value=2, max_value=500)) @pytest.mark.usefixtures("empty_temp_db") -def test_datasaver_numeric_and_array_paramtype(bg_writing, N) -> None: +def test_datasaver_numeric_and_array_paramtype(bg_writing: bool, N: int) -> None: """ Test saving one parameter with 'numeric' paramtype and one parameter with 'array' paramtype @@ -930,7 +966,7 @@ def test_datasaver_numeric_and_array_paramtype(bg_writing, N) -> None: @pytest.mark.parametrize("bg_writing", [True, False]) @pytest.mark.usefixtures("empty_temp_db") -def test_datasaver_numeric_after_array_paramtype(bg_writing) -> None: +def test_datasaver_numeric_after_array_paramtype(bg_writing: bool) -> None: """ Test that passing values for 'array' parameter in `add_result` before passing values for 'numeric' parameter works. @@ -967,7 +1003,7 @@ def test_datasaver_numeric_after_array_paramtype(bg_writing) -> None: @pytest.mark.parametrize("bg_writing", [True, False]) @pytest.mark.usefixtures("experiment") -def test_datasaver_foul_input(bg_writing) -> None: +def test_datasaver_foul_input(bg_writing: bool) -> None: meas = Measurement() meas.register_custom_parameter( @@ -987,7 +1023,9 @@ def test_datasaver_foul_input(bg_writing) -> None: @pytest.mark.usefixtures("empty_temp_db") @pytest.mark.parametrize("bg_writing", [True, False]) @pytest.mark.parametrize("storage_type", ["numeric", "array"]) -def test_datasaver_unsized_arrays(N, storage_type, bg_writing) -> None: +def test_datasaver_unsized_arrays( + N: int, storage_type: Literal["numeric", "array"], bg_writing: bool +) -> None: new_experiment("firstexp", sample_name="no sample") meas = Measurement() @@ -1048,7 +1086,14 @@ def test_datasaver_unsized_arrays(N, storage_type, bg_writing) -> None: @pytest.mark.parametrize("param_type", ["np_array", "tuple", "list"]) @pytest.mark.parametrize("storage_type", ["numeric", "array"]) def test_datasaver_arrayparams( - SpectrumAnalyzer, DAC, N, M, param_type, storage_type, seed, bg_writing + SpectrumAnalyzer: DummyInstrument, + DAC: DummyInstrument, + N: int, + M: int, + param_type: Literal["np_array", "tuple", "list"], + storage_type: Literal["numeric", "array"], + seed: int, + bg_writing: bool, ) -> None: """ Test that data is stored correctly for array parameters that @@ -1130,7 +1175,11 @@ def test_datasaver_arrayparams( @pytest.mark.parametrize("storage_type", ["numeric", "array"]) @pytest.mark.usefixtures("experiment") def test_datasaver_array_parameters_channel( - channel_array_instrument, DAC, N, storage_type, bg_writing + channel_array_instrument: DummyChannelInstrument, + DAC: DummyInstrument, + N: int, + storage_type: Literal["numeric", "array"], + bg_writing: bool, ) -> None: meas = Measurement() @@ -1197,7 +1246,11 @@ def test_datasaver_array_parameters_channel( @pytest.mark.parametrize("storage_type", ["numeric", "array"]) @pytest.mark.usefixtures("experiment") def test_datasaver_parameter_with_setpoints( - channel_array_instrument, DAC, n, storage_type, bg_writing + channel_array_instrument: DummyChannelInstrument, + DAC: DummyInstrument, + n: int, + storage_type: Literal["numeric", "array"], + bg_writing: bool, ) -> None: chan = channel_array_instrument.A param = chan.dummy_parameter_with_setpoints @@ -1274,7 +1327,11 @@ def test_datasaver_parameter_with_setpoints( @pytest.mark.parametrize("storage_type", ["numeric", "array"]) @pytest.mark.usefixtures("experiment") def test_datasaver_parameter_with_setpoints_explicitly_expanded( - channel_array_instrument, DAC, n, storage_type, bg_writing + channel_array_instrument: DummyChannelInstrument, + DAC: DummyInstrument, + n: int, + storage_type: Literal["numeric", "array"], + bg_writing: bool, ) -> None: chan = channel_array_instrument.A param = chan.dummy_parameter_with_setpoints @@ -1343,7 +1400,7 @@ def test_datasaver_parameter_with_setpoints_explicitly_expanded( @pytest.mark.usefixtures("experiment") def test_datasaver_parameter_with_setpoints_that_are_different_raises( - channel_array_instrument, DAC + channel_array_instrument: DummyChannelInstrument, DAC: DummyInstrument ) -> None: chan = channel_array_instrument.A param = chan.dummy_parameter_with_setpoints_2d @@ -1383,7 +1440,10 @@ def test_datasaver_parameter_with_setpoints_that_are_different_raises( @given(n=hst.integers(min_value=5, max_value=500)) @pytest.mark.usefixtures("experiment") def test_datasaver_parameter_with_setpoints_complex( - channel_array_instrument, DAC, n, bg_writing + channel_array_instrument: DummyChannelInstrument, + DAC: DummyInstrument, + n: int, + bg_writing: bool, ) -> None: chan = channel_array_instrument.A param = chan.dummy_parameter_with_setpoints_complex @@ -1441,7 +1501,10 @@ def test_datasaver_parameter_with_setpoints_complex( @given(n=hst.integers(min_value=5, max_value=500)) @pytest.mark.usefixtures("experiment") def test_datasaver_parameter_with_setpoints_complex_explicitly_expanded( - channel_array_instrument, DAC, n, bg_writing + channel_array_instrument: DummyChannelInstrument, + DAC: DummyInstrument, + n: int, + bg_writing: bool, ) -> None: chan = channel_array_instrument.A param = chan.dummy_parameter_with_setpoints_complex @@ -1494,7 +1557,10 @@ def test_datasaver_parameter_with_setpoints_complex_explicitly_expanded( @pytest.mark.parametrize("storage_type", ["numeric", "array"]) @pytest.mark.usefixtures("experiment") def test_datasaver_parameter_with_setpoints_missing_reg_raises( - channel_array_instrument, DAC, storage_type, bg_writing + channel_array_instrument: DummyChannelInstrument, + DAC: DummyInstrument, + storage_type: Literal["numeric", "array"], + bg_writing: bool, ) -> None: """ Test that if for whatever reason new setpoints are added after @@ -1537,7 +1603,10 @@ def test_datasaver_parameter_with_setpoints_missing_reg_raises( @pytest.mark.parametrize("storage_type", ["numeric", "array"]) @pytest.mark.usefixtures("experiment") def test_datasaver_parameter_with_setpoints_reg_but_missing_validator( - channel_array_instrument, DAC, storage_type, bg_writing + channel_array_instrument: DummyChannelInstrument, + DAC: DummyInstrument, + storage_type: Literal["numeric", "array"], + bg_writing: bool, ) -> None: """ Test that if for whatever reason the setpoints are removed between @@ -1594,7 +1663,10 @@ def test_datasaver_parameter_with_setpoints_reg_but_missing_validator( @pytest.mark.parametrize("storage_type", ["numeric", "array"]) @pytest.mark.usefixtures("experiment") def test_datasaver_parameter_with_setpoints_reg_but_missing( - channel_array_instrument, DAC, storage_type, bg_writing + channel_array_instrument: DummyChannelInstrument, + DAC: DummyInstrument, + storage_type: Literal["numeric", "array"], + bg_writing: bool, ) -> None: """ Test that if for whatever reason the setpoints of a QCoDeS parameter are @@ -1636,7 +1708,11 @@ def test_datasaver_parameter_with_setpoints_reg_but_missing( @pytest.mark.parametrize("storage_type", ["numeric", "array"]) @pytest.mark.parametrize("bg_writing", [True, False]) def test_datasaver_array_parameters_array( - channel_array_instrument, DAC, N, storage_type, bg_writing + channel_array_instrument: DummyChannelInstrument, + DAC: DummyInstrument, + N: int, + storage_type: Literal["numeric", "array"], + bg_writing: bool, ) -> None: """ Test that storing array parameters inside a loop works as expected @@ -1722,7 +1798,10 @@ def test_datasaver_array_parameters_array( @given(N=hst.integers(min_value=5, max_value=500)) @pytest.mark.usefixtures("experiment") def test_datasaver_complex_array_parameters_array( - channel_array_instrument, DAC, N, bg_writing + channel_array_instrument: DummyChannelInstrument, + DAC: DummyInstrument, + N: int, + bg_writing: bool, ) -> None: """ Test that storing a complex array parameters inside a loop with the sqlite @@ -1783,7 +1862,7 @@ def test_datasaver_complex_array_parameters_array( @pytest.mark.parametrize("bg_writing", [True, False]) -def test_datasaver_multidim_array(experiment, bg_writing) -> None: +def test_datasaver_multidim_array(experiment: Experiment, bg_writing: bool) -> None: """ Test that inserting multidim parameters as arrays works as expected """ @@ -1834,7 +1913,12 @@ def test_datasaver_multidim_array(experiment, bg_writing) -> None: @pytest.mark.parametrize("export", [True, False]) @pytest.mark.parametrize("export_type", [DataExportType.CSV, DataExportType.NETCDF]) def test_datasaver_export( - experiment, bg_writing, tmp_path_factory, export, export_type, mocker + experiment: Experiment, + bg_writing: bool, + tmp_path_factory: TempPathFactory, + export: bool, + export_type: DataExportType, + mocker: MockerFixture, ) -> None: """ Test export data to csv after measurement ends @@ -1894,7 +1978,7 @@ def test_datasaver_export( @pytest.mark.parametrize("bg_writing", [True, False]) -def test_datasaver_multidim_numeric(experiment, bg_writing) -> None: +def test_datasaver_multidim_numeric(experiment: Experiment, bg_writing: bool) -> None: """ Test that inserting multidim parameters as numeric works as expected """ @@ -1935,7 +2019,7 @@ def test_datasaver_multidim_numeric(experiment, bg_writing) -> None: @pytest.mark.parametrize("bg_writing", [True, False]) @pytest.mark.usefixtures("experiment") def test_datasaver_multidimarrayparameter_as_array( - SpectrumAnalyzer, bg_writing + SpectrumAnalyzer: DummyInstrument, bg_writing: bool ) -> None: """ Test that inserting multidim Arrrayparameters as array works as expected @@ -1982,7 +2066,7 @@ def test_datasaver_multidimarrayparameter_as_array( @pytest.mark.parametrize("bg_writing", [True, False]) @pytest.mark.usefixtures("experiment") def test_datasaver_multidimarrayparameter_as_numeric( - SpectrumAnalyzer, bg_writing + SpectrumAnalyzer: DummyInstrument, bg_writing: bool ) -> None: """ Test that storing a multidim Array parameter as numeric unravels the @@ -2029,7 +2113,7 @@ def test_datasaver_multidimarrayparameter_as_numeric( @pytest.mark.parametrize("bg_writing", [True, False]) @pytest.mark.usefixtures("experiment") def test_datasaver_multi_parameters_scalar( - channel_array_instrument, bg_writing + channel_array_instrument: DummyChannelInstrument, bg_writing: bool ) -> None: """ Test that we can register multiparameters that are scalar. @@ -2056,7 +2140,9 @@ def test_datasaver_multi_parameters_scalar( @pytest.mark.parametrize("bg_writing", [True, False]) @pytest.mark.usefixtures("experiment") -def test_datasaver_multi_parameters_array(channel_array_instrument, bg_writing) -> None: +def test_datasaver_multi_parameters_array( + channel_array_instrument: DummyChannelInstrument, bg_writing: bool +) -> None: """ Test that we can register multiparameters that are array like. """ @@ -2099,7 +2185,7 @@ def test_datasaver_multi_parameters_array(channel_array_instrument, bg_writing) @pytest.mark.parametrize("bg_writing", [True, False]) @pytest.mark.usefixtures("experiment") def test_datasaver_2d_multi_parameters_array( - channel_array_instrument, bg_writing + channel_array_instrument: DummyChannelInstrument, bg_writing: bool ) -> None: """ Test that we can register multiparameters that are array like and 2D. @@ -2166,7 +2252,9 @@ def test_datasaver_2d_multi_parameters_array( @pytest.mark.parametrize("storage_type", ["numeric", "array"]) @settings(deadline=None, max_examples=10) @given(Ns=hst.lists(hst.integers(2, 10), min_size=2, max_size=5)) -def test_datasaver_arrays_of_different_length(storage_type, Ns, bg_writing) -> None: +def test_datasaver_arrays_of_different_length( + storage_type: Literal["numeric", "array"], Ns: list[int], bg_writing: bool +) -> None: """ Test that we can save arrays of different length in a single call to datasaver.add_result @@ -2210,7 +2298,9 @@ def test_datasaver_arrays_of_different_length(storage_type, Ns, bg_writing) -> N @pytest.mark.parametrize("bg_writing", [True, False]) @pytest.mark.usefixtures("experiment") -def test_save_complex_num(complex_num_instrument, bg_writing) -> None: +def test_save_complex_num( + complex_num_instrument: DummyInstrument, bg_writing: bool +) -> None: """ Test that we can save various parameters mixed with complex parameters """ @@ -2297,7 +2387,9 @@ def test_save_complex_num(complex_num_instrument, bg_writing) -> None: @pytest.mark.parametrize("bg_writing", [True, False]) @pytest.mark.usefixtures("experiment") -def test_save_and_reload_complex_standalone(complex_num_instrument, bg_writing) -> None: +def test_save_and_reload_complex_standalone( + complex_num_instrument: DummyInstrument, bg_writing: bool +) -> None: param = complex_num_instrument.complex_num complex_num_instrument.setpoint(1) Parameter("test", set_cmd=None, get_cmd=lambda: 1 + 1j, vals=vals.ComplexNumbers()) @@ -2315,7 +2407,9 @@ def test_save_and_reload_complex_standalone(complex_num_instrument, bg_writing) @pytest.mark.parametrize("bg_writing", [True, False]) @pytest.mark.usefixtures("experiment") -def test_save_complex_num_setpoints(complex_num_instrument, bg_writing) -> None: +def test_save_complex_num_setpoints( + complex_num_instrument: DummyInstrument, bg_writing: bool +) -> None: """ Test that we can save a parameter with complex setpoints """ @@ -2343,7 +2437,9 @@ def test_save_complex_num_setpoints(complex_num_instrument, bg_writing) -> None: @pytest.mark.parametrize("bg_writing", [True, False]) @pytest.mark.usefixtures("experiment") -def test_save_complex_num_setpoints_array(complex_num_instrument, bg_writing) -> None: +def test_save_complex_num_setpoints_array( + complex_num_instrument: DummyInstrument, bg_writing: bool +) -> None: """ Test that we can save an array parameter with complex setpoints """ @@ -2382,7 +2478,9 @@ def test_save_complex_num_setpoints_array(complex_num_instrument, bg_writing) -> @pytest.mark.parametrize("bg_writing", [True, False]) @pytest.mark.usefixtures("experiment") -def test_save_complex_as_num_raises(complex_num_instrument, bg_writing) -> None: +def test_save_complex_as_num_raises( + complex_num_instrument: DummyInstrument, bg_writing: bool +) -> None: setparam = complex_num_instrument.setpoint param = complex_num_instrument.complex_num meas = Measurement() @@ -2403,7 +2501,9 @@ def test_save_complex_as_num_raises(complex_num_instrument, bg_writing) -> None: @pytest.mark.parametrize("bg_writing", [True, False]) @pytest.mark.usefixtures("experiment") -def test_save_numeric_as_complex_raises(complex_num_instrument, bg_writing) -> None: +def test_save_numeric_as_complex_raises( + complex_num_instrument: DummyInstrument, bg_writing: bool +) -> None: setparam = complex_num_instrument.setpoint param = complex_num_instrument.complex_num meas = Measurement() @@ -2421,7 +2521,7 @@ def test_save_numeric_as_complex_raises(complex_num_instrument, bg_writing) -> N datasaver.add_result((setparam, setparam()), (param, setparam())) -def test_parameter_inference(channel_array_instrument) -> None: +def test_parameter_inference(channel_array_instrument: DummyChannelInstrument) -> None: chan = channel_array_instrument.channels[0] # default values assert Measurement._infer_paramtype(chan.temperature, None) == "numeric" @@ -2457,7 +2557,7 @@ def test_parameter_inference(channel_array_instrument) -> None: @pytest.mark.parametrize("bg_writing", [True, False]) @pytest.mark.usefixtures("experiment") -def test_adding_parents(bg_writing, DAC) -> None: +def test_adding_parents(bg_writing: bool, DAC: DummyInstrument) -> None: """ Test that we can register a DataSet as the parent of another DataSet as created by the Measurement diff --git a/tests/dataset/measurement/test_register_name.py b/tests/dataset/measurement/test_register_name.py index ba01b97b935..9d88ce4da11 100644 --- a/tests/dataset/measurement/test_register_name.py +++ b/tests/dataset/measurement/test_register_name.py @@ -10,7 +10,7 @@ @pytest.mark.usefixtures("experiment") -def test_register_name_with_manual_parameters(): +def test_register_name_with_manual_parameters() -> None: indep_param = ManualParameter( "indep_param", initial_value=1, register_name="renamed_indep_param" ) @@ -26,7 +26,7 @@ def test_register_name_with_manual_parameters(): @pytest.mark.usefixtures("experiment") -def test_register_name_with_pws(): +def test_register_name_with_pws() -> None: setpoints_param = Parameter( name="setpoints_param", get_cmd=partial(np.linspace, 0, 1, 101), @@ -51,7 +51,7 @@ def test_register_name_with_pws(): assert paramspecs[1].name in ("renamed_setpoints", "renamed_meas_param") -def test_register_name_utils(): +def test_register_name_utils() -> None: indep_param = ManualParameter( "indep_param", initial_value=1, register_name="renamed_indep_param" ) diff --git a/tests/dataset/measurement/test_self_registering_parameters.py b/tests/dataset/measurement/test_self_registering_parameters.py index 055b8523255..2f764a1a798 100644 --- a/tests/dataset/measurement/test_self_registering_parameters.py +++ b/tests/dataset/measurement/test_self_registering_parameters.py @@ -37,7 +37,9 @@ def dependent_parameters() -> ( yield dep1, indep1, indep2 -def test_registering_control_param_registers_components(control_parameters) -> None: +def test_registering_control_param_registers_components( + control_parameters: tuple[ManualParameter, ManualParameter, ManualParameter], +) -> None: control1, comp1, comp2 = control_parameters meas = Measurement() meas.register_parameter(control1) @@ -46,7 +48,9 @@ def test_registering_control_param_registers_components(control_parameters) -> N assert comp2 in meas._registered_parameters -def test_registering_component_param_registers_control(control_parameters) -> None: +def test_registering_component_param_registers_control( + control_parameters: tuple[ManualParameter, ManualParameter, ManualParameter], +) -> None: control1, comp1, comp2 = control_parameters meas = Measurement() meas.register_parameter(comp1) @@ -55,7 +59,9 @@ def test_registering_component_param_registers_control(control_parameters) -> No assert comp2 in meas._registered_parameters -def test_registering_dependent_param_registers_indeps(dependent_parameters) -> None: +def test_registering_dependent_param_registers_indeps( + dependent_parameters: tuple[ManualParameter, ManualParameter, ManualParameter], +) -> None: dep1, indep1, indep2 = dependent_parameters meas = Measurement() meas.register_parameter(dep1) @@ -65,7 +71,9 @@ def test_registering_dependent_param_registers_indeps(dependent_parameters) -> N # Note, registering indep1 is not expected to also register dep1 automatically -def test_registering_chain_of_control_parameters(control_parameters) -> None: +def test_registering_chain_of_control_parameters( + control_parameters: tuple[ManualParameter, ManualParameter, ManualParameter], +) -> None: control1, comp1, comp2 = control_parameters comp11 = ManualParameter("comp11") comp12 = ManualParameter("comp12") @@ -89,7 +97,9 @@ def test_registering_chain_of_control_parameters(control_parameters) -> None: assert comp22 in meas._registered_parameters -def test_registering_dependent_param_with_setpoints(dependent_parameters) -> None: +def test_registering_dependent_param_with_setpoints( + dependent_parameters: tuple[ManualParameter, ManualParameter, ManualParameter], +) -> None: dep1, indep1, indep2 = dependent_parameters setpoints1 = ManualParameter("setpoints1") setpoints2 = ManualParameter("setpoints2") diff --git a/tests/dataset/measurement/test_self_unpacking.py b/tests/dataset/measurement/test_self_unpacking.py index 3a39a12fc27..0f1e21fc71d 100644 --- a/tests/dataset/measurement/test_self_unpacking.py +++ b/tests/dataset/measurement/test_self_unpacking.py @@ -21,6 +21,7 @@ from collections.abc import Generator from qcodes.dataset.data_set_protocol import ValuesType + from qcodes.dataset.experiment_container import Experiment from qcodes.parameters.parameter_base import ParameterBase @@ -62,7 +63,12 @@ def controlling_parameters() -> ( yield control1, comp1, comp2 -def test_add_result_self_unpack(controlling_parameters, experiment): +def test_add_result_self_unpack( + controlling_parameters: tuple[ + ControllingParameter, ManualParameter, ManualParameter + ], + experiment: "Experiment", +) -> None: control1, comp1, comp2 = controlling_parameters meas1 = ManualParameter("meas1") @@ -93,7 +99,12 @@ def test_add_result_self_unpack(controlling_parameters, experiment): assert meas1_data["comp2"] == pytest.approx(np.linspace(10, 9, 11)) -def test_add_result_self_unpack_with_PWS(controlling_parameters, experiment): +def test_add_result_self_unpack_with_PWS( + controlling_parameters: tuple[ + ControllingParameter, ManualParameter, ManualParameter + ], + experiment: "Experiment", +) -> None: control1, comp1, comp2 = controlling_parameters pws_setpoints = Parameter( "pws_setpoints", diff --git a/tests/dataset/measurement/test_shapes.py b/tests/dataset/measurement/test_shapes.py index b9504dd47cc..bc1dc079074 100644 --- a/tests/dataset/measurement/test_shapes.py +++ b/tests/dataset/measurement/test_shapes.py @@ -10,6 +10,9 @@ if TYPE_CHECKING: from pytest import LogCaptureFixture + from qcodes.dataset.experiment_container import Experiment + from qcodes.instrument_drivers.mock_instruments import DummyInstrument + @given(n_points=hst.integers(min_value=1, max_value=50)) @example(n_points=5) @@ -19,7 +22,11 @@ suppress_health_check=(HealthCheck.function_scoped_fixture,), ) def test_datasaver_1d( - experiment, DAC, DMM, caplog: "LogCaptureFixture", n_points + experiment: "Experiment", + DAC: "DummyInstrument", + DMM: "DummyInstrument", + caplog: "LogCaptureFixture", + n_points: int, ) -> None: meas = Measurement() meas.register_parameter(DAC.ch1) @@ -80,7 +87,12 @@ def test_datasaver_1d( ) @example(n_points_1=5, n_points_2=10) def test_datasaver_2d( - experiment, DAC, DMM, caplog: "LogCaptureFixture", n_points_1, n_points_2 + experiment: "Experiment", + DAC: "DummyInstrument", + DMM: "DummyInstrument", + caplog: "LogCaptureFixture", + n_points_1: int, + n_points_2: int, ) -> None: meas = Measurement() meas.register_parameter(DAC.ch1) diff --git a/tests/dataset/test__get_data_from_ds.py b/tests/dataset/test__get_data_from_ds.py index 9111bad1c12..022100f2152 100644 --- a/tests/dataset/test__get_data_from_ds.py +++ b/tests/dataset/test__get_data_from_ds.py @@ -1,3 +1,5 @@ +from typing import TYPE_CHECKING + import hypothesis.strategies as hst import numpy as np import pytest @@ -13,8 +15,18 @@ from qcodes.dataset.measurements import Measurement from qcodes.parameters import ManualParameter, ParamSpecBase +if TYPE_CHECKING: + from pathlib import Path + + from qcodes.dataset.data_set import DataSet + from qcodes.dataset.experiment_container import Experiment + from qcodes.instrument_drivers.mock_instruments import ( + DummyChannelInstrument, + DummyInstrument, + ) -def test_get_data_by_id_order(dataset) -> None: + +def test_get_data_by_id_order(dataset: "DataSet") -> None: """ Test that the added values of setpoints end up associated with the correct setpoint parameter, irrespective of the ordering of those setpoint @@ -52,7 +64,7 @@ def test_get_data_by_id_order(dataset) -> None: @pytest.mark.parametrize("bg_writing", [True, False]) @pytest.mark.usefixtures("experiment") def test_datasaver_multidimarrayparameter_as_array( - SpectrumAnalyzer, bg_writing + SpectrumAnalyzer: "DummyInstrument", bg_writing: "bool" ) -> None: array_param = SpectrumAnalyzer.multidimspectrum meas = Measurement() @@ -101,7 +113,7 @@ def test_datasaver_multidimarrayparameter_as_array( @pytest.mark.parametrize("bg_writing", [True, False]) @pytest.mark.usefixtures("experiment") def test_datasaver_multidimarrayparameter_as_numeric( - SpectrumAnalyzer, bg_writing + SpectrumAnalyzer: "DummyInstrument", bg_writing: "bool" ) -> None: """ Test that storing a multidim Array parameter as numeric unravels the @@ -165,7 +177,11 @@ def test_datasaver_multidimarrayparameter_as_numeric( @pytest.mark.parametrize("storage_type", ["numeric", "array"]) @pytest.mark.usefixtures("experiment") def test_datasaver_array_parameters_channel( - channel_array_instrument, DAC, N, storage_type, bg_writing + channel_array_instrument: "DummyChannelInstrument", + DAC: "DummyInstrument", + N: "int", + storage_type: "str", + bg_writing: "bool", ) -> None: array_param = channel_array_instrument.A.dummy_array_parameter meas = Measurement() @@ -198,7 +214,10 @@ def test_datasaver_array_parameters_channel( @pytest.mark.usefixtures("experiment") @pytest.mark.parametrize("bg_writing", [True, False]) def test_datasaver_array_parameters_array( - channel_array_instrument, DAC, N, bg_writing + channel_array_instrument: "DummyChannelInstrument", + DAC: "DummyInstrument", + N: "int", + bg_writing: "bool", ) -> None: """ Test that storing array parameters inside a loop works as expected @@ -243,7 +262,7 @@ def test_datasaver_array_parameters_array( @pytest.mark.parametrize("bg_writing", [True, False]) -def test_datasaver_multidim_array(experiment, bg_writing) -> None: +def test_datasaver_multidim_array(experiment: "Experiment", bg_writing: "bool") -> None: """ Test that inserting multidim parameters as arrays works as expected """ @@ -290,7 +309,9 @@ def test_datasaver_multidim_array(experiment, bg_writing) -> None: @pytest.mark.parametrize("bg_writing", [True, False]) -def test_datasaver_multidim_numeric(experiment, bg_writing) -> None: +def test_datasaver_multidim_numeric( + experiment: "Experiment", bg_writing: "bool" +) -> None: """ Test that inserting multidim parameters as numeric works as expected """ @@ -330,7 +351,7 @@ def test_datasaver_multidim_numeric(experiment, bg_writing) -> None: def test_get_data_from_ds_non_unique_setpoint_after_netcdf_reload( - experiment, tmp_path + experiment: "Experiment", tmp_path: "Path" ) -> None: """ Test that _get_data_from_ds works on a dataset reloaded from netcdf when diff --git a/tests/dataset/test_concurrent_datasets.py b/tests/dataset/test_concurrent_datasets.py index c205efff3d0..56a511c7f40 100644 --- a/tests/dataset/test_concurrent_datasets.py +++ b/tests/dataset/test_concurrent_datasets.py @@ -2,13 +2,20 @@ Test that multiple datasets can coexist as expected """ +from typing import TYPE_CHECKING + import pytest from qcodes.dataset import new_experiment from qcodes.dataset.data_set import DataSet +if TYPE_CHECKING: + from qcodes.dataset.sqlite.connection import AtomicConnection + -def test_foreground_after_background_raises(empty_temp_db_connection) -> None: +def test_foreground_after_background_raises( + empty_temp_db_connection: "AtomicConnection", +) -> None: new_experiment("test", "test1", conn=empty_temp_db_connection) ds1 = DataSet(conn=empty_temp_db_connection) ds1.mark_started(start_bg_writer=True) @@ -21,7 +28,9 @@ def test_foreground_after_background_raises(empty_temp_db_connection) -> None: ds1.mark_completed() -def test_background_after_foreground_raises(empty_temp_db_connection) -> None: +def test_background_after_foreground_raises( + empty_temp_db_connection: "AtomicConnection", +) -> None: new_experiment("test", "test1", conn=empty_temp_db_connection) ds1 = DataSet(conn=empty_temp_db_connection) ds1.mark_started(start_bg_writer=False) @@ -31,7 +40,7 @@ def test_background_after_foreground_raises(empty_temp_db_connection) -> None: ds2.mark_started(start_bg_writer=True) -def test_background_twice(empty_temp_db_connection) -> None: +def test_background_twice(empty_temp_db_connection: "AtomicConnection") -> None: new_experiment("test", "test1", conn=empty_temp_db_connection) ds1 = DataSet(conn=empty_temp_db_connection) ds1.mark_started(start_bg_writer=True) @@ -44,7 +53,7 @@ def test_background_twice(empty_temp_db_connection) -> None: ds2.mark_completed() -def test_foreground_twice(empty_temp_db_connection) -> None: +def test_foreground_twice(empty_temp_db_connection: "AtomicConnection") -> None: new_experiment("test", "test1", conn=empty_temp_db_connection) ds1 = DataSet(conn=empty_temp_db_connection) ds1.mark_started(start_bg_writer=False) @@ -53,7 +62,9 @@ def test_foreground_twice(empty_temp_db_connection) -> None: ds2.mark_started(start_bg_writer=False) -def test_foreground_after_background_non_concurrent(empty_temp_db_connection) -> None: +def test_foreground_after_background_non_concurrent( + empty_temp_db_connection: "AtomicConnection", +) -> None: new_experiment("test", "test1", conn=empty_temp_db_connection) ds1 = DataSet(conn=empty_temp_db_connection) ds1.mark_started(start_bg_writer=True) diff --git a/tests/dataset/test_converters.py b/tests/dataset/test_converters.py index 579fda90805..e970618d482 100644 --- a/tests/dataset/test_converters.py +++ b/tests/dataset/test_converters.py @@ -1,3 +1,5 @@ +from typing import TYPE_CHECKING + from qcodes.dataset.descriptions.rundescriber import RunDescriber from qcodes.dataset.descriptions.versioning.converters import ( new_to_old, @@ -18,8 +20,14 @@ from qcodes.dataset.descriptions.versioning.serialization import from_dict_to_current from qcodes.dataset.descriptions.versioning.v0 import InterDependencies +if TYPE_CHECKING: + from qcodes.dataset.descriptions.dependencies import InterDependencies_ + from qcodes.dataset.descriptions.param_spec import ParamSpec + -def test_convert_v0_to_newer(some_paramspecs) -> None: +def test_convert_v0_to_newer( + some_paramspecs: "dict[int, dict[str, ParamSpec]]", +) -> None: pgroup1 = some_paramspecs[1] interdeps = InterDependencies( @@ -33,7 +41,7 @@ def test_convert_v0_to_newer(some_paramspecs) -> None: _assert_dicts_are_related_as_expected(v0, v1, v2) -def test_convert_v1(some_interdeps) -> None: +def test_convert_v1(some_interdeps: "list[InterDependencies_]") -> None: interdeps_ = some_interdeps[0] v1 = RunDescriberV1Dict(interdependencies=interdeps_._to_dict(), version=1) @@ -42,7 +50,7 @@ def test_convert_v1(some_interdeps) -> None: _assert_dicts_are_related_as_expected(v0, v1, v2) -def test_convert_v2(some_interdeps) -> None: +def test_convert_v2(some_interdeps: "list[InterDependencies_]") -> None: interdeps_ = some_interdeps[0] interdeps = new_to_old(interdeps_) @@ -56,7 +64,9 @@ def test_convert_v2(some_interdeps) -> None: _assert_dicts_are_related_as_expected(v0, v1, v2) -def _assert_dicts_are_related_as_expected(v0, v1, v2): +def _assert_dicts_are_related_as_expected( + v0: "RunDescriberV0Dict", v1: "RunDescriberV1Dict", v2: "RunDescriberV2Dict" +) -> "None": assert ( v1["interdependencies"] == old_to_new(InterDependencies._from_dict(v0["interdependencies"]))._to_dict() @@ -74,7 +84,9 @@ def _assert_dicts_are_related_as_expected(v0, v1, v2): assert len(v2) == 3 -def test_construct_current_rundescriber_from_v0(some_paramspecs) -> None: +def test_construct_current_rundescriber_from_v0( + some_paramspecs: "dict[int, dict[str, ParamSpec]]", +) -> None: pgroup1 = some_paramspecs[1] interdeps = InterDependencies( @@ -111,7 +123,9 @@ def _assert_v3_dicts_equal_up_to_paramspec_order( } -def test_construct_current_rundescriber_from_v1(some_interdeps) -> None: +def test_construct_current_rundescriber_from_v1( + some_interdeps: "list[InterDependencies_]", +) -> None: interdeps_ = some_interdeps[0] interdeps = new_to_old(interdeps_) @@ -129,7 +143,9 @@ def test_construct_current_rundescriber_from_v1(some_interdeps) -> None: assert rds_upgraded._to_dict() == expected_v3_dict -def test_construct_current_rundescriber_from_v2(some_interdeps) -> None: +def test_construct_current_rundescriber_from_v2( + some_interdeps: "list[InterDependencies_]", +) -> None: interdeps_ = some_interdeps[0] interdeps = new_to_old(interdeps_) @@ -152,7 +168,9 @@ def test_construct_current_rundescriber_from_v2(some_interdeps) -> None: assert rds_upgraded._to_dict() == expected_v3_dict -def test_construct_current_rundescriber_from_v3(some_interdeps) -> None: +def test_construct_current_rundescriber_from_v3( + some_interdeps: "list[InterDependencies_]", +) -> None: interdeps_ = some_interdeps[0] interdeps = new_to_old(interdeps_) @@ -168,7 +186,9 @@ def test_construct_current_rundescriber_from_v3(some_interdeps) -> None: assert rds_upgraded._to_dict() == v3 -def test_construct_current_rundescriber_from_fake_v4(some_interdeps) -> None: +def test_construct_current_rundescriber_from_fake_v4( + some_interdeps: "list[InterDependencies_]", +) -> None: interdeps_ = some_interdeps[0] interdeps = new_to_old(interdeps_) diff --git a/tests/dataset/test_data_set_cache.py b/tests/dataset/test_data_set_cache.py index 4a49e0ab60c..893f9a23187 100644 --- a/tests/dataset/test_data_set_cache.py +++ b/tests/dataset/test_data_set_cache.py @@ -2,6 +2,7 @@ from math import ceil from string import ascii_uppercase +from typing import TYPE_CHECKING, Literal import hypothesis.strategies as hst import numpy as np @@ -12,6 +13,15 @@ from qcodes.dataset.descriptions.detect_shapes import detect_shape_of_measurement from qcodes.dataset.measurements import Measurement +if TYPE_CHECKING: + from qcodes.dataset.data_set_protocol import ParameterData + from qcodes.dataset.experiment_container import Experiment + from qcodes.instrument_drivers.mock_instruments import ( + DummyChannelInstrument, + DummyInstrument, + ) + from qcodes.parameters import Parameter + @pytest.mark.parametrize("bg_writing", [True, False]) @pytest.mark.parametrize("set_shape", [True, False]) @@ -23,13 +33,13 @@ ) @given(n_points=hst.integers(min_value=1, max_value=11)) def test_cache_standalone( - experiment, - DMM, - n_points, - bg_writing, - channel_array_instrument, - set_shape, - in_memory_cache, + experiment: Experiment, + DMM: DummyInstrument, + n_points: int, + bg_writing: bool, + channel_array_instrument: DummyChannelInstrument, + set_shape: bool, + in_memory_cache: bool, ) -> None: meas1 = Measurement() meas1.register_parameter(DMM.v1) @@ -198,15 +208,15 @@ def test_cache_standalone( ) @given(n_points=hst.integers(min_value=1, max_value=11)) def test_cache_1d( - experiment, - DAC, - DMM, - n_points, - bg_writing, - channel_array_instrument, - setpoints_type, - set_shape, - in_memory_cache, + experiment: Experiment, + DAC: DummyInstrument, + DMM: DummyInstrument, + n_points: int, + bg_writing: bool, + channel_array_instrument: DummyChannelInstrument, + setpoints_type: Literal["text", "numeric"], + set_shape: bool, + in_memory_cache: bool, ) -> None: setpoints_param, setpoints_values = _prepare_setpoints_1d( DAC, channel_array_instrument, n_points, setpoints_type @@ -383,14 +393,14 @@ def test_cache_1d( ) @given(n_points=hst.integers(min_value=1, max_value=101)) def test_cache_1d_every_other_point( - experiment, - DAC, - DMM, - n_points, - bg_writing, - channel_array_instrument, - setpoints_type, - in_memory_cache, + experiment: Experiment, + DAC: DummyInstrument, + DMM: DummyInstrument, + n_points: int, + bg_writing: bool, + channel_array_instrument: DummyChannelInstrument, + setpoints_type: Literal["text", "numeric"], + in_memory_cache: bool, ) -> None: setpoints_param, setpoints_values = _prepare_setpoints_1d( DAC, channel_array_instrument, n_points, setpoints_type @@ -467,14 +477,14 @@ def test_cache_1d_every_other_point( n_points_inner=hst.integers(min_value=1, max_value=11), ) def test_cache_2d( - experiment, - DAC, - DMM, - n_points_outer, - n_points_inner, - bg_writing, - channel_array_instrument, - in_memory_cache, + experiment: Experiment, + DAC: DummyInstrument, + DMM: DummyInstrument, + n_points_outer: int, + n_points_inner: int, + bg_writing: bool, + channel_array_instrument: DummyChannelInstrument, + in_memory_cache: bool, ) -> None: meas = Measurement() @@ -535,14 +545,14 @@ def test_cache_2d( n_points_inner=hst.integers(min_value=1, max_value=11), ) def test_cache_2d_num_with_multiple_storage_types( - experiment, - DAC, - DMM, - n_points_outer, - n_points_inner, - bg_writing, - storage_type, - in_memory_cache, + experiment: Experiment, + DAC: DummyInstrument, + DMM: DummyInstrument, + n_points_outer: int, + n_points_inner: int, + bg_writing: bool, + storage_type: Literal["numeric", "array"] | None, + in_memory_cache: bool, ) -> None: meas = Measurement() @@ -592,13 +602,13 @@ def test_cache_2d_num_with_multiple_storage_types( ) @given(n_points=hst.integers(min_value=1, max_value=21)) def test_cache_1d_array_in_1d( - experiment, - DAC, - channel_array_instrument, - n_points, - bg_writing, - storage_type, - in_memory_cache, + experiment: Experiment, + DAC: DummyInstrument, + channel_array_instrument: DummyChannelInstrument, + n_points: int, + bg_writing: bool, + storage_type: Literal["numeric", "array"] | None, + in_memory_cache: bool, ) -> None: param = channel_array_instrument.A.dummy_array_parameter meas = Measurement() @@ -640,13 +650,13 @@ def test_cache_1d_array_in_1d( ) @given(n_points=hst.integers(min_value=1, max_value=21)) def test_cache_multiparam_in_1d( - experiment, - DAC, - channel_array_instrument, - n_points, - bg_writing, - storage_type, - in_memory_cache, + experiment: Experiment, + DAC: DummyInstrument, + channel_array_instrument: DummyChannelInstrument, + n_points: int, + bg_writing: bool, + storage_type: Literal["numeric", "array"] | None, + in_memory_cache: bool, ) -> None: param = channel_array_instrument.A.dummy_2d_multi_parameter meas = Measurement() @@ -689,14 +699,14 @@ def test_cache_multiparam_in_1d( ) @given(n_points=hst.integers(min_value=1, max_value=21)) def test_cache_complex_array_param_in_1d( - experiment, - DAC, - channel_array_instrument, - n_points, - bg_writing, - storage_type, - outer_param_type, - in_memory_cache, + experiment: Experiment, + DAC: DummyInstrument, + channel_array_instrument: DummyChannelInstrument, + n_points: int, + bg_writing: bool, + storage_type: Literal["array"] | None, + outer_param_type: Literal["numeric", "text"], + in_memory_cache: bool, ) -> None: param = channel_array_instrument.A.dummy_complex_array_parameter meas = Measurement() @@ -745,14 +755,14 @@ def test_cache_complex_array_param_in_1d( ) @given(n_points=hst.integers(min_value=1, max_value=11)) def test_cache_1d_shape( - experiment, - DAC, - DMM, - n_points, - bg_writing, - channel_array_instrument, - setpoints_type, - in_memory_cache, + experiment: Experiment, + DAC: DummyInstrument, + DMM: DummyInstrument, + n_points: int, + bg_writing: bool, + channel_array_instrument: DummyChannelInstrument, + setpoints_type: Literal["text", "numeric"], + in_memory_cache: bool, ) -> None: setpoints_param, setpoints_values = _prepare_setpoints_1d( DAC, channel_array_instrument, n_points, setpoints_type @@ -851,15 +861,15 @@ def test_cache_1d_shape( pws_n_points=hst.integers(min_value=1, max_value=11), ) def test_cache_2d_shape( - experiment, - DAC, - DMM, - n_points_outer, - n_points_inner, - pws_n_points, - bg_writing, - channel_array_instrument, - cache_size, + experiment: Experiment, + DAC: DummyInstrument, + DMM: DummyInstrument, + n_points_outer: int, + n_points_inner: int, + pws_n_points: int, + bg_writing: bool, + channel_array_instrument: DummyChannelInstrument, + cache_size: Literal["too_large", "correct", "too_small"], ) -> None: meas = Measurement() @@ -985,8 +995,11 @@ def test_cache_2d_shape( def _assert_completed_cache_is_as_expected( - cache_data_trees, param_data_trees, flatten=False, clip=False -): + cache_data_trees: ParameterData, + param_data_trees: ParameterData, + flatten: bool = False, + clip: bool = False, +) -> None: # there is a tiny round trip loss in accuracy # when serializing float types approx_kinds = ("f", "c") @@ -1027,12 +1040,12 @@ def _assert_completed_cache_is_as_expected( def _assert_partial_cache_is_as_expected( - cache_data_trees, - expected_shapes, - n_points_measured, - param_data_trees, - cache_correct=True, -): + cache_data_trees: ParameterData, + expected_shapes: dict[str, tuple[int, ...]], + n_points_measured: int, + param_data_trees: ParameterData, + cache_correct: bool = True, +) -> None: assert sorted(cache_data_trees.keys()) == sorted(expected_shapes.keys()) # there is a tiny round trip loss in accuracy # when serializing float types @@ -1068,7 +1081,7 @@ def _assert_parameter_data_is_identical( expected: dict[str, dict[str, np.ndarray]], actual: dict[str, dict[str, np.ndarray]], shaped_partial: bool = False, -): +) -> None: assert expected.keys() == actual.keys() # there is a tiny round trip loss in accuracy # when serializing float types @@ -1112,7 +1125,12 @@ def _array_param_used_in_tree(measurement: Measurement) -> bool: return found_array -def _prepare_setpoints_1d(DAC, channel_array_instrument, n_points, setpoints_type): +def _prepare_setpoints_1d( + DAC: DummyInstrument, + channel_array_instrument: DummyChannelInstrument, + n_points: int, + setpoints_type: Literal["numeric", "text"], +) -> tuple[Parameter[float] | Parameter[str], npt.NDArray[np.float64] | list[str]]: if setpoints_type == "numeric": setpoints_param = DAC.ch1 setpoints_values = np.linspace(-1, 1, n_points) diff --git a/tests/dataset/test_database_creation_and_upgrading.py b/tests/dataset/test_database_creation_and_upgrading.py index bd27c1df347..e9b6c18ec96 100644 --- a/tests/dataset/test_database_creation_and_upgrading.py +++ b/tests/dataset/test_database_creation_and_upgrading.py @@ -55,6 +55,9 @@ from tests.common import error_caused_by, skip_if_no_fixtures from tests.dataset.conftest import temporarily_copied_DB +if TYPE_CHECKING: + from qcodes.dataset.data_set_protocol import DataSetProtocol + if TYPE_CHECKING: from collections.abc import Generator @@ -749,7 +752,9 @@ def test_update_existing_guids(caplog: LogCaptureFixture) -> None: _assert_loc_station(ds5, old_loc, old_ws) -def _assert_loc_station(ds, expected_loc, expected_station): +def _assert_loc_station( + ds: "DataSetProtocol", expected_loc: "int", expected_station: "int" +) -> "None": guid_dict = parse_guid(ds.guid) assert guid_dict["location"] == expected_loc assert guid_dict["work_station"] == expected_station diff --git a/tests/dataset/test_database_extract_runs.py b/tests/dataset/test_database_extract_runs.py index a4fa880ad4e..7ded903b41a 100644 --- a/tests/dataset/test_database_extract_runs.py +++ b/tests/dataset/test_database_extract_runs.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os import random import re @@ -30,7 +32,7 @@ ) from qcodes.dataset.linked_datasets.links import Link from qcodes.dataset.measurements import Measurement -from qcodes.dataset.sqlite.connection import path_to_dbfile +from qcodes.dataset.sqlite.connection import AtomicConnection, path_to_dbfile from qcodes.dataset.sqlite.database import get_db_version_and_newest_available_version from qcodes.dataset.sqlite.queries import get_experiments from qcodes.instrument_drivers.mock_instruments import DummyInstrument @@ -38,11 +40,13 @@ from tests.common import error_caused_by, skip_if_no_fixtures if TYPE_CHECKING: - from collections.abc import Callable + from collections.abc import Callable, Generator + + from qcodes.dataset.descriptions.dependencies import InterDependencies_ @contextmanager -def raise_if_file_changed(path_to_file: str): +def raise_if_file_changed(path_to_file: str) -> Generator[None, None, None]: """ Context manager that raises if a file is modified. On Windows, the OS modification time resolution is 100 ns @@ -57,7 +61,7 @@ def raise_if_file_changed(path_to_file: str): @pytest.fixture(scope="function", name="inst") -def _make_inst(): +def _make_inst() -> Generator[DummyInstrument, None, None]: """ Dummy instrument for testing, ensuring that it's instance gets closed and removed from the global register of instruments, which, if not done, @@ -68,7 +72,10 @@ def _make_inst(): inst.close() -def test_missing_runs_raises(two_empty_temp_db_connections, some_interdeps) -> None: +def test_missing_runs_raises( + two_empty_temp_db_connections: tuple[AtomicConnection, AtomicConnection], + some_interdeps: list[InterDependencies_], +) -> None: """ Test that an error is raised if we attempt to extract a run not present in the source DB @@ -107,7 +114,10 @@ def test_missing_runs_raises(two_empty_temp_db_connections, some_interdeps) -> N extract_runs_into_db(source_path, target_path, *run_ids) -def test_basic_extraction(two_empty_temp_db_connections, some_interdeps) -> None: +def test_basic_extraction( + two_empty_temp_db_connections: tuple[AtomicConnection, AtomicConnection], + some_interdeps: list[InterDependencies_], +) -> None: source_conn, target_conn = two_empty_temp_db_connections source_path = path_to_dbfile(source_conn) @@ -198,7 +208,10 @@ def test_basic_extraction(two_empty_temp_db_connections, some_interdeps) -> None extract_runs_into_db(source_path, target_path, source_dataset.run_id) -def test_real_dataset_1d(two_empty_temp_db_connections, inst) -> None: +def test_real_dataset_1d( + two_empty_temp_db_connections: tuple[AtomicConnection, AtomicConnection], + inst: DummyInstrument, +) -> None: source_conn, target_conn = two_empty_temp_db_connections source_path = path_to_dbfile(source_conn) @@ -226,7 +239,10 @@ def test_real_dataset_1d(two_empty_temp_db_connections, inst) -> None: assert_array_equal(source_data_vals, target_data_vals) -def test_real_dataset_1d_pathlib_path(two_empty_temp_db_connections, inst) -> None: +def test_real_dataset_1d_pathlib_path( + two_empty_temp_db_connections: tuple[AtomicConnection, AtomicConnection], + inst: DummyInstrument, +) -> None: source_conn, target_conn = two_empty_temp_db_connections source_path = Path(path_to_dbfile(source_conn)) @@ -254,7 +270,10 @@ def test_real_dataset_1d_pathlib_path(two_empty_temp_db_connections, inst) -> No assert_array_equal(source_data_vals, target_data_vals) -def test_real_dataset_2d(two_empty_temp_db_connections, inst) -> None: +def test_real_dataset_2d( + two_empty_temp_db_connections: tuple[AtomicConnection, AtomicConnection], + inst: DummyInstrument, +) -> None: source_conn, target_conn = two_empty_temp_db_connections source_path = path_to_dbfile(source_conn) @@ -285,7 +304,8 @@ def test_real_dataset_2d(two_empty_temp_db_connections, inst) -> None: def test_correct_experiment_routing( - two_empty_temp_db_connections, some_interdeps + two_empty_temp_db_connections: tuple[AtomicConnection, AtomicConnection], + some_interdeps: list[InterDependencies_], ) -> None: """ Test that existing experiments are correctly identified AND that multiple @@ -382,7 +402,8 @@ def test_correct_experiment_routing( def test_runs_from_different_experiments_raises( - two_empty_temp_db_connections, some_interdeps + two_empty_temp_db_connections: tuple[AtomicConnection, AtomicConnection], + some_interdeps: list[InterDependencies_], ) -> None: """ Test that inserting runs from multiple experiments raises @@ -437,7 +458,9 @@ def test_runs_from_different_experiments_raises( extract_runs_into_db(source_path, target_path, *run_ids) -def test_extracting_dataless_run(two_empty_temp_db_connections) -> None: +def test_extracting_dataless_run( + two_empty_temp_db_connections: tuple[AtomicConnection, AtomicConnection], +) -> None: """ Although contrived, it could happen that a run with no data is extracted """ @@ -460,7 +483,8 @@ def test_extracting_dataless_run(two_empty_temp_db_connections) -> None: def test_result_table_naming_and_run_id( - two_empty_temp_db_connections, some_interdeps + two_empty_temp_db_connections: tuple[AtomicConnection, AtomicConnection], + some_interdeps: list[InterDependencies_], ) -> None: """ Check that a correct result table name is given and that a correct run_id @@ -505,7 +529,10 @@ def test_result_table_naming_and_run_id( assert target_ds.the_same_dataset_as(source_ds_2_2) -def test_load_by_X_functions(two_empty_temp_db_connections, some_interdeps) -> None: +def test_load_by_X_functions( + two_empty_temp_db_connections: tuple[AtomicConnection, AtomicConnection], + some_interdeps: list[InterDependencies_], +) -> None: """ Test some different loading functions """ @@ -557,7 +584,9 @@ def test_load_by_X_functions(two_empty_temp_db_connections, some_interdeps) -> N def test_combine_runs( - two_empty_temp_db_connections, empty_temp_db_connection, some_interdeps + two_empty_temp_db_connections: tuple[AtomicConnection, AtomicConnection], + empty_temp_db_connection: AtomicConnection, + some_interdeps: list[InterDependencies_], ) -> None: """ Test that datasets that are exported in random order from 2 datasets @@ -645,7 +674,8 @@ def test_combine_runs( def test_copy_datasets_and_add_new( - two_empty_temp_db_connections, some_interdeps + two_empty_temp_db_connections: tuple[AtomicConnection, AtomicConnection], + some_interdeps: list[InterDependencies_], ) -> None: """ Test that new runs get the correct captured_run_id and captured_counter @@ -727,7 +757,8 @@ def test_copy_datasets_and_add_new( def test_old_versions_not_touched( - two_empty_temp_db_connections, some_interdeps + two_empty_temp_db_connections: tuple[AtomicConnection, AtomicConnection], + some_interdeps: list[InterDependencies_], ) -> None: source_conn, target_conn = two_empty_temp_db_connections @@ -782,7 +813,8 @@ def test_old_versions_not_touched( def test_experiments_with_NULL_sample_name( - two_empty_temp_db_connections, some_interdeps + two_empty_temp_db_connections: tuple[AtomicConnection, AtomicConnection], + some_interdeps: list[InterDependencies_], ) -> None: """ In older API versions (corresponding to DB version 3), @@ -833,7 +865,8 @@ def test_experiments_with_NULL_sample_name( def test_integration_station_and_measurement( - two_empty_temp_db_connections, inst + two_empty_temp_db_connections: tuple[AtomicConnection, AtomicConnection], + inst: DummyInstrument, ) -> None: """ An integration test where the runs in the source DB file are produced @@ -869,7 +902,10 @@ def test_integration_station_and_measurement( assert datasaver.dataset.the_same_dataset_as(target_ds) -def test_atomicity(two_empty_temp_db_connections, some_interdeps) -> None: +def test_atomicity( + two_empty_temp_db_connections: tuple[AtomicConnection, AtomicConnection], + some_interdeps: list[InterDependencies_], +) -> None: """ Test the atomicity of the transaction by extracting and inserting two runs where the second one is not completed. The not completed error must @@ -903,7 +939,11 @@ def test_atomicity(two_empty_temp_db_connections, some_interdeps) -> None: extract_runs_into_db(source_path, target_path, 1, 2) -def test_column_mismatch(two_empty_temp_db_connections, some_interdeps, inst) -> None: +def test_column_mismatch( + two_empty_temp_db_connections: tuple[AtomicConnection, AtomicConnection], + some_interdeps: list[InterDependencies_], + inst: DummyInstrument, +) -> None: """ Test insertion of runs with no metadata and no snapshot into a DB already containing a run that has both diff --git a/tests/dataset/test_datasaver.py b/tests/dataset/test_datasaver.py index 1a4f965adec..a552b1b7c23 100644 --- a/tests/dataset/test_datasaver.py +++ b/tests/dataset/test_datasaver.py @@ -18,7 +18,13 @@ CALLBACK_SNAPSHOT = None -def callback(result_list, data_set_len, state, run_id, snapshot): +def callback( + result_list: "list[tuple[object, ...]]", + data_set_len: "int", + state: "object", + run_id: "int", + snapshot: "str | None", +) -> "None": """ default_callback example function implemented in the Web UI. """ @@ -28,7 +34,7 @@ def callback(result_list, data_set_len, state, run_id, snapshot): CALLBACK_SNAPSHOT = snapshot -def reset_callback_globals(): +def reset_callback_globals() -> "None": global CALLBACK_COUNT, CALLBACK_RUN_ID, CALLBACK_SNAPSHOT CALLBACK_COUNT = 0 CALLBACK_RUN_ID = None @@ -37,7 +43,7 @@ def reset_callback_globals(): @pytest.mark.usefixtures("experiment") @pytest.mark.parametrize("bg_writing", [True, False]) -def test_default_callback(bg_writing) -> None: +def test_default_callback(bg_writing: "bool") -> None: """ The Web UI needs to know the results of an experiment with the metadata. So a default_callback class variable is set by the Web UI with a callback @@ -75,7 +81,7 @@ def test_default_callback(bg_writing) -> None: @pytest.mark.usefixtures("experiment") @pytest.mark.parametrize("bg_writing", [True, False]) -def test_numpy_types(bg_writing) -> None: +def test_numpy_types(bg_writing: "bool") -> None: """ Test that we can save numpy types in the data set """ @@ -135,7 +141,10 @@ def test_numpy_types(bg_writing) -> None: ], ) @pytest.mark.parametrize("bg_writing", [True, False]) -def test_saving_numeric_values_as_text(numeric_type, bg_writing) -> None: +def test_saving_numeric_values_as_text( + numeric_type: "Callable[[int], int | float | np.integer | np.floating]", + bg_writing: "bool", +) -> None: """ Test the saving numeric values into 'text' parameter raises an exception """ diff --git a/tests/dataset/test_dataset_basic.py b/tests/dataset/test_dataset_basic.py index 1b35782d8ca..9a43ce6c465 100644 --- a/tests/dataset/test_dataset_basic.py +++ b/tests/dataset/test_dataset_basic.py @@ -41,12 +41,13 @@ from pytest_mock import MockerFixture from qcodes.dataset.experiment_container import Experiment + from qcodes.dataset.sqlite.connection import AtomicConnection from qcodes.dataset.sqlite.query_helpers import VALUE n_experiments = 0 -def make_shadow_dataset(dataset: DataSet): +def make_shadow_dataset(dataset: DataSet) -> DataSet: """ Creates a new DataSet object that points to the same run_id in the same database file as the given dataset object. Note that for a pristine run, @@ -125,7 +126,7 @@ def test_dataset_length() -> None: assert len(ds) == 1 -def test_dataset_location(empty_temp_db_connection) -> None: +def test_dataset_location(empty_temp_db_connection: "AtomicConnection") -> None: """ Test that an dataset and experiment points to the correct db file when a connection is supplied. @@ -207,7 +208,7 @@ def test_dataset_states() -> None: @pytest.mark.parametrize("start_bg_writer", (True, False)) @pytest.mark.usefixtures("experiment") -def test_mark_completed_twice(start_bg_writer) -> None: +def test_mark_completed_twice(start_bg_writer: bool) -> None: """ Ensure that its not an error to call mark_completed on an already completed dataset @@ -247,7 +248,7 @@ def test_integer_timestamps_in_database_are_supported() -> None: assert isinstance(ds.completed_timestamp(), str) -def test_dataset_read_only_properties(dataset) -> None: +def test_dataset_read_only_properties(dataset: DataSet) -> None: read_only_props = [ "run_id", "path_to_db", @@ -276,7 +277,9 @@ def test_dataset_read_only_properties(dataset) -> None: @pytest.mark.usefixtures("experiment") @pytest.mark.parametrize("non_existing_run_id", (1, 0, -1, "number#42")) -def test_create_dataset_from_non_existing_run_id(non_existing_run_id) -> None: +def test_create_dataset_from_non_existing_run_id( + non_existing_run_id, +) -> None: with pytest.raises( ValueError, match=f"Run with run_id {non_existing_run_id} does not exist in the database", @@ -284,7 +287,9 @@ def test_create_dataset_from_non_existing_run_id(non_existing_run_id) -> None: _ = DataSet(run_id=non_existing_run_id) -def test_create_dataset_pass_both_connection_and_path_to_db(experiment) -> None: +def test_create_dataset_pass_both_connection_and_path_to_db( + experiment: "Experiment", +) -> None: some_valid_connection = experiment.conn with pytest.raises( ValueError, @@ -295,7 +300,7 @@ def test_create_dataset_pass_both_connection_and_path_to_db(experiment) -> None: _ = DataSet(path_to_db="some valid path", conn=some_valid_connection) -def test_load_by_id(dataset) -> None: +def test_load_by_id(dataset: DataSet) -> None: ds = load_by_id(dataset.run_id) assert dataset.run_id == ds.run_id assert dataset.path_to_db == ds.path_to_db @@ -303,7 +308,7 @@ def test_load_by_id(dataset) -> None: @pytest.mark.usefixtures("experiment") @pytest.mark.parametrize("non_existing_run_id", (1, 0, -1)) -def test_load_by_id_for_nonexisting_run_id(non_existing_run_id) -> None: +def test_load_by_id_for_nonexisting_run_id(non_existing_run_id: int) -> None: with pytest.raises( ValueError, match=f"Run with run_id {non_existing_run_id} does not exist in the database", @@ -329,7 +334,9 @@ def test_load_by_id_for_none() -> None: ), ) @pytest.mark.usefixtures("empty_temp_db") -def test_add_experiments(experiment_name, sample_name, dataset_name) -> None: +def test_add_experiments( + experiment_name: str, sample_name: str, dataset_name: str +) -> None: qc.config.GUID_components.GUID_type = "random_sample" global n_experiments @@ -398,7 +405,7 @@ def test_dependent_parameters() -> None: assert ds.dependent_parameters == (pss[3], pss[0]) -def test_set_interdependencies(dataset) -> None: +def test_set_interdependencies(dataset: DataSet) -> None: exps = experiments() assert len(exps) == 1 exp = exps[0] @@ -561,7 +568,9 @@ def test_load_by_counter() -> None: @pytest.mark.usefixtures("experiment") @pytest.mark.parametrize("nonexisting_counter", (-1, 0, 1, None)) -def test_load_by_counter_for_nonexisting_counter(nonexisting_counter) -> None: +def test_load_by_counter_for_nonexisting_counter( + nonexisting_counter, +) -> None: exp_id = 1 with pytest.raises(RuntimeError, match="Expected one row"): _ = load_by_counter(exp_id, nonexisting_counter) @@ -569,7 +578,9 @@ def test_load_by_counter_for_nonexisting_counter(nonexisting_counter) -> None: @pytest.mark.usefixtures("empty_temp_db") @pytest.mark.parametrize("nonexisting_exp_id", (-1, 0, 1, None)) -def test_load_by_counter_for_nonexisting_experiment(nonexisting_exp_id) -> None: +def test_load_by_counter_for_nonexisting_experiment( + nonexisting_exp_id, +) -> None: with pytest.raises(RuntimeError, match="Expected one row"): _ = load_by_counter(nonexisting_exp_id, 1) @@ -580,13 +591,13 @@ def test_dataset_with_no_experiment_raises() -> None: new_data_set("test-dataset") -def test_guid(dataset) -> None: +def test_guid(dataset: DataSet) -> None: guid = dataset.guid assert len(guid) == 36 parse_guid(guid) -def test_numpy_ints(dataset) -> None: +def test_numpy_ints(dataset: DataSet) -> None: """ Test that we can insert numpy integers in the data set """ @@ -603,7 +614,7 @@ def test_numpy_ints(dataset) -> None: ) -def test_numpy_floats(dataset) -> None: +def test_numpy_floats(dataset: DataSet) -> None: """ Test that we can insert numpy floats in the data set """ @@ -635,7 +646,7 @@ def test_numpy_complex(dataset) -> None: np.testing.assert_array_equal(result["x"]["x"], expected_result) -def test_numpy_nan(dataset) -> None: +def test_numpy_nan(dataset: DataSet) -> None: parameter_m = ParamSpecBase("m", "numeric") idps = InterDependencies_(standalones=(parameter_m,)) dataset.set_interdependencies(idps) @@ -647,7 +658,7 @@ def test_numpy_nan(dataset) -> None: assert np.isnan(retrieved[1]) -def test_numpy_inf(dataset) -> None: +def test_numpy_inf(dataset: DataSet) -> None: """ Test that we can insert and retrieve numpy inf in the data set """ @@ -671,7 +682,7 @@ def test_backward_compat__adapt_array_v0_33() -> None: assert arr == _convert_array(out.read()) -def test_missing_keys(dataset) -> None: +def test_missing_keys(dataset: DataSet) -> None: """ Test that we can now have partial results with keys missing. This is for example handy when having an interleaved 1D and 2D sweep. @@ -686,10 +697,10 @@ def test_missing_keys(dataset) -> None: dataset.set_interdependencies(idps) dataset.mark_started() - def fa(xv): + def fa(xv: int) -> int: return xv + 1 - def fb(xv, yv): + def fb(xv: int, yv: int) -> int: return xv + 2 - yv * 3 results = [] @@ -717,7 +728,9 @@ def fb(xv, yv): ) -def test_get_description(experiment, some_interdeps) -> None: +def test_get_description( + experiment: "Experiment", some_interdeps: list[InterDependencies_] +) -> None: ds = DataSet() assert ds.run_id == 1 @@ -743,7 +756,7 @@ def test_get_description(experiment, some_interdeps) -> None: assert loaded_ds.description == expected_desc -def test_metadata(experiment, request: FixtureRequest) -> None: +def test_metadata(experiment: "Experiment", request: FixtureRequest) -> None: metadata1 = {"number": 1, "string": "Once upon a time..."} metadata2 = {"more": "meta"} @@ -803,7 +816,9 @@ def test_metadata(experiment, request: FixtureRequest) -> None: assert ds1.metadata["np_scalar"] == 3 -def test_the_same_dataset_as(some_interdeps, experiment) -> None: +def test_the_same_dataset_as( + some_interdeps: list[InterDependencies_], experiment: "Experiment" +) -> None: ds = DataSet() ds.set_interdependencies(some_interdeps[1]) ds.mark_started() @@ -841,7 +856,7 @@ def test_parent_dataset_links_invalid_input() -> None: @pytest.mark.usefixtures("experiment") -def test_parent_dataset_links(some_interdeps) -> None: +def test_parent_dataset_links(some_interdeps: list[InterDependencies_]) -> None: """ Test that we can set links and retrieve them when loading the dataset """ @@ -883,7 +898,7 @@ class TestGetData: xdata = np.array(xvals) @pytest.fixture(autouse=True) - def ds_with_vals(self, dataset): + def ds_with_vals(self, dataset: DataSet) -> DataSet: """ This fixture creates a DataSet with values that is to be used by all the tests in this class @@ -927,9 +942,13 @@ def ds_with_vals(self, dataset): ], ) def test_get_data_with_start_and_end_args( - self, ds_with_vals, start, end, expected + self, + ds_with_vals: DataSet, + start: int | None, + end: int | None, + expected: npt.NDArray[np.int64] | list[np.int64], ) -> None: - data = ds_with_vals.get_parameter_data(self.x, start=start, end=end)["x"] + data = ds_with_vals.get_parameter_data(self.x.name, start=start, end=end)["x"] data = data["x"] np.testing.assert_array_equal(data, expected) @@ -939,7 +958,9 @@ def test_get_data_with_start_and_end_args( start=hst.one_of(hst.integers(1, 10**3), hst.none()), end=hst.one_of(hst.integers(1, 10**3), hst.none()), ) -def test_get_parameter_data(scalar_dataset, start, end) -> None: +def test_get_parameter_data( + scalar_dataset: DataSet, start: int | None, end: int | None +) -> None: input_names = ["param_3"] expected_names = {} @@ -966,7 +987,9 @@ def test_get_parameter_data(scalar_dataset, start, end) -> None: ) -def test_get_scalar_parameter_data_no_nulls(scalar_dataset_with_nulls) -> None: +def test_get_scalar_parameter_data_no_nulls( + scalar_dataset_with_nulls: DataSet, +) -> None: expected_names = {} expected_names["first_value"] = ["first_value", "setpoint"] expected_names["second_value"] = ["second_value", "setpoint"] @@ -986,7 +1009,9 @@ def test_get_scalar_parameter_data_no_nulls(scalar_dataset_with_nulls) -> None: ) -def test_get_array_parameter_data_no_nulls(array_dataset_with_nulls) -> None: +def test_get_array_parameter_data_no_nulls( + array_dataset_with_nulls: DataSet, +) -> None: types = [p.type for p in array_dataset_with_nulls.paramspecs.values()] expected_names = {} @@ -1018,7 +1043,7 @@ def test_get_array_parameter_data_no_nulls(array_dataset_with_nulls) -> None: ) -def test_get_array_parameter_data(array_dataset) -> None: +def test_get_array_parameter_data(array_dataset: DataSet) -> None: paramspecs = array_dataset.paramspecs types = [param.type for param in paramspecs.values()] par_name = "array_setpoint_param" @@ -1047,7 +1072,7 @@ def test_get_array_parameter_data(array_dataset) -> None: ) -def test_get_multi_parameter_data(multi_dataset) -> None: +def test_get_multi_parameter_data(multi_dataset: DataSet) -> None: paramspecs = multi_dataset.paramspecs types = [param.type for param in paramspecs.values()] @@ -1106,7 +1131,9 @@ def test_get_multi_parameter_data(multi_dataset) -> None: start=hst.one_of(hst.integers(1, 9), hst.none()), end=hst.one_of(hst.integers(1, 9), hst.none()), ) -def test_get_array_in_scalar_param_data(array_in_scalar_dataset, start, end) -> None: +def test_get_array_in_scalar_param_data( + array_in_scalar_dataset: DataSet, start: int | None, end: int | None +) -> None: par_name = "array_setpoint_param" setpoint_name = "array_setpoint_param_this_setpoint" @@ -1148,7 +1175,9 @@ def test_get_array_in_scalar_param_data(array_in_scalar_dataset, start, end) -> ) -def test_get_varlen_array_in_scalar_param_data(varlen_array_in_scalar_dataset) -> None: +def test_get_varlen_array_in_scalar_param_data( + varlen_array_in_scalar_dataset: DataSet, +) -> None: par_name = "array_setpoint_param" setpoint_name = "array_setpoint_param_this_setpoint" @@ -1196,7 +1225,7 @@ def test_get_varlen_array_in_scalar_param_data(varlen_array_in_scalar_dataset) - end=hst.one_of(hst.integers(1, 45), hst.none()), ) def test_get_array_in_scalar_param_unrolled( - array_in_scalar_dataset_unrolled, start, end + array_in_scalar_dataset_unrolled: DataSet, start: int | None, end: int | None ) -> None: par_name = "array_setpoint_param" setpoint_name = "array_setpoint_param_this_setpoint" @@ -1238,7 +1267,7 @@ def test_get_array_in_scalar_param_unrolled( ) -def test_get_array_in_str_param_data(array_in_str_dataset) -> None: +def test_get_array_in_str_param_data(array_in_str_dataset: DataSet) -> None: paramspecs = array_in_str_dataset.paramspecs types = [param.type for param in paramspecs.values()] @@ -1331,7 +1360,7 @@ def parameter_test_helper( expected_values: dict[str, list[np.ndarray]], start: int | None = None, end: int | None = None, -): +) -> None: """ A helper function to compare the data we actually read out of a given dataset with the expected data. @@ -1405,14 +1434,20 @@ def parameter_test_helper( def limit_data_to_start_end( - start, end, input_names, expected_names, expected_shapes, expected_values -): + start: int | None, + end: int | None, + input_names: list[str], + expected_names: dict[str, list[str]], + expected_shapes, + expected_values, +) -> tuple[int | None, int | None]: if not (start is None and end is None): if start is None: start = 1 elif end is None: # all the shapes are the same so pick the first one end = expected_shapes[input_names[0]][0][0] + assert start is not None and end is not None if end < start: for name in input_names: expected_shapes[name] = () diff --git a/tests/dataset/test_dataset_export.py b/tests/dataset/test_dataset_export.py index 7183f6fa6a0..027be867622 100644 --- a/tests/dataset/test_dataset_export.py +++ b/tests/dataset/test_dataset_export.py @@ -66,7 +66,7 @@ def _make_mock_empty_dataset(experiment: Experiment) -> DataSet: @pytest.fixture(name="mock_dataset") -def _make_mock_dataset(experiment) -> DataSet: +def _make_mock_dataset(experiment: Experiment) -> DataSet: dataset = new_data_set("dataset") xparam = ParamSpecBase("x", "numeric") yparam = ParamSpecBase("y", "numeric") diff --git a/tests/dataset/test_dataset_in_mem_import.py b/tests/dataset/test_dataset_in_mem_import.py index 024bc988950..4d04d4df7e4 100644 --- a/tests/dataset/test_dataset_in_mem_import.py +++ b/tests/dataset/test_dataset_in_mem_import.py @@ -1,4 +1,7 @@ +from __future__ import annotations + import json +from typing import TYPE_CHECKING import numpy as np @@ -6,11 +9,19 @@ from qcodes.dataset.data_set import DataSet from qcodes.dataset.data_set_in_memory import DataSetInMem from qcodes.dataset.experiment_container import Experiment -from qcodes.dataset.sqlite.connection import path_to_dbfile +from qcodes.dataset.sqlite.connection import AtomicConnection, path_to_dbfile + +if TYPE_CHECKING: + from collections.abc import Sequence + from pathlib import Path + + from qcodes.dataset.data_set_protocol import DataSetProtocol def test_basic_roundtrip( - two_empty_temp_db_connections, some_interdeps, tmp_path + two_empty_temp_db_connections: tuple[AtomicConnection, AtomicConnection], + some_interdeps, + tmp_path: Path, ) -> None: """ Test that we can export from one db and import into another in @@ -68,7 +79,9 @@ def test_basic_roundtrip( def test_roundtrip_to_db_with_existing_meas( - two_empty_temp_db_connections, some_interdeps, tmp_path + two_empty_temp_db_connections: tuple[AtomicConnection, AtomicConnection], + some_interdeps, + tmp_path: Path, ) -> None: """ Test that we can export from one db and import into another in @@ -152,7 +165,9 @@ def test_roundtrip_to_db_with_existing_meas( def test_roundtrip_to_db_with_existing_meas_in_same_exp( - two_empty_temp_db_connections, some_interdeps, tmp_path + two_empty_temp_db_connections: tuple[AtomicConnection, AtomicConnection], + some_interdeps, + tmp_path: Path, ) -> None: """ Test that we can export from one db and import into another in @@ -232,7 +247,9 @@ def test_roundtrip_to_db_with_existing_meas_in_same_exp( ) -def _assert_before_writing_metadata(source_datasets, loaded_datasets) -> None: +def _assert_before_writing_metadata( + source_datasets: Sequence[DataSetInMem], loaded_datasets: Sequence[DataSetInMem] +) -> None: for ds, lds in zip(source_datasets, loaded_datasets): # these should always be equal assert ds.guid == lds.guid @@ -265,12 +282,12 @@ def _assert_before_writing_metadata(source_datasets, loaded_datasets) -> None: def _assert_after_writing_metadata( - source_datasets, - loaded_datasets, - reloaded_ds, - offset_total_ds, - offset_ds_in_exp, - offset_exps, + source_datasets: Sequence[DataSetInMem], + loaded_datasets: Sequence[DataSetInMem], + reloaded_ds: Sequence[DataSetProtocol], + offset_total_ds: int, + offset_ds_in_exp: int, + offset_exps: int, ) -> None: for ds, lds, rlds in zip(source_datasets, loaded_datasets, reloaded_ds): # these should always be equal diff --git a/tests/dataset/test_dataset_in_memory.py b/tests/dataset/test_dataset_in_memory.py index df00b01c0cb..69362b92c1c 100644 --- a/tests/dataset/test_dataset_in_memory.py +++ b/tests/dataset/test_dataset_in_memory.py @@ -21,12 +21,20 @@ from qcodes.parameters import ManualParameter, Parameter, ParamSpecBase from qcodes.station import Station +if TYPE_CHECKING: + from qcodes.dataset.data_set_protocol import DataSetProtocol +if TYPE_CHECKING: + from qcodes.instrument_drivers.mock_instruments import DummyInstrument + if TYPE_CHECKING: from qcodes.dataset.experiment_container import Experiment def test_dataset_in_memory_reload_from_db( - meas_with_registered_param, DMM, DAC, tmp_path + meas_with_registered_param: "Measurement", + DMM: "DummyInstrument", + DAC: "DummyInstrument", + tmp_path: "Path", ) -> None: with meas_with_registered_param.run( dataset_class=DataSetType.DataSetInMem @@ -62,7 +70,12 @@ def test_dataset_in_memory_reload_from_db( shape2=hst.integers(min_value=1, max_value=100), ) def test_dataset_in_memory_reload_from_db_2d( - meas_with_registered_param_2d, DMM, DAC, tmp_path, shape1, shape2 + meas_with_registered_param_2d: "Measurement", + DMM: "DummyInstrument", + DAC: "DummyInstrument", + tmp_path: "Path", + shape1: "int", + shape2: "int", ) -> None: meas_with_registered_param_2d.set_shapes( { @@ -120,7 +133,13 @@ def test_dataset_in_memory_reload_from_db_2d( shape3=hst.integers(min_value=1, max_value=10), ) def test_dataset_in_memory_reload_from_db_3d( - meas_with_registered_param_3d, DMM, DAC3D, tmp_path, shape1, shape2, shape3 + meas_with_registered_param_3d: "Measurement", + DMM: "DummyInstrument", + DAC3D: "DummyInstrument", + tmp_path: "Path", + shape1: "int", + shape2: "int", + shape3: "int", ) -> None: meas_with_registered_param_3d.set_shapes( { @@ -175,7 +194,10 @@ def test_dataset_in_memory_reload_from_db_3d( def test_dataset_in_memory_without_cache_raises( - meas_with_registered_param, DMM, DAC, tmp_path + meas_with_registered_param: "Measurement", + DMM: "DummyInstrument", + DAC: "DummyInstrument", + tmp_path: "Path", ) -> None: with ( # noqa: PT012 pytest.raises( @@ -195,7 +217,10 @@ def test_dataset_in_memory_without_cache_raises( def test_dataset_in_memory_reload_from_db_complex( - meas_with_registered_param_complex, DAC, complex_num_instrument, tmp_path + meas_with_registered_param_complex: "Measurement", + DAC: "DummyInstrument", + complex_num_instrument: "DummyInstrument", + tmp_path: "Path", ) -> None: with meas_with_registered_param_complex.run( dataset_class=DataSetType.DataSetInMem @@ -219,7 +244,10 @@ def test_dataset_in_memory_reload_from_db_complex( def test_dataset_in_memory_reload_from_netcdf_complex( - meas_with_registered_param_complex, DAC, complex_num_instrument, tmp_path + meas_with_registered_param_complex: "Measurement", + DAC: "DummyInstrument", + complex_num_instrument: "DummyInstrument", + tmp_path: "Path", ) -> None: with meas_with_registered_param_complex.run( dataset_class=DataSetType.DataSetInMem @@ -245,7 +273,10 @@ def test_dataset_in_memory_reload_from_netcdf_complex( def test_dataset_in_memory_no_export_warns( - meas_with_registered_param, DMM, DAC, tmp_path + meas_with_registered_param: "Measurement", + DMM: "DummyInstrument", + DAC: "DummyInstrument", + tmp_path: "Path", ) -> None: with meas_with_registered_param.run( dataset_class=DataSetType.DataSetInMem @@ -272,7 +303,10 @@ def test_dataset_in_memory_no_export_warns( def test_dataset_in_memory_missing_file_warns( - meas_with_registered_param, DMM, DAC, tmp_path + meas_with_registered_param: "Measurement", + DMM: "DummyInstrument", + DAC: "DummyInstrument", + tmp_path: "Path", ) -> None: with meas_with_registered_param.run( dataset_class=DataSetType.DataSetInMem @@ -295,7 +329,10 @@ def test_dataset_in_memory_missing_file_warns( def test_dataset_in_reload_from_netcdf( - meas_with_registered_param, DMM, DAC, tmp_path + meas_with_registered_param: "Measurement", + DMM: "DummyInstrument", + DAC: "DummyInstrument", + tmp_path: "Path", ) -> None: with meas_with_registered_param.run( dataset_class=DataSetType.DataSetInMem @@ -321,7 +358,10 @@ def test_dataset_in_reload_from_netcdf( def test_dataset_load_from_netcdf_and_db( - meas_with_registered_param, DMM, DAC, tmp_path + meas_with_registered_param: "Measurement", + DMM: "DummyInstrument", + DAC: "DummyInstrument", + tmp_path: "Path", ) -> None: with meas_with_registered_param.run( dataset_class=DataSetType.DataSetInMem @@ -339,7 +379,7 @@ def test_dataset_load_from_netcdf_and_db( get_v = DMM.v1() datasaver.add_result((DAC.ch1, set_v), (DMM.v1, get_v)) - path_to_db = datasaver.dataset._path_to_db + path_to_db = datasaver.dataset.path_to_db ds = datasaver.dataset ds.add_metadata("mymetadatatag", 42) @@ -359,7 +399,10 @@ def test_dataset_load_from_netcdf_and_db( def test_dataset_in_memory_does_not_create_runs_table( - meas_with_registered_param, DMM, DAC, tmp_path + meas_with_registered_param: "Measurement", + DMM: "DummyInstrument", + DAC: "DummyInstrument", + tmp_path: "Path", ) -> None: with meas_with_registered_param.run( dataset_class=DataSetType.DataSetInMem @@ -370,7 +413,8 @@ def test_dataset_in_memory_does_not_create_runs_table( datasaver.add_result((DAC.ch1, set_v), (DMM.v1, get_v)) ds = datasaver.dataset - dbfile = datasaver.dataset._path_to_db + dbfile = datasaver.dataset.path_to_db + assert dbfile is not None conn = AtomicConnection(dbfile) @@ -381,7 +425,7 @@ def test_dataset_in_memory_does_not_create_runs_table( assert all(ds.name not in table_name for table_name in tablenames) -def test_load_from_netcdf_and_write_metadata_to_db(empty_temp_db) -> None: +def test_load_from_netcdf_and_write_metadata_to_db(empty_temp_db: "None") -> None: netcdf_file_path = ( Path(__file__).parent / "fixtures" / "db_files" / "netcdf" / "qcodes_2.nc" ) @@ -414,7 +458,7 @@ def test_load_from_netcdf_and_write_metadata_to_db(empty_temp_db) -> None: compare_datasets(ds, loaded_ds) -def test_load_from_netcdf_no_db_file(non_created_db) -> None: +def test_load_from_netcdf_no_db_file(non_created_db: "None") -> None: netcdf_file_path = ( Path(__file__).parent / "fixtures" / "db_files" / "netcdf" / "qcodes_2.nc" ) @@ -429,7 +473,12 @@ def test_load_from_netcdf_no_db_file(non_created_db) -> None: compare_datasets(ds, loaded_ds) -def test_load_from_db(meas_with_registered_param, DMM, DAC, tmp_path) -> None: +def test_load_from_db( + meas_with_registered_param: "Measurement", + DMM: "DummyInstrument", + DAC: "DummyInstrument", + tmp_path: "Path", +) -> None: Station(DAC, DMM) with meas_with_registered_param.run( dataset_class=DataSetType.DataSetInMem @@ -459,7 +508,12 @@ def test_load_from_db(meas_with_registered_param, DMM, DAC, tmp_path) -> None: compare_datasets(ds, loaded_ds) -def test_load_from_file(meas_with_registered_param, DMM, DAC, tmp_path) -> None: +def test_load_from_file( + meas_with_registered_param: "Measurement", + DMM: "DummyInstrument", + DAC: "DummyInstrument", + tmp_path: "Path", +) -> None: qcodes.config["dataset"]["export_prefix"] = "my_export_prefix" qcodes.config["dataset"]["export_type"] = "netcdf" @@ -472,7 +526,7 @@ def test_load_from_file(meas_with_registered_param, DMM, DAC, tmp_path) -> None: get_v = DMM.v1() datasaver.add_result((DAC.ch1, set_v), (DMM.v1, get_v)) - ds: DataSetInMem = datasaver.dataset + ds: DataSetProtocol = datasaver.dataset ds.add_metadata("foo", "bar") ds.export(path=tmp_path) ds.add_metadata("metadata_added_after_export", "42") @@ -494,7 +548,12 @@ def test_load_from_file(meas_with_registered_param, DMM, DAC, tmp_path) -> None: compare_datasets(ds, loaded_ds) -def test_load_from_file_by_id(meas_with_registered_param, DMM, DAC, tmp_path) -> None: +def test_load_from_file_by_id( + meas_with_registered_param: "Measurement", + DMM: "DummyInstrument", + DAC: "DummyInstrument", + tmp_path: "Path", +) -> None: qcodes.config["dataset"]["export_prefix"] = "my_export_prefix" qcodes.config["dataset"]["export_type"] = "netcdf" qcodes.config["dataset"]["load_from_exported_file"] = True @@ -507,7 +566,7 @@ def test_load_from_file_by_id(meas_with_registered_param, DMM, DAC, tmp_path) -> get_v = DMM.v1() datasaver.add_result((DAC.ch1, set_v), (DMM.v1, get_v)) - ds: DataSetInMem = datasaver.dataset + ds: DataSetProtocol = datasaver.dataset assert not isinstance(ds, DataSetInMem) ds.export(path=tmp_path) @@ -522,7 +581,9 @@ def test_load_from_file_by_id(meas_with_registered_param, DMM, DAC, tmp_path) -> assert not isinstance(loaded_ds_from_db, DataSetInMem) -def test_load_from_netcdf_non_completed_dataset(experiment, tmp_path) -> None: +def test_load_from_netcdf_non_completed_dataset( + experiment: "Experiment", tmp_path: "Path" +) -> None: """Test that non-completed datasets can be loaded from netcdf files.""" # Create a non-completed dataset by NOT using the measurement context manager # which automatically completes the dataset on exit @@ -565,7 +626,7 @@ def test_load_from_netcdf_non_completed_dataset(experiment, tmp_path) -> None: assert loaded_ds.run_timestamp_raw == ds.run_timestamp_raw -def test_load_from_netcdf_legacy_version(non_created_db) -> None: +def test_load_from_netcdf_legacy_version(non_created_db: "None") -> None: # Qcodes 0.26 exported netcdf files did not contain # the parent dataset links and used a different engine to write data # check that it still loads correctly @@ -584,7 +645,7 @@ def test_load_from_netcdf_legacy_version(non_created_db) -> None: compare_datasets(ds, loaded_ds) -def compare_datasets(ds, loaded_ds): +def compare_datasets(ds: "DataSetProtocol", loaded_ds: "DataSetProtocol") -> "None": assert ds.the_same_dataset_as(loaded_ds) assert len(ds) == len(loaded_ds) assert len(ds) != 0 @@ -606,7 +667,10 @@ def compare_datasets(ds, loaded_ds): def test_load_from_db_dataset_moved( - meas_with_registered_param, DMM, DAC, tmp_path + meas_with_registered_param: "Measurement", + DMM: "DummyInstrument", + DAC: "DummyInstrument", + tmp_path: "Path", ) -> None: Station(DAC, DMM) with meas_with_registered_param.run( diff --git a/tests/dataset/test_dataset_in_memory_bacis.py b/tests/dataset/test_dataset_in_memory_bacis.py index 1531642d476..d8ab0305cc8 100644 --- a/tests/dataset/test_dataset_in_memory_bacis.py +++ b/tests/dataset/test_dataset_in_memory_bacis.py @@ -1,5 +1,6 @@ import re from pathlib import Path +from typing import TYPE_CHECKING import pytest @@ -9,8 +10,11 @@ from qcodes.dataset.descriptions.dependencies import InterDependencies_ from qcodes.parameters import ParamSpecBase +if TYPE_CHECKING: + from qcodes.dataset.experiment_container import Experiment -def test_create_dataset_in_memory_explicit_db(empty_temp_db) -> None: + +def test_create_dataset_in_memory_explicit_db(empty_temp_db: "None") -> None: default_db_location = qc.config["core"]["db_location"] extra_db_location = str(Path(default_db_location).parent / "extra.db") @@ -25,7 +29,7 @@ def test_create_dataset_in_memory_explicit_db(empty_temp_db) -> None: assert default_db_location != extra_db_location -def test_empty_ds_parameters(experiment) -> None: +def test_empty_ds_parameters(experiment: "Experiment") -> None: ds = DataSetInMem._create_new_run(name="foo") assert ds._parameters is None @@ -38,7 +42,7 @@ def test_empty_ds_parameters(experiment) -> None: assert ds._parameters is None -def test_write_metadata_to_explicit_db(empty_temp_db) -> None: +def test_write_metadata_to_explicit_db(empty_temp_db: "None") -> None: default_db_location = qc.config["core"]["db_location"] extra_db_location = str(Path(default_db_location).parent / "extra.db") load_or_create_experiment(experiment_name="myexp", sample_name="mysample") @@ -55,13 +59,13 @@ def test_write_metadata_to_explicit_db(empty_temp_db) -> None: ds.the_same_dataset_as(loaded_ds) -def test_no_interdeps_raises_in_prepare(experiment) -> None: +def test_no_interdeps_raises_in_prepare(experiment: "Experiment") -> None: ds = DataSetInMem._create_new_run(name="foo") with pytest.raises(RuntimeError, match="No parameters supplied"): ds.prepare(interdeps=InterDependencies_(), snapshot={}) -def test_prepare_twice_raises(experiment) -> None: +def test_prepare_twice_raises(experiment: "Experiment") -> None: ds = DataSetInMem._create_new_run(name="foo") pss: list[ParamSpecBase] = [] @@ -77,7 +81,7 @@ def test_prepare_twice_raises(experiment) -> None: ds.prepare(interdeps=idps, snapshot={}) -def test_timestamps(experiment) -> None: +def test_timestamps(experiment: "Experiment") -> None: ds = DataSetInMem._create_new_run(name="foo") assert ds.run_timestamp() is None @@ -111,7 +115,7 @@ def test_timestamps(experiment) -> None: ds.mark_completed() -def test_mark_pristine_completed_raises(experiment) -> None: +def test_mark_pristine_completed_raises(experiment: "Experiment") -> None: ds = DataSetInMem._create_new_run(name="foo") with pytest.raises( @@ -120,7 +124,7 @@ def test_mark_pristine_completed_raises(experiment) -> None: ds.mark_completed() -def test_load_from_non_existing_guid(experiment) -> None: +def test_load_from_non_existing_guid(experiment: "Experiment") -> None: guid = "This is not a guid" with pytest.raises( RuntimeError, match="Could not find the requested run with GUID" diff --git a/tests/dataset/test_dataset_loading.py b/tests/dataset/test_dataset_loading.py index ebdb732f255..784fe2667e4 100644 --- a/tests/dataset/test_dataset_loading.py +++ b/tests/dataset/test_dataset_loading.py @@ -3,6 +3,7 @@ import datetime import time from math import floor +from typing import TYPE_CHECKING import numpy as np import pytest @@ -26,6 +27,11 @@ raw_time_to_str_time, ) +if TYPE_CHECKING: + from collections.abc import Callable +if TYPE_CHECKING: + from qcodes.dataset.descriptions.dependencies import InterDependencies_ + @pytest.mark.usefixtures("experiment") def test_load_by_id() -> None: @@ -250,7 +256,7 @@ def test_completed_timestamp_with_default_format() -> None: @pytest.mark.usefixtures("experiment") -def test_load_by_guid(some_interdeps) -> None: +def test_load_by_guid(some_interdeps: list[InterDependencies_]) -> None: ds = DataSet() ds.set_interdependencies(some_interdeps[1]) ds.mark_started() @@ -261,8 +267,10 @@ def test_load_by_guid(some_interdeps) -> None: assert loaded_ds.the_same_dataset_as(ds) -def test_load_by_run_spec(empty_temp_db, some_interdeps) -> None: - def create_ds_with_exp_id(exp_id): +def test_load_by_run_spec( + empty_temp_db: None, some_interdeps: list[InterDependencies_] +) -> None: + def create_ds_with_exp_id(exp_id: int) -> DataSet: ds = DataSet(exp_id=exp_id) ds.set_interdependencies(some_interdeps[1]) ds.mark_started() @@ -357,7 +365,7 @@ def create_ds_with_exp_id(exp_id): def test_callback(scalar_datasets_parameterized: DataSet) -> None: called_progress: list[float] = [] - def callback_closure(called_progress: list[float]): + def callback_closure(called_progress: list[float]) -> Callable[[float], None]: def callback(progress: float) -> None: called_progress.append(progress) diff --git a/tests/dataset/test_dependencies.py b/tests/dataset/test_dependencies.py index d43699c8c9d..8aee0c94575 100644 --- a/tests/dataset/test_dependencies.py +++ b/tests/dataset/test_dependencies.py @@ -27,7 +27,9 @@ def test_wrong_input_raises() -> None: InterDependencies(pspecs) # type: ignore[arg-type] -def test_init(some_paramspecbases) -> None: +def test_init( + some_paramspecbases: "tuple[ParamSpecBase, ParamSpecBase, ParamSpecBase, ParamSpecBase]", +) -> None: """ Assert that the init functions correctly sets up the object. Assert via the public-facing methods. @@ -56,7 +58,9 @@ def test_init(some_paramspecbases) -> None: assert idps.top_level_parameters == (ps1, ps4) -def test_init_validation_raises(some_paramspecbases) -> None: +def test_init_validation_raises( + some_paramspecbases: "tuple[ParamSpecBase, ParamSpecBase, ParamSpecBase, ParamSpecBase]", +) -> None: (ps1, ps2, ps3, ps4) = some_paramspecbases # First test validation of trees invalid in their own right @@ -106,8 +110,10 @@ def test_init_validation_raises(some_paramspecbases) -> None: ) -def test_to_dict(some_paramspecbases) -> None: - def tester(idps) -> None: +def test_to_dict( + some_paramspecbases: "tuple[ParamSpecBase, ParamSpecBase, ParamSpecBase, ParamSpecBase]", +) -> None: + def tester(idps: "InterDependencies_") -> None: ser = idps._to_dict() json.dumps(ser) idps_deser = InterDependencies_._from_dict(ser) @@ -127,14 +133,16 @@ def tester(idps) -> None: tester(idps) -def test_old_to_new_and_back(some_paramspecs) -> None: +def test_old_to_new_and_back( + some_paramspecs: "dict[int, dict[str, ParamSpec]]", +) -> None: idps_old = InterDependencies(*some_paramspecs[1].values()) idps_new = old_to_new(idps_old) assert new_to_old(idps_new) == idps_old -def test_old_to_new(some_paramspecs) -> None: +def test_old_to_new(some_paramspecs: "dict[int, dict[str, ParamSpec]]") -> None: ps1 = some_paramspecs[1]["ps1"] ps2 = some_paramspecs[1]["ps2"] ps3 = some_paramspecs[1]["ps3"] @@ -183,7 +191,9 @@ def test_old_to_new(some_paramspecs) -> None: assert idps_new._id_to_paramspec == {ps.name: ps for ps in paramspecs3} -def test_new_to_old(some_paramspecbases) -> None: +def test_new_to_old( + some_paramspecbases: "tuple[ParamSpecBase, ParamSpecBase, ParamSpecBase, ParamSpecBase]", +) -> None: (ps1, ps2, ps3, ps4) = some_paramspecbases idps_new = InterDependencies_(dependencies={ps1: (ps2, ps3)}, standalones=(ps4,)) @@ -235,7 +245,9 @@ def test_new_to_old(some_paramspecbases) -> None: assert new_to_old(idps_new) == idps_old_expected -def test_validate_subset(some_paramspecbases) -> None: +def test_validate_subset( + some_paramspecbases: "tuple[ParamSpecBase, ParamSpecBase, ParamSpecBase, ParamSpecBase]", +) -> None: ps1, ps2, ps3, ps4 = some_paramspecbases idps = InterDependencies_( @@ -274,7 +286,9 @@ def test_validate_subset(some_paramspecbases) -> None: idps.validate_subset((ps2, ps42, ps4)) -def test_extend(some_paramspecbases) -> None: +def test_extend( + some_paramspecbases: "tuple[ParamSpecBase, ParamSpecBase, ParamSpecBase, ParamSpecBase]", +) -> None: ps1, ps2, ps3, _ = some_paramspecbases idps = InterDependencies_(standalones=(ps1, ps2)) @@ -317,7 +331,9 @@ def test_extend(some_paramspecbases) -> None: idps_ext = idps.extend(inferences={ps2: (ps1,)}) -def test_remove(some_paramspecbases) -> None: +def test_remove( + some_paramspecbases: "tuple[ParamSpecBase, ParamSpecBase, ParamSpecBase, ParamSpecBase]", +) -> None: ps1, ps2, ps3, ps4 = some_paramspecbases idps = InterDependencies_(dependencies={ps1: (ps2, ps3)}, inferences={ps2: (ps4,)}) @@ -347,7 +363,7 @@ def test_remove(some_paramspecbases) -> None: assert idps_rem == idps_expected -def test_equality_old(some_paramspecs) -> None: +def test_equality_old(some_paramspecs: "dict[int, dict[str, ParamSpec]]") -> None: # TODO: make this more fancy with itertools ps1 = some_paramspecs[1]["ps1"] @@ -441,7 +457,9 @@ def test_collect_related__complex( assert idps2.find_all_parameters_in_tree(ps1) == {ps1, ps2, ps3} -def test_all_parameters_in_tree_by_group_raises_on_non_top_level(some_paramspecbases): +def test_all_parameters_in_tree_by_group_raises_on_non_top_level( + some_paramspecbases: "tuple[ParamSpecBase, ParamSpecBase, ParamSpecBase, ParamSpecBase]", +) -> "None": (ps1, ps2, ps3, ps4) = some_paramspecbases idps = InterDependencies_(dependencies={ps1: (ps2,)}, inferences={ps2: (ps3,)}) @@ -478,7 +496,9 @@ def test_dependency_on_middle_parameter( assert idps.find_all_parameters_in_tree(ps1) == {ps1, ps2, ps3, ps4} -def test_frozen_interdependencies(some_paramspecbases) -> None: +def test_frozen_interdependencies( + some_paramspecbases: "tuple[ParamSpecBase, ParamSpecBase, ParamSpecBase, ParamSpecBase]", +) -> None: ps1, ps2, ps3, ps4 = some_paramspecbases idps = InterDependencies_(dependencies={ps1: (ps2, ps3)}, inferences={ps2: (ps4,)}) @@ -525,7 +545,9 @@ def test_frozen_interdependencies(some_paramspecbases) -> None: assert frozen._top_level_parameters_cache is not None -def test_frozen_from_dict(some_paramspecbases) -> None: +def test_frozen_from_dict( + some_paramspecbases: "tuple[ParamSpecBase, ParamSpecBase, ParamSpecBase, ParamSpecBase]", +) -> None: ps1, ps2, ps3, _ = some_paramspecbases idps = InterDependencies_(dependencies={ps1: (ps2, ps3)}) ser = idps._to_dict() diff --git a/tests/dataset/test_descriptions.py b/tests/dataset/test_descriptions.py index 248c52ebfc9..44747961758 100644 --- a/tests/dataset/test_descriptions.py +++ b/tests/dataset/test_descriptions.py @@ -1,4 +1,5 @@ import json +from typing import TYPE_CHECKING import pytest from ruamel.yaml import YAML @@ -8,6 +9,9 @@ from qcodes.dataset.descriptions.versioning import serialization as serial from qcodes.dataset.descriptions.versioning.converters import new_to_old +if TYPE_CHECKING: + from qcodes.parameters import ParamSpecBase + def test_wrong_input_type_raises() -> None: for interdeps in ("interdeps", ["p1", "p2"], 0): @@ -15,7 +19,9 @@ def test_wrong_input_type_raises() -> None: RunDescriber(interdeps=interdeps) # type: ignore[arg-type] -def test_equality(some_paramspecbases) -> None: +def test_equality( + some_paramspecbases: "tuple[ParamSpecBase, ParamSpecBase, ParamSpecBase, ParamSpecBase]", +) -> None: (psb1, psb2, psb3, _) = some_paramspecbases idp1 = InterDependencies_(dependencies={psb1: (psb2, psb3)}) @@ -31,7 +37,7 @@ def test_equality(some_paramspecbases) -> None: assert desc_3 != desc_2 -def test_keys_of_result_of_to_dict(some_interdeps) -> None: +def test_keys_of_result_of_to_dict(some_interdeps: "list[InterDependencies_]") -> None: for idps in some_interdeps: desc = RunDescriber(interdeps=idps) @@ -44,7 +50,7 @@ def test_keys_of_result_of_to_dict(some_interdeps) -> None: ] -def test_to_and_from_dict_roundtrip(some_interdeps) -> None: +def test_to_and_from_dict_roundtrip(some_interdeps: "list[InterDependencies_]") -> None: for idps in some_interdeps: desc = RunDescriber(interdeps=idps) @@ -56,7 +62,7 @@ def test_to_and_from_dict_roundtrip(some_interdeps) -> None: assert desc == new_desc -def test_yaml_creation_and_loading(some_interdeps) -> None: +def test_yaml_creation_and_loading(some_interdeps: "list[InterDependencies_]") -> None: yaml = YAML() for idps in some_interdeps: @@ -77,7 +83,9 @@ def test_yaml_creation_and_loading(some_interdeps) -> None: assert new_desc == desc -def test_jsonization_as_v0_for_storage(some_interdeps) -> None: +def test_jsonization_as_v0_for_storage( + some_interdeps: "list[InterDependencies_]", +) -> None: """ Test that a RunDescriber can be json-dumped as version 0 """ @@ -90,7 +98,9 @@ def test_jsonization_as_v0_for_storage(some_interdeps) -> None: assert serial.to_json_as_version(new_desc, 0) == old_json -def test_default_jsonization_for_storage(some_interdeps) -> None: +def test_default_jsonization_for_storage( + some_interdeps: "list[InterDependencies_]", +) -> None: """ Test that a RunDescriber is json-dumped as version 2 """ @@ -110,7 +120,9 @@ def test_default_jsonization_for_storage(some_interdeps) -> None: assert serial.to_json_for_storage(new_desc) == expected_json -def test_dictization_as_v0_for_storage(some_interdeps) -> None: +def test_dictization_as_v0_for_storage( + some_interdeps: "list[InterDependencies_]", +) -> None: """ Test that a RunDescriber always gets converted to dict that represents an old style RunDescriber, even when given new style interdeps @@ -125,7 +137,9 @@ def test_dictization_as_v0_for_storage(some_interdeps) -> None: assert serial.to_dict_as_version(new_desc, 0) == old_desc -def test_default_dictization_for_storage(some_interdeps) -> None: +def test_default_dictization_for_storage( + some_interdeps: "list[InterDependencies_]", +) -> None: """ Test that a RunDescriber always gets converted to dict that represents an old style RunDescriber, even when given new style interdeps @@ -145,7 +159,9 @@ def test_default_dictization_for_storage(some_interdeps) -> None: assert serial.to_dict_for_storage(new_desc) == old_desc -def test_dictization_of_current_version(some_interdeps) -> None: +def test_dictization_of_current_version( + some_interdeps: "list[InterDependencies_]", +) -> None: """ Test conversion to dictionary of a RunDescriber """ diff --git a/tests/dataset/test_detect_shape.py b/tests/dataset/test_detect_shape.py index 1fa2b097451..7a2f64025c4 100644 --- a/tests/dataset/test_detect_shape.py +++ b/tests/dataset/test_detect_shape.py @@ -1,3 +1,7 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + import hypothesis.strategies as hst import numpy as np import pytest @@ -6,6 +10,7 @@ from qcodes.dataset.descriptions.detect_shapes import detect_shape_of_measurement from qcodes.instrument_drivers.mock_instruments import ( ArraySetPointParam, + DummyChannelInstrument, Multi2DSetPointParam, Multi2DSetPointParam2Sizes, MultiSetPointParam, @@ -15,9 +20,12 @@ from .conftest import ArrayshapedParam +if TYPE_CHECKING: + from collections.abc import Callable, Sized + @given(loop_shape=hst.lists(hst.integers(min_value=1), min_size=1, max_size=10)) -def test_get_shape_for_parameter_from_len(loop_shape) -> None: +def test_get_shape_for_parameter_from_len(loop_shape: list[int]) -> None: a = Parameter(name="a", initial_cache_value=10) shape = detect_shape_of_measurement((a,), loop_shape) assert shape == {"a": tuple(loop_shape)} @@ -29,7 +37,9 @@ def test_get_shape_for_parameter_from_len(loop_shape) -> None: ) ) @pytest.mark.parametrize("range_func", [range, np.arange]) -def test_get_shape_for_parameter_from_sequence(loop_shape, range_func) -> None: +def test_get_shape_for_parameter_from_sequence( + loop_shape: list[int], range_func: Callable[[int], Sized] +) -> None: a = Parameter(name="a", initial_cache_value=10) loop_sequence = tuple(range_func(x) for x in loop_shape) shape = detect_shape_of_measurement((a,), loop_sequence) @@ -37,7 +47,7 @@ def test_get_shape_for_parameter_from_sequence(loop_shape, range_func) -> None: @given(loop_shape=hst.lists(hst.integers(min_value=1), min_size=1, max_size=10)) -def test_get_shape_for_array_parameter_from_len(loop_shape) -> None: +def test_get_shape_for_array_parameter_from_len(loop_shape: list[int]) -> None: a = ArraySetPointParam(name="a") shape = detect_shape_of_measurement((a,), loop_shape) expected_shape = tuple(loop_shape) + tuple(a.shape) @@ -50,7 +60,9 @@ def test_get_shape_for_array_parameter_from_len(loop_shape) -> None: ) ) @pytest.mark.parametrize("range_func", [range, np.arange]) -def test_get_shape_for_array_parameter_from_shape(loop_shape, range_func) -> None: +def test_get_shape_for_array_parameter_from_shape( + loop_shape: list[int], range_func: Callable[[int], Sized] +) -> None: a = ArraySetPointParam(name="a") loop_sequence = tuple(range_func(x) for x in loop_shape) shape = detect_shape_of_measurement((a,), loop_sequence) @@ -63,7 +75,12 @@ def test_get_shape_for_array_parameter_from_shape(loop_shape, range_func) -> Non "multiparamtype", [MultiSetPointParam, Multi2DSetPointParam, Multi2DSetPointParam2Sizes], ) -def test_get_shape_for_multiparam_from_len(loop_shape, multiparamtype) -> None: +def test_get_shape_for_multiparam_from_len( + loop_shape: list[int], + multiparamtype: type[ + MultiSetPointParam | Multi2DSetPointParam | Multi2DSetPointParam2Sizes + ], +) -> None: param = multiparamtype(name="meas_param") shapes = detect_shape_of_measurement((param,), loop_shape) expected_shapes = {} @@ -83,7 +100,11 @@ def test_get_shape_for_multiparam_from_len(loop_shape, multiparamtype) -> None: ) @pytest.mark.parametrize("range_func", [range, np.arange]) def test_get_shape_for_multiparam_from_shape( - loop_shape, multiparamtype, range_func + loop_shape: list[int], + multiparamtype: type[ + MultiSetPointParam | Multi2DSetPointParam | Multi2DSetPointParam2Sizes + ], + range_func: Callable[[int], Sized], ) -> None: param = multiparamtype(name="meas_param") loop_sequence = tuple(range_func(x) for x in loop_shape) @@ -99,7 +120,9 @@ def test_get_shape_for_multiparam_from_shape( loop_shape=hst.lists(hst.integers(min_value=1), min_size=1, max_size=10), n_points=hst.integers(min_value=1, max_value=1000), ) -def test_get_shape_for_pws_from_len(dummyinstrument, loop_shape, n_points) -> None: +def test_get_shape_for_pws_from_len( + dummyinstrument: DummyChannelInstrument, loop_shape: list[int], n_points: int +) -> None: param = dummyinstrument.A.dummy_parameter_with_setpoints dummyinstrument.A.dummy_n_points(n_points) shapes = detect_shape_of_measurement((param,), loop_shape) @@ -119,7 +142,10 @@ def test_get_shape_for_pws_from_len(dummyinstrument, loop_shape, n_points) -> No n_points=hst.integers(min_value=1, max_value=1000), ) def test_get_shape_for_pws_from_shape( - dummyinstrument, loop_shape, range_func, n_points + dummyinstrument: DummyChannelInstrument, + loop_shape: list[int], + range_func: Callable[[int], Sized], + n_points: int, ) -> None: param = dummyinstrument.A.dummy_parameter_with_setpoints dummyinstrument.A.dummy_n_points(n_points) @@ -140,7 +166,10 @@ def test_get_shape_for_pws_from_shape( n_points=hst.integers(min_value=1, max_value=1000), ) def test_get_shape_for_pws_with_register_name_from_shape( - dummyinstrument, loop_shape, range_func, n_points + dummyinstrument: DummyChannelInstrument, + loop_shape: list[int], + range_func: Callable[[int], Sized], + n_points: int, ) -> None: param = dummyinstrument.A.dummy_parameter_with_setpoints param._register_name = f"register_{param.full_name}" # register_name can ordinarily only be set at init @@ -158,7 +187,9 @@ def test_get_shape_for_pws_with_register_name_from_shape( hst.integers(min_value=1, max_value=1000), min_size=1, max_size=10 ) ) -def test_get_shape_for_param_with_array_validator_from_shape(loop_shape) -> None: +def test_get_shape_for_param_with_array_validator_from_shape( + loop_shape: list[int], +) -> None: param = ArrayshapedParam(name="paramwitharrayval", vals=Arrays(shape=(10,))) shapes = detect_shape_of_measurement((param,), loop_shape) @@ -174,7 +205,10 @@ def test_get_shape_for_param_with_array_validator_from_shape(loop_shape) -> None n_points_2=hst.integers(min_value=1, max_value=1000), ) def test_get_shape_for_multiple_parameters( - dummyinstrument, loop_shape, n_points_1, n_points_2 + dummyinstrument: DummyChannelInstrument, + loop_shape: list[int], + n_points_1: int, + n_points_2: int, ) -> None: param1 = dummyinstrument.A.dummy_parameter_with_setpoints dummyinstrument.A.dummy_n_points(n_points_1) diff --git a/tests/dataset/test_experiment_container.py b/tests/dataset/test_experiment_container.py index 23e819e210f..5393ac64041 100644 --- a/tests/dataset/test_experiment_container.py +++ b/tests/dataset/test_experiment_container.py @@ -1,5 +1,6 @@ import logging import re +from typing import TYPE_CHECKING import pytest from pytest import LogCaptureFixture @@ -20,8 +21,11 @@ from qcodes.dataset.measurements import Measurement from qcodes.dataset.sqlite.database import conn_from_dbpath_or_conn, get_DB_location +if TYPE_CHECKING: + from qcodes.dataset.data_set import DataSet -def assert_experiments_equal(exp, exp_2): + +def assert_experiments_equal(exp: "Experiment", exp_2: "Experiment") -> "None": for attr in ["name", "sample_name", "path_to_db", "last_counter"]: assert getattr(exp, attr) == getattr(exp_2, attr) assert len(exp_2) == len(exp) @@ -45,7 +49,7 @@ def test_run_loaded_experiment() -> None: pass -def test_last_data_set_from_experiment(dataset) -> None: +def test_last_data_set_from_experiment(dataset: "DataSet") -> None: experiment = load_experiment(dataset.exp_id) ds = experiment.last_data_set() @@ -59,7 +63,9 @@ def test_last_data_set_from_experiment(dataset) -> None: assert experiment.path_to_db == ds.path_to_db -def test_last_data_set_from_experiment_with_no_datasets(experiment) -> None: +def test_last_data_set_from_experiment_with_no_datasets( + experiment: "Experiment", +) -> None: with pytest.raises(ValueError, match="There are no runs in this experiment"): _ = experiment.last_data_set() @@ -141,7 +147,7 @@ def test_has_attributes_after_init() -> None: getattr(exp, attr) -def test_experiment_read_only_properties(experiment) -> None: +def test_experiment_read_only_properties(experiment: "Experiment") -> None: read_only_props = [ "name", "exp_id", @@ -168,7 +174,7 @@ def test_create_experiment_from_non_existing_id(non_existing_id) -> None: @pytest.mark.usefixtures("empty_temp_db") @pytest.mark.parametrize("non_existing_id", (1, 0, -1)) -def test_load_experiment_from_non_existing_id(non_existing_id) -> None: +def test_load_experiment_from_non_existing_id(non_existing_id: "int") -> None: with pytest.raises(ValueError, match="No such experiment in the database"): _ = load_experiment(non_existing_id) @@ -180,7 +186,7 @@ def test_load_experiment_from_bad_id(bad_id) -> None: _ = load_experiment(bad_id) -def test_format_string(empty_temp_db) -> None: +def test_format_string(empty_temp_db: "None") -> None: # default format string exp1 = Experiment(exp_id=None) assert "{}-{}-{}" == exp1.format_string @@ -201,7 +207,7 @@ def test_format_string(empty_temp_db) -> None: _ = Experiment(exp_id=None, format_string=fmt_str) -def test_load_experiment_by_name_defaults(empty_temp_db) -> None: +def test_load_experiment_by_name_defaults(empty_temp_db: "None") -> None: exp1 = Experiment(exp_id=None) exp2 = load_experiment_by_name("experiment_1") @@ -211,7 +217,7 @@ def test_load_experiment_by_name_defaults(empty_temp_db) -> None: assert_experiments_equal(exp1, exp3) -def test_load_experiment_by_name(empty_temp_db) -> None: +def test_load_experiment_by_name(empty_temp_db: "None") -> None: exp1 = Experiment(exp_id=None, name="myname") exp2 = load_experiment_by_name("myname") @@ -229,7 +235,7 @@ def test_load_experiment_by_name(empty_temp_db) -> None: assert_experiments_equal(exp4, exp6) -def test_load_experiment_by_name_bad_name(empty_temp_db) -> None: +def test_load_experiment_by_name_bad_name(empty_temp_db: "None") -> None: Experiment(exp_id=None, name="myname") with pytest.raises(ValueError, match="Experiment not found"): @@ -242,7 +248,7 @@ def test_load_experiment_by_name_bad_name(empty_temp_db) -> None: _ = load_experiment_by_name("myname__", "some_sample__") -def test_load_experiment_by_name_bad_sample_name(empty_temp_db) -> None: +def test_load_experiment_by_name_bad_sample_name(empty_temp_db: "None") -> None: Experiment(exp_id=None, sample_name="mysample") with pytest.raises(ValueError, match="Experiment not found"): @@ -255,7 +261,7 @@ def test_load_experiment_by_name_bad_sample_name(empty_temp_db) -> None: _ = load_experiment_by_name("experiment_1__", "mysample__") -def test_load_experiment_by_name_duplicate_name(empty_temp_db) -> None: +def test_load_experiment_by_name_duplicate_name(empty_temp_db: "None") -> None: exp1 = Experiment(exp_id=None, name="exp") exp2 = Experiment(exp_id=None, name="exp") @@ -292,7 +298,7 @@ def test_load_experiment_by_name_duplicate_name(empty_temp_db) -> None: assert_experiments_equal(exp3, exp3_loaded) -def test_load_experiment_by_name_duplicate_sample_name(empty_temp_db) -> None: +def test_load_experiment_by_name_duplicate_sample_name(empty_temp_db: "None") -> None: exp1 = Experiment(exp_id=None, name="exp1", sample_name="sss") exp2 = Experiment(exp_id=None, name="exp2", sample_name="sss") @@ -309,7 +315,9 @@ def test_load_experiment_by_name_duplicate_sample_name(empty_temp_db) -> None: assert_experiments_equal(exp2, exp2_loaded_with_sample) -def test_load_experiment_by_name_duplicate_name_and_sample_name(empty_temp_db) -> None: +def test_load_experiment_by_name_duplicate_name_and_sample_name( + empty_temp_db: "None", +) -> None: exp1 = Experiment(exp_id=None, name="exp", sample_name="sss") exp2 = Experiment(exp_id=None, name="exp", sample_name="sss") @@ -335,7 +343,7 @@ def test_load_experiment_by_name_duplicate_name_and_sample_name(empty_temp_db) - def test_new_experiment_duplicate_name_and_sample_name( - empty_temp_db, caplog: LogCaptureFixture + empty_temp_db: "None", caplog: LogCaptureFixture ) -> None: """ Test new_experiment to raise warning if it wants to create experiment @@ -368,7 +376,7 @@ def test_new_experiment_duplicate_name_and_sample_name( caplog.clear() -def test_load_last_experiment(empty_temp_db) -> None: +def test_load_last_experiment(empty_temp_db: "None") -> None: # test in case of no experiments with pytest.raises( ValueError, match="There are no experiments in the database file" @@ -388,7 +396,7 @@ def test_load_last_experiment(empty_temp_db) -> None: assert last_exp.path_to_db == exp2.path_to_db -def test_active_experiment(empty_temp_db) -> None: +def test_active_experiment(empty_temp_db: "None") -> None: conn = conn_from_dbpath_or_conn(conn=None, path_to_db=empty_temp_db) with pytest.raises(ValueError): get_default_experiment_id(conn) diff --git a/tests/dataset/test_export_info.py b/tests/dataset/test_export_info.py index ce2915de91f..8c8eac8d34d 100644 --- a/tests/dataset/test_export_info.py +++ b/tests/dataset/test_export_info.py @@ -38,7 +38,7 @@ def test_invalid_key_raises() -> None: _ = ExportInfo({"nd": nd_path}) -def test_export_info_json_roundtrip(basic_export_info) -> None: +def test_export_info_json_roundtrip(basic_export_info: "ExportInfo") -> None: exported_str = basic_export_info.to_str() loaded_export_info = ExportInfo.from_str(exported_str) assert loaded_export_info == basic_export_info diff --git a/tests/dataset/test_fix_functions.py b/tests/dataset/test_fix_functions.py index bececdbe3e0..860e843456c 100644 --- a/tests/dataset/test_fix_functions.py +++ b/tests/dataset/test_fix_functions.py @@ -69,7 +69,7 @@ def test_fix_wrong_run_descriptions() -> None: skip_if_no_fixtures(dbname_old) - def make_ps(n): + def make_ps(n: "int") -> "ParamSpec": ps = ParamSpec( f"p{n}", label=f"Parameter {n}", unit=f"unit {n}", paramtype="numeric" ) diff --git a/tests/dataset/test_guids.py b/tests/dataset/test_guids.py index 7dcc5edf79d..6ecc8c8a35d 100644 --- a/tests/dataset/test_guids.py +++ b/tests/dataset/test_guids.py @@ -1,6 +1,7 @@ import random import re import time +from typing import TYPE_CHECKING from uuid import uuid4 import hypothesis.strategies as hst @@ -18,9 +19,14 @@ validate_guid_format, ) +if TYPE_CHECKING: + from qcodes.configuration import Config +if TYPE_CHECKING: + from collections.abc import Generator + @pytest.fixture(name="seed_random") -def _make_seed_random(): +def _make_seed_random() -> "Generator[None, None, None]": state = random.getstate() random.seed(a=0) try: @@ -35,7 +41,7 @@ def _make_seed_random(): stat=hst.integers(0, 0xFFFF), smpl=hst.integers(0, 0xFF_FFF_FFF), ) -def test_generate_legacy_guid(loc, stat, smpl) -> None: +def test_generate_legacy_guid(loc: "int", stat: "int", smpl: "int") -> None: """ Generate a guid in the legacy format (with an assigned sample name) and verify that it is parsed @@ -75,7 +81,7 @@ def test_generate_legacy_guid(loc, stat, smpl) -> None: stat=hst.integers(0, 0xFFFF), smpl=hst.integers(0, 0xFF_FFF_FFF), ) -def test_generate_guid(loc, stat, smpl) -> None: +def test_generate_guid(loc: "int", stat: "int", smpl: "int") -> None: """ Generate a guid and verify that it is parsed or raises if a sample name is given @@ -116,7 +122,7 @@ def test_generate_guid(loc, stat, smpl) -> None: suppress_health_check=(HealthCheck.function_scoped_fixture,), ) @given(loc=hst.integers(-10, 350)) -def test_set_guid_location_code(loc, monkeypatch) -> None: +def test_set_guid_location_code(loc: "int", monkeypatch: "pytest.MonkeyPatch") -> None: monkeypatch.setattr("builtins.input", lambda x: str(loc)) orig_cfg = qc.config original_loc = orig_cfg["GUID_components"]["location"] @@ -136,7 +142,9 @@ def test_set_guid_location_code(loc, monkeypatch) -> None: suppress_health_check=(HealthCheck.function_scoped_fixture,), ) @given(ws=hst.integers(-10, 17000000)) -def test_set_guid_workstation_code(ws, monkeypatch) -> None: +def test_set_guid_workstation_code( + ws: "int", monkeypatch: "pytest.MonkeyPatch" +) -> None: monkeypatch.setattr("builtins.input", lambda x: str(ws)) orig_cfg = qc.config @@ -158,8 +166,8 @@ def test_set_guid_workstation_code(ws, monkeypatch) -> None: stats=hst.lists(hst.integers(0, 0xFFFF), min_size=2, max_size=2, unique=True), smpls=hst.lists(hst.integers(0, 0xFF_FFF_FFF), min_size=2, max_size=2, unique=True), ) -def test_filter_guid(locs, stats, smpls) -> None: - def make_test_guid(cfg, loc: int, smpl: int, stat: int): +def test_filter_guid(locs: "list[int]", stats: "list[int]", smpls: "list[int]") -> None: + def make_test_guid(cfg: "Config", loc: int, smpl: int, stat: int) -> "str": cfg["GUID_components"]["location"] = loc cfg["GUID_components"]["work_station"] = stat cfg["GUID_components"]["sample"] = smpl diff --git a/tests/dataset/test_links.py b/tests/dataset/test_links.py index e0b3571299f..9cb3433830d 100644 --- a/tests/dataset/test_links.py +++ b/tests/dataset/test_links.py @@ -72,7 +72,7 @@ def test_link_construction_passes() -> None: @settings(max_examples=20) @given(not_guid=hst.text()) -def test_link_construction_raises(not_guid) -> None: +def test_link_construction_raises(not_guid: "str") -> None: head_guid = generate_guid() tail_guid = generate_guid() edge_type = "fit" @@ -146,7 +146,7 @@ def test_link_to_string_and_back() -> None: @settings(max_examples=5, deadline=1200) @given(N=hst.integers(min_value=0, max_value=25)) -def test_links_to_str_and_back(N) -> None: +def test_links_to_str_and_back(N: "int") -> None: links = generate_some_links(N) new_links = str_to_links(links_to_str(links)) diff --git a/tests/dataset/test_measurement_extensions.py b/tests/dataset/test_measurement_extensions.py index 37136b786cf..ab84afca6cd 100644 --- a/tests/dataset/test_measurement_extensions.py +++ b/tests/dataset/test_measurement_extensions.py @@ -24,31 +24,48 @@ from qcodes.parameters import Parameter, ParameterWithSetpoints from qcodes.validators import Arrays +if TYPE_CHECKING: + from numpy.typing import NDArray + + from qcodes.dataset.experiment_container import Experiment +if TYPE_CHECKING: + from pytest_mock import MockerFixture + + from qcodes.dataset.data_set_protocol import DataSetProtocol + if TYPE_CHECKING: from collections.abc import Sequence def assert_dataset_as_expected( - dataset, dims_dict: dict[str, int], data_vars: "Sequence[str]" -): + dataset: "DataSetProtocol", dims_dict: dict[str, int], data_vars: "Sequence[str]" +) -> "None": xr_ds = dataset.to_xarray_dataset() assert xr_ds.sizes == dims_dict assert set(xr_ds.data_vars) == set(data_vars) @pytest.fixture(name="default_params") -def default_params_(): - set1 = Parameter("set1", get_cmd=None, set_cmd=None, initial_value=0) - set2 = Parameter("set2", get_cmd=None, set_cmd=None, initial_value=0) - set3 = Parameter("set3", get_cmd=None, set_cmd=None, initial_value=0) +def default_params_() -> ( + "tuple[Parameter[float], Parameter[float], Parameter[float], Parameter[float], Parameter[float], Parameter[float]]" +): + set1: Parameter[float] = Parameter( + "set1", get_cmd=None, set_cmd=None, initial_value=0 + ) + set2: Parameter[float] = Parameter( + "set2", get_cmd=None, set_cmd=None, initial_value=0 + ) + set3: Parameter[float] = Parameter( + "set3", get_cmd=None, set_cmd=None, initial_value=0 + ) - def get_set1(): + def get_set1() -> "float": return set1() - def get_sum23(): + def get_sum23() -> "float": return set2() + set3() - def get_diff23(): + def get_diff23() -> "float": return set2() - set3() meas1 = Parameter("meas1", get_cmd=get_set1, set_cmd=False) @@ -59,12 +76,14 @@ def get_diff23(): @pytest.fixture(name="pws_params") -def pws_params_(default_params): +def pws_params_( + default_params: "tuple[Parameter[float], Parameter[float], Parameter[float], Parameter[float], Parameter[float], Parameter[float]]", +) -> "tuple[ParameterWithSetpoints[NDArray[np.float64]], Parameter[float]]": sweep_start = 0 sweep_stop = 10 sweep_points = 11 - def get_pws_results(): + def get_pws_results() -> "NDArray[np.float64]": setpoints_arr = np.linspace(sweep_start, sweep_stop, sweep_points) return setpoints_arr**2 * set1() @@ -85,14 +104,19 @@ def get_pws_results(): @pytest.fixture(name="default_database_and_experiment") -def default_database_and_experiment_(tmp_path): +def default_database_and_experiment_( + tmp_path: "Path", +) -> "Experiment": db_path = Path(tmp_path) / "context_tests.db" initialise_or_create_database_at(db_path) experiment = load_or_create_experiment("context_tests") return experiment -def test_context(default_params, default_database_and_experiment): +def test_context( + default_params: "tuple[Parameter[float], Parameter[float], Parameter[float], Parameter[float], Parameter[float], Parameter[float]]", + default_database_and_experiment: "Experiment", +) -> "None": _ = default_database_and_experiment set1, set2, set3, meas1, meas2, meas3 = default_params metadata_dict = { # Nesting dictionaries does NOT work here @@ -153,7 +177,10 @@ def test_context(default_params, default_database_and_experiment): assert datasets[1].metadata == {} -def test_dond_into(default_params, default_database_and_experiment): +def test_dond_into( + default_params: "tuple[Parameter[float], Parameter[float], Parameter[float], Parameter[float], Parameter[float], Parameter[float]]", + default_database_and_experiment: "Experiment", +) -> "None": experiment = default_database_and_experiment set1, _, _, meas1, _, _ = default_params @@ -178,7 +205,10 @@ def test_dond_into(default_params, default_database_and_experiment): ) -def test_dond_into_and_context(default_params, default_database_and_experiment): +def test_dond_into_and_context( + default_params: "tuple[Parameter[float], Parameter[float], Parameter[float], Parameter[float], Parameter[float], Parameter[float]]", + default_database_and_experiment: "Experiment", +) -> "None": _ = default_database_and_experiment set1, set2, set3, meas1, meas2, meas3 = default_params @@ -211,7 +241,10 @@ def test_dond_into_and_context(default_params, default_database_and_experiment): ) -def test_linsweeper(default_params, default_database_and_experiment): +def test_linsweeper( + default_params: "tuple[Parameter[float], Parameter[float], Parameter[float], Parameter[float], Parameter[float], Parameter[float]]", + default_database_and_experiment: "Experiment", +) -> "None": _ = default_database_and_experiment set1, set2, _, meas1, meas2, _ = default_params @@ -235,7 +268,9 @@ def test_linsweeper(default_params, default_database_and_experiment): ) -def test_nested_linsweeper(default_params): +def test_nested_linsweeper( + default_params: "tuple[Parameter[float], Parameter[float], Parameter[float], Parameter[float], Parameter[float], Parameter[float]]", +) -> "None": set1, set2, _, _, _, _ = default_params linsweeper1 = LinSweeper(set1, 0, 1, 11, 0.001) linsweeper2 = LinSweeper(set2, -1, 0, 6, 0.001) @@ -259,7 +294,10 @@ def test_nested_linsweeper(default_params): ) -def test_context_with_pws(pws_params, default_database_and_experiment): +def test_context_with_pws( + pws_params: "tuple[ParameterWithSetpoints[NDArray[np.float64]], Parameter[float]]", + default_database_and_experiment: "Experiment", +) -> "None": _ = default_database_and_experiment pws1, set1 = pws_params dataset_definition = [DataSetDefinition("dataset_1", [set1], [pws1])] @@ -270,6 +308,8 @@ def test_context_with_pws(pws_params, default_database_and_experiment): datasets = [datasaver.dataset for datasaver in datasavers] assert len(datasets) == 1 + assert isinstance(pws1.setpoints[0].vals, Arrays) + assert pws1.setpoints[0].vals.shape is not None assert_dataset_as_expected( datasets[0], dims_dict={ @@ -281,8 +321,10 @@ def test_context_with_pws(pws_params, default_database_and_experiment): def test_dond_into_with_callables( - default_params, default_database_and_experiment, mocker -): + default_params: "tuple[Parameter[float], Parameter[float], Parameter[float], Parameter[float], Parameter[float], Parameter[float]]", + default_database_and_experiment: "Experiment", + mocker: "MockerFixture", +) -> "None": experiment = default_database_and_experiment set1, _, _, meas1, _, _ = default_params @@ -312,8 +354,9 @@ def test_dond_into_with_callables( def test_dond_into_fails_with_together_sweeps( - default_params, default_database_and_experiment -): + default_params: "tuple[Parameter[float], Parameter[float], Parameter[float], Parameter[float], Parameter[float], Parameter[float]]", + default_database_and_experiment: "Experiment", +) -> "None": experiment = default_database_and_experiment set1, set2, _, meas1, _, _ = default_params @@ -333,7 +376,10 @@ def test_dond_into_fails_with_together_sweeps( ) -def test_dond_into_fails_with_groups(default_params, default_database_and_experiment): +def test_dond_into_fails_with_groups( + default_params: "tuple[Parameter[float], Parameter[float], Parameter[float], Parameter[float], Parameter[float], Parameter[float]]", + default_database_and_experiment: "Experiment", +) -> "None": experiment = default_database_and_experiment set1, _, _, meas1, meas2, _ = default_params @@ -354,8 +400,9 @@ def test_dond_into_fails_with_groups(default_params, default_database_and_experi def test_context_with_multiple_experiments( - default_params, default_database_and_experiment -): + default_params: "tuple[Parameter[float], Parameter[float], Parameter[float], Parameter[float], Parameter[float], Parameter[float]]", + default_database_and_experiment: "Experiment", +) -> "None": experiment = default_database_and_experiment experiment2 = load_or_create_experiment("other_experiment") set1, set2, set3, meas1, meas2, meas3 = default_params @@ -399,7 +446,10 @@ def test_context_with_multiple_experiments( assert datasets[1].exp_name == experiment2.name -def test_context_with_no_experiment(default_params, default_database_and_experiment): +def test_context_with_no_experiment( + default_params: "tuple[Parameter[float], Parameter[float], Parameter[float], Parameter[float], Parameter[float], Parameter[float]]", + default_database_and_experiment: "Experiment", +) -> "None": _ = default_database_and_experiment experiment2 = load_or_create_experiment("other_experiment") set1, set2, set3, meas1, meas2, meas3 = default_params @@ -441,8 +491,9 @@ def test_context_with_no_experiment(default_params, default_database_and_experim def test_context_with_override_experiment( - default_params, default_database_and_experiment -): + default_params: "tuple[Parameter[float], Parameter[float], Parameter[float], Parameter[float], Parameter[float], Parameter[float]]", + default_database_and_experiment: "Experiment", +) -> "None": experiment = default_database_and_experiment experiment2 = load_or_create_experiment("other_experiment") set1, set2, set3, meas1, meas2, meas3 = default_params @@ -488,8 +539,9 @@ def test_context_with_override_experiment( def test_shapes_in_dataset_definition_with_scalar_params( - default_params, default_database_and_experiment -): + default_params: "tuple[Parameter[float], Parameter[float], Parameter[float], Parameter[float], Parameter[float], Parameter[float]]", + default_database_and_experiment: "Experiment", +) -> "None": """Test that shapes specified in DataSetDefinition are stored on the dataset.""" _ = default_database_and_experiment set1, set2, _, meas1, meas2, _ = default_params @@ -516,8 +568,9 @@ def test_shapes_in_dataset_definition_with_scalar_params( def test_shapes_in_dataset_definition_with_pws( - pws_params, default_database_and_experiment -): + pws_params: "tuple[ParameterWithSetpoints[NDArray[np.float64]], Parameter[float]]", + default_database_and_experiment: "Experiment", +) -> "None": """Test that shapes for ParameterWithSetpoints are stored on the dataset.""" _ = default_database_and_experiment pws1, set1 = pws_params @@ -541,7 +594,10 @@ def test_shapes_in_dataset_definition_with_pws( assert datasets[0].description.shapes == expected_shapes -def test_shapes_none_by_default(default_params, default_database_and_experiment): +def test_shapes_none_by_default( + default_params: "tuple[Parameter[float], Parameter[float], Parameter[float], Parameter[float], Parameter[float], Parameter[float]]", + default_database_and_experiment: "Experiment", +) -> "None": """Test that shapes is None when not specified in DataSetDefinition.""" _ = default_database_and_experiment set1, _, _, meas1, _, _ = default_params @@ -562,8 +618,9 @@ def test_shapes_none_by_default(default_params, default_database_and_experiment) def test_setup_measurement_instances_sets_shapes( - default_params, default_database_and_experiment -): + default_params: "tuple[Parameter[float], Parameter[float], Parameter[float], Parameter[float], Parameter[float], Parameter[float]]", + default_database_and_experiment: "Experiment", +) -> "None": """Test that setup_measurement_instances correctly sets shapes on Measurement.""" _ = default_database_and_experiment set1, _, _, meas1, _, _ = default_params @@ -583,8 +640,9 @@ def test_setup_measurement_instances_sets_shapes( def test_setup_measurement_instances_no_shapes( - default_params, default_database_and_experiment -): + default_params: "tuple[Parameter[float], Parameter[float], Parameter[float], Parameter[float], Parameter[float], Parameter[float]]", + default_database_and_experiment: "Experiment", +) -> "None": """Test that setup_measurement_instances leaves shapes as None when not specified.""" _ = default_database_and_experiment set1, _, _, meas1, _, _ = default_params @@ -601,7 +659,10 @@ def test_setup_measurement_instances_no_shapes( assert measurements[0]._shapes is None -def test_shapes_with_multiple_datasets(default_params, default_database_and_experiment): +def test_shapes_with_multiple_datasets( + default_params: "tuple[Parameter[float], Parameter[float], Parameter[float], Parameter[float], Parameter[float], Parameter[float]]", + default_database_and_experiment: "Experiment", +) -> "None": """Test shapes are correctly applied to multiple datasets independently.""" _ = default_database_and_experiment set1, set2, set3, meas1, _, meas3 = default_params diff --git a/tests/dataset/test_metadata.py b/tests/dataset/test_metadata.py index 2fc61cfe5a6..a7b8ae244d3 100644 --- a/tests/dataset/test_metadata.py +++ b/tests/dataset/test_metadata.py @@ -1,15 +1,21 @@ -def test_get_metadata_from_dataset(dataset) -> None: +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from qcodes.dataset.data_set import DataSet + + +def test_get_metadata_from_dataset(dataset: "DataSet") -> None: dataset.add_metadata("something", 123) something = dataset.get_metadata("something") assert 123 == something -def test_get_nonexisting_metadata(dataset) -> None: +def test_get_nonexisting_metadata(dataset: "DataSet") -> None: data = dataset.get_metadata("something") assert data is None -def test_get_metadata_lower_upper_case(dataset) -> None: +def test_get_metadata_lower_upper_case(dataset: "DataSet") -> None: dataset.add_metadata("something", 123) something_lower = dataset.metadata.get("something", "didnt find lowercase") diff --git a/tests/dataset/test_nested_measurements.py b/tests/dataset/test_nested_measurements.py index dc832f7cf19..95e9d5ff86a 100644 --- a/tests/dataset/test_nested_measurements.py +++ b/tests/dataset/test_nested_measurements.py @@ -1,3 +1,5 @@ +from typing import TYPE_CHECKING + import hypothesis.strategies as hst import numpy as np import pytest @@ -10,12 +12,20 @@ from qcodes.parameters import ParamSpecBase from tests.common import retry_until_does_not_throw +if TYPE_CHECKING: + from collections.abc import Callable +if TYPE_CHECKING: + from qcodes.dataset.experiment_container import Experiment + from qcodes.instrument_drivers.mock_instruments import DummyInstrument + VALUE = str | float | list | np.ndarray | bool @pytest.mark.usefixtures("experiment") @pytest.mark.parametrize("bg_writing", [True, False]) -def test_nested_measurement_basic(DAC, DMM, bg_writing) -> None: +def test_nested_measurement_basic( + DAC: "DummyInstrument", DMM: "DummyInstrument", bg_writing: "bool" +) -> None: meas1 = Measurement() meas1.register_parameter(DAC.ch1) meas1.register_parameter(DMM.v1, setpoints=(DAC.ch1,)) @@ -51,7 +61,7 @@ def test_nested_measurement_basic(DAC, DMM, bg_writing) -> None: @pytest.mark.usefixtures("experiment") @pytest.mark.parametrize("bg_writing", [True, False]) -def test_nested_measurement(bg_writing) -> None: +def test_nested_measurement(bg_writing: "bool") -> None: meas1 = Measurement() meas1.register_custom_parameter("foo1") meas1.register_custom_parameter("bar1", setpoints=("foo1",)) @@ -93,7 +103,7 @@ def test_nested_measurement(bg_writing) -> None: inner_len2=hst.integers(min_value=1, max_value=200), ) def test_nested_measurement_array( - bg_writing, outer_len, inner_len1, inner_len2 + bg_writing: "bool", outer_len: "int", inner_len1: "int", inner_len2: "int" ) -> None: meas1 = Measurement() meas1.register_custom_parameter("foo1", paramtype="numeric") @@ -164,13 +174,16 @@ def test_nested_measurement_array( @pytest.fixture(scope="function") -def basic_subscriber(): +def basic_subscriber() -> "Callable[[list[tuple[VALUE]], int, dict | None], None]": """ A basic subscriber that just puts results and length into state """ - def subscriber(results: list[tuple[VALUE]], length: int, state: dict) -> None: + def subscriber( + results: list[tuple[VALUE]], length: int, state: dict | None + ) -> None: + assert state is not None state[length] = results return subscriber @@ -178,7 +191,10 @@ def subscriber(results: list[tuple[VALUE]], length: int, state: dict) -> None: @pytest.mark.flaky(reruns=5) @pytest.mark.serial -def test_subscription_on_dual_datasets(experiment, basic_subscriber) -> None: +def test_subscription_on_dual_datasets( + experiment: "Experiment", + basic_subscriber: "Callable[[list[tuple[VALUE]], int, dict | None], None]", +) -> None: xparam = ParamSpecBase(name="x", paramtype="numeric", label="x parameter", unit="V") yparam1 = ParamSpecBase( name="y1", paramtype="numeric", label="y parameter", unit="Hz" @@ -223,7 +239,7 @@ def test_subscription_on_dual_datasets(experiment, basic_subscriber) -> None: @retry_until_does_not_throw( exception_class_to_expect=AssertionError, delay=0.5, tries=10 ) - def assert_expected_state(): + def assert_expected_state() -> "None": assert dataset1.subscribers[sub_id_1].state == expected_state_1 assert dataset2.subscribers[sub_id_2].state == expected_state_2 diff --git a/tests/dataset/test_parameter_with_setpoints_has_control.py b/tests/dataset/test_parameter_with_setpoints_has_control.py index d2aae82e39f..dcbb25fcad2 100644 --- a/tests/dataset/test_parameter_with_setpoints_has_control.py +++ b/tests/dataset/test_parameter_with_setpoints_has_control.py @@ -34,7 +34,7 @@ def unpack_self(self, value): # type: ignore[override] return p -def test_parameter_with_setpoints_has_control(experiment: "Experiment"): +def test_parameter_with_setpoints_has_control(experiment: "Experiment") -> "None": mp_data = np.arange(10) p1_data = np.linspace(-1, 1, 10) @@ -90,7 +90,7 @@ def test_parameter_with_setpoints_has_control(experiment: "Experiment"): npt.assert_array_almost_equal(raw_data["p2"]["p1"].ravel(), p1_data) -def test_parameter_with_setpoints_has_control_2d(experiment: "Experiment"): +def test_parameter_with_setpoints_has_control_2d(experiment: "Experiment") -> "None": """Test that an inferred parameter with the same size as its parent but different from the full dimension product is correctly included.""" diff --git a/tests/dataset/test_paramspec.py b/tests/dataset/test_paramspec.py index ed344a01203..89a6b1fadb7 100644 --- a/tests/dataset/test_paramspec.py +++ b/tests/dataset/test_paramspec.py @@ -1,5 +1,6 @@ from keyword import iskeyword from numbers import Number +from typing import TYPE_CHECKING import hypothesis.strategies as hst import pytest @@ -9,8 +10,13 @@ from qcodes.dataset.descriptions.param_spec import ParamSpec from qcodes.parameters import ParamSpecBase +if TYPE_CHECKING: + from hypothesis.strategies import SearchStrategy -def valid_identifier(**kwargs): + from qcodes.dataset.descriptions.param_spec import ParamSpecDict + + +def valid_identifier(**kwargs: "int") -> "SearchStrategy[str]": """Return a strategy which generates a valid Python Identifier""" if "min_size" not in kwargs: kwargs["min_size"] = 4 @@ -39,7 +45,7 @@ def valid_identifier(**kwargs): @pytest.fixture -def version_0_dicts(): +def version_0_dicts() -> "list[ParamSpecDict]": sers = [] sers.append( { @@ -65,7 +71,7 @@ def version_0_dicts(): @pytest.fixture -def version_0_objects(): +def version_0_objects() -> "list[ParamSpec]": """ The ParamSpecs that the dictionaries above represent """ @@ -103,7 +109,14 @@ def version_0_objects(): elements=hst.sampled_from(["numeric", "array", "text"]), min_size=6, max_size=6 ), ) -def test_creation(name, sp1, sp2, inff1, inff2, paramtype) -> None: +def test_creation( + name: "str", + sp1: "str", + sp2: "str", + inff1: "str", + inff2: "str", + paramtype: "list[str]", +) -> None: invalid_types = ("np.array", "ndarray", "lala", "", Number, ndarray, 0, None) for inv_type in invalid_types: with pytest.raises(ValueError): @@ -149,7 +162,7 @@ def test_creation(name, sp1, sp2, inff1, inff2, paramtype) -> None: @given(name=hst.text(min_size=1)) -def test_repr(name) -> None: +def test_repr(name: "str") -> None: okay_types = ["array", "numeric", "text"] for okt in okay_types: @@ -172,7 +185,7 @@ def test_repr(name) -> None: name2=hst.text(min_size=4, max_size=100, alphabet=alphabet), name3=hst.text(min_size=4, max_size=100, alphabet=alphabet), ) -def test_depends_on(name1, name2, name3) -> None: +def test_depends_on(name1: "str", name2: "str", name3: "str") -> None: ps2 = ParamSpec(name2, "numeric") ps3 = ParamSpec(name3, "numeric") @@ -194,7 +207,7 @@ def test_depends_on(name1, name2, name3) -> None: name2=hst.text(min_size=4, max_size=100, alphabet=alphabet), name3=hst.text(min_size=4, max_size=100, alphabet=alphabet), ) -def test_inferred_from(name1, name2, name3) -> None: +def test_inferred_from(name1: "str", name2: "str", name3: "str") -> None: ps2 = ParamSpec(name2, "numeric") ps3 = ParamSpec(name3, "numeric") @@ -216,7 +229,7 @@ def test_inferred_from(name1, name2, name3) -> None: name1=hst.text(min_size=4, max_size=100, alphabet=alphabet), name2=hst.text(min_size=4, max_size=100, alphabet=alphabet), ) -def test_copy(name1, name2) -> None: +def test_copy(name1: "str", name2: "str") -> None: ps_indep = ParamSpec(name1, "numeric") ps = ParamSpec(name2, "numeric", depends_on=[ps_indep, "other_param"]) ps_copy = ps.copy() @@ -265,7 +278,9 @@ def test_convert_to_dict() -> None: assert ser["inferred_from"] == p1._inferred_from -def test_from_dict(version_0_dicts, version_0_objects) -> None: +def test_from_dict( + version_0_dicts: "list[ParamSpecDict]", version_0_objects: "list[ParamSpec]" +) -> None: for sdict, ps in zip(version_0_dicts, version_0_objects): deps = ParamSpec._from_dict(sdict) assert ps == deps @@ -300,7 +315,11 @@ def test_hash(paramspecs) -> None: add_to_2_dep=hst.booleans(), ) def test_hash_with_deferred_and_inferred_as_paramspecs( - paramspecs, add_to_1_inf, add_to_1_dep, add_to_2_inf, add_to_2_dep + paramspecs, + add_to_1_inf: "bool", + add_to_1_dep: "bool", + add_to_2_inf: "bool", + add_to_2_dep: "bool", ) -> None: """ Test that hashing works if 'inferred_from' and/or 'depends_on' contain diff --git a/tests/dataset/test_plotting.py b/tests/dataset/test_plotting.py index 5ceb47476a7..d052bbe54f3 100644 --- a/tests/dataset/test_plotting.py +++ b/tests/dataset/test_plotting.py @@ -23,6 +23,12 @@ from qcodes.parameters import Parameter from qcodes.plotting.axis_labels import _ENGINEERING_PREFIXES, _UNITS_FOR_RESCALING +if TYPE_CHECKING: + from hypothesis.strategies import DataObject + from numpy.typing import NDArray +if TYPE_CHECKING: + from qcodes.dataset.experiment_container import Experiment + if TYPE_CHECKING: from qcodes.dataset.data_export import DSPlotData @@ -61,7 +67,11 @@ class TerminateLoopException(Exception): ) @settings(suppress_health_check=[HealthCheck.too_slow]) def test_rescaled_ticks_and_units( - scale, unit, param_name, param_label, data_strategy + scale: "int", + unit: "str", + param_name: "str", + param_label: "str", + data_strategy: "DataObject | NDArray[np.float64]", ) -> None: if isinstance(data_strategy, np.ndarray): # No need to generate data, because it is being passed @@ -126,7 +136,9 @@ def test_rescaled_ticks_and_units( assert "2.12346" == ticks_formatter(2.123456789 / (10 ** (-scale))) -def test_plot_by_id_line_and_heatmap(experiment, request: FixtureRequest) -> None: +def test_plot_by_id_line_and_heatmap( + experiment: "Experiment", request: FixtureRequest +) -> None: """ Test that line plots and heatmaps can be plotted together """ @@ -160,7 +172,10 @@ def test_plot_by_id_line_and_heatmap(experiment, request: FixtureRequest) -> Non @pytest.mark.parametrize("nan_setpoints", [True, False]) @pytest.mark.parametrize("shifted", [True, False]) def test_plot_dataset_2d_shaped( - experiment, request: FixtureRequest, nan_setpoints, shifted + experiment: "Experiment", + request: FixtureRequest, + nan_setpoints: "bool", + shifted: "bool", ) -> None: """ Test plotting of preshaped data on a grid that may or may not be shifted @@ -378,7 +393,9 @@ def test__complex_to_real_preparser_complex_setpoint() -> None: @pytest.mark.parametrize("params", [None, "y", "y2", ["y", "y2"]]) -def test_plot_dataset_parameters(experiment, request: FixtureRequest, params) -> None: +def test_plot_dataset_parameters( + experiment: "Experiment", request: FixtureRequest, params: "str | list[str] | None" +) -> None: """ Test plotting of specified parameters with plot_dataset function. diff --git a/tests/dataset/test_raw_data_storage.py b/tests/dataset/test_raw_data_storage.py index 04e6eee2f35..f4e9aa165d3 100644 --- a/tests/dataset/test_raw_data_storage.py +++ b/tests/dataset/test_raw_data_storage.py @@ -418,7 +418,9 @@ def test_subscribe_before_start_defers_and_fires(self) -> None: received: list[int] = [] - def callback(results, length, state): + def callback( + results: list[tuple[object, ...]], length: int, state: object + ) -> None: received.append(length) # Subscribe while still pristine - no results table exists yet. diff --git a/tests/dataset/test_snapshot.py b/tests/dataset/test_snapshot.py index b723033574e..d58a4077bb8 100644 --- a/tests/dataset/test_snapshot.py +++ b/tests/dataset/test_snapshot.py @@ -1,4 +1,5 @@ import json +from typing import TYPE_CHECKING import numpy import pytest @@ -8,16 +9,21 @@ from qcodes.parameters import ManualParameter, Parameter from qcodes.station import Station +if TYPE_CHECKING: + from collections.abc import Generator +if TYPE_CHECKING: + from qcodes.dataset.experiment_container import Experiment + @pytest.fixture # scope is "function" per default -def dac(): +def dac() -> "Generator[DummyInstrument, None, None]": dac = DummyInstrument("dummy_dac", gates=["ch1", "ch2"]) yield dac dac.close() @pytest.fixture -def dmm(): +def dmm() -> "Generator[DummyInstrument, None, None]": dmm = DummyInstrument("dummy_dmm", gates=["v1", "v2"]) yield dmm dmm.close() @@ -25,7 +31,10 @@ def dmm(): @pytest.mark.parametrize("pass_station", (True, False)) def test_station_snapshot_during_measurement( - experiment, dac, dmm, pass_station + experiment: "Experiment", + dac: "DummyInstrument", + dmm: "DummyInstrument", + pass_station: "bool", ) -> None: station = Station() station.add_component(dac) @@ -77,7 +86,9 @@ def test_station_snapshot_during_measurement( assert expected_snapshot == data_saver.dataset.snapshot -def test_snapshot_creation_for_types_not_supported_by_builtin_json(experiment) -> None: +def test_snapshot_creation_for_types_not_supported_by_builtin_json( + experiment: "Experiment", +) -> None: """ Test that `Measurement`/`Runner`/`DataSaver` infrastructure successfully dumps station snapshots in JSON format in cases when the @@ -119,7 +130,7 @@ def test_snapshot_creation_for_types_not_supported_by_builtin_json(experiment) - def test_station_snapshot_in_measurement_refreshes_only_invalid_caches( - experiment, + experiment: "Experiment", ) -> None: """ The station snapshot taken by a ``Measurement`` uses ``update="Only_invalid"`` diff --git a/tests/dataset/test_sqlite_base.py b/tests/dataset/test_sqlite_base.py index a48978d00ae..19af1c240bd 100644 --- a/tests/dataset/test_sqlite_base.py +++ b/tests/dataset/test_sqlite_base.py @@ -7,6 +7,7 @@ import time import unicodedata from contextlib import contextmanager +from typing import TYPE_CHECKING from unittest.mock import patch import hypothesis.strategies as hst @@ -34,11 +35,20 @@ from .helper_functions import verify_data_dict +if TYPE_CHECKING: + from collections.abc import Generator + + from qcodes.dataset.sqlite.connection import AtomicConnection +if TYPE_CHECKING: + from pathlib import Path + + from qcodes.dataset.experiment_container import Experiment + _unicode_categories = ("Lu", "Ll", "Lt", "Lm", "Lo", "Nd", "Pc", "Pd", "Zs") @contextmanager -def shadow_conn(path_to_db: str): +def shadow_conn(path_to_db: str) -> "Generator[AtomicConnection, None, None]": """ Simple context manager to create a connection for testing and close it on exit @@ -49,7 +59,7 @@ def shadow_conn(path_to_db: str): @pytest.fixture(name="simple_run_describer") -def _make_simple_run_describer(): +def _make_simple_run_describer() -> "Generator[RunDescriber, None, None]": x = ParamSpecBase("x", "numeric") t = ParamSpecBase("t", "numeric") y = ParamSpecBase("y", "numeric") @@ -61,7 +71,7 @@ def _make_simple_run_describer(): yield rundescriber -def test_path_to_dbfile(tmp_path) -> None: +def test_path_to_dbfile(tmp_path: "Path") -> None: tempdb = str(tmp_path / "database.db") conn = mut_db.connect(tempdb) try: @@ -71,14 +81,14 @@ def test_path_to_dbfile(tmp_path) -> None: conn.close() -def test_one_raises_on_no_results(experiment) -> None: +def test_one_raises_on_no_results(experiment: "Experiment") -> None: conn = experiment.conn with pytest.raises(RuntimeError, match="Expected one row"): mut_help.one(conn.cursor(), column="Something_you_dont_have") -def test_one_raises_on_more_than_one_result(experiment) -> None: +def test_one_raises_on_more_than_one_result(experiment: "Experiment") -> None: conn = experiment.conn # create another experiment with so that the query below returns @@ -95,7 +105,7 @@ def test_one_raises_on_more_than_one_result(experiment) -> None: mut_help.one(cur, column="exp_id") -def test_one_raises_on_wrong_column_name(experiment) -> None: +def test_one_raises_on_wrong_column_name(experiment: "Experiment") -> None: conn = experiment.conn query = """ @@ -108,7 +118,7 @@ def test_one_raises_on_wrong_column_name(experiment) -> None: mut_help.one(cur, column="eXP_id") -def test_one_raises_on_wrong_column_index(experiment) -> None: +def test_one_raises_on_wrong_column_index(experiment: "Experiment") -> None: conn = experiment.conn query = """ @@ -121,7 +131,7 @@ def test_one_raises_on_wrong_column_index(experiment) -> None: mut_help.one(cur, column=1) -def test_one_works_if_given_column_index(experiment) -> None: +def test_one_works_if_given_column_index(experiment: "Experiment") -> None: # This test relies on the fact that there's only one experiment in the # given database conn = experiment.conn @@ -137,7 +147,7 @@ def test_one_works_if_given_column_index(experiment) -> None: assert exp_id == experiment.exp_id -def test_one_works_if_given_column_name(experiment) -> None: +def test_one_works_if_given_column_name(experiment: "Experiment") -> None: # This test relies on the fact that there's only one experiment in the # given database conn = experiment.conn @@ -153,7 +163,7 @@ def test_one_works_if_given_column_name(experiment) -> None: assert exp_id == experiment.exp_id -def test_atomic_transaction_raises(experiment) -> None: +def test_atomic_transaction_raises(experiment: "Experiment") -> None: conn = experiment.conn bad_sql = '""' @@ -162,7 +172,7 @@ def test_atomic_transaction_raises(experiment) -> None: mut_conn.atomic_transaction(conn, bad_sql) -def test_atomic_raises(experiment) -> None: +def test_atomic_raises(experiment: "Experiment") -> None: conn = experiment.conn bad_sql = '""' @@ -172,7 +182,7 @@ def test_atomic_raises(experiment) -> None: assert error_caused_by(excinfo, "syntax error") -def test_insert_many_values_raises(experiment) -> None: +def test_insert_many_values_raises(experiment: "Experiment") -> None: conn = experiment.conn with pytest.raises(ValueError): @@ -181,7 +191,7 @@ def test_insert_many_values_raises(experiment) -> None: ) -def test_get_non_existing_metadata_returns_none(experiment) -> None: +def test_get_non_existing_metadata_returns_none(experiment: "Experiment") -> None: assert ( mut_queries.get_data_by_tag_and_table_name( experiment.conn, "something", "results" @@ -191,7 +201,7 @@ def test_get_non_existing_metadata_returns_none(experiment) -> None: @given(table_name=hst.text(max_size=50)) -def test__validate_table_raises(table_name) -> None: +def test__validate_table_raises(table_name: "str") -> None: should_raise = False for char in table_name: if unicodedata.category(char) not in _unicode_categories: @@ -204,7 +214,9 @@ def test__validate_table_raises(table_name) -> None: assert mut_queries._validate_table_name(table_name) -def test_get_dependents_simple(experiment, simple_run_describer) -> None: +def test_get_dependents_simple( + experiment: "Experiment", simple_run_describer: "RunDescriber" +) -> None: (_, run_id, _) = mut_queries.create_run( experiment.conn, experiment.exp_id, @@ -220,7 +232,7 @@ def test_get_dependents_simple(experiment, simple_run_describer) -> None: assert deps == [layout_id] -def test_get_layout_id_with_invalid_parameter_type(dataset) -> None: +def test_get_layout_id_with_invalid_parameter_type(dataset: "DataSet") -> None: with pytest.raises(ValueError, match="Wrong parameter type, must be ParamSpec"): mut_queries._get_layout_id( dataset.conn, @@ -229,7 +241,7 @@ def test_get_layout_id_with_invalid_parameter_type(dataset) -> None: ) -def test_get_dependents(experiment) -> None: +def test_get_dependents(experiment: "Experiment") -> None: # more parameters, more complicated dependencies x = ParamSpecBase("x", "numeric") t = ParamSpecBase("t", "numeric") @@ -265,36 +277,36 @@ def test_get_dependents(experiment) -> None: assert deps == expected_deps -def test_column_in_table(dataset) -> None: +def test_column_in_table(dataset: "DataSet") -> None: assert mut_help.is_column_in_table(dataset.conn, "runs", "run_id") assert not mut_help.is_column_in_table(dataset.conn, "runs", "non-existing-column") -def test_run_exist(dataset) -> None: +def test_run_exist(dataset: "DataSet") -> None: assert mut_queries.run_exists(dataset.conn, dataset.run_id) assert not mut_queries.run_exists(dataset.conn, dataset.run_id + 1) -def test_get_last_run(dataset) -> None: +def test_get_last_run(dataset: "DataSet") -> None: assert dataset.run_id == mut_queries.get_last_run(dataset.conn, dataset.exp_id) assert dataset.run_id == mut_queries.get_last_run(dataset.conn) -def test_get_last_run_no_runs(experiment) -> None: +def test_get_last_run_no_runs(experiment: "Experiment") -> None: assert None is mut_queries.get_last_run(experiment.conn, experiment.exp_id) assert None is mut_queries.get_last_run(experiment.conn) -def test_get_last_experiment(experiment) -> None: +def test_get_last_experiment(experiment: "Experiment") -> None: assert experiment.exp_id == mut_queries.get_last_experiment(experiment.conn) -def test_get_last_experiment_no_experiments(empty_temp_db) -> None: +def test_get_last_experiment_no_experiments(empty_temp_db: "None") -> None: conn = mut_db.connect(get_DB_location()) assert None is mut_queries.get_last_experiment(conn) -def test_update_runs_description(dataset) -> None: +def test_update_runs_description(dataset: "DataSet") -> None: invalid_descs = ["{}", "description"] for idesc in invalid_descs: @@ -305,7 +317,7 @@ def test_update_runs_description(dataset) -> None: mut_queries.update_run_description(dataset.conn, dataset.run_id, desc) -def test_runs_table_columns(empty_temp_db) -> None: +def test_runs_table_columns(empty_temp_db: "None") -> None: """ Ensure that the column names of a pristine runs table are what we expect """ @@ -320,7 +332,7 @@ def test_runs_table_columns(empty_temp_db) -> None: assert colnames == [] -def test_get_parameter_data(scalar_dataset) -> None: +def test_get_parameter_data(scalar_dataset: "DataSet") -> None: ds = scalar_dataset input_names = ["param_3"] @@ -380,7 +392,7 @@ def test_get_parameter_data_independent_parameters( ) -def test_is_run_id_in_db(empty_temp_db) -> None: +def test_is_run_id_in_db(empty_temp_db: "None") -> None: conn = mut_db.connect(get_DB_location()) mut_queries.new_experiment(conn, "test_exp", "no_sample") @@ -400,13 +412,15 @@ def test_is_run_id_in_db(empty_temp_db) -> None: assert expected_dict == acquired_dict -def test_atomic_creation(experiment, simple_run_describer) -> None: +def test_atomic_creation( + experiment: "Experiment", simple_run_describer: "RunDescriber" +) -> None: """ " Test that dataset creation is atomic. Test for https://github.com/QCoDeS/Qcodes/issues/1444 """ - def just_throw(*args): + def just_throw(*args: "object") -> "None": raise RuntimeError("This breaks adding metadata") # first we patch add_data_to_dynamic_columns to throw an exception @@ -456,7 +470,7 @@ def just_throw(*args): assert len(runs) == 1 -def test_set_run_timestamp(dataset) -> None: +def test_set_run_timestamp(dataset: "DataSet") -> None: assert dataset.run_timestamp_raw is None assert dataset.completed_timestamp_raw is None @@ -476,7 +490,7 @@ def test_set_run_timestamp(dataset) -> None: assert error_caused_by(ei, ("Can not set run_timestamp; it has already been set")) -def test_set_run_timestamp_explicit(dataset) -> None: +def test_set_run_timestamp_explicit(dataset: "DataSet") -> None: assert dataset.run_timestamp_raw is None assert dataset.completed_timestamp_raw is None @@ -495,7 +509,7 @@ def test_set_run_timestamp_explicit(dataset) -> None: assert error_caused_by(ei, "Can not set run_timestamp; it has already been set") -def test_mark_run_complete(dataset) -> None: +def test_mark_run_complete(dataset: "DataSet") -> None: assert dataset.run_timestamp_raw is None assert dataset.completed_timestamp_raw is None @@ -510,7 +524,7 @@ def test_mark_run_complete(dataset) -> None: assert dataset.completed_timestamp_raw > time_now -def test_mark_run_complete_twice(dataset, caplog: LogCaptureFixture) -> None: +def test_mark_run_complete_twice(dataset: "DataSet", caplog: LogCaptureFixture) -> None: assert dataset.run_timestamp_raw is None assert dataset.completed_timestamp_raw is None @@ -548,7 +562,7 @@ def test_mark_run_complete_twice(dataset, caplog: LogCaptureFixture) -> None: assert dataset.completed_timestamp_raw > completed_time -def test_mark_run_complete_explicit_time(dataset) -> None: +def test_mark_run_complete_explicit_time(dataset: "DataSet") -> None: assert dataset.run_timestamp_raw is None assert dataset.completed_timestamp_raw is None @@ -562,7 +576,7 @@ def test_mark_run_complete_explicit_time(dataset) -> None: mut_queries.mark_run_complete(dataset.conn, dataset.run_id) -def test_connect_read_only_mode(tmp_path): +def test_connect_read_only_mode(tmp_path: "Path") -> "None": """ Test that connecting with read_only=True allows reading but not writing. """ diff --git a/tests/dataset/test_sqlite_connection.py b/tests/dataset/test_sqlite_connection.py index 8cfc935d99e..e55d29f645d 100644 --- a/tests/dataset/test_sqlite_connection.py +++ b/tests/dataset/test_sqlite_connection.py @@ -29,14 +29,14 @@ from pathlib import Path -def sqlite_conn_in_transaction(conn: sqlite3.Connection): +def sqlite_conn_in_transaction(conn: sqlite3.Connection) -> "bool": assert isinstance(conn, sqlite3.Connection) assert True is conn.in_transaction assert None is conn.isolation_level return True -def conn_plus_in_transaction(conn: AtomicConnection): +def conn_plus_in_transaction(conn: AtomicConnection) -> "bool": assert isinstance(conn, AtomicConnection) assert True is conn.atomic_in_progress assert None is conn.isolation_level @@ -44,14 +44,16 @@ def conn_plus_in_transaction(conn: AtomicConnection): return True -def sqlite_conn_is_idle(conn: sqlite3.Connection, isolation=None): +def sqlite_conn_is_idle( + conn: sqlite3.Connection, isolation: "str | None" = None +) -> "bool": assert isinstance(conn, sqlite3.Connection) assert False is conn.in_transaction assert isolation == conn.isolation_level return True -def conn_plus_is_idle(conn: AtomicConnection, isolation=None): +def conn_plus_is_idle(conn: AtomicConnection, isolation: "str | None" = None) -> "bool": assert isinstance(conn, AtomicConnection) assert False is conn.atomic_in_progress assert isolation == conn.isolation_level @@ -124,7 +126,7 @@ def test_atomic_with_exception() -> None: assert 25 == sqlite_conn.execute("PRAGMA user_version").fetchall()[0][0] -def test_atomic_with_keyboardinterrupt(tmp_path) -> None: +def test_atomic_with_keyboardinterrupt(tmp_path: "Path") -> None: dbfile = str(tmp_path / "temp.db") conn = AtomicConnection(dbfile) isolation_level = conn.isolation_level @@ -149,7 +151,7 @@ def test_atomic_with_keyboardinterrupt(tmp_path) -> None: assert [(2,)] == reader.execute("SELECT x FROM t").fetchall() -def test_nested_atomic_with_keyboardinterrupt(tmp_path) -> None: +def test_nested_atomic_with_keyboardinterrupt(tmp_path: "Path") -> None: dbfile = str(tmp_path / "temp.db") conn = AtomicConnection(dbfile) isolation_level = conn.isolation_level @@ -185,7 +187,7 @@ def test_atomic_on_outmost_connection_that_is_in_transaction() -> None: @pytest.mark.parametrize("in_transaction", (True, False)) -def test_atomic_on_connection_plus_that_is_in_progress(in_transaction) -> None: +def test_atomic_on_connection_plus_that_is_in_progress(in_transaction: "bool") -> None: sqlite_conn = AtomicConnection(":memory:") # explicitly set to True for testing purposes @@ -248,7 +250,7 @@ def test_two_nested_atomics() -> None: def test_that_use_of_atomic_commits_only_at_outermost_context( - tmp_path, + tmp_path: "Path", ) -> None: """ This test tests the behavior of `AtomicConnection` that is created from @@ -332,7 +334,7 @@ def test_that_use_of_atomic_commits_only_at_outermost_context( assert 3 == len(control_conn.execute(get_all_runs).fetchall()) -def test_atomic_transaction(tmp_path) -> None: +def test_atomic_transaction(tmp_path: "Path") -> None: """Test that atomic_transaction works for AtomicConnection""" dbfile = str(tmp_path / "temp.db") @@ -347,7 +349,7 @@ def test_atomic_transaction(tmp_path) -> None: assert sql_create_table in ctrl_conn.execute(sql_table_exists).fetchall()[0] -def test_atomic_transaction_on_sqlite3_connection_raises(tmp_path) -> None: +def test_atomic_transaction_on_sqlite3_connection_raises(tmp_path: "Path") -> None: """Test that atomic_transaction does not work for sqlite3.Connection""" dbfile = str(tmp_path / "temp.db") diff --git a/tests/dataset/test_string_data.py b/tests/dataset/test_string_data.py index 928ffbd7069..3dba7e800d2 100644 --- a/tests/dataset/test_string_data.py +++ b/tests/dataset/test_string_data.py @@ -1,4 +1,5 @@ import re +from typing import TYPE_CHECKING import hypothesis.extra.numpy as hypnumpy import hypothesis.strategies as hst @@ -12,8 +13,13 @@ from qcodes.parameters import ManualParameter, Parameter, ParamSpecBase from qcodes.validators import Strings +if TYPE_CHECKING: + from numpy.typing import NDArray +if TYPE_CHECKING: + from qcodes.dataset.experiment_container import Experiment -def test_string_via_dataset(experiment) -> None: + +def test_string_via_dataset(experiment: "Experiment") -> None: """ Test that we can save text into database via DataSet API """ @@ -31,7 +37,7 @@ def test_string_via_dataset(experiment) -> None: assert test_set.get_parameter_data()["p"]["p"] == [["some text"]] -def test_string_via_datasaver(experiment) -> None: +def test_string_via_datasaver(experiment: "Experiment") -> None: """ Test that we can save text into database via DataSaver API """ @@ -56,7 +62,7 @@ def test_string_via_datasaver(experiment) -> None: assert test_set.get_parameter_data()["p"]["p"] == np.array(["some text"]) -def test_string(experiment) -> None: +def test_string(experiment: "Experiment") -> None: """ Test that we can save text into database via Measurement API """ @@ -80,7 +86,7 @@ def test_string(experiment) -> None: assert test_set.get_parameter_data()["p"]["p"] == np.array(["some text"]) -def test_string_with_wrong_paramtype(experiment) -> None: +def test_string_with_wrong_paramtype(experiment: "Experiment") -> None: """ Test that an exception occurs when saving string data if when registering a string parameter the paramtype was not set to 'text' @@ -165,7 +171,7 @@ def test_string_saved_and_loaded_as_numeric_via_dataset() -> None: test_set.conn.close() -def test_list_of_strings(experiment) -> None: +def test_list_of_strings(experiment: "Experiment") -> None: """ Test saving list of strings via DataSaver """ @@ -211,7 +217,10 @@ def test_list_of_strings(experiment) -> None: shape=hypnumpy.array_shapes(), ) ) -def test_string_and_date_data_in_array(experiment, p_values) -> None: +def test_string_and_date_data_in_array( + experiment: "Experiment", + p_values: "NDArray[np.str_ | np.bytes_ | np.timedelta64 | np.datetime64]", +) -> None: p = Parameter( "p", label="String parameter", diff --git a/tests/dataset/test_subscribing.py b/tests/dataset/test_subscribing.py index 07750119070..2bb73dae895 100644 --- a/tests/dataset/test_subscribing.py +++ b/tests/dataset/test_subscribing.py @@ -1,7 +1,7 @@ # Test some subscription scenarios import logging from numbers import Number -from typing import Any +from typing import TYPE_CHECKING, Any import pytest from numpy import ndarray @@ -12,6 +12,14 @@ from qcodes.parameters import ParamSpecBase from tests.common import retry_until_does_not_throw +if TYPE_CHECKING: + from collections.abc import Callable +if TYPE_CHECKING: + from collections.abc import Generator + from pathlib import Path + + from qcodes.dataset.data_set import DataSet + log = logging.getLogger(__name__) VALUE = str | Number | list[Any] | ndarray | bool @@ -28,7 +36,7 @@ class MockSubscriber: dataset! """ - def __init__(self, ds, lg): + def __init__(self, ds: "DataSet", lg: "bool"): self.lg = lg self.ds = ds @@ -39,8 +47,13 @@ def __call__( state[length] = results -def config_subscriber_factory(ds, log): - def config_subscriber(results, length, state): +def config_subscriber_factory( + ds: "DataSet", log: "logging.Logger" +) -> "Callable[[list[tuple[VALUE]], int, dict | None], None]": + def config_subscriber( + results: "list[tuple[VALUE]]", length: "int", state: "dict | None" + ) -> "None": + assert state is not None state[length] = results log.debug(f"got log {log} and dataset {ds.completed}.") @@ -48,20 +61,23 @@ def config_subscriber(results, length, state): @pytest.fixture(scope="function") -def basic_subscriber(): +def basic_subscriber() -> "Callable[[list[tuple[VALUE]], int, dict | None], None]": """ A basic subscriber that just puts results and length into state """ - def subscriber(results: list[tuple[VALUE]], length: int, state: dict) -> None: + def subscriber( + results: list[tuple[VALUE]], length: int, state: dict | None + ) -> None: + assert state is not None state[length] = results return subscriber @pytest.fixture(name="working_subscriber_config") -def _make_working_subscriber_config(tmp_path): +def _make_working_subscriber_config(tmp_path: "Path") -> "Generator[None, None, None]": # This string represents the config file in the home directory: config = """ { @@ -90,7 +106,7 @@ def _make_working_subscriber_config(tmp_path): @pytest.fixture(name="broken_subscriber_config") -def _make_broken_subscriber_config(tmp_path): +def _make_broken_subscriber_config(tmp_path: "Path") -> "Generator[None, None, None]": # This string represents the config file in the home directory: config = """ { @@ -120,7 +136,10 @@ def _make_broken_subscriber_config(tmp_path): @pytest.mark.flaky(reruns=5) @pytest.mark.serial -def test_basic_subscription(dataset, basic_subscriber) -> None: +def test_basic_subscription( + dataset: "DataSet", + basic_subscriber: "Callable[[list[tuple[VALUE]], int, dict | None], None]", +) -> None: xparam = ParamSpecBase(name="x", paramtype="numeric", label="x parameter", unit="V") yparam = ParamSpecBase( name="y", paramtype="numeric", label="y parameter", unit="Hz" @@ -144,7 +163,7 @@ def test_basic_subscription(dataset, basic_subscriber) -> None: @retry_until_does_not_throw( exception_class_to_expect=AssertionError, delay=0.5, tries=10 ) - def assert_expected_state(): + def assert_expected_state() -> "None": assert dataset.subscribers[sub_id].state == expected_state assert_expected_state() @@ -161,7 +180,10 @@ def assert_expected_state(): @pytest.mark.usefixtures("working_subscriber_config") -def test_subscription_from_config(dataset, basic_subscriber) -> None: +def test_subscription_from_config( + dataset: "DataSet", + basic_subscriber: "Callable[[list[tuple[VALUE]], int, dict | None], None]", +) -> None: """ This test is similar to `test_basic_subscription`, with the only difference that another subscriber from a config file is added. @@ -191,7 +213,7 @@ def test_subscription_from_config(dataset, basic_subscriber) -> None: expected_state[x + 1] = [(x, y)] @retry_until_does_not_throw(exception_class_to_expect=AssertionError, tries=10) - def assert_expected_state(): + def assert_expected_state() -> "None": assert dataset.subscribers[sub_id].state == expected_state assert dataset.subscribers[sub_id_c].state == expected_state @@ -199,7 +221,7 @@ def assert_expected_state(): @pytest.mark.usefixtures("broken_subscriber_config") -def test_subscription_from_config_wrong_name(dataset) -> None: +def test_subscription_from_config_wrong_name(dataset: "DataSet") -> None: """ This test checks that an exception is thrown if a wrong name for a subscriber is passed From acef850ebab7fc13c89f7f7390f3cdafc10cd7a4 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Mon, 19 Oct 2026 14:57:41 +0200 Subject: [PATCH 04/20] Add type annotations that are meant to fail --- tests/dataset/dond/test_dond.py | 6 +++--- tests/dataset/test_dataset_basic.py | 12 ++++++------ tests/dataset/test_experiment_container.py | 8 ++++---- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/dataset/dond/test_dond.py b/tests/dataset/dond/test_dond.py index 6e1de2e46de..0d1a7949bfa 100644 --- a/tests/dataset/dond/test_dond.py +++ b/tests/dataset/dond/test_dond.py @@ -1083,15 +1083,15 @@ def test_dond_2d_multi_datasets_with_callable_output_data( _param_complex_2: Parameter[complex], _param_set: Parameter[float | None], _param_set_2: Parameter[float | None], - _string_callable, + _string_callable: str, ) -> None: sweep_1 = LinSweep(_param_set, 0, 0.5, 5, 0) sweep_2 = LinSweep(_param_set_2, 0.5, 1, 5, 0) exp_1 = dond( sweep_1, sweep_2, - [_string_callable, _param, _param_complex], - [_string_callable, _param_2, _param_complex_2], + [_string_callable, _param, _param_complex], # pyright: ignore[reportArgumentType] + [_string_callable, _param_2, _param_complex_2], # pyright: ignore[reportArgumentType] ) datasets = exp_1[0] assert isinstance(datasets, tuple) diff --git a/tests/dataset/test_dataset_basic.py b/tests/dataset/test_dataset_basic.py index 9a43ce6c465..615cc38f619 100644 --- a/tests/dataset/test_dataset_basic.py +++ b/tests/dataset/test_dataset_basic.py @@ -278,13 +278,13 @@ def test_dataset_read_only_properties(dataset: DataSet) -> None: @pytest.mark.usefixtures("experiment") @pytest.mark.parametrize("non_existing_run_id", (1, 0, -1, "number#42")) def test_create_dataset_from_non_existing_run_id( - non_existing_run_id, + non_existing_run_id: int | str, ) -> None: with pytest.raises( ValueError, match=f"Run with run_id {non_existing_run_id} does not exist in the database", ): - _ = DataSet(run_id=non_existing_run_id) + _ = DataSet(run_id=non_existing_run_id) # pyright: ignore[reportArgumentType] def test_create_dataset_pass_both_connection_and_path_to_db( @@ -569,20 +569,20 @@ def test_load_by_counter() -> None: @pytest.mark.usefixtures("experiment") @pytest.mark.parametrize("nonexisting_counter", (-1, 0, 1, None)) def test_load_by_counter_for_nonexisting_counter( - nonexisting_counter, + nonexisting_counter: int | None, ) -> None: exp_id = 1 with pytest.raises(RuntimeError, match="Expected one row"): - _ = load_by_counter(exp_id, nonexisting_counter) + _ = load_by_counter(exp_id, nonexisting_counter) # pyright: ignore[reportArgumentType] @pytest.mark.usefixtures("empty_temp_db") @pytest.mark.parametrize("nonexisting_exp_id", (-1, 0, 1, None)) def test_load_by_counter_for_nonexisting_experiment( - nonexisting_exp_id, + nonexisting_exp_id: int | None, ) -> None: with pytest.raises(RuntimeError, match="Expected one row"): - _ = load_by_counter(nonexisting_exp_id, 1) + _ = load_by_counter(nonexisting_exp_id, 1) # pyright: ignore[reportArgumentType] @pytest.mark.usefixtures("empty_temp_db") diff --git a/tests/dataset/test_experiment_container.py b/tests/dataset/test_experiment_container.py index 5393ac64041..720f47757a2 100644 --- a/tests/dataset/test_experiment_container.py +++ b/tests/dataset/test_experiment_container.py @@ -167,9 +167,9 @@ def test_experiment_read_only_properties(experiment: "Experiment") -> None: @pytest.mark.usefixtures("empty_temp_db") @pytest.mark.parametrize("non_existing_id", (1, 0, -1, "number#42")) -def test_create_experiment_from_non_existing_id(non_existing_id) -> None: +def test_create_experiment_from_non_existing_id(non_existing_id: int | str) -> None: with pytest.raises(ValueError, match="No such experiment in the database"): - _ = Experiment(exp_id=non_existing_id) + _ = Experiment(exp_id=non_existing_id) # pyright: ignore[reportArgumentType] @pytest.mark.usefixtures("empty_temp_db") @@ -181,9 +181,9 @@ def test_load_experiment_from_non_existing_id(non_existing_id: "int") -> None: @pytest.mark.usefixtures("empty_temp_db") @pytest.mark.parametrize("bad_id", (None, "number#42")) -def test_load_experiment_from_bad_id(bad_id) -> None: +def test_load_experiment_from_bad_id(bad_id: str | None) -> None: with pytest.raises(ValueError, match="Experiment ID must be an integer"): - _ = load_experiment(bad_id) + _ = load_experiment(bad_id) # pyright: ignore[reportArgumentType] def test_format_string(empty_temp_db: "None") -> None: From 530fff2971ae07b5e510a82f9ff3cbeab071fa45 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Tue, 20 Oct 2026 07:14:23 +0200 Subject: [PATCH 05/20] Add type annotations to driver and mocker tests --- tests/drivers/AlazarTech/test_alazar_api.py | 41 +++++++----- .../drivers/AlazarTech/test_alazar_buffer.py | 6 +- tests/drivers/AlazarTech/test_dll_wrapper.py | 4 +- tests/drivers/test_agilent_e8257d.py | 2 +- tests/drivers/test_aimtti_pl601p.py | 9 ++- tests/drivers/test_coppermountain_m5065.py | 37 ++++++----- tests/drivers/test_coppermountain_m5180.py | 17 +++-- tests/drivers/test_cryomagnetics_tm620.py | 18 ++++-- tests/drivers/test_keithley_2450.py | 18 ++++-- tests/drivers/test_keithley_3706a.py | 56 +++++++++------- tests/drivers/test_keithley_7510.py | 15 +++-- tests/drivers/test_keithley_s46.py | 8 ++- tests/drivers/test_keysight_34465a.py | 22 ++++--- tests/drivers/test_keysight_34934a.py | 57 ++++++++++------- tests/drivers/test_keysight_34980a.py | 6 +- tests/drivers/test_keysight_b220x.py | 64 ++++++++++--------- tests/drivers/test_keysight_e4980a.py | 22 +++++-- tests/drivers/test_keysight_n6705b.py | 11 +++- tests/drivers/test_keysight_n9030b.py | 21 ++++-- tests/drivers/test_lakeshore_325.py | 7 +- tests/drivers/test_lakeshore_335.py | 29 +++++---- tests/drivers/test_lakeshore_336.py | 33 ++++++---- tests/drivers/test_lakeshore_372.py | 50 ++++++++------- tests/drivers/test_lakeshore_file_parser.py | 10 ++- tests/drivers/test_mercuryips.py | 40 ++++++++---- tests/drivers/test_rigol_ds1074z.py | 25 +++++--- tests/drivers/test_rs_sgs100a.py | 44 +++++++------ tests/drivers/test_rto_1000.py | 11 +++- tests/drivers/test_stahl.py | 16 +++-- tests/drivers/test_tektronix_awg5208.py | 10 ++- tests/drivers/test_tektronix_awg70000a.py | 37 +++++++---- tests/mockers/test_simulated_ats_api.py | 38 ++++++++--- 32 files changed, 481 insertions(+), 303 deletions(-) diff --git a/tests/drivers/AlazarTech/test_alazar_api.py b/tests/drivers/AlazarTech/test_alazar_api.py index 7b778abbb55..431816024d6 100644 --- a/tests/drivers/AlazarTech/test_alazar_api.py +++ b/tests/drivers/AlazarTech/test_alazar_api.py @@ -7,6 +7,7 @@ import gc import logging import os +from typing import TYPE_CHECKING from weakref import WeakValueDictionary import pytest @@ -18,11 +19,15 @@ API_SUCCESS, ERROR_CODES, Capability, + ReturnCode, ) from qcodes.instrument_drivers.AlazarTech.dll_wrapper import DllWrapperMeta +if TYPE_CHECKING: + from collections.abc import Generator -def _skip_if_alazar_dll_and_boards_not_installed(): + +def _skip_if_alazar_dll_and_boards_not_installed() -> pytest.MarkDecorator: if not os.path.exists(f"{AlazarTechATS.dll_path}.dll"): return pytest.mark.skip( "Alazar API DLL was not found in 'AlazarTechATS.dll_path'." @@ -43,22 +48,22 @@ def _skip_if_alazar_dll_and_boards_not_installed(): @pytest.fixture -def alazar(): +def alazar() -> "Generator[AlazarTechATS, None, None]": alazar = AlazarTechATS("alazar", system_id=SYSTEM_ID, board_id=BOARD_ID) yield alazar alazar.close() @pytest.fixture -def alazar_api(): +def alazar_api() -> "Generator[AlazarATSAPI, None, None]": yield AlazarATSAPI(AlazarTechATS.dll_path) def test_alazar_api_singleton_behavior(caplog: LogCaptureFixture) -> None: - def using_msg(dll_path): + def using_msg(dll_path: str) -> str: return f"Using existing instance for DLL path {dll_path}." - def creating_msg(dll_path): + def creating_msg(dll_path: str) -> str: return f"Creating new instance for DLL path {dll_path}." assert DllWrapperMeta._instances == WeakValueDictionary() @@ -119,7 +124,7 @@ def test_find_boards() -> None: assert boards[0]["board_id"] == BOARD_ID -def test_get_board_info(alazar_api) -> None: +def test_get_board_info(alazar_api: AlazarATSAPI) -> None: info = AlazarTechATS.get_board_info( api=alazar_api, system_id=SYSTEM_ID, board_id=BOARD_ID ) @@ -134,7 +139,7 @@ def test_get_board_info(alazar_api) -> None: assert info["board_id"] == BOARD_ID -def test_idn(alazar) -> None: +def test_idn(alazar: AlazarTechATS) -> None: idn = alazar.get_idn() assert { "firmware", @@ -153,10 +158,12 @@ def test_idn(alazar) -> None: "max_samples", } == set(idn.keys()) assert idn["vendor"] == "AlazarTech" - assert idn["model"][:3] == "ATS" + model = idn["model"] + assert isinstance(model, str) + assert model[:3] == "ATS" -def test_return_codes_are_correct(alazar_api) -> None: +def test_return_codes_are_correct(alazar_api: AlazarATSAPI) -> None: """ Test correctness of the coded return codes (success, failure, unknowns), and consistency with what `AlazarErrorToText` function returns. @@ -167,44 +174,44 @@ def test_return_codes_are_correct(alazar_api) -> None: assert alazar_api.error_to_text(API_SUCCESS) == "ApiSuccess" - lower_unknown = API_SUCCESS - 1 + lower_unknown = ReturnCode(API_SUCCESS - 1) assert alazar_api.error_to_text(lower_unknown) == "Unknown" - upper_unknown = max(list(ERROR_CODES.keys())) + 1 + upper_unknown = ReturnCode(max(list(ERROR_CODES.keys())) + 1) assert alazar_api.error_to_text(upper_unknown) == "Unknown" -def test_get_channel_info_convenient(alazar) -> None: +def test_get_channel_info_convenient(alazar: AlazarTechATS) -> None: bps, max_s = alazar.api.get_channel_info_(alazar._handle) assert isinstance(bps, int) assert isinstance(max_s, int) -def test_get_cpld_version_convenient(alazar) -> None: +def test_get_cpld_version_convenient(alazar: AlazarTechATS) -> None: cpld_ver = alazar.api.get_cpld_version_(alazar._handle) assert isinstance(cpld_ver, str) assert len(cpld_ver.split(".")) == 2 -def test_get_driver_version_convenient(alazar_api) -> None: +def test_get_driver_version_convenient(alazar_api: AlazarATSAPI) -> None: driver_ver = alazar_api.get_driver_version_() assert isinstance(driver_ver, str) assert len(driver_ver.split(".")) == 3 -def test_get_sdk_version_convenient(alazar_api) -> None: +def test_get_sdk_version_convenient(alazar_api: AlazarATSAPI) -> None: sdk_ver = alazar_api.get_sdk_version_() assert isinstance(sdk_ver, str) assert len(sdk_ver.split(".")) == 3 -def test_query_capability_convenient(alazar) -> None: +def test_query_capability_convenient(alazar: AlazarTechATS) -> None: cap = Capability.GET_SERIAL_NUMBER cap_value = alazar.api.query_capability_(alazar._handle, cap) assert isinstance(cap_value, int) -def test_writing_and_reading_registers(alazar) -> None: +def test_writing_and_reading_registers(alazar: AlazarTechATS) -> None: """ The approach is to read the register that includes information about trigger holdoff parameter, and write the same value back to the board. diff --git a/tests/drivers/AlazarTech/test_alazar_buffer.py b/tests/drivers/AlazarTech/test_alazar_buffer.py index 5a88d112892..b557f861ec5 100644 --- a/tests/drivers/AlazarTech/test_alazar_buffer.py +++ b/tests/drivers/AlazarTech/test_alazar_buffer.py @@ -1,9 +1,13 @@ import ctypes +from typing import TYPE_CHECKING import pytest from qcodes.instrument_drivers.AlazarTech.ATS import Buffer +if TYPE_CHECKING: + from qcodes.instrument_drivers.AlazarTech.ATS import CtypesTypes + def test_buffer_is_allocated_when_initiated() -> None: b = Buffer(ctypes.c_uint8, 25) @@ -14,5 +18,5 @@ def test_buffer_is_allocated_when_initiated() -> None: "ctype", (ctypes.c_uint8, ctypes.c_uint16, ctypes.c_uint32, ctypes.c_int32, ctypes.c_float), ) -def test_supported_ctypes_for_sample(ctype) -> None: +def test_supported_ctypes_for_sample(ctype: "CtypesTypes") -> None: Buffer(ctype, 8) diff --git a/tests/drivers/AlazarTech/test_dll_wrapper.py b/tests/drivers/AlazarTech/test_dll_wrapper.py index d371b885400..12db274653e 100644 --- a/tests/drivers/AlazarTech/test_dll_wrapper.py +++ b/tests/drivers/AlazarTech/test_dll_wrapper.py @@ -16,10 +16,10 @@ def test_wrapped_dll_singleton_behavior(caplog: LogCaptureFixture) -> None: - def using_msg(dll_path): + def using_msg(dll_path: str) -> str: return f"Using existing instance for DLL path {dll_path}." - def creating_msg(dll_path): + def creating_msg(dll_path: str) -> str: return f"Creating new instance for DLL path {dll_path}." dll_path_1 = "ntdll.dll" diff --git a/tests/drivers/test_agilent_e8257d.py b/tests/drivers/test_agilent_e8257d.py index d8d0ea9375e..0e1bc6e2442 100644 --- a/tests/drivers/test_agilent_e8257d.py +++ b/tests/drivers/test_agilent_e8257d.py @@ -12,7 +12,7 @@ class TestAgilentE8257D(DriverTestCase[AgilentE8257D]): driver = AgilentE8257D @classmethod - def setUpClass(cls): + def setUpClass(cls) -> None: super().setUpClass() cls.instrument.off() # Not a test but a safety measure diff --git a/tests/drivers/test_aimtti_pl601p.py b/tests/drivers/test_aimtti_pl601p.py index df4a0fa4768..6c5582aa6e4 100644 --- a/tests/drivers/test_aimtti_pl601p.py +++ b/tests/drivers/test_aimtti_pl601p.py @@ -1,10 +1,15 @@ +from typing import TYPE_CHECKING + import pytest from qcodes.instrument_drivers.AimTTi import AimTTiPL601 +if TYPE_CHECKING: + from collections.abc import Generator + @pytest.fixture(scope="function", name="driver") -def _make_driver(): +def _make_driver() -> "Generator[AimTTiPL601, None, None]": driver = AimTTiPL601( "AimTTi", address="GPIB::1::INSTR", pyvisa_sim_file="AimTTi_PL601P.yaml" ) @@ -13,7 +18,7 @@ def _make_driver(): driver.close() -def test_idn(driver) -> None: +def test_idn(driver: AimTTiPL601) -> None: assert { "firmware": "3.05-4.06", "model": "PL601-P", diff --git a/tests/drivers/test_coppermountain_m5065.py b/tests/drivers/test_coppermountain_m5065.py index a091001fc79..b4e676960be 100644 --- a/tests/drivers/test_coppermountain_m5065.py +++ b/tests/drivers/test_coppermountain_m5065.py @@ -1,18 +1,23 @@ +from typing import TYPE_CHECKING + import pytest from qcodes.instrument_drivers.CopperMountain import CopperMountainM5065 +if TYPE_CHECKING: + from collections.abc import Generator + class DummyM5065: - def __init__(self, *args, **kwargs): + def __init__(self, *args: object, **kwargs: object) -> None: pass - def start(self): + def start(self) -> float: return 1e6 @pytest.fixture() -def vna(): +def vna() -> "Generator[CopperMountainM5065, None, None]": instance = CopperMountainM5065( name="M5065", address="TCPIP0::localhost::hislip0::INSTR", @@ -23,45 +28,45 @@ def vna(): instance.close() -def test_m5065_instantiation(vna): +def test_m5065_instantiation(vna: CopperMountainM5065) -> None: assert vna.name == "M5065" assert vna.address == "TCPIP0::localhost::hislip0::INSTR" -def test_idn_command(vna): +def test_idn_command(vna: CopperMountainM5065) -> None: idn = vna.get_idn() assert idn["vendor"] == "CMT" assert idn["model"] == "M5065" -def test_min_and_max_frequency_defaults(vna): +def test_min_and_max_frequency_defaults(vna: CopperMountainM5065) -> None: assert vna.min_freq == 300e3 assert vna.max_freq == 6.5e9 -def test_output_param(vna): +def test_output_param(vna: CopperMountainM5065) -> None: vna.output(True) assert vna.output() is True vna.output(False) assert vna.output() is False -def test_power_param(vna): +def test_power_param(vna: CopperMountainM5065) -> None: vna.power(-20) assert vna.power() == -20 -def test_if_bandwidth_param(vna): +def test_if_bandwidth_param(vna: CopperMountainM5065) -> None: vna.if_bandwidth(1000) assert vna.if_bandwidth() == 1000 -def test_averages_param(vna): +def test_averages_param(vna: CopperMountainM5065) -> None: vna.averages(2) assert vna.averages() == 2 -def test_frequency_params(vna): +def test_frequency_params(vna: CopperMountainM5065) -> None: vna.start(1e6) vna.stop(2e6) vna.center(1.5e6) @@ -72,21 +77,23 @@ def test_frequency_params(vna): assert vna.span() == 1e6 -def test_setting_start_raises_error_when_larger_than_stop(vna): +def test_setting_start_raises_error_when_larger_than_stop( + vna: CopperMountainM5065, +) -> None: vna.stop(1e6) with pytest.raises(ValueError, match="Stop frequency"): vna.start(1e7) -def test_number_of_points_param(vna): +def test_number_of_points_param(vna: CopperMountainM5065) -> None: vna.number_of_points(201) assert vna.number_of_points() == 201 -def test_trigger_source_param(vna): +def test_trigger_source_param(vna: CopperMountainM5065) -> None: vna.trigger_source("bus") assert vna.trigger_source() == "bus" -def test_update_lin_traces(vna): +def test_update_lin_traces(vna: CopperMountainM5065) -> None: vna.update_lin_traces() diff --git a/tests/drivers/test_coppermountain_m5180.py b/tests/drivers/test_coppermountain_m5180.py index 84a55af3107..562bf444f7c 100644 --- a/tests/drivers/test_coppermountain_m5180.py +++ b/tests/drivers/test_coppermountain_m5180.py @@ -1,18 +1,23 @@ +from typing import TYPE_CHECKING + import pytest from qcodes.instrument_drivers.CopperMountain import CopperMountainM5180 +if TYPE_CHECKING: + from collections.abc import Generator + class DummyM5065: - def __init__(self, *args, **kwargs): + def __init__(self, *args: object, **kwargs: object) -> None: pass - def start(self): + def start(self) -> float: return 1e6 @pytest.fixture() -def vna(): +def vna() -> "Generator[CopperMountainM5180, None, None]": instance = CopperMountainM5180( name="M5180", address="TCPIP0::localhost::hislip0::INSTR", @@ -23,17 +28,17 @@ def vna(): instance.close() -def test_m5180_instantiation(vna): +def test_m5180_instantiation(vna: CopperMountainM5180) -> None: assert vna.name == "M5180" assert vna.address == "TCPIP0::localhost::hislip0::INSTR" -def test_idn_command(vna): +def test_idn_command(vna: CopperMountainM5180) -> None: idn = vna.get_idn() assert idn["vendor"] == "CMT" assert idn["model"] == "M5180" -def test_min_and_max_frequency_defaults(vna): +def test_min_and_max_frequency_defaults(vna: CopperMountainM5180) -> None: assert vna.min_freq == 300e3 assert vna.max_freq == 18e9 diff --git a/tests/drivers/test_cryomagnetics_tm620.py b/tests/drivers/test_cryomagnetics_tm620.py index 5608e9b816a..2c9bc7294cb 100644 --- a/tests/drivers/test_cryomagnetics_tm620.py +++ b/tests/drivers/test_cryomagnetics_tm620.py @@ -24,29 +24,31 @@ def fixture_tm620() -> "Generator[CryomagneticsModelTM620, None, None]": instrument.close() -def test_initialization(tm620): +def test_initialization(tm620: CryomagneticsModelTM620) -> None: assert tm620.name == "test_cryo_tm620" assert tm620.address == "GPIB0::1::INSTR" assert hasattr(tm620, "shield") assert hasattr(tm620, "magnet") -def test_get_A_success(tm620): +def test_get_A_success(tm620: CryomagneticsModelTM620) -> None: with patch.object(tm620, "ask", return_value="55.12K"): assert tm620._get_A() == 55.12 -def test_get_B_success(tm620): +def test_get_B_success(tm620: CryomagneticsModelTM620) -> None: with patch.object(tm620, "ask", return_value="4.21K"): assert tm620._get_B() == 4.21 -def test_parse_output_valid(tm620): +def test_parse_output_valid(tm620: CryomagneticsModelTM620) -> None: assert tm620._parse_output("55.12K") == "55.12" assert tm620._parse_output("4.34C") == "4.34" -def test_parse_output_invalid(tm620, caplog): +def test_parse_output_invalid( + tm620: CryomagneticsModelTM620, caplog: pytest.LogCaptureFixture +) -> None: with ( caplog.at_level("ERROR"), pytest.raises(ValueError, match="No floating point number found"), @@ -55,12 +57,14 @@ def test_parse_output_invalid(tm620, caplog): assert "No floating point number found in output" in caplog.text -def test_convert_to_numerics_valid(tm620): +def test_convert_to_numerics_valid(tm620: CryomagneticsModelTM620) -> None: assert tm620._convert_to_numeric("55.12") == 55.12 assert tm620._convert_to_numeric("4.34") == 4.34 -def test_convert_to_numerics_invalid(tm620, caplog): +def test_convert_to_numerics_invalid( + tm620: CryomagneticsModelTM620, caplog: pytest.LogCaptureFixture +) -> None: with caplog.at_level("ERROR"), pytest.raises(ValueError, match="Unable to convert"): tm620._convert_to_numeric("not_a_number") assert "Error converting 'not_a_number' to float" in caplog.text diff --git a/tests/drivers/test_keithley_2450.py b/tests/drivers/test_keithley_2450.py index 4818f4fd018..c108ef1dd7f 100644 --- a/tests/drivers/test_keithley_2450.py +++ b/tests/drivers/test_keithley_2450.py @@ -1,4 +1,5 @@ import logging +from typing import TYPE_CHECKING import numpy as np import pytest @@ -6,9 +7,12 @@ from qcodes.instrument_drivers.Keithley import Keithley2450 +if TYPE_CHECKING: + from collections.abc import Generator + @pytest.fixture(scope="function") -def k2450(): +def k2450() -> "Generator[Keithley2450, None, None]": """ Create a Keithley 2450 instrument """ @@ -34,7 +38,7 @@ def test_wrong_mode(caplog: LogCaptureFixture) -> None: instrument.close() -def test_change_source_function(k2450) -> None: +def test_change_source_function(k2450: Keithley2450) -> None: """ The parameters available on the source sub-module depend on the function. """ @@ -54,7 +58,7 @@ def test_change_source_function(k2450) -> None: ) -def test_source_change_error(k2450) -> None: +def test_source_change_error(k2450: Keithley2450) -> None: """ If the sense function is in resistance mode, test that an error is generated when we change the source function @@ -72,7 +76,7 @@ def test_source_change_error(k2450) -> None: k2450.source.function("current") -def test_sense_current_mode(k2450) -> None: +def test_sense_current_mode(k2450: Keithley2450) -> None: """ Test that when we are in a sense function, for example, 'current', that the sense property is returning to the correct submodule. We also test @@ -89,7 +93,7 @@ def test_sense_current_mode(k2450) -> None: assert not hasattr(k2450.sense, other_sense_function) -def test_setpoint_always_follows_source_function(k2450) -> None: +def test_setpoint_always_follows_source_function(k2450: Keithley2450) -> None: """ Changing the source and/or sense functions should not confuse the setpoints. These should always follow the source module @@ -108,7 +112,7 @@ def test_setpoint_always_follows_source_function(k2450) -> None: assert k2450.sense.sweep.setpoints == (k2450.source.sweep_axis,) -def test_reset_sweep_on_source_change(k2450) -> None: +def test_reset_sweep_on_source_change(k2450: Keithley2450) -> None: """ If we change the source function, we need to run the sweep setup again """ @@ -123,7 +127,7 @@ def test_reset_sweep_on_source_change(k2450) -> None: k2450.source.get_sweep_axis() -def test_sweep(k2450) -> None: +def test_sweep(k2450: Keithley2450) -> None: """ Verify that we can start sweeps """ diff --git a/tests/drivers/test_keithley_3706a.py b/tests/drivers/test_keithley_3706a.py index 12364532b56..8032a6b5359 100644 --- a/tests/drivers/test_keithley_3706a.py +++ b/tests/drivers/test_keithley_3706a.py @@ -1,12 +1,16 @@ import itertools +from typing import TYPE_CHECKING import pytest from qcodes.instrument_drivers.Keithley.Keithley_3706A import Keithley3706A +if TYPE_CHECKING: + from collections.abc import Generator + @pytest.fixture(scope="function", name="driver") -def _make_driver(): +def _make_driver() -> "Generator[Keithley3706A, None, None]": driver = Keithley3706A( "Keithley_3706A", address="GPIB::11::INSTR", @@ -17,7 +21,7 @@ def _make_driver(): @pytest.fixture() -def channels_in_slot_one(): +def channels_in_slot_one() -> list[str]: channels = [] for i in range(1, 7): for j in range(int(f"1{i}01"), int(f"1{i}17")): @@ -26,7 +30,7 @@ def channels_in_slot_one(): @pytest.fixture() -def channels_in_slot_two(): +def channels_in_slot_two() -> list[str]: channels = [] for i in range(1, 7): for j in range(int(f"2{i}01"), int(f"2{i}17")): @@ -35,7 +39,7 @@ def channels_in_slot_two(): @pytest.fixture() -def channels_in_slot_three(): +def channels_in_slot_three() -> list[str]: channels = [] for i in range(1, 7): for j in range(int(f"3{i}01"), int(f"3{i}17")): @@ -44,7 +48,7 @@ def channels_in_slot_three(): @pytest.fixture() -def matrix_channels(): +def matrix_channels() -> list[str]: slots = ["1", "2", "3"] rows = [["1", "2", "3", "4", "5", "6"]] * 3 columns = [ @@ -74,7 +78,7 @@ def matrix_channels(): return m_channels -def test_idn(driver) -> None: +def test_idn(driver: Keithley3706A) -> None: assert { "firmware": "01.56a", "model": "3706A-SNFP", @@ -83,7 +87,7 @@ def test_idn(driver) -> None: } == driver.get_idn() -def test_switch_card_idn(driver) -> None: +def test_switch_card_idn(driver: Keithley3706A) -> None: assert { "slot_no": "1", "model": "3730", @@ -93,15 +97,15 @@ def test_switch_card_idn(driver) -> None: } == driver.get_switch_cards()[0] -def test_installed_card_id(driver) -> None: +def test_installed_card_id(driver: Keithley3706A) -> None: assert ["1", "2", "3"] == driver._get_slot_ids() -def test_slot_names(driver) -> None: +def test_slot_names(driver: Keithley3706A) -> None: assert ["slot1", "slot2", "slot3"] == driver._get_slot_names() -def test_get_interlock_state(driver) -> None: +def test_get_interlock_state(driver: Keithley3706A) -> None: dict_list = ( { "slot_no": "1", @@ -118,22 +122,22 @@ def test_get_interlock_state(driver) -> None: assert dict_list == driver.get_interlock_state() -def test_get_number_of_rows(driver) -> None: +def test_get_number_of_rows(driver: Keithley3706A) -> None: assert [6, 6, 6] == driver._get_number_of_rows() -def test_get_number_of_columns(driver) -> None: +def test_get_number_of_columns(driver: Keithley3706A) -> None: assert [16, 16, 16] == driver._get_number_of_columns() @pytest.mark.parametrize("val", [0, 1, 2]) -def test_get_rows(driver, val) -> None: +def test_get_rows(driver: Keithley3706A, val: int) -> None: rows_in_a_slot = ["1", "2", "3", "4", "5", "6"] assert rows_in_a_slot == driver._get_rows()[val] @pytest.mark.parametrize("val", [0, 1, 2]) -def test_get_columns(driver, val) -> None: +def test_get_columns(driver: Keithley3706A, val: int) -> None: columns_in_a_slot = [ "01", "02", @@ -156,31 +160,37 @@ def test_get_columns(driver, val) -> None: @pytest.mark.parametrize("val", [1, 2, 3]) -def test_number_of_channels_by_slot(driver, val) -> None: +def test_number_of_channels_by_slot(driver: Keithley3706A, val: int) -> None: assert 96 == len(driver.get_channels_by_slot(val)) -def test_total_number_of_channels(driver) -> None: +def test_total_number_of_channels(driver: Keithley3706A) -> None: assert 3 * 96 == len(driver.get_channels()) -def test_channels_in_slot_one(driver, channels_in_slot_one) -> None: +def test_channels_in_slot_one( + driver: Keithley3706A, channels_in_slot_one: list[str] +) -> None: assert channels_in_slot_one == driver.get_channels_by_slot(1) -def test_channels_in_slot_two(driver, channels_in_slot_two) -> None: +def test_channels_in_slot_two( + driver: Keithley3706A, channels_in_slot_two: list[str] +) -> None: assert channels_in_slot_two == driver.get_channels_by_slot(2) -def test_channels_in_slot_three(driver, channels_in_slot_three) -> None: +def test_channels_in_slot_three( + driver: Keithley3706A, channels_in_slot_three: list[str] +) -> None: assert channels_in_slot_three == driver.get_channels_by_slot(3) -def test_matrix_channels(driver, matrix_channels) -> None: +def test_matrix_channels(driver: Keithley3706A, matrix_channels: list[str]) -> None: assert matrix_channels == driver.get_channels() -def test_analog_backplane_specifiers(driver) -> None: +def test_analog_backplane_specifiers(driver: Keithley3706A) -> None: specifiers = [ "1911", "1912", @@ -205,12 +215,12 @@ def test_analog_backplane_specifiers(driver) -> None: @pytest.mark.parametrize("val", ["slot1", "allslots", "3111", "3912", "2103:2116"]) -def test_validator_truth(driver, val) -> None: +def test_validator_truth(driver: Keithley3706A, val: str) -> None: assert driver._validator(val) is True @pytest.mark.parametrize( "val", ["slot12", "alslots", "5111", "912", "123213", "QCoDeS"] ) -def test_validator_falsehood(driver, val) -> None: +def test_validator_falsehood(driver: Keithley3706A, val: str) -> None: assert driver._validator(val) is False diff --git a/tests/drivers/test_keithley_7510.py b/tests/drivers/test_keithley_7510.py index 2d23aaffcba..0f2082b6dfc 100644 --- a/tests/drivers/test_keithley_7510.py +++ b/tests/drivers/test_keithley_7510.py @@ -1,12 +1,17 @@ +from typing import TYPE_CHECKING + import hypothesis.strategies as st import pytest from hypothesis import HealthCheck, given, settings from qcodes.instrument_drivers.Keithley import Keithley7510 +if TYPE_CHECKING: + from collections.abc import Generator + @pytest.fixture(scope="function") -def dmm_7510_driver(): +def dmm_7510_driver() -> "Generator[Keithley7510, None, None]": inst = Keithley7510( "Keithley_7510_sim", address="GPIB::1::INSTR", @@ -19,7 +24,7 @@ def dmm_7510_driver(): inst.close() -def test_get_idn(dmm_7510_driver) -> None: +def test_get_idn(dmm_7510_driver: Keithley7510) -> None: assert dmm_7510_driver.IDN() == { "vendor": "KEITHLEY INSTRUMENTS", "model": "DMM7510", @@ -28,7 +33,7 @@ def test_get_idn(dmm_7510_driver) -> None: } -def test_change_sense_function(dmm_7510_driver) -> None: +def test_change_sense_function(dmm_7510_driver: Keithley7510) -> None: """ Measurement should be the same as the sense function, e.g., only voltage measurement is allowed when the sense function is "voltage". @@ -42,7 +47,9 @@ def test_change_sense_function(dmm_7510_driver) -> None: @settings(deadline=None, suppress_health_check=(HealthCheck.function_scoped_fixture,)) @given(st.sampled_from((0.1, 1, 10, 100, 1000)), st.floats(0.01, 10)) -def test_set_range_and_nplc(dmm_7510_driver, upper_limit, nplc) -> None: +def test_set_range_and_nplc( + dmm_7510_driver: Keithley7510, upper_limit: float, nplc: float +) -> None: """ Test the ability of setting range and nplc value for sense function. "Voltage" is used as an example. diff --git a/tests/drivers/test_keithley_s46.py b/tests/drivers/test_keithley_s46.py index 5f5e94c0032..fc950bcb639 100644 --- a/tests/drivers/test_keithley_s46.py +++ b/tests/drivers/test_keithley_s46.py @@ -1,4 +1,5 @@ import logging +from typing import TYPE_CHECKING import pytest from pytest import FixtureRequest, LogCaptureFixture @@ -8,6 +9,9 @@ KeithleyS46LockAcquisitionError, ) +if TYPE_CHECKING: + from collections.abc import Generator + def test_aliases_dict() -> None: """ @@ -28,7 +32,7 @@ def calc_channel_nr(alias: str) -> int: @pytest.fixture(scope="function") -def s46_six(): +def s46_six() -> "Generator[KeithleyS46, None, None]": """ A six channel-per-relay instrument """ @@ -43,7 +47,7 @@ def s46_six(): @pytest.fixture(scope="function") -def s46_four(): +def s46_four() -> "Generator[KeithleyS46, None, None]": """ A four channel-per-relay instrument """ diff --git a/tests/drivers/test_keysight_34465a.py b/tests/drivers/test_keysight_34465a.py index e7beb972ea2..bcd5f175a6e 100644 --- a/tests/drivers/test_keysight_34465a.py +++ b/tests/drivers/test_keysight_34465a.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Protocol import numpy as np import pytest @@ -11,6 +11,10 @@ from collections.abc import Generator +class _AskCallable(Protocol): + def __call__(self, cmd: str) -> str: ... + + @pytest.fixture(scope="function") def driver() -> "Generator[Keysight34465A, None, None]": keysight_sim = Keysight34465A( @@ -27,7 +31,7 @@ def driver() -> "Generator[Keysight34465A, None, None]": @pytest.fixture(scope="function") def driver_with_read_and_fetch_mocked( - val_volt, + val_volt: str, ) -> "Generator[Keysight34465A, None, None]": keysight_sim = Keysight34465A( "keysight_34465A_sim", @@ -35,7 +39,9 @@ def driver_with_read_and_fetch_mocked( pyvisa_sim_file="Keysight_34465A.yaml", ) - def get_ask_with_read_mock(original_ask, read_value): + def get_ask_with_read_mock( + original_ask: _AskCallable, read_value: str + ) -> _AskCallable: def ask_with_read_mock(cmd: str) -> str: if cmd in ("READ?", "FETCH?"): return read_value @@ -69,7 +75,7 @@ def test_reset(driver: Keysight34465A) -> None: driver.reset() -def test_NPLC(driver) -> None: +def test_NPLC(driver: Keysight34465A) -> None: assert driver.NPLC.get() == 10.0 driver.NPLC.set(0.2) assert driver.NPLC.get() == 0.2 @@ -78,7 +84,7 @@ def test_NPLC(driver) -> None: @pytest.mark.parametrize("val_volt", ["100.0"]) def test_get_voltage( - driver_with_read_and_fetch_mocked: Keysight34465A, val_volt: float + driver_with_read_and_fetch_mocked: Keysight34465A, val_volt: str ) -> None: voltage = driver_with_read_and_fetch_mocked.volt.get() assert voltage == 100.0 @@ -86,7 +92,7 @@ def test_get_voltage( @pytest.mark.parametrize("val_volt", ["9.9e37"]) def test_get_voltage_plus_inf( - driver_with_read_and_fetch_mocked: Keysight34465A, val_volt: float + driver_with_read_and_fetch_mocked: Keysight34465A, val_volt: str ) -> None: voltage = driver_with_read_and_fetch_mocked.volt.get() assert voltage == np.inf @@ -94,7 +100,7 @@ def test_get_voltage_plus_inf( @pytest.mark.parametrize("val_volt", ["-9.9e37"]) def test_get_voltage_minus_inf( - driver_with_read_and_fetch_mocked: Keysight34465A, val_volt: float + driver_with_read_and_fetch_mocked: Keysight34465A, val_volt: str ) -> None: voltage = driver_with_read_and_fetch_mocked.volt.get() assert voltage == -np.inf @@ -102,7 +108,7 @@ def test_get_voltage_minus_inf( @pytest.mark.parametrize("val_volt", ["10, 9.9e37, -9.9e37"]) def test_get_timetrace( - driver_with_read_and_fetch_mocked: Keysight34465A, val_volt: float + driver_with_read_and_fetch_mocked: Keysight34465A, val_volt: str ) -> None: driver_with_read_and_fetch_mocked.timetrace_npts(3) assert driver_with_read_and_fetch_mocked.timetrace_npts() == 3 diff --git a/tests/drivers/test_keysight_34934a.py b/tests/drivers/test_keysight_34934a.py index 2dd5de3b984..c7208c69f94 100644 --- a/tests/drivers/test_keysight_34934a.py +++ b/tests/drivers/test_keysight_34934a.py @@ -1,3 +1,5 @@ +from typing import TYPE_CHECKING + import hypothesis.strategies as st import pytest from hypothesis import given @@ -5,9 +7,12 @@ from qcodes.instrument_drivers.Keysight.keysight_34934a import Keysight34934A from qcodes.instrument_drivers.Keysight.keysight_34980a import Keysight34980A +if TYPE_CHECKING: + from collections.abc import Callable, Generator + @pytest.fixture(scope="function") -def switch_driver(): +def switch_driver() -> "Generator[Keysight34980A, None, None]": inst = Keysight34980A( "keysight_34980A_sim", address="GPIB::1::INSTR", @@ -20,19 +25,23 @@ def switch_driver(): inst.close() -def test_protection_mode(switch_driver) -> None: +def test_protection_mode(switch_driver: Keysight34980A) -> None: """ To check the protection mode (34934A module only) """ - assert switch_driver.module[1].protection_mode() == "AUTO100" + module = switch_driver.module[1] + assert isinstance(module, Keysight34934A) + assert module.protection_mode() == "AUTO100" -def test_connection(switch_driver) -> None: +def test_connection(switch_driver: Keysight34980A) -> None: """ To check if a channel is closed or open """ - assert not switch_driver.module[1].is_closed(2, 3) - assert switch_driver.module[1].is_open(2, 3) + module = switch_driver.module[1] + assert isinstance(module, Keysight34934A) + assert not module.is_closed(2, 3) + assert module.is_open(2, 3) # The following is to test the numbering function for the module 34934A @@ -44,95 +53,95 @@ def test_connection(switch_driver) -> None: @given( st.sampled_from(("M1H", "M1L", "M2H", "M2L")), st.integers(1, 4), st.integers(1, 32) ) -def test_4x32(config, row, column) -> None: +def test_4x32(config: str, row: int, column: int) -> None: f = Keysight34934A.get_numbering_function(4, 32, config) g = numbering_function_4x32(config) assert f(row, column) == g(row, column) @given(st.sampled_from(("MH", "ML")), st.integers(1, 4), st.integers(1, 64)) -def test_4x64(config, row, column) -> None: +def test_4x64(config: str, row: int, column: int) -> None: f = Keysight34934A.get_numbering_function(4, 64, config) g = numbering_function_4x64(config) assert f(row, column) == g(row, column) @given(st.integers(1, 4), st.integers(1, 128)) -def test_4x128(row, column) -> None: +def test_4x128(row: int, column: int) -> None: f = Keysight34934A.get_numbering_function(4, 128) g = numbering_function_4x128() assert f(row, column) == g(row, column) @given(st.sampled_from(("MH", "ML")), st.integers(1, 8), st.integers(1, 32)) -def test_8x32(config, row, column) -> None: +def test_8x32(config: str, row: int, column: int) -> None: f = Keysight34934A.get_numbering_function(8, 32, config) g = numbering_function_8x32(config) assert f(row, column) == g(row, column) @given(st.integers(1, 8), st.integers(1, 64)) -def test_8x64(row, column) -> None: +def test_8x64(row: int, column: int) -> None: f = Keysight34934A.get_numbering_function(8, 64) g = numbering_function_8x64() assert f(row, column) == g(row, column) @given(st.integers(1, 16), st.integers(1, 32)) -def test_16x32(row, column) -> None: +def test_16x32(row: int, column: int) -> None: f = Keysight34934A.get_numbering_function(16, 32) g = numbering_function_16x32() assert f(row, column) == g(row, column) -def numbering_function_4x32(wiring_config): +def numbering_function_4x32(wiring_config: str) -> "Callable[[int, int], str]": offsets = {"M1H": 0, "M2H": 32, "M1L": 64, "M2L": 96} - def numbering_function(row, col): + def numbering_function(row: int, col: int) -> str: n = 100 * (2 * row - 1) + col + offsets[wiring_config] return str(int(n)) return numbering_function -def numbering_function_4x64(wiring_config): +def numbering_function_4x64(wiring_config: str) -> "Callable[[int, int], str]": offsets = {"MH": 0, "ML": 64} - def numbering_function(row, col): + def numbering_function(row: int, col: int) -> str: n = 100 * (2 * row - 1) + col + offsets[wiring_config] return str(int(n)) return numbering_function -def numbering_function_4x128(): - def numbering_function(row, col): +def numbering_function_4x128() -> "Callable[[int, int], str]": + def numbering_function(row: int, col: int) -> str: n = 100 * (2 * row - 1) + col return str(int(n)) return numbering_function -def numbering_function_8x32(wiring_config): +def numbering_function_8x32(wiring_config: str) -> "Callable[[int, int], str]": offsets = {"MH": 0, "ML": 32} - def numbering_function(row, col): + def numbering_function(row: int, col: int) -> str: n = 100 * row + col + offsets[wiring_config] return str(int(n)) return numbering_function -def numbering_function_8x64(): - def numbering_function(row, col): +def numbering_function_8x64() -> "Callable[[int, int], str]": + def numbering_function(row: int, col: int) -> str: n = 100 * row + col return str(int(n)) return numbering_function -def numbering_function_16x32(): - def numbering_function(row, col): +def numbering_function_16x32() -> "Callable[[int, int], str]": + def numbering_function(row: int, col: int) -> str: n = 50 * (row + 1) + col return str(int(n)) diff --git a/tests/drivers/test_keysight_34980a.py b/tests/drivers/test_keysight_34980a.py index 06dbd2ea480..01a194e4a9d 100644 --- a/tests/drivers/test_keysight_34980a.py +++ b/tests/drivers/test_keysight_34980a.py @@ -1,13 +1,17 @@ import logging +from typing import TYPE_CHECKING import pytest from pytest import FixtureRequest, LogCaptureFixture from qcodes.instrument_drivers.Keysight.keysight_34980a import Keysight34980A +if TYPE_CHECKING: + from collections.abc import Generator + @pytest.fixture(scope="function") -def switch_driver(): +def switch_driver() -> "Generator[Keysight34980A, None, None]": inst = Keysight34980A( "keysight_34980A_sim", address="GPIB::1::INSTR", diff --git a/tests/drivers/test_keysight_b220x.py b/tests/drivers/test_keysight_b220x.py index 8df739cfcff..38cbe414c50 100644 --- a/tests/drivers/test_keysight_b220x.py +++ b/tests/drivers/test_keysight_b220x.py @@ -33,17 +33,17 @@ def uut() -> "Generator[KeysightB220X, None, None]": instance.close() -def test_idn_command(uut) -> None: +def test_idn_command(uut: KeysightB220X) -> None: assert "AGILENT" in uut.IDN()["vendor"] assert 0 == uut.get_status() -def test_connect(uut) -> None: +def test_connect(uut: KeysightB220X) -> None: uut.connect(2, 48) assert 0 == uut.get_status() -def test_connect_throws_at_invalid_channel_number(uut) -> None: +def test_connect_throws_at_invalid_channel_number(uut: KeysightB220X) -> None: with pytest.raises(ValueError): uut.connect(2, 49) with pytest.raises(ValueError): @@ -54,7 +54,7 @@ def test_connect_throws_at_invalid_channel_number(uut) -> None: uut.connect(15, 10) -def test_connect_emits_warning_on_statusbyte_not_null(uut) -> None: +def test_connect_emits_warning_on_statusbyte_not_null(uut: KeysightB220X) -> None: # some tricks are used to trigger an instrument error both in the # simulation as well as in the real instrument: # 1. with gnd mode enabled, it is illegal to connect to input channel 12, @@ -74,7 +74,7 @@ def test_connect_emits_warning_on_statusbyte_not_null(uut) -> None: uut.gnd_mode(False) -def test_disconnect_throws_at_invalid_channel_number(uut) -> None: +def test_disconnect_throws_at_invalid_channel_number(uut: KeysightB220X) -> None: with pytest.raises(ValueError): uut.disconnect(2, 49) with pytest.raises(ValueError): @@ -85,35 +85,35 @@ def test_disconnect_throws_at_invalid_channel_number(uut) -> None: uut.disconnect(15, 10) -def test_connections(uut) -> None: +def test_connections(uut: KeysightB220X) -> None: uut.connect(2, 48) uut.connect(10, 12) assert {(2, 48), (10, 12)} == uut.connections() -def test_to_channel_list(uut) -> None: +def test_to_channel_list(uut: KeysightB220X) -> None: assert "(@00345,01109)" == uut.to_channel_list([(3, 45), (11, 9)]) -def test_connect_paths(uut) -> None: +def test_connect_paths(uut: KeysightB220X) -> None: uut.disconnect_all() uut.connect_paths([(3, 45), (11, 9)]) assert 0 == uut.get_status() -def test_disconnect_paths(uut) -> None: +def test_disconnect_paths(uut: KeysightB220X) -> None: uut.connect_paths([(3, 45), (11, 9)]) uut.disconnect_paths([(3, 45), (11, 9)]) assert 0 == uut.get_status() -def test_disconnect_all(uut) -> None: +def test_disconnect_all(uut: KeysightB220X) -> None: uut.connect(2, 48) uut.disconnect_all() assert 0 == uut.get_status() -def test_disconnect(uut) -> None: +def test_disconnect(uut: KeysightB220X) -> None: uut.connect(2, 48) assert 0 == uut.get_status() uut.disconnect(2, 48) @@ -123,21 +123,23 @@ def test_disconnect(uut) -> None: @pytest.mark.filterwarnings("ignore:When going") -def test_connection_rule(uut) -> None: +def test_connection_rule(uut: KeysightB220X) -> None: uut.connection_rule("single") assert 0 == uut.get_status() assert "single" == uut.connection_rule() assert 0 == uut.get_status() -def test_connection_rule_emits_warning_when_going_from_free_to_single(uut) -> None: +def test_connection_rule_emits_warning_when_going_from_free_to_single( + uut: KeysightB220X, +) -> None: uut.connection_rule("free") # uut should already be in free mode after reset with pytest.warns(UserWarning): uut.connection_rule("single") -def test_connection_sequence(uut) -> None: +def test_connection_sequence(uut: KeysightB220X) -> None: assert "bbm" == uut.connection_sequence() assert 0 == uut.get_status() uut.connection_sequence("mbb") @@ -145,74 +147,74 @@ def test_connection_sequence(uut) -> None: assert "mbb" == uut.connection_sequence() -def test_bias_disable_all_outputs(uut) -> None: +def test_bias_disable_all_outputs(uut: KeysightB220X) -> None: uut.bias_disable_all_outputs() assert 0 == uut.get_status() -def test_bias_disable_ouput(uut) -> None: +def test_bias_disable_ouput(uut: KeysightB220X) -> None: uut.bias_disable_output(1) assert 0 == uut.get_status() -def test_bias_enable_all_outputs(uut) -> None: +def test_bias_enable_all_outputs(uut: KeysightB220X) -> None: uut.bias_enable_all_outputs() assert 0 == uut.get_status() -def test_bias_enable_output(uut) -> None: +def test_bias_enable_output(uut: KeysightB220X) -> None: uut.bias_enable_output(1) assert 0 == uut.get_status() -def test_bias_input_port(uut) -> None: +def test_bias_input_port(uut: KeysightB220X) -> None: assert 10 == uut.bias_input_port() uut.bias_input_port(9) assert 9 == uut.bias_input_port() assert 0 == uut.get_status() -def test_bias_mode(uut) -> None: +def test_bias_mode(uut: KeysightB220X) -> None: uut.bias_mode(True) assert uut.bias_mode() assert 0 == uut.get_status() -def test_gnd_disable_all_outputs(uut) -> None: +def test_gnd_disable_all_outputs(uut: KeysightB220X) -> None: uut.gnd_disable_all_outputs() assert 0 == uut.get_status() -def test_gnd_disable_output(uut) -> None: +def test_gnd_disable_output(uut: KeysightB220X) -> None: uut.gnd_disable_output(1) assert 0 == uut.get_status() -def test_gnd_enable_all_outputs(uut) -> None: +def test_gnd_enable_all_outputs(uut: KeysightB220X) -> None: uut.gnd_enable_all_outputs() assert 0 == uut.get_status() -def test_gnd_enable_output(uut) -> None: +def test_gnd_enable_output(uut: KeysightB220X) -> None: uut.gnd_enable_output(1) assert 0 == uut.get_status() -def test_gnd_input_port(uut) -> None: +def test_gnd_input_port(uut: KeysightB220X) -> None: assert 12 == uut.gnd_input_port() uut.gnd_input_port(5) assert 5 == uut.gnd_input_port() assert 0 == uut.get_status() -def test_gnd_mode(uut) -> None: +def test_gnd_mode(uut: KeysightB220X) -> None: assert not uut.gnd_mode() uut.gnd_mode(True) assert uut.gnd_mode() assert 0 == uut.get_status() -def test_unused_inputs(uut) -> None: +def test_unused_inputs(uut: KeysightB220X) -> None: uut.unused_inputs() assert 0 == uut.get_status() @@ -225,14 +227,14 @@ def test_unused_inputs(uut) -> None: assert [5, 6, 7, 8] == uut.unused_inputs() -def test_couple_mode(uut) -> None: +def test_couple_mode(uut: KeysightB220X) -> None: assert not uut.couple_mode() uut.couple_mode(True) assert uut.couple_mode() assert 0 == uut.get_status() -def test_couple_ports(uut) -> None: +def test_couple_ports(uut: KeysightB220X) -> None: assert not uut.couple_ports() assert 0 == uut.get_status() @@ -250,12 +252,12 @@ def test_couple_ports(uut) -> None: uut.couple_ports([2, 3]) -def test_couple_port_autodetect(uut) -> None: +def test_couple_port_autodetect(uut: KeysightB220X) -> None: uut.couple_port_autodetect() assert 0 == uut.get_status() -def test_get_error(uut) -> None: +def test_get_error(uut: KeysightB220X) -> None: uut.get_error() assert 0 == uut.get_status() diff --git a/tests/drivers/test_keysight_e4980a.py b/tests/drivers/test_keysight_e4980a.py index 5b0ae9d3db1..7375171a977 100644 --- a/tests/drivers/test_keysight_e4980a.py +++ b/tests/drivers/test_keysight_e4980a.py @@ -1,12 +1,16 @@ import re +from typing import TYPE_CHECKING import pytest import qcodes.instrument_drivers.Keysight.keysight_e4980a as E4980A +if TYPE_CHECKING: + from collections.abc import Generator + @pytest.fixture(name="driver") -def _make_driver(): +def _make_driver() -> "Generator[E4980A.KeysightE4980A, None, None]": instr = E4980A.KeysightE4980A( "E4980A", address="GPIB::1::INSTR", pyvisa_sim_file="Keysight_E4980A.yaml" ) @@ -14,7 +18,7 @@ def _make_driver(): instr.close() -def test_idn(driver) -> None: +def test_idn(driver: E4980A.KeysightE4980A) -> None: assert { "firmware": "A.02.10", "model": "E4980A", @@ -23,7 +27,9 @@ def test_idn(driver) -> None: } == driver.IDN() -def test_raise_error_for_volt_level_query_when_signal_set_as_current(driver) -> None: +def test_raise_error_for_volt_level_query_when_signal_set_as_current( + driver: E4980A.KeysightE4980A, +) -> None: driver.current_level(0.01) msg = re.escape( "Cannot get voltage level as signal is set with current level parameter." @@ -32,12 +38,12 @@ def test_raise_error_for_volt_level_query_when_signal_set_as_current(driver) -> driver.voltage_level() -def test_voltage_level_set_method(driver) -> None: +def test_voltage_level_set_method(driver: E4980A.KeysightE4980A) -> None: driver.voltage_level(3) assert driver.voltage_level() == 3 -def test_signal_mode_parameter(driver) -> None: +def test_signal_mode_parameter(driver: E4980A.KeysightE4980A) -> None: driver.voltage_level(2) assert driver.signal_mode() == "Voltage" @@ -45,7 +51,9 @@ def test_signal_mode_parameter(driver) -> None: assert driver.signal_mode() == "Current" -def test_raise_error_for_curr_level_query_when_signal_set_as_voltage(driver) -> None: +def test_raise_error_for_curr_level_query_when_signal_set_as_voltage( + driver: E4980A.KeysightE4980A, +) -> None: driver.voltage_level(1) msg = re.escape( "Cannot get current level as signal is set with voltage level parameter." @@ -54,6 +62,6 @@ def test_raise_error_for_curr_level_query_when_signal_set_as_voltage(driver) -> driver.current_level() -def test_current_level_set_method(driver) -> None: +def test_current_level_set_method(driver: E4980A.KeysightE4980A) -> None: driver.current_level(0.003) assert driver.current_level() == 0.003 diff --git a/tests/drivers/test_keysight_n6705b.py b/tests/drivers/test_keysight_n6705b.py index f0e437fc60a..9ebc17f2834 100644 --- a/tests/drivers/test_keysight_n6705b.py +++ b/tests/drivers/test_keysight_n6705b.py @@ -1,10 +1,15 @@ +from typing import TYPE_CHECKING + import pytest import qcodes.instrument_drivers.Keysight.Keysight_N6705B as N6705B_module +if TYPE_CHECKING: + from collections.abc import Generator + @pytest.fixture(scope="function", name="driver") -def _make_driver(): +def _make_driver() -> "Generator[N6705B_module.KeysightN6705B, None, None]": driver = N6705B_module.KeysightN6705B( "N6705B", address="GPIB::1::INSTR", pyvisa_sim_file="Keysight_N6705B.yaml" ) @@ -12,7 +17,7 @@ def _make_driver(): driver.close() -def test_idn(driver) -> None: +def test_idn(driver: N6705B_module.KeysightN6705B) -> None: assert { "firmware": "D.01.08", "model": "N6705B", @@ -21,7 +26,7 @@ def test_idn(driver) -> None: } == driver.IDN() -def test_channels(driver) -> None: +def test_channels(driver: N6705B_module.KeysightN6705B) -> None: # Ensure each channel got instantiated assert len(driver.channels) == 4 for i, ch in enumerate(driver.channels): diff --git a/tests/drivers/test_keysight_n9030b.py b/tests/drivers/test_keysight_n9030b.py index 87a926d64cc..466d645017f 100644 --- a/tests/drivers/test_keysight_n9030b.py +++ b/tests/drivers/test_keysight_n9030b.py @@ -1,3 +1,5 @@ +from typing import TYPE_CHECKING + import pytest from qcodes.instrument_drivers.Keysight.Keysight_N9030B import ( @@ -6,9 +8,12 @@ KeysightN9030BSpectrumAnalyzerMode, ) +if TYPE_CHECKING: + from collections.abc import Generator + @pytest.fixture(name="driver") -def _make_driver(): +def _make_driver() -> "Generator[KeysightN9030B, None, None]": driver = KeysightN9030B( "n9030B_sim", address="GPIB::1::INSTR", pyvisa_sim_file="Keysight_N9030B.yaml" ) @@ -17,16 +22,20 @@ def _make_driver(): @pytest.fixture(name="sa") -def _activate_swept_sa_measurement(driver): +def _activate_swept_sa_measurement( + driver: KeysightN9030B, +) -> "Generator[KeysightN9030BSpectrumAnalyzerMode, None, None]": yield driver.sa @pytest.fixture(name="pn") -def _activate_log_plot_measurement(driver): +def _activate_log_plot_measurement( + driver: KeysightN9030B, +) -> "Generator[KeysightN9030BPhaseNoiseMode, None, None]": yield driver.pn -def test_idn(driver) -> None: +def test_idn(driver: KeysightN9030B) -> None: assert { "firmware": "0.1", "model": "N9030B", @@ -35,7 +44,7 @@ def test_idn(driver) -> None: } == driver.IDN() -def test_swept_sa_setup(sa) -> None: +def test_swept_sa_setup(sa: KeysightN9030BSpectrumAnalyzerMode) -> None: assert isinstance(sa, KeysightN9030BSpectrumAnalyzerMode) sa.setup_swept_sa_sweep(123, 11e3, 501) @@ -47,7 +56,7 @@ def test_swept_sa_setup(sa) -> None: assert sa.npts() == 501 -def test_log_plot_setup(pn) -> None: +def test_log_plot_setup(pn: KeysightN9030BPhaseNoiseMode) -> None: assert isinstance(pn, KeysightN9030BPhaseNoiseMode) pn.setup_log_plot_sweep(1000, 1e7, 10001) diff --git a/tests/drivers/test_lakeshore_325.py b/tests/drivers/test_lakeshore_325.py index 1eec724ff91..f683421a52f 100644 --- a/tests/drivers/test_lakeshore_325.py +++ b/tests/drivers/test_lakeshore_325.py @@ -17,14 +17,17 @@ sorted # type: ignore[arg-type] ) ) -def test_decode_sensor_status(list_of_codes) -> None: +def test_decode_sensor_status(list_of_codes: list[LakeshoreModel325Status]) -> None: """ The sensor status is one of the status codes, or a sum thereof. Multiple status are possible as they are not necessarily mutually exclusive. The static method 'decode_sensor_status' in the Model_325_Sensor class can decode the status into status code(s). """ - codes = [code.name.replace("_", " ") for code in list_of_codes[::-1]] + codes = [] + for code in list_of_codes[::-1]: + assert code.name is not None + codes.append(code.name.replace("_", " ")) codes_message = ", ".join(codes) sum_of_codes = int(sum(list_of_codes)) status_messages = LakeshoreModel325Sensor.decode_sensor_status(sum_of_codes) diff --git a/tests/drivers/test_lakeshore_335.py b/tests/drivers/test_lakeshore_335.py index ab3c866780a..039f0a442ac 100644 --- a/tests/drivers/test_lakeshore_335.py +++ b/tests/drivers/test_lakeshore_335.py @@ -1,5 +1,6 @@ import logging import time +from typing import Any from qcodes.instrument import InstrumentBase from qcodes.instrument_drivers.Lakeshore import LakeshoreModel335 @@ -17,7 +18,7 @@ class LakeshoreModel335Mock(MockVisaInstrument, LakeshoreModel335): - def __init__(self, *args, **kwargs) -> None: + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) # initial values @@ -67,11 +68,11 @@ def __init__(self, *args, **kwargs) -> None: self.simulate_heating = False self.start_heating_time = time.perf_counter() - def start_heating(self): + def start_heating(self) -> None: self.start_heating_time = time.perf_counter() self.simulate_heating = True - def get_t_when_heating(self): + def get_t_when_heating(self) -> float: """ Simply define a fixed setpoint of 4 k for now """ @@ -81,7 +82,7 @@ def get_t_when_heating(self): return max(4, 7 - delta) @query("KRDG?") - def temperature(self, output): + def temperature(self, output: str) -> float | str: chan = self.channel_mock[output] if self.simulate_heating: return self.get_t_when_heating() @@ -89,7 +90,7 @@ def temperature(self, output): @instrument_fixture(scope="function", name="lakeshore_335") -def _make_lakeshore_335(): +def _make_lakeshore_335() -> LakeshoreModel335Mock: return LakeshoreModel335Mock( "lakeshore_335_fixture", "GPIB::2::INSTR", @@ -98,7 +99,7 @@ def _make_lakeshore_335(): ) -def test_pid_set(lakeshore_335) -> None: +def test_pid_set(lakeshore_335: LakeshoreModel335Mock) -> None: ls = lakeshore_335 P, I, D = 1, 2, 3 # noqa: E741 # Only current source outputs/heaters have PID parameters, @@ -111,7 +112,7 @@ def test_pid_set(lakeshore_335) -> None: assert (h.P(), h.I(), h.D()) == (P, I, D) -def test_output_mode(lakeshore_335) -> None: +def test_output_mode(lakeshore_335: LakeshoreModel335Mock) -> None: ls = lakeshore_335 mode = "off" input_channel = "A" @@ -126,7 +127,7 @@ def test_output_mode(lakeshore_335) -> None: assert h.powerup_enable() == powerup_enable -def test_range(lakeshore_335) -> None: +def test_range(lakeshore_335: LakeshoreModel335Mock) -> None: ls = lakeshore_335 output_range = "medium" outputs = [getattr(ls, f"output_{n}") for n in range(1, 3)] @@ -135,7 +136,7 @@ def test_range(lakeshore_335) -> None: assert h.output_range() == output_range -def test_tlimit(lakeshore_335) -> None: +def test_tlimit(lakeshore_335: LakeshoreModel335Mock) -> None: ls = lakeshore_335 tlimit = 5.1 for ch in ls.channels: @@ -143,7 +144,7 @@ def test_tlimit(lakeshore_335) -> None: assert ch.t_limit() == tlimit -def test_setpoint(lakeshore_335) -> None: +def test_setpoint(lakeshore_335: LakeshoreModel335Mock) -> None: ls = lakeshore_335 setpoint = 5.1 outputs = [getattr(ls, f"output_{n}") for n in range(1, 3)] @@ -152,7 +153,7 @@ def test_setpoint(lakeshore_335) -> None: assert h.setpoint() == setpoint -def test_select_range_limits(lakeshore_335) -> None: +def test_select_range_limits(lakeshore_335: LakeshoreModel335Mock) -> None: h = lakeshore_335.output_1 ranges = [1, 2, 3] h.range_limits(ranges) @@ -166,14 +167,16 @@ def test_select_range_limits(lakeshore_335) -> None: assert h.output_range() == h.INVERSE_RANGES[len(ranges)] -def test_set_and_wait_unit_setpoint_reached(lakeshore_335) -> None: +def test_set_and_wait_unit_setpoint_reached( + lakeshore_335: LakeshoreModel335Mock, +) -> None: ls = lakeshore_335 ls.output_1.setpoint(4) ls.start_heating() ls.output_1.wait_until_set_point_reached() -def test_blocking_t(lakeshore_335) -> None: +def test_blocking_t(lakeshore_335: LakeshoreModel335Mock) -> None: ls = lakeshore_335 h = ls.output_1 ranges = [1.2, 2.4, 3.1] diff --git a/tests/drivers/test_lakeshore_336.py b/tests/drivers/test_lakeshore_336.py index 62486b1b47b..5f1dc0a7997 100644 --- a/tests/drivers/test_lakeshore_336.py +++ b/tests/drivers/test_lakeshore_336.py @@ -1,5 +1,6 @@ import logging import time +from typing import Any import pytest @@ -19,7 +20,7 @@ class LakeshoreModel336Mock(MockVisaInstrument, LakeshoreModel336): - def __init__(self, *args, **kwargs) -> None: + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) # initial values @@ -89,11 +90,11 @@ def __init__(self, *args, **kwargs) -> None: self.simulate_heating = False self.start_heating_time = time.perf_counter() - def start_heating(self): + def start_heating(self) -> None: self.start_heating_time = time.perf_counter() self.simulate_heating = True - def get_t_when_heating(self): + def get_t_when_heating(self) -> float: """ Simply define a fixed setpoint of 4 k for now """ @@ -103,7 +104,7 @@ def get_t_when_heating(self): return max(4, 7 - delta) @query("KRDG?") - def temperature(self, output): + def temperature(self, output: str) -> float | str: chan = self.channel_mock[output] if self.simulate_heating: return self.get_t_when_heating() @@ -111,7 +112,7 @@ def temperature(self, output): @instrument_fixture(scope="function", name="lakeshore_336") -def _make_lakeshore_336(): +def _make_lakeshore_336() -> LakeshoreModel336Mock: return LakeshoreModel336Mock( "lakeshore_336_fixture", "GPIB::2::INSTR", @@ -120,7 +121,7 @@ def _make_lakeshore_336(): ) -def test_pid_set(lakeshore_336) -> None: +def test_pid_set(lakeshore_336: LakeshoreModel336Mock) -> None: ls = lakeshore_336 P, I, D = 1, 2, 3 # noqa: E741 # Only current source outputs/heaters have PID parameters, @@ -136,7 +137,9 @@ def test_pid_set(lakeshore_336) -> None: @pytest.mark.parametrize("output_num", [1, 2, 3, 4]) @pytest.mark.parametrize("mode", ["off", "closed_loop", "zone", "open_loop"]) @pytest.mark.parametrize("input_channel", ["A", "B", "C", "D"]) -def test_output_mode(lakeshore_336, output_num, mode, input_channel) -> None: +def test_output_mode( + lakeshore_336: LakeshoreModel336Mock, output_num: int, mode: str, input_channel: str +) -> None: ls = lakeshore_336 mode = "off" h = getattr(ls, f"output_{output_num}") @@ -148,7 +151,7 @@ def test_output_mode(lakeshore_336, output_num, mode, input_channel) -> None: assert h.powerup_enable() -def test_range(lakeshore_336) -> None: +def test_range(lakeshore_336: LakeshoreModel336Mock) -> None: ls = lakeshore_336 output_range = "medium" outputs = [getattr(ls, f"output_{n}") for n in range(1, 5)] @@ -157,7 +160,7 @@ def test_range(lakeshore_336) -> None: assert h.output_range() == output_range -def test_tlimit(lakeshore_336) -> None: +def test_tlimit(lakeshore_336: LakeshoreModel336Mock) -> None: ls = lakeshore_336 tlimit = 5.1 for ch in ls.channels: @@ -165,7 +168,7 @@ def test_tlimit(lakeshore_336) -> None: assert ch.t_limit() == tlimit -def test_setpoint(lakeshore_336) -> None: +def test_setpoint(lakeshore_336: LakeshoreModel336Mock) -> None: ls = lakeshore_336 setpoint = 5.1 outputs = [getattr(ls, f"output_{n}") for n in range(1, 5)] @@ -174,7 +177,7 @@ def test_setpoint(lakeshore_336) -> None: assert h.setpoint() == setpoint -def test_curve_parameters(lakeshore_336) -> None: +def test_curve_parameters(lakeshore_336: LakeshoreModel336Mock) -> None: # The curve numbers are assigned in the simulation pyvisa sim # YAML file for each sensor/channel, and properties of the # curves also include curve number in them to help testing @@ -187,7 +190,7 @@ def test_curve_parameters(lakeshore_336) -> None: assert ch.curve_coefficient() == "negative" -def test_select_range_limits(lakeshore_336) -> None: +def test_select_range_limits(lakeshore_336: LakeshoreModel336Mock) -> None: h = lakeshore_336.output_1 ranges = [1, 2, 3] h.range_limits(ranges) @@ -201,14 +204,16 @@ def test_select_range_limits(lakeshore_336) -> None: assert h.output_range() == h.INVERSE_RANGES[len(ranges)] -def test_set_and_wait_unit_setpoint_reached(lakeshore_336) -> None: +def test_set_and_wait_unit_setpoint_reached( + lakeshore_336: LakeshoreModel336Mock, +) -> None: ls = lakeshore_336 ls.output_1.setpoint(4) ls.start_heating() ls.output_1.wait_until_set_point_reached() -def test_blocking_t(lakeshore_336) -> None: +def test_blocking_t(lakeshore_336: LakeshoreModel336Mock) -> None: ls = lakeshore_336 h = ls.output_1 ranges = [1.2, 2.4, 3.1] diff --git a/tests/drivers/test_lakeshore_372.py b/tests/drivers/test_lakeshore_372.py index 76ff8497f22..dca80adbec8 100644 --- a/tests/drivers/test_lakeshore_372.py +++ b/tests/drivers/test_lakeshore_372.py @@ -8,7 +8,7 @@ import pytest -from qcodes.instrument import InstrumentBase +from qcodes.instrument import Instrument, InstrumentBase from qcodes.instrument_drivers.Lakeshore import LakeshoreModel372 from qcodes.instrument_drivers.Lakeshore.lakeshore_base import ( LakeshoreBaseSensorChannel, @@ -17,7 +17,7 @@ from qcodes.utils import QCoDeSDeprecationWarning if TYPE_CHECKING: - from collections.abc import Callable + from collections.abc import Callable, Generator log = logging.getLogger(__name__) @@ -30,7 +30,7 @@ class MockVisaInstrument: instrument. """ - def __init__(self, *args, **kwargs) -> None: + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) self.visa_log = get_instrument_logger(self, VISA_LOGGER) # type: ignore[arg-type] @@ -62,7 +62,7 @@ def __init__(self, *args, **kwargs) -> None: with suppress(AttributeError): self.cmds[f.command_name] = f - def write_raw(self, cmd) -> None: + def write_raw(self, cmd: str) -> None: cmd_parts = cmd.split(" ") cmd_str = cmd_parts[0].upper() if cmd_str in self.cmds: @@ -72,7 +72,7 @@ def write_raw(self, cmd) -> None: else: super().write_raw(cmd) # type: ignore[misc] - def ask_raw(self, cmd) -> Any: + def ask_raw(self, cmd: str) -> Any: query_parts = cmd.split(" ") query_str = query_parts[0].upper() if query_str in self.queries: @@ -104,14 +104,14 @@ def wrapper(func: Callable[P, T]) -> Callable[P, T]: class DictClass: - def __init__(self, **kwargs): + def __init__(self, **kwargs: Any) -> None: # https://stackoverflow.com/questions/16237659/python-how-to-implement-getattr super().__setattr__("_attrs", kwargs) for kwarg, value in kwargs.items(): self._attrs[kwarg] = value - def __getattr__(self, attr): + def __getattr__(self, attr: str) -> Any: try: return self._attrs[attr] except KeyError as e: @@ -122,7 +122,7 @@ def __setattr__(self, name: str, value: Any) -> None: class LakeshoreModel372Mock(MockVisaInstrument, LakeshoreModel372): - def __init__(self, *args, **kwargs) -> None: + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) # initial values @@ -190,11 +190,11 @@ def __init__(self, *args, **kwargs) -> None: self.simulate_heating = False self.start_heating_time = time.perf_counter() - def start_heating(self): + def start_heating(self) -> None: self.start_heating_time = time.perf_counter() self.simulate_heating = True - def get_t_when_heating(self): + def get_t_when_heating(self) -> float: """ Simply define a fixed setpoint of 4 k for now """ @@ -204,7 +204,7 @@ def get_t_when_heating(self): return max(4, 7 - delta) @query("KRDG?") - def temperature(self, output): + def temperature(self, output: str) -> float | str: chan = self.channel_mock[output] if self.simulate_heating: return self.get_t_when_heating() @@ -213,11 +213,11 @@ def temperature(self, output): def instrument_fixture( scope: Literal["session", "package", "module", "class", "function"] = "function", - name=None, -): - def wrapper(func): + name: str | None = None, +) -> Callable[[Callable[[], Instrument]], Callable[..., object]]: + def wrapper(func: Callable[[], Instrument]) -> Callable[..., object]: @pytest.fixture(scope=scope, name=name) - def wrapped_fixture(): + def wrapped_fixture() -> Generator[Instrument, None, None]: inst = func() try: yield inst @@ -230,7 +230,7 @@ def wrapped_fixture(): @instrument_fixture(scope="function") -def lakeshore_372(): +def lakeshore_372() -> LakeshoreModel372Mock: return LakeshoreModel372Mock( "lakeshore_372_fixture", "GPIB::3::INSTR", @@ -239,7 +239,7 @@ def lakeshore_372(): ) -def test_pid_set(lakeshore_372) -> None: +def test_pid_set(lakeshore_372: LakeshoreModel372Mock) -> None: ls = lakeshore_372 P, I, D = 1, 2, 3 # noqa: E741 for h in (ls.warmup_heater, ls.analog_heater, ls.sample_heater): @@ -249,7 +249,7 @@ def test_pid_set(lakeshore_372) -> None: assert (h.P(), h.I(), h.D()) == (P, I, D) -def test_output_mode(lakeshore_372) -> None: +def test_output_mode(lakeshore_372: LakeshoreModel372Mock) -> None: ls = lakeshore_372 mode = "off" input_channel = 1 @@ -272,7 +272,7 @@ def test_output_mode(lakeshore_372) -> None: assert h.delay() == delay -def test_range(lakeshore_372) -> None: +def test_range(lakeshore_372: LakeshoreModel372Mock) -> None: ls = lakeshore_372 output_range = "10mA" for h in (ls.warmup_heater, ls.analog_heater, ls.sample_heater): @@ -280,7 +280,7 @@ def test_range(lakeshore_372) -> None: assert h.output_range() == output_range -def test_tlimit(lakeshore_372) -> None: +def test_tlimit(lakeshore_372: LakeshoreModel372Mock) -> None: ls = lakeshore_372 tlimit = 5.1 for ch in ls.channels: @@ -288,7 +288,7 @@ def test_tlimit(lakeshore_372) -> None: assert ch.t_limit() == tlimit -def test_setpoint(lakeshore_372) -> None: +def test_setpoint(lakeshore_372: LakeshoreModel372Mock) -> None: ls = lakeshore_372 setpoint = 5.1 for h in (ls.warmup_heater, ls.analog_heater, ls.sample_heater): @@ -296,7 +296,7 @@ def test_setpoint(lakeshore_372) -> None: assert h.setpoint() == setpoint -def test_select_range_limits(lakeshore_372) -> None: +def test_select_range_limits(lakeshore_372: LakeshoreModel372Mock) -> None: h = lakeshore_372.sample_heater ranges = list(range(1, 9)) h.range_limits(ranges) @@ -309,14 +309,16 @@ def test_select_range_limits(lakeshore_372) -> None: assert h.output_range() == h.INVERSE_RANGES[len(ranges)] -def test_set_and_wait_unit_setpoint_reached(lakeshore_372) -> None: +def test_set_and_wait_unit_setpoint_reached( + lakeshore_372: LakeshoreModel372Mock, +) -> None: ls = lakeshore_372 ls.sample_heater.setpoint(4) ls.start_heating() ls.sample_heater.wait_until_set_point_reached() -def test_blocking_t(lakeshore_372) -> None: +def test_blocking_t(lakeshore_372: LakeshoreModel372Mock) -> None: ls = lakeshore_372 h = lakeshore_372.sample_heater ranges = list(range(1, 9)) diff --git a/tests/drivers/test_lakeshore_file_parser.py b/tests/drivers/test_lakeshore_file_parser.py index da94c8706f7..4bcf94111ed 100644 --- a/tests/drivers/test_lakeshore_file_parser.py +++ b/tests/drivers/test_lakeshore_file_parser.py @@ -1,4 +1,5 @@ import io +from typing import TYPE_CHECKING import pytest @@ -7,6 +8,9 @@ _read_curve_file, ) +if TYPE_CHECKING: + from collections.abc import Generator + curve_file_content = """ \ Sensor Model: CX-1050-SD-HT-1.4L Serial Number: X116121 @@ -27,11 +31,11 @@ @pytest.fixture(scope="function") -def curve_file(): +def curve_file() -> "Generator[io.StringIO, None, None]": yield io.StringIO(curve_file_content) -def test_file_parser(curve_file) -> None: +def test_file_parser(curve_file: io.StringIO) -> None: file_data = _read_curve_file(curve_file) assert list(file_data.keys()) == ["metadata", "data"] @@ -52,7 +56,7 @@ def test_file_parser(curve_file) -> None: } -def test_sanitise_data(curve_file) -> None: +def test_sanitise_data(curve_file: io.StringIO) -> None: file_data = _read_curve_file(curve_file) data_dict = _get_sanitize_data(file_data) diff --git a/tests/drivers/test_mercuryips.py b/tests/drivers/test_mercuryips.py index d6e19e6df29..e63ecec31c3 100644 --- a/tests/drivers/test_mercuryips.py +++ b/tests/drivers/test_mercuryips.py @@ -1,4 +1,5 @@ import logging +from typing import TYPE_CHECKING import hypothesis.strategies as hst import numpy as np @@ -9,9 +10,12 @@ from qcodes.instrument_drivers.oxford.MercuryiPS_VISA import OxfordMercuryiPS from qcodes.math_utils.field_vector import FieldVector +if TYPE_CHECKING: + from collections.abc import Generator, Iterable + @pytest.fixture(scope="function") -def driver(): +def driver() -> "Generator[OxfordMercuryiPS, None, None]": mips = OxfordMercuryiPS( "mips", address="GPIB::1::INSTR", pyvisa_sim_file="MercuryiPS.yaml" ) @@ -20,12 +24,12 @@ def driver(): @pytest.fixture(scope="function") -def driver_spher_lim(): - def spherical_limits(x, y, z): +def driver_spher_lim() -> "Generator[OxfordMercuryiPS, None, None]": + def spherical_limits(x: float, y: float, z: float) -> bool: """ Checks that the field is inside a sphere of radius 2 """ - return np.sqrt(x**2 + y**2 + z**2) <= 2 + return bool(np.sqrt(x**2 + y**2 + z**2) <= 2) mips_sl = OxfordMercuryiPS( "mips_sl", @@ -39,12 +43,12 @@ def spherical_limits(x, y, z): @pytest.fixture(scope="function") -def driver_cyl_lim(): - def cylindrical_limits(x, y, z): +def driver_cyl_lim() -> "Generator[OxfordMercuryiPS, None, None]": + def cylindrical_limits(x: float, y: float, z: float) -> bool: """ Checks that the field is inside a particular cylinder """ - rho_check = np.sqrt(x**2 + y**2) <= 2 + rho_check = bool(np.sqrt(x**2 + y**2) <= 2) z_check = z < 3 and z > -1 return rho_check and z_check @@ -60,11 +64,11 @@ def cylindrical_limits(x, y, z): mips_cl.close() -def test_idn(driver) -> None: +def test_idn(driver: OxfordMercuryiPS) -> None: assert driver.IDN()["model"] == "SIMULATED MERCURY iPS" -def test_simple_setting(driver) -> None: +def test_simple_setting(driver: OxfordMercuryiPS) -> None: """ Some very simple setting of parameters. Mainly just to sanity-check the pyvisa-sim setup. @@ -74,13 +78,13 @@ def test_simple_setting(driver) -> None: assert driver.GRPX.field_target() == 0.1 -def test_vector_setting(driver) -> None: +def test_vector_setting(driver: OxfordMercuryiPS) -> None: assert driver.field_target().distance(FieldVector(0, 0, 0)) <= 1e-8 driver.field_target(FieldVector(r=0.1, theta=0, phi=0)) assert driver.field_target().distance(FieldVector(r=0.1, theta=0, phi=0)) <= 1e-8 -def test_vector_ramp_rate(driver) -> None: +def test_vector_ramp_rate(driver: OxfordMercuryiPS) -> None: driver.field_ramp_rate(FieldVector(0.1, 0.1, 0.1)) assert driver.field_ramp_rate().distance(FieldVector(0.1, 0.1, 0.1)) <= 1e-8 @@ -102,7 +106,13 @@ def test_wrong_field_limit_raises() -> None: y=hst.floats(min_value=-3, max_value=3), z=hst.floats(min_value=-3, max_value=3), ) -def test_field_limits(x, y, z, driver_spher_lim, driver_cyl_lim) -> None: +def test_field_limits( + x: float, + y: float, + z: float, + driver_spher_lim: OxfordMercuryiPS, + driver_cyl_lim: OxfordMercuryiPS, +) -> None: """ Try with a few different field_limits functions and see if we get no-go exceptions when we expect them @@ -129,7 +139,7 @@ def test_field_limits(x, y, z, driver_spher_lim, driver_cyl_lim) -> None: mip.z_target(z) -def get_ramp_order(caplog_records): +def get_ramp_order(caplog_records: "Iterable[logging.LogRecord]") -> list[str]: """ Helper function used in test_ramp_safely """ @@ -148,7 +158,9 @@ def get_ramp_order(caplog_records): y=hst.floats(min_value=-3, max_value=3), z=hst.floats(min_value=-3, max_value=3), ) -def test_ramp_safely(driver, x, y, z, caplog: LogCaptureFixture) -> None: +def test_ramp_safely( + driver: OxfordMercuryiPS, x: float, y: float, z: float, caplog: LogCaptureFixture +) -> None: """ Test that we get the first-down-then-up order right """ diff --git a/tests/drivers/test_rigol_ds1074z.py b/tests/drivers/test_rigol_ds1074z.py index 1064ce5c43e..9790508488e 100644 --- a/tests/drivers/test_rigol_ds1074z.py +++ b/tests/drivers/test_rigol_ds1074z.py @@ -1,10 +1,15 @@ +from typing import TYPE_CHECKING + import pytest from qcodes.instrument_drivers.rigol import RigolDS1074Z +if TYPE_CHECKING: + from collections.abc import Generator + @pytest.fixture(scope="function") -def driver(): +def driver() -> "Generator[RigolDS1074Z, None, None]": rigol = RigolDS1074Z( "rigol", address="GPIB::1::INSTR", @@ -16,7 +21,7 @@ def driver(): rigol.close() -def test_initialize(driver) -> None: +def test_initialize(driver: RigolDS1074Z) -> None: """ Test that simple initialisation works """ @@ -24,32 +29,32 @@ def test_initialize(driver) -> None: assert idn_dict["vendor"] == "QCoDeS" -def test_gets_correct_waveform_xorigin(driver) -> None: +def test_gets_correct_waveform_xorigin(driver: RigolDS1074Z) -> None: assert driver.waveform_xorigin() == 0 -def test_gets_correct_waveform_xincrem(driver) -> None: +def test_gets_correct_waveform_xincrem(driver: RigolDS1074Z) -> None: assert driver.waveform_xincrem() == 0.1 -def test_sets_correct_waveform_npoints(driver) -> None: +def test_sets_correct_waveform_npoints(driver: RigolDS1074Z) -> None: driver.waveform_npoints(1000) assert driver.waveform_npoints() == 1000 -def test_gets_correct_waveform_yorigin(driver) -> None: +def test_gets_correct_waveform_yorigin(driver: RigolDS1074Z) -> None: assert driver.waveform_yorigin() == 0 -def test_gets_correct_waveform_yincrem(driver) -> None: +def test_gets_correct_waveform_yincrem(driver: RigolDS1074Z) -> None: assert driver.waveform_yincrem() == 0.1 -def test_gets_correct_waveform_yref(driver) -> None: +def test_gets_correct_waveform_yref(driver: RigolDS1074Z) -> None: assert driver.waveform_yref() == 0 -def test_sets_correct_trigger_mode(driver) -> None: +def test_sets_correct_trigger_mode(driver: RigolDS1074Z) -> None: driver.trigger_mode("edge") assert driver.trigger_mode() == "edge" driver.trigger_mode("pattern") @@ -60,7 +65,7 @@ def test_sets_correct_trigger_mode(driver) -> None: assert driver.trigger_mode() == "video" -def test_get_data_source(driver) -> None: +def test_get_data_source(driver: RigolDS1074Z) -> None: driver.data_source("ch1") assert driver.data_source() == "ch1" driver.data_source("ch2") diff --git a/tests/drivers/test_rs_sgs100a.py b/tests/drivers/test_rs_sgs100a.py index 76382ec1c23..9361c709859 100644 --- a/tests/drivers/test_rs_sgs100a.py +++ b/tests/drivers/test_rs_sgs100a.py @@ -6,11 +6,11 @@ from qcodes.instrument_drivers.rohde_schwarz.SGS100A import RohdeSchwarzSGS100A if TYPE_CHECKING: - from collections.abc import Sequence + from collections.abc import Generator, Sequence @pytest.fixture(scope="function", name="sg") -def _make_sg(): +def _make_sg() -> "Generator[RohdeSchwarzSGS100A, None, None]": """ Create a RohdeSchwarz SGS100A instrument """ @@ -21,7 +21,9 @@ def _make_sg(): driver.close() -def verify_property(sg, param_name, vals: "Sequence[Any]"): +def verify_property( + sg: RohdeSchwarzSGS100A, param_name: str, vals: "Sequence[Any]" +) -> None: param = getattr(sg, param_name) for val in vals: param(val) @@ -32,74 +34,74 @@ def verify_property(sg, param_name, vals: "Sequence[Any]"): assert new_val == val -def test_frequency(sg) -> None: +def test_frequency(sg: RohdeSchwarzSGS100A) -> None: verify_property(sg, "frequency", [1e6, 2e6, 3e9, 20e9]) -def test_phase(sg) -> None: +def test_phase(sg: RohdeSchwarzSGS100A) -> None: verify_property(sg, "phase", [0, 45, 90, 180, 270, 359, 360]) -def test_power(sg) -> None: +def test_power(sg: RohdeSchwarzSGS100A) -> None: verify_property(sg, "power", [-120, -50, 0, 10, 25]) -def test_status(sg) -> None: +def test_status(sg: RohdeSchwarzSGS100A) -> None: verify_property(sg, "status", [True, False]) -def test_IQ_state(sg) -> None: +def test_IQ_state(sg: RohdeSchwarzSGS100A) -> None: verify_property(sg, "IQ_state", [True, False]) -def test_pulsemod_state(sg) -> None: +def test_pulsemod_state(sg: RohdeSchwarzSGS100A) -> None: verify_property(sg, "pulsemod_state", [True, False]) -def test_pulsemod_source(sg) -> None: +def test_pulsemod_source(sg: RohdeSchwarzSGS100A) -> None: verify_property(sg, "pulsemod_source", ["INT", "EXT"]) -def test_ref_osc_source(sg) -> None: +def test_ref_osc_source(sg: RohdeSchwarzSGS100A) -> None: verify_property(sg, "ref_osc_source", ["INT", "EXT"]) -def test_LO_source(sg) -> None: +def test_LO_source(sg: RohdeSchwarzSGS100A) -> None: verify_property(sg, "LO_source", ["INT", "EXT"]) -def test_ref_LO_out(sg) -> None: +def test_ref_LO_out(sg: RohdeSchwarzSGS100A) -> None: verify_property(sg, "ref_LO_out", ["REF", "LO", "OFF"]) -def test_ref_osc_output_freq(sg) -> None: +def test_ref_osc_output_freq(sg: RohdeSchwarzSGS100A) -> None: verify_property(sg, "ref_osc_output_freq", ["10MHz", "100MHz", "1000MHz"]) -def test_ref_osc_external_freq(sg) -> None: +def test_ref_osc_external_freq(sg: RohdeSchwarzSGS100A) -> None: verify_property(sg, "ref_osc_external_freq", ["10MHz", "100MHz", "1000MHz"]) -def test_IQ_impairments(sg) -> None: +def test_IQ_impairments(sg: RohdeSchwarzSGS100A) -> None: verify_property(sg, "IQ_impairments", [True, False]) -def test_I_offset(sg) -> None: +def test_I_offset(sg: RohdeSchwarzSGS100A) -> None: verify_property(sg, "I_offset", [-10, 0, 10]) -def test_Q_offset(sg) -> None: +def test_Q_offset(sg: RohdeSchwarzSGS100A) -> None: verify_property(sg, "Q_offset", [-10, 0, 10]) -def test_IQ_gain_imbalance(sg) -> None: +def test_IQ_gain_imbalance(sg: RohdeSchwarzSGS100A) -> None: verify_property(sg, "IQ_gain_imbalance", [-1, 0, 1]) -def test_IQ_angle(sg) -> None: +def test_IQ_angle(sg: RohdeSchwarzSGS100A) -> None: verify_property(sg, "IQ_angle", [-8, -4, 0, 4, 8]) -def test_operation_mode(sg) -> None: +def test_operation_mode(sg: RohdeSchwarzSGS100A) -> None: print(f"operation mode is set to {sg.operation_mode()}") verify_property(sg, "operation_mode", ["NORMal", "BBBYpass"]) diff --git a/tests/drivers/test_rto_1000.py b/tests/drivers/test_rto_1000.py index faa23b92201..4987a6dc4ee 100644 --- a/tests/drivers/test_rto_1000.py +++ b/tests/drivers/test_rto_1000.py @@ -1,10 +1,15 @@ +from typing import TYPE_CHECKING + import pytest from qcodes.instrument_drivers.rohde_schwarz.RTO1000 import RTO1000 +if TYPE_CHECKING: + from collections.abc import Generator + @pytest.fixture(scope="function") -def driver(): +def driver() -> "Generator[RTO1000, None, None]": rto_sim = RTO1000( "rto_sim", address="GPIB::1::INSTR", @@ -16,13 +21,13 @@ def driver(): rto_sim.close() -def test_init(driver) -> None: +def test_init(driver: RTO1000) -> None: idn_dict = driver.IDN() assert idn_dict["vendor"] == "QCoDeS" -def test_trigger_source_level(driver) -> None: +def test_trigger_source_level(driver: RTO1000) -> None: assert driver.trigger_source() == "CH1" assert driver.trigger_level() == 0 driver.trigger_level(1.0) diff --git a/tests/drivers/test_stahl.py b/tests/drivers/test_stahl.py index 3f43143adcd..e06a61ddd77 100644 --- a/tests/drivers/test_stahl.py +++ b/tests/drivers/test_stahl.py @@ -1,15 +1,19 @@ import logging +from typing import TYPE_CHECKING import pytest from pytest import LogCaptureFixture from qcodes.instrument_drivers.stahl import Stahl +if TYPE_CHECKING: + from collections.abc import Generator + LOG_NAME = "qcodes.instrument.instrument_base" @pytest.fixture(scope="function") -def stahl_instrument(): +def stahl_instrument() -> "Generator[Stahl, None, None]": inst = Stahl("Stahl", "ASRL3", pyvisa_sim_file="stahl.yaml") try: @@ -34,7 +38,7 @@ def test_parse_idn_string() -> None: Stahl.parse_idn_string("HS123 005 16 bla b") -def test_get_idn(stahl_instrument) -> None: +def test_get_idn(stahl_instrument: Stahl) -> None: """ Instrument attributes are set correctly after getting the IDN """ @@ -50,7 +54,7 @@ def test_get_idn(stahl_instrument) -> None: assert stahl_instrument.output_type == "bipolar" -def test_get_set_voltage(stahl_instrument, caplog: LogCaptureFixture) -> None: +def test_get_set_voltage(stahl_instrument: Stahl, caplog: LogCaptureFixture) -> None: """ Test that we can correctly get/set voltages """ @@ -66,7 +70,7 @@ def test_get_set_voltage(stahl_instrument, caplog: LogCaptureFixture) -> None: def test_get_set_voltage_assert_warning( - stahl_instrument, caplog: LogCaptureFixture + stahl_instrument: Stahl, caplog: LogCaptureFixture ) -> None: """ On channel 2 we have deliberately introduced an error in the @@ -80,7 +84,7 @@ def test_get_set_voltage_assert_warning( ) -def test_get_current(stahl_instrument) -> None: +def test_get_current(stahl_instrument: Stahl) -> None: """ Test that we can read currents and that the unit is in Ampere """ @@ -88,7 +92,7 @@ def test_get_current(stahl_instrument) -> None: assert stahl_instrument.channel[0].current.unit == "A" -def test_get_temperature(stahl_instrument) -> None: +def test_get_temperature(stahl_instrument: Stahl) -> None: """ Due to limitations in pyvisa-sim, we cannot test this. Line 191 of pyvisa-sim/component.py should read diff --git a/tests/drivers/test_tektronix_awg5208.py b/tests/drivers/test_tektronix_awg5208.py index e6b16a101ff..4a8b68d5bce 100644 --- a/tests/drivers/test_tektronix_awg5208.py +++ b/tests/drivers/test_tektronix_awg5208.py @@ -1,12 +1,16 @@ import os +from typing import TYPE_CHECKING import pytest from qcodes.instrument_drivers.tektronix.AWG5208 import TektronixAWG5208 +if TYPE_CHECKING: + from collections.abc import Generator + @pytest.fixture(scope="function") -def awg(): +def awg() -> "Generator[TektronixAWG5208, None, None]": awg_sim = TektronixAWG5208( "awg_sim", address="GPIB0::1::INSTR", pyvisa_sim_file="Tektronix_AWG5208.yaml" ) @@ -15,13 +19,13 @@ def awg(): awg_sim.close() -def test_init_awg(awg) -> None: +def test_init_awg(awg: TektronixAWG5208) -> None: idn_dict = awg.IDN() assert idn_dict["vendor"] == "QCoDeS" -def test_channel_resolution_docstring(awg) -> None: +def test_channel_resolution_docstring(awg: TektronixAWG5208) -> None: expected_docstring = ( "12 bit resolution allows for four " "markers, 13 bit resolution " diff --git a/tests/drivers/test_tektronix_awg70000a.py b/tests/drivers/test_tektronix_awg70000a.py index 99c955aefaf..6248fa50eba 100644 --- a/tests/drivers/test_tektronix_awg70000a.py +++ b/tests/drivers/test_tektronix_awg70000a.py @@ -3,6 +3,7 @@ import logging import zipfile from io import BytesIO, StringIO +from typing import TYPE_CHECKING, Any import hypothesis.strategies as hst import numpy as np @@ -18,6 +19,11 @@ TektronixAWG70002A, ) +if TYPE_CHECKING: + from collections.abc import Callable, Generator + + import numpy.typing as npt + def strip_outer_tags(sml: str) -> str: """ @@ -36,7 +42,7 @@ def strip_outer_tags(sml: str) -> str: @pytest.fixture(scope="function") -def awg2(): +def awg2() -> Generator[TektronixAWG70002A, None, None]: awg2_sim = TektronixAWG70002A( "awg2_sim", address="GPIB0::2::INSTR", @@ -48,13 +54,13 @@ def awg2(): @pytest.fixture(scope="function") -def random_wfm_m1_m2_package(): +def random_wfm_m1_m2_package() -> Callable[[], npt.NDArray[np.number]]: """ Make a random 2400 points np.array([wfm, m1, m2]). The waveform has values in [-0.1, 0.1) """ - def make(): + def make() -> npt.NDArray[np.number]: rng = np.random.default_rng() length = rng.integers(2400, 2500) wfm = 0.2 * (rng.random(length) - 0.5) @@ -66,7 +72,7 @@ def make(): @pytest.fixture(scope="module", name="forged_sequence") -def _make_forged_sequence(): +def _make_forged_sequence() -> dict[int, dict[str, Any]]: """ Return an example forged sequence containing a subsequence @@ -76,7 +82,7 @@ def _make_forged_sequence(): num_chans = 3 types = ["element"] * (N - 1) + ["subsequence"] - def random_element(num_chans): + def random_element(num_chans: int) -> dict[int, dict[str, npt.NDArray[Any]]]: """ Return an element with random values @@ -109,7 +115,7 @@ def random_element(num_chans): return seq -def test_init_awg2(awg2) -> None: +def test_init_awg2(awg2: TektronixAWG70002A) -> None: idn_dict = awg2.IDN() assert idn_dict["vendor"] == "QCoDeS" @@ -117,7 +123,7 @@ def test_init_awg2(awg2) -> None: @settings(deadline=2500, max_examples=7) @given(N=hst.integers(1, 100)) -def test_SML_successful_generation_vary_length(N) -> None: +def test_SML_successful_generation_vary_length(N: int) -> None: tw = [0] * N nreps = [1] * N ejs = [0] * N @@ -136,18 +142,18 @@ def test_SML_successful_generation_vary_length(N) -> None: @given(num_samples=hst.integers(min_value=2400), markers_included=hst.booleans()) -def test_WFMXHeader_succesful(num_samples, markers_included) -> None: +def test_WFMXHeader_succesful(num_samples: int, markers_included: bool) -> None: xmlstr = TektronixAWG70000Base._makeWFMXFileHeader(num_samples, markers_included) etree.parse(StringIO(xmlstr)) @given(num_samples=hst.integers(max_value=2399), markers_included=hst.booleans()) -def test_WFMXHeader_failing(num_samples, markers_included) -> None: +def test_WFMXHeader_failing(num_samples: int, markers_included: bool) -> None: with pytest.raises(ValueError): TektronixAWG70000Base._makeWFMXFileHeader(num_samples, markers_included) -def test_seqxfilefromfs_failing(forged_sequence) -> None: +def test_seqxfilefromfs_failing(forged_sequence: dict[int, dict[str, Any]]) -> None: # typing convenience make_seqx = TektronixAWG70000Base.make_SEQX_from_forged_sequence @@ -188,7 +194,9 @@ def test_seqxfilefromfs_failing(forged_sequence) -> None: ) -def test_seqxfilefromfs_warns(forged_sequence, caplog: LogCaptureFixture) -> None: +def test_seqxfilefromfs_warns( + forged_sequence: dict[int, dict[str, Any]], caplog: LogCaptureFixture +) -> None: """ Test that a warning is logged when waveform is clipped """ @@ -203,7 +211,7 @@ def test_seqxfilefromfs_warns(forged_sequence, caplog: LogCaptureFixture) -> Non assert "Waveform exceeds specified channel range" in message -def test_seqxfile_from_fs(forged_sequence) -> None: +def test_seqxfile_from_fs(forged_sequence: dict[int, dict[str, Any]]) -> None: # typing convenience make_seqx = TektronixAWG70000Base.make_SEQX_from_forged_sequence @@ -238,7 +246,10 @@ def test_seqxfile_from_fs(forged_sequence) -> None: # TODO: Add some failing tests for inproper input -def test_makeSEQXFile(awg2, random_wfm_m1_m2_package) -> None: +def test_makeSEQXFile( + awg2: TektronixAWG70002A, + random_wfm_m1_m2_package: Callable[[], npt.NDArray[np.number]], +) -> None: """ Test that this function works (for some input) """ diff --git a/tests/mockers/test_simulated_ats_api.py b/tests/mockers/test_simulated_ats_api.py index 93d0b1a9a9c..165575a029e 100644 --- a/tests/mockers/test_simulated_ats_api.py +++ b/tests/mockers/test_simulated_ats_api.py @@ -1,19 +1,24 @@ import logging +from typing import TYPE_CHECKING import numpy as np +import numpy.typing as npt import pytest from qcodes.instrument.mockers.simulated_ats_api import SimulatedATS9360API from qcodes.instrument_drivers.AlazarTech.ATS import AcquisitionInterface from qcodes.instrument_drivers.AlazarTech.ATS9360 import AlazarTechATS9360 as ATS9360 +if TYPE_CHECKING: + from collections.abc import Generator -def ones_generator(data): + +def ones_generator(data: npt.NDArray) -> None: data[:] = np.ones(data.shape) @pytest.fixture(scope="function") -def simulated_alazar(): +def simulated_alazar() -> "Generator[ATS9360, None, None]": driver = ATS9360( "Alazar", api=SimulatedATS9360API(dll_path="simulated", buffer_generator=ones_generator), @@ -28,22 +33,28 @@ def simulated_alazar(): @pytest.fixture(scope="function") -def alazar_ctrl(): - class TestAcquisitionController(AcquisitionInterface): - def __init__(self): - self.buffers = [] +def alazar_ctrl() -> "Generator[AcquisitionInterface[list[npt.NDArray]], None, None]": + class TestAcquisitionController(AcquisitionInterface[list[npt.NDArray]]): + def __init__(self) -> None: + self.buffers: list[npt.NDArray] = [] - def handle_buffer(self, buffer, buffer_number=None): + def handle_buffer( + self, buffer: npt.NDArray, buffer_number: int | None = None + ) -> None: self.buffers.append(np.copy(buffer)) - def post_acquire(self): + def post_acquire(self) -> list[npt.NDArray]: return self.buffers yield TestAcquisitionController() @pytest.mark.win32 -def test_simulated_alazar(simulated_alazar, alazar_ctrl, caplog) -> None: +def test_simulated_alazar( + simulated_alazar: ATS9360, + alazar_ctrl: AcquisitionInterface[list[npt.NDArray]], + caplog: pytest.LogCaptureFixture, +) -> None: alazar = simulated_alazar buffers_per_acquisition = 10 with caplog.at_level(logging.DEBUG): @@ -60,7 +71,14 @@ def test_simulated_alazar(simulated_alazar, alazar_ctrl, caplog) -> None: get_processed_data=None, allocated_buffers=4, buffer_timeout=None, - acquisition_controller=alazar_ctrl, + # TODO: AlazarTechATS.acquire annotates acquisition_controller as + # AcquisitionController[OutputType] but only uses methods from + # AcquisitionInterface, so any AcquisitionInterface subclass works + # at runtime. AcquisitionController also subclasses + # AcquisitionInterface[Any], so OutputType is never bound to the + # return type. Widen the annotation to + # AcquisitionInterface[OutputType] and remove this ignore. + acquisition_controller=alazar_ctrl, # pyright: ignore[reportArgumentType] ) assert "tot acquire time" in caplog.records[-1].msg assert len(data) == buffers_per_acquisition From 790d75030163cbd21bf6d2bd14f1efaa4a2d9399 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Tue, 20 Oct 2026 07:17:45 +0200 Subject: [PATCH 06/20] Add type annotations to extension tests --- .../parameters/test_parameter_mixin.py | 12 +- .../test_parameter_mixin_group_registry.py | 26 ++-- .../test_parameter_mixin_interdependent.py | 56 ++++++--- .../test_parameter_mixin_on_cache_change.py | 75 +++++++++--- ...arameter_mixin_set_cache_value_on_reset.py | 53 +++++--- tests/extensions/test_infer.py | 114 +++++++++++++----- tests/extensions/test_refactoring.py | 2 +- 7 files changed, 233 insertions(+), 105 deletions(-) diff --git a/tests/extensions/parameters/test_parameter_mixin.py b/tests/extensions/parameters/test_parameter_mixin.py index adfcc22b214..424397260b6 100644 --- a/tests/extensions/parameters/test_parameter_mixin.py +++ b/tests/extensions/parameters/test_parameter_mixin.py @@ -73,7 +73,7 @@ class IncorrectNamedMixin(ParameterMixin): ] -def test_typeerror_if_multiple_parameterbase_leaves(): +def test_typeerror_if_multiple_parameterbase_leaves() -> None: class FakeParam1(ParameterBase): pass @@ -187,7 +187,7 @@ class LeafComplexBParameterMixin(ComplexParameterMixin, LeafParameterMixin): ] -def test_sets_common_compatible_bases(): +def test_sets_common_compatible_bases() -> None: class BaseA(ParameterBase): pass @@ -206,7 +206,7 @@ class ComboParameterMixin(MixinAParameterMixin, MixinBParameterMixin): assert ComboParameterMixin._COMPATIBLE_BASES == [BaseB] -def test_multiple_mixins_no_compatible(): +def test_multiple_mixins_no_compatible() -> None: class DummyBase(ParameterBase): pass @@ -224,7 +224,7 @@ class ComboParameterMixin(EmptyAParameterMixin, EmptyBParameterMixin): _PARAMETER_MIXIN_CLASSES_COMPATIBLE: Final[bool] = True -def test_raises_if_incompatible(): +def test_raises_if_incompatible() -> None: class BadBase(ParameterBase): pass @@ -310,7 +310,7 @@ class ComplexParameterD( pass -def test_typeerror_multiple_parameter_mixin_with_parameterbase(): +def test_typeerror_multiple_parameter_mixin_with_parameterbase() -> None: class MyBase(ParameterBase): pass @@ -396,7 +396,7 @@ class ComplexParameterF(ComplexParameterMixin, CompatibleParameter): assert ParameterMixin not in all_mixins -def test_leaf_class_detection(): +def test_leaf_class_detection() -> None: class MyBase(ParameterBase): pass diff --git a/tests/extensions/parameters/test_parameter_mixin_group_registry.py b/tests/extensions/parameters/test_parameter_mixin_group_registry.py index e0d81204cf3..d69a83a1321 100644 --- a/tests/extensions/parameters/test_parameter_mixin_group_registry.py +++ b/tests/extensions/parameters/test_parameter_mixin_group_registry.py @@ -7,14 +7,14 @@ class GroupRegistryTestParameterMixin(GroupRegistryParameterMixin): pass -def test_register_and_trigger_group_callbacks(): - calls = [] +def test_register_and_trigger_group_callbacks() -> None: + calls: list[str] = [] GroupRegistryTestParameterMixin._group_registry.clear() - def cb1(): + def cb1() -> None: calls.append("cb1") - def cb2(): + def cb2() -> None: calls.append("cb2") GroupRegistryTestParameterMixin.register_group_callback("alpha", cb1) @@ -24,7 +24,7 @@ def cb2(): assert calls == ["cb1", "cb2"] -def test_trigger_group_warns_if_none(): +def test_trigger_group_warns_if_none() -> None: GroupRegistryTestParameterMixin._group_registry.clear() with pytest.warns(UserWarning, match="No callbacks registered for group 'missing'"): GroupRegistryTestParameterMixin.trigger_group("missing") @@ -35,17 +35,17 @@ def test_warning_no_callbacks_for_group() -> None: GroupRegistryParameterMixin.trigger_group("empty_group") -def test_multiple_callbacks_in_group(): +def test_multiple_callbacks_in_group() -> None: """ Test that multiple callbacks registered to a group are all called in order. """ - call_order = [] + call_order: list[str] = [] GroupRegistryTestParameterMixin._group_registry.clear() - def callback_one(): + def callback_one() -> None: call_order.append("callback_one") - def callback_two(): + def callback_two() -> None: call_order.append("callback_two") GroupRegistryTestParameterMixin.register_group_callback( @@ -59,17 +59,17 @@ def callback_two(): assert call_order == ["callback_one", "callback_two"] -def test_callback_execution_order(): +def test_callback_execution_order() -> None: """ Test that callbacks are executed in the order they were registered. """ - execution_sequence = [] + execution_sequence: list[str] = [] GroupRegistryTestParameterMixin._group_registry.clear() - def first_callback(): + def first_callback() -> None: execution_sequence.append("first") - def second_callback(): + def second_callback() -> None: execution_sequence.append("second") GroupRegistryTestParameterMixin.register_group_callback( diff --git a/tests/extensions/parameters/test_parameter_mixin_interdependent.py b/tests/extensions/parameters/test_parameter_mixin_interdependent.py index 4a008798452..407a44f795d 100644 --- a/tests/extensions/parameters/test_parameter_mixin_interdependent.py +++ b/tests/extensions/parameters/test_parameter_mixin_interdependent.py @@ -1,5 +1,5 @@ import re -from typing import Any, cast +from typing import TYPE_CHECKING, Any, cast import pytest @@ -8,13 +8,19 @@ from qcodes.parameters import Parameter from qcodes.utils import QCoDeSDeprecationWarning +if TYPE_CHECKING: + from collections.abc import Callable, Generator + class MockInstrument(Instrument): """ A mock instrument that can host parameters. """ - def __init__(self, name: str, **kwargs): + some_param: "InterdependentParameter" + managed_param: "InterdependentParameter" + + def __init__(self, name: str, **kwargs: Any) -> None: super().__init__(name, **kwargs) @@ -23,7 +29,7 @@ class InterdependentParameter(InterdependentParameterMixin, Parameter): @pytest.fixture -def store(): +def store() -> dict[str, Any]: """ Provides a dictionary to store parameter values. """ @@ -31,7 +37,7 @@ def store(): @pytest.fixture -def callback_flag(): +def callback_flag() -> dict[str, bool]: """ Provides a mutable flag to track callback invocation. """ @@ -39,7 +45,7 @@ def callback_flag(): @pytest.fixture -def callback(callback_flag): +def callback(callback_flag: dict[str, bool]) -> "Callable[[], None]": """ Provides a callback that sets callback_flag["called"] to True. """ @@ -51,7 +57,7 @@ def _callback(**kwargs: Any) -> None: @pytest.fixture -def callback_data(): +def callback_data() -> list[tuple[tuple[Any, ...], dict[str, Any]]]: """ Provides a list to store callback invocation data. """ @@ -59,19 +65,21 @@ def callback_data(): @pytest.fixture -def data_callback(callback_data): +def data_callback( + callback_data: list[tuple[tuple[Any, ...], dict[str, Any]]], +) -> "Callable[[], None]": """ Provides a callback that records (args, kwargs) into callback_data. """ - def _callback(*args, **kwargs): + def _callback(*args: Any, **kwargs: Any) -> None: callback_data.append((args, kwargs)) return _callback @pytest.fixture -def mock_instr(): +def mock_instr() -> "Generator[MockInstrument, None, None]": """ Provides a mock instrument for interdependent parameter tests. """ @@ -81,7 +89,10 @@ def mock_instr(): def test_dependency_update_invoked_on_change( - store, callback_flag, callback, mock_instr + store: dict[str, Any], + callback_flag: dict[str, bool], + callback: "Callable[[], None]", + mock_instr: MockInstrument, ) -> None: mock_instr.some_param = cast( "InterdependentParameter", @@ -114,7 +125,10 @@ def test_dependency_update_invoked_on_change( def test_adding_dependent_parameter_later( - store, callback_flag, callback, mock_instr + store: dict[str, Any], + callback_flag: dict[str, bool], + callback: "Callable[[], None]", + mock_instr: MockInstrument, ) -> None: mock_instr.some_param = cast( "InterdependentParameter", @@ -148,7 +162,7 @@ def test_adding_dependent_parameter_later( def test_error_on_non_interdependent_dependency( - store, mock_instr: MockInstrument + store: dict[str, Any], mock_instr: MockInstrument ) -> None: mock_instr.add_parameter( @@ -180,8 +194,10 @@ def test_error_on_non_interdependent_dependency( # Parameter managed_param depends on a non-interdependent param -def test_parsers_and_dependency_propagation(store, mock_instr) -> None: - def dependency_update(): +def test_parsers_and_dependency_propagation( + store: dict[str, Any], mock_instr: MockInstrument +) -> None: + def dependency_update() -> None: mock_instr.managed_param.set(999) mock_instr.some_param = cast( @@ -218,7 +234,9 @@ def dependency_update(): assert mock_instr.managed_param.get() == 999, "Dependency not updated." -def test_typeerror_if_dependency_update_method_invalid(mock_instr): +def test_typeerror_if_dependency_update_method_invalid( + mock_instr: MockInstrument, +) -> None: param = mock_instr.add_parameter( name="some_param", parameter_class=InterdependentParameter, @@ -229,10 +247,10 @@ def test_typeerror_if_dependency_update_method_invalid(mock_instr): with pytest.raises( TypeError, match=re.escape("dependency_update_method must be callable or None.") ): - param.dependency_update_method = 123 + param.dependency_update_method = 123 # pyright: ignore[reportAttributeAccessIssue] -def test_typeerror_if_dependent_on_invalid(mock_instr): +def test_typeerror_if_dependent_on_invalid(mock_instr: MockInstrument) -> None: param = mock_instr.add_parameter( name="some_param", parameter_class=InterdependentParameter, @@ -243,10 +261,10 @@ def test_typeerror_if_dependent_on_invalid(mock_instr): with pytest.raises( TypeError, match=re.escape("dependent_on must be a list of strings.") ): - param.dependent_on = "not_a_list" + param.dependent_on = "not_a_list" # pyright: ignore[reportAttributeAccessIssue] # Assign a list with non-string with pytest.raises( TypeError, match=re.escape("dependent_on must be a list of strings.") ): - param.dependent_on = [123, None] + param.dependent_on = [123, None] # pyright: ignore[reportAttributeAccessIssue] diff --git a/tests/extensions/parameters/test_parameter_mixin_on_cache_change.py b/tests/extensions/parameters/test_parameter_mixin_on_cache_change.py index dd5bed99014..27d161307ac 100644 --- a/tests/extensions/parameters/test_parameter_mixin_on_cache_change.py +++ b/tests/extensions/parameters/test_parameter_mixin_on_cache_change.py @@ -1,4 +1,4 @@ -from typing import Any, cast +from typing import TYPE_CHECKING, Any, cast import pytest @@ -6,13 +6,22 @@ from qcodes.instrument import Instrument from qcodes.parameters import Parameter +if TYPE_CHECKING: + from collections.abc import Generator + + from qcodes.extensions.parameters.parameter_mixin_on_cache_change import ( + OnCacheChangeCallback, + ) + class MockInstrument(Instrument): """ A mock instrument to host parameters. """ - def __init__(self, name: str): + test_parameter: "OnCacheChangeParameter" + + def __init__(self, name: str) -> None: super().__init__(name) @@ -23,7 +32,7 @@ class OnCacheChangeParameter(OnCacheChangeParameterMixin, Parameter): @pytest.fixture -def store(): +def store() -> dict[str, Any]: """ Provides a dictionary to store parameter values. """ @@ -31,7 +40,7 @@ def store(): @pytest.fixture -def callback_flag(): +def callback_flag() -> dict[str, bool]: """ Provides a mutable flag to track callback invocation. """ @@ -39,7 +48,7 @@ def callback_flag(): @pytest.fixture -def callback(callback_flag): +def callback(callback_flag: dict[str, bool]) -> "OnCacheChangeCallback": """ Provides a callback that sets callback_flag["called"] to True. """ @@ -51,7 +60,7 @@ def _callback(**kwargs: Any) -> None: @pytest.fixture -def callback_data(): +def callback_data() -> list[Any]: """ Provides a list to store callback invocation data. """ @@ -59,19 +68,19 @@ def callback_data(): @pytest.fixture -def data_callback(callback_data): +def data_callback(callback_data: list[Any]) -> "OnCacheChangeCallback": """ Provides a callback that records (args, kwargs) into callback_data. """ - def _callback(**kwargs): + def _callback(**kwargs: Any) -> None: callback_data.append(kwargs) return _callback @pytest.fixture -def mock_instr(): +def mock_instr() -> "Generator[MockInstrument, None, None]": """ Provides a mock instrument to host parameters. """ @@ -81,18 +90,24 @@ def mock_instr(): @pytest.mark.parametrize("invalid_callback", ["not_a_callable", 123, [], {}]) -def test_error_on_non_callable_callback(mock_instr, invalid_callback) -> None: +def test_error_on_non_callable_callback( + mock_instr: MockInstrument, + invalid_callback: str | int | list[Any] | dict[Any, Any], +) -> None: test_parameter: OnCacheChangeParameter = mock_instr.add_parameter( name="test_parameter", parameter_class=OnCacheChangeParameter, docstring="A parameter for testing non-callable callbacks.", ) with pytest.raises(TypeError): - test_parameter.on_cache_change = invalid_callback + test_parameter.on_cache_change = invalid_callback # pyright: ignore[reportAttributeAccessIssue] def test_no_callback_invocation_on_init_or_get( - store, callback_flag, callback, mock_instr + store: dict[str, Any], + callback_flag: dict[str, bool], + callback: "OnCacheChangeCallback", + mock_instr: MockInstrument, ) -> None: test_parameter: OnCacheChangeParameter = mock_instr.add_parameter( name="test_parameter", @@ -107,7 +122,12 @@ def test_no_callback_invocation_on_init_or_get( assert not callback_flag["called"], "Callback invoked unexpectedly." -def test_callback_invoked_on_set(store, callback_flag, callback, mock_instr) -> None: +def test_callback_invoked_on_set( + store: dict[str, Any], + callback_flag: dict[str, bool], + callback: "OnCacheChangeCallback", + mock_instr: MockInstrument, +) -> None: test_parameter: OnCacheChangeParameter = mock_instr.add_parameter( name="test_parameter", parameter_class=OnCacheChangeParameter, @@ -121,7 +141,10 @@ def test_callback_invoked_on_set(store, callback_flag, callback, mock_instr) -> def test_changing_callback_after_init( - store, callback_flag, callback, mock_instr + store: dict[str, Any], + callback_flag: dict[str, bool], + callback: "OnCacheChangeCallback", + mock_instr: MockInstrument, ) -> None: test_parameter: OnCacheChangeParameter = mock_instr.add_parameter( name="test_parameter", @@ -138,7 +161,11 @@ def test_changing_callback_after_init( assert callback_flag["called"] -def test_callback_on_get_value_change(callback_flag, callback, mock_instr) -> None: +def test_callback_on_get_value_change( + callback_flag: dict[str, bool], + callback: "OnCacheChangeCallback", + mock_instr: MockInstrument, +) -> None: get_reply = None test_parameter: OnCacheChangeParameter = mock_instr.add_parameter( @@ -157,7 +184,10 @@ def test_callback_on_get_value_change(callback_flag, callback, mock_instr) -> No def test_callback_on_direct_cache_update( - store, callback_data, data_callback, mock_instr + store: dict[str, Any], + callback_data: list[dict[str, Any]], + data_callback: "OnCacheChangeCallback", + mock_instr: MockInstrument, ) -> None: test_parameter: OnCacheChangeParameter = mock_instr.add_parameter( name="test_parameter", @@ -174,7 +204,10 @@ def test_callback_on_direct_cache_update( def test_no_callback_if_value_unchanged( - store, callback_data, data_callback, mock_instr + store: dict[str, Any], + callback_data: list[dict[str, Any]], + data_callback: "OnCacheChangeCallback", + mock_instr: MockInstrument, ) -> None: test_parameter: OnCacheChangeParameter = mock_instr.add_parameter( name="test_parameter", @@ -193,8 +226,12 @@ def test_no_callback_if_value_unchanged( assert not callback_data -def test_callback_value_parsers(store, callback_data, mock_instr) -> None: - def cb(**kwargs): +def test_callback_value_parsers( + store: dict[str, Any], + callback_data: list[tuple[Any, Any, Any, Any]], + mock_instr: MockInstrument, +) -> None: + def cb(**kwargs: Any) -> None: callback_data.append( ( kwargs.get("value_old"), diff --git a/tests/extensions/parameters/test_parameter_mixin_set_cache_value_on_reset.py b/tests/extensions/parameters/test_parameter_mixin_set_cache_value_on_reset.py index 23a48755215..3f8eca9eb8d 100644 --- a/tests/extensions/parameters/test_parameter_mixin_set_cache_value_on_reset.py +++ b/tests/extensions/parameters/test_parameter_mixin_set_cache_value_on_reset.py @@ -1,4 +1,4 @@ -from typing import Any +from typing import TYPE_CHECKING, Any import pytest @@ -7,17 +7,20 @@ from qcodes.parameters import Parameter from qcodes.utils import QCoDeSDeprecationWarning +if TYPE_CHECKING: + from collections.abc import Generator + class MockResetInstrument(Instrument): """ A mock instrument that records calls to perform_reset. """ - def __init__(self, name: str): + def __init__(self, name: str) -> None: super().__init__(name) self.reset_call_args: list[tuple[tuple[Any, ...], dict[str, Any]]] = [] - def perform_reset(self, *args: Any, **kwargs: Any): + def perform_reset(self, *args: Any, **kwargs: Any) -> None: self.reset_call_args.append((args, kwargs)) SetCacheValueOnResetParameterMixin.trigger_group("reset_group_general") @@ -29,7 +32,7 @@ class ResetTestParameter(SetCacheValueOnResetParameterMixin, Parameter): @pytest.fixture -def store(): +def store() -> dict[str, Any]: """ Provides a dictionary to store parameter values. """ @@ -37,7 +40,7 @@ def store(): @pytest.fixture -def reset_instr(): +def reset_instr() -> "Generator[MockResetInstrument, None, None]": """ Provides a mock instrument with reset capability. """ @@ -47,11 +50,13 @@ def reset_instr(): @pytest.fixture(autouse=True) -def clear_reset_registry(): +def clear_reset_registry() -> None: SetCacheValueOnResetParameterMixin._group_registry.clear() -def test_cache_resets_to_value(store, reset_instr) -> None: +def test_cache_resets_to_value( + store: dict[str, Any], reset_instr: MockResetInstrument +) -> None: test_param: ResetTestParameter = reset_instr.add_parameter( name="test_param", parameter_class=ResetTestParameter, @@ -67,7 +72,9 @@ def test_cache_resets_to_value(store, reset_instr) -> None: assert test_param.get() == 42 -def test_warning_if_cache_value_unset(store, reset_instr) -> None: +def test_warning_if_cache_value_unset( + store: dict[str, Any], reset_instr: MockResetInstrument +) -> None: with pytest.warns( UserWarning, match="cache_value_after_reset for parameter 'test_param' is not set", @@ -81,7 +88,9 @@ def test_warning_if_cache_value_unset(store, reset_instr) -> None: ) -def test_cache_resets_to_none(store, reset_instr) -> None: +def test_cache_resets_to_none( + store: dict[str, Any], reset_instr: MockResetInstrument +) -> None: test_param: ResetTestParameter = reset_instr.add_parameter( name="test_param_none", parameter_class=ResetTestParameter, @@ -97,7 +106,9 @@ def test_cache_resets_to_none(store, reset_instr) -> None: assert test_param.get() is None -def test_set_parser_with_default_get_parser(store, reset_instr) -> None: +def test_set_parser_with_default_get_parser( + store: dict[str, Any], reset_instr: MockResetInstrument +) -> None: test_param: ResetTestParameter = reset_instr.add_parameter( name="test_param", parameter_class=ResetTestParameter, @@ -116,7 +127,9 @@ def test_set_parser_with_default_get_parser(store, reset_instr) -> None: assert test_param.get_raw() == 30 -def test_direct_cache_update_and_reset(store, reset_instr) -> None: +def test_direct_cache_update_and_reset( + store: dict[str, Any], reset_instr: MockResetInstrument +) -> None: test_param: ResetTestParameter = reset_instr.add_parameter( name="test_param", parameter_class=ResetTestParameter, @@ -165,7 +178,9 @@ def test_error_if_get_parser_supplied(reset_instr: MockResetInstrument) -> None: ) -def test_parameter_in_multiple_reset_groups(store, reset_instr) -> None: +def test_parameter_in_multiple_reset_groups( + store: dict[str, Any], reset_instr: MockResetInstrument +) -> None: test_param: ResetTestParameter = reset_instr.add_parameter( name="test_param", parameter_class=ResetTestParameter, @@ -184,7 +199,7 @@ def test_parameter_in_multiple_reset_groups(store, reset_instr) -> None: assert test_param.get() == 100 -def test_get_raw_with_val_mapping(reset_instr) -> None: +def test_get_raw_with_val_mapping(reset_instr: MockResetInstrument) -> None: class MyParam(SetCacheValueOnResetParameterMixin, Parameter): pass @@ -205,7 +220,9 @@ class MyParam(SetCacheValueOnResetParameterMixin, Parameter): assert p.get_raw() == "1" -def test_warning_if_group_names_is_none(store, reset_instr): +def test_warning_if_group_names_is_none( + store: dict[str, Any], reset_instr: MockResetInstrument +) -> None: with pytest.warns(UserWarning, match="No group_name"): reset_instr.add_parameter( name="test_param", @@ -216,7 +233,9 @@ def test_warning_if_group_names_is_none(store, reset_instr): ) -def test_warning_if_group_names_missing(store, reset_instr): +def test_warning_if_group_names_missing( + store: dict[str, Any], reset_instr: MockResetInstrument +) -> None: with pytest.warns(UserWarning, match="No group_name"): reset_instr.add_parameter( name="test_param", @@ -226,7 +245,9 @@ def test_warning_if_group_names_missing(store, reset_instr): ) -def test_typeerror_if_group_names_invalid(store, reset_instr): +def test_typeerror_if_group_names_invalid( + store: dict[str, Any], reset_instr: MockResetInstrument +) -> None: with ( pytest.warns(QCoDeSDeprecationWarning), pytest.raises(TypeError, match="group_names must be a list of strings or None"), diff --git a/tests/extensions/test_infer.py b/tests/extensions/test_infer.py index afcd12e4cba..c16a609ac5a 100644 --- a/tests/extensions/test_infer.py +++ b/tests/extensions/test_infer.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any +from typing import TYPE_CHECKING, Any import numpy as np import pytest @@ -21,9 +21,12 @@ from qcodes.instrument import Instrument, InstrumentBase, InstrumentModule from qcodes.parameters import DelegateParameter, ManualParameter, Parameter +if TYPE_CHECKING: + from collections.abc import Generator + class DummyModule(InstrumentModule): - def __init__(self, name: str, parent: Instrument): + def __init__(self, name: str, parent: Instrument) -> None: super().__init__(name=name, parent=parent) self.good_chan_parameter = ManualParameter( "good_chan_parameter", instrument=self @@ -32,7 +35,7 @@ def __init__(self, name: str, parent: Instrument): class DummyInstrument(Instrument): - def __init__(self, name: str): + def __init__(self, name: str) -> None: super().__init__(name=name) self.good_inst_parameter = ManualParameter( "good_inst_parameter", instrument=self @@ -42,12 +45,12 @@ def __init__(self, name: str): class DummyInstrument2(DummyInstrument): - def __init__(self, name: str): + def __init__(self, name: str) -> None: super().__init__(name=name) class DummyDelegateInstrument(InstrumentBase): - def __init__(self, name: str): + def __init__(self, name: str) -> None: super().__init__(name=name) self.inst_delegate = DelegateParameter( name="inst_delegate", source=None, instrument=self, bind_to_instrument=True @@ -59,7 +62,7 @@ def __init__(self, name: str): class DummyDelegateModule(InstrumentModule): - def __init__(self, name: str, parent: InstrumentBase): + def __init__(self, name: str, parent: InstrumentBase) -> None: super().__init__(name=name, parent=parent) self.chan_delegate = DelegateParameter( name="chan_delegate", source=None, instrument=self, bind_to_instrument=True @@ -69,13 +72,13 @@ def __init__(self, name: str, parent: InstrumentBase): class UserLinkingParameter(Parameter): def __init__( self, name: str, linked_parameter: Parameter | None = None, **kwargs: Any - ): + ) -> None: super().__init__(name=name, **kwargs) self.linked_parameter: Parameter | None = linked_parameter @pytest.fixture(name="instrument_fixture") -def make_instrument_fixture(): +def make_instrument_fixture() -> Generator[DummyInstrument, None, None]: inst = DummyInstrument("dummy_instrument") InferAttrs.clear() try: @@ -85,7 +88,7 @@ def make_instrument_fixture(): @pytest.fixture(name="instrument_fixture2") -def make_instrument_fixture2(): +def make_instrument_fixture2() -> Generator[DummyInstrument2, None, None]: inst = DummyInstrument2("dummy_instrument2") InferAttrs.clear() try: @@ -95,7 +98,9 @@ def make_instrument_fixture2(): @pytest.fixture(name="good_inst_delegates") -def make_good_delegate_parameters(instrument_fixture): +def make_good_delegate_parameters( + instrument_fixture: DummyInstrument, +) -> tuple[DelegateParameter, DelegateParameter, UserLinkingParameter]: inst = instrument_fixture good_inst_del_1 = DelegateParameter( "good_inst_del_1", source=inst.good_inst_parameter @@ -108,7 +113,9 @@ def make_good_delegate_parameters(instrument_fixture): @pytest.fixture(name="multi_inst_chain") -def make_multi_instrument_chain(instrument_fixture, instrument_fixture2): +def make_multi_instrument_chain( + instrument_fixture: DummyInstrument, instrument_fixture2: DummyInstrument2 +) -> tuple[DelegateParameter, DelegateParameter, UserLinkingParameter]: inst = instrument_fixture inst2 = instrument_fixture2 good_inst_del_1 = DelegateParameter( @@ -125,7 +132,12 @@ def make_multi_instrument_chain(instrument_fixture, instrument_fixture2): return good_inst_del_1, good_inst_del_2, good_inst_del_3 -def test_get_root_parameter_valid(instrument_fixture, good_inst_delegates): +def test_get_root_parameter_valid( + instrument_fixture: DummyInstrument, + good_inst_delegates: tuple[ + DelegateParameter, DelegateParameter, UserLinkingParameter + ], +) -> None: inst = instrument_fixture good_inst_del_1, good_inst_del_2, good_inst_del_3 = good_inst_delegates @@ -144,7 +156,11 @@ def test_get_root_parameter_valid(instrument_fixture, good_inst_delegates): assert get_root_parameter(good_inst_del_3) is inst.good_inst_parameter -def test_get_root_parameter_no_source(good_inst_delegates): +def test_get_root_parameter_no_source( + good_inst_delegates: tuple[ + DelegateParameter, DelegateParameter, UserLinkingParameter + ], +) -> None: good_inst_del_1, good_inst_del_2, _ = good_inst_delegates good_inst_del_1.source = None @@ -154,13 +170,21 @@ def test_get_root_parameter_no_source(good_inst_delegates): assert "is not attached to a source" in str(exc_info.value) -def test_get_root_parameter_no_user_attr(good_inst_delegates): +def test_get_root_parameter_no_user_attr( + good_inst_delegates: tuple[ + DelegateParameter, DelegateParameter, UserLinkingParameter + ], +) -> None: _, _, good_inst_del_3 = good_inst_delegates InferAttrs.clear() assert get_root_parameter(good_inst_del_3, "external_parameter") is good_inst_del_3 -def test_get_root_parameter_none_user_attr(good_inst_delegates): +def test_get_root_parameter_none_user_attr( + good_inst_delegates: tuple[ + DelegateParameter, DelegateParameter, UserLinkingParameter + ], +) -> None: _, _, good_inst_del_3 = good_inst_delegates good_inst_del_3.linked_parameter = None with pytest.raises(InferError) as exc_info: @@ -168,14 +192,19 @@ def test_get_root_parameter_none_user_attr(good_inst_delegates): assert "is not attached to a source on attribute" in str(exc_info.value) -def test_infer_instrument_valid(instrument_fixture, good_inst_delegates): +def test_infer_instrument_valid( + instrument_fixture: DummyInstrument, + good_inst_delegates: tuple[ + DelegateParameter, DelegateParameter, UserLinkingParameter + ], +) -> None: inst = instrument_fixture _, _, good_inst_del_3 = good_inst_delegates InferAttrs.add("linked_parameter") assert infer_instrument(good_inst_del_3) is inst -def test_infer_instrument_no_instrument(instrument_fixture): +def test_infer_instrument_no_instrument(instrument_fixture: DummyInstrument) -> None: inst = instrument_fixture no_inst_delegate = DelegateParameter( "no_inst_delegate", source=inst.bad_inst_parameter @@ -185,7 +214,7 @@ def test_infer_instrument_no_instrument(instrument_fixture): assert "has no instrument" in str(exc_info.value) -def test_infer_instrument_root_instrument_base(): +def test_infer_instrument_root_instrument_base() -> None: delegate_inst = DummyDelegateInstrument("dummy_delegate_instrument") with pytest.raises(InferError) as exc_info: @@ -193,7 +222,7 @@ def test_infer_instrument_root_instrument_base(): assert "Could not determine source instrument for parameter" in str(exc_info.value) -def test_infer_channel_valid(instrument_fixture): +def test_infer_channel_valid(instrument_fixture: DummyInstrument) -> None: inst = instrument_fixture chan_delegate = DelegateParameter( "chan_delegate", source=inst.module.good_chan_parameter @@ -201,7 +230,7 @@ def test_infer_channel_valid(instrument_fixture): assert infer_channel(chan_delegate) is inst.module -def test_infer_channel_no_channel(instrument_fixture): +def test_infer_channel_no_channel(instrument_fixture: DummyInstrument) -> None: inst = instrument_fixture no_chan_delegate = DelegateParameter( "no_chan_delegate", source=inst.module.bad_chan_parameter @@ -218,7 +247,12 @@ def test_infer_channel_no_channel(instrument_fixture): assert "Could not determine a root instrument channel" in str(exc_info.value) -def test_get_parameter_chain(instrument_fixture, good_inst_delegates): +def test_get_parameter_chain( + instrument_fixture: DummyInstrument, + good_inst_delegates: tuple[ + DelegateParameter, DelegateParameter, UserLinkingParameter + ], +) -> None: inst = instrument_fixture good_inst_del_1, good_inst_del_2, good_inst_del_3 = good_inst_delegates parameter_chain = get_parameter_chain(good_inst_del_3, "linked_parameter") @@ -259,7 +293,12 @@ def test_get_parameter_chain(instrument_fixture, good_inst_delegates): ) -def test_parameters_on_delegate_instruments(instrument_fixture, good_inst_delegates): +def test_parameters_on_delegate_instruments( + instrument_fixture: DummyInstrument, + good_inst_delegates: tuple[ + DelegateParameter, DelegateParameter, UserLinkingParameter + ], +) -> None: inst = instrument_fixture _, good_inst_del_2, _ = good_inst_delegates @@ -272,7 +311,7 @@ def test_parameters_on_delegate_instruments(instrument_fixture, good_inst_delega assert infer_instrument(delegate_inst.inst_delegate) is inst -def test_merge_user_and_class_attrs(): +def test_merge_user_and_class_attrs() -> None: InferAttrs.add("attr1") attr_set = _merge_user_and_class_attrs("attr2") assert {"attr1", "attr2"} == attr_set @@ -281,7 +320,7 @@ def test_merge_user_and_class_attrs(): assert {"attr1", "attr2", "attr3"} == attr_set_list -def test_infer_attrs(): +def test_infer_attrs() -> None: InferAttrs.clear() assert InferAttrs.known_attrs() == () @@ -296,7 +335,11 @@ def test_infer_attrs(): assert set(InferAttrs.known_attrs()) == {"attr1", "attr2", "attr3"} -def test_get_parameter_chain_with_loops(good_inst_delegates): +def test_get_parameter_chain_with_loops( + good_inst_delegates: tuple[ + DelegateParameter, DelegateParameter, UserLinkingParameter + ], +) -> None: good_inst_del_1, good_inst_del_2, good_inst_del_3 = good_inst_delegates good_inst_del_1.source = good_inst_del_3 parameter_chain = get_parameter_chain(good_inst_del_3, "linked_parameter") @@ -311,7 +354,11 @@ def test_get_parameter_chain_with_loops(good_inst_delegates): ) -def test_get_root_parameter_with_loops(good_inst_delegates): +def test_get_root_parameter_with_loops( + good_inst_delegates: tuple[ + DelegateParameter, DelegateParameter, UserLinkingParameter + ], +) -> None: good_inst_del_1, good_inst_del_2, good_inst_del_3 = good_inst_delegates good_inst_del_1.source = good_inst_del_3 with pytest.raises(InferError) as exc_info: @@ -319,7 +366,7 @@ def test_get_root_parameter_with_loops(good_inst_delegates): assert "generated a loop of linking parameters" in str(exc_info.value) -def test_chain_links_of_type(): +def test_chain_links_of_type() -> None: root_param = ManualParameter("root", initial_value=0) user_link = UserLinkingParameter("user_link", linked_parameter=root_param) delegate_link = DelegateParameter("delegate_link", source=user_link) @@ -330,7 +377,7 @@ def test_chain_links_of_type(): assert set(user_links) == {user_link, user_link_2} -def test_sole_chain_link_of_type(): +def test_sole_chain_link_of_type() -> None: root_param = ManualParameter("root", initial_value=0) user_link = UserLinkingParameter("user_link", linked_parameter=root_param) delegate_link = DelegateParameter("delegate_link", source=user_link) @@ -352,8 +399,10 @@ def test_sole_chain_link_of_type(): def test_get_instrument_from_chain( - instrument_fixture, instrument_fixture2, multi_inst_chain -): + instrument_fixture: DummyInstrument, + instrument_fixture2: DummyInstrument2, + multi_inst_chain: tuple[DelegateParameter, DelegateParameter, UserLinkingParameter], +) -> None: inst = instrument_fixture inst2 = instrument_fixture2 _good_inst_del_1, _good_inst_del_2, good_inst_del_3 = multi_inst_chain @@ -365,7 +414,10 @@ def test_get_instrument_from_chain( assert set(instruments) == {inst, inst2} -def test_get_sole_instrument_from_chain(instrument_fixture2, multi_inst_chain): +def test_get_sole_instrument_from_chain( + instrument_fixture2: DummyInstrument2, + multi_inst_chain: tuple[DelegateParameter, DelegateParameter, UserLinkingParameter], +) -> None: inst2 = instrument_fixture2 _good_inst_del_1, _good_inst_del_2, good_inst_del_3 = multi_inst_chain diff --git a/tests/extensions/test_refactoring.py b/tests/extensions/test_refactoring.py index 1a26d479dcd..6321d6da979 100644 --- a/tests/extensions/test_refactoring.py +++ b/tests/extensions/test_refactoring.py @@ -148,7 +148,7 @@ def test_transform_add_parameter_multiline_docstring() -> None: assert output_lines[-2] == '"""This is the first line This is the second line"""' -def test_transform_add_parameter_dedent(): +def test_transform_add_parameter_dedent() -> None: code = dedent( """ self.add_parameter( From 49e3cd6fa259a0e7d56743f1ecfa89b16eca48b9 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Tue, 20 Oct 2026 07:22:12 +0200 Subject: [PATCH 07/20] Add type annotations to parameter tests --- tests/parameter/conftest.py | 18 ++++----- tests/parameter/test_array_parameter.py | 2 +- tests/parameter/test_delegate_parameter.py | 4 +- tests/parameter/test_get_latest.py | 2 +- tests/parameter/test_get_set_wrapping.py | 4 +- tests/parameter/test_group_parameter.py | 4 +- tests/parameter/test_issequence.py | 11 ++++-- tests/parameter/test_issequenceof.py | 23 ++++++++---- ...test_multi_channel_instrument_parameter.py | 2 +- tests/parameter/test_multi_parameter.py | 2 +- tests/parameter/test_parameter_cache.py | 2 +- .../test_parameter_context_manager.py | 2 +- .../test_parameter_on_set_callback.py | 4 +- tests/parameter/test_parameter_override.py | 37 ++++++++++++------- tests/parameter/test_parameter_ramp.py | 10 ++--- .../parameter/test_parameter_registration.py | 4 +- .../parameter/test_parameter_scale_offset.py | 23 ++++++++++-- .../test_parameter_with_setpoints.py | 17 +++++++-- tests/parameter/test_permissive_range.py | 6 +-- tests/parameter/test_scaled_parameter.py | 2 +- tests/parameter/test_validators.py | 6 +-- 21 files changed, 117 insertions(+), 68 deletions(-) diff --git a/tests/parameter/conftest.py b/tests/parameter/conftest.py index 8ddb9f1395a..49128488b22 100644 --- a/tests/parameter/conftest.py +++ b/tests/parameter/conftest.py @@ -61,7 +61,7 @@ def _make_dummy_instrument() -> Generator[DummyChannelInstrument, None, None]: class GettableParam(Parameter): """Parameter that keeps track of number of get operations""" - def __init__(self, *args: Any, **kwargs: Any): + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) self._get_count = 0 @@ -74,7 +74,7 @@ class BetterGettableParam(Parameter): """Parameter that keeps track of number of get operations, But can actually store values""" - def __init__(self, *args: Any, **kwargs: Any): + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) self._get_count = 0 @@ -86,7 +86,7 @@ def get_raw(self) -> Any: class SettableParam(Parameter): """Parameter that keeps track of number of set operations""" - def __init__(self, *args: Any, **kwargs: Any): + def __init__(self, *args: Any, **kwargs: Any) -> None: self._set_count = 0 super().__init__(*args, **kwargs) @@ -97,7 +97,7 @@ def set_raw(self, value: Any) -> None: class OverwriteGetParam(Parameter): """Parameter that overwrites get.""" - def __init__(self, *args: Any, **kwargs: Any): + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) self._value = 42 self.set_count = 0 @@ -111,7 +111,7 @@ def get(self) -> int: class OverwriteSetParam(Parameter): """Parameter that overwrites set.""" - def __init__(self, *args: Any, **kwargs: Any): + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) self._value = 42 self.set_count = 0 @@ -125,7 +125,7 @@ def set(self, value: Any) -> None: class GetSetRawParameter(Parameter): """Parameter that implements get and set raw""" - def __init__(self, *args: Any, **kwargs: Any): + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) def get_raw(self) -> ParamRawDataType: @@ -142,7 +142,7 @@ class BookkeepingValidator[T](vals.Validator[T]): def __init__( self, min_value: float = -float("inf"), max_value: float = float("inf") - ): + ) -> None: self.values_validated: list[T] = [] def validate(self, value: T, context: str = "") -> None: @@ -152,7 +152,7 @@ def validate(self, value: T, context: str = "") -> None: class MemoryParameter(Parameter): - def __init__(self, get_cmd: Callable[[], Any] | None = None, **kwargs: Any): + def __init__(self, get_cmd: Callable[[], Any] | None = None, **kwargs: Any) -> None: self.set_values: list[Any] = [] self.get_values: list[Any] = [] super().__init__( @@ -177,7 +177,7 @@ def get_func() -> ParamDataType: class VirtualParameter(Parameter): - def __init__(self, name: str, param: Parameter, **kwargs: Any): + def __init__(self, name: str, param: Parameter, **kwargs: Any) -> None: self._param = param super().__init__(name=name, **kwargs) diff --git a/tests/parameter/test_array_parameter.py b/tests/parameter/test_array_parameter.py index 8d9ff7cc75e..710e94344fc 100644 --- a/tests/parameter/test_array_parameter.py +++ b/tests/parameter/test_array_parameter.py @@ -8,7 +8,7 @@ class SimpleArrayParam(ArrayParameter): - def __init__(self, return_val: ParamRawDataType, *args: Any, **kwargs: Any): + def __init__(self, return_val: ParamRawDataType, *args: Any, **kwargs: Any) -> None: self._return_val = return_val self._get_count = 0 super().__init__(*args, **kwargs) diff --git a/tests/parameter/test_delegate_parameter.py b/tests/parameter/test_delegate_parameter.py index 55278f3f9aa..0fe8245d87a 100644 --- a/tests/parameter/test_delegate_parameter.py +++ b/tests/parameter/test_delegate_parameter.py @@ -41,7 +41,7 @@ def _make_simple_param(numeric_val: int) -> Generator[Parameter, None, None]: class ObservableParam(Parameter): - def __init__(self, *args: Any, **kwargs: Any): + def __init__(self, *args: Any, **kwargs: Any) -> None: self.instr_val = None super().__init__(*args, **kwargs) @@ -792,7 +792,7 @@ def test_value_validation_with_offset_and_scale() -> None: delegate_param.set(1) -def test_delegate_of_delegate_updates_settable_gettable(): +def test_delegate_of_delegate_updates_settable_gettable() -> None: gettable_settable_source_param = Parameter( "source", set_cmd=None, get_cmd=None, vals=vals.Numbers(-5, 5) ) diff --git a/tests/parameter/test_get_latest.py b/tests/parameter/test_get_latest.py index 856f7443933..5d9fb36c89f 100644 --- a/tests/parameter/test_get_latest.py +++ b/tests/parameter/test_get_latest.py @@ -167,7 +167,7 @@ def test_no_get_max_val_age() -> None: # could be added in a subclass. Here we create a subclass that does add a # get command and also does not implement the check for max_val_age class LocalParameter(ParameterBase): - def __init__(self, *args: Any, **kwargs: Any): + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) self.set_raw = lambda x: x # type: ignore[method-assign] self.set = self._wrap_set(self.set_raw) diff --git a/tests/parameter/test_get_set_wrapping.py b/tests/parameter/test_get_set_wrapping.py index 3bbe3416d5b..83220d3a7d6 100644 --- a/tests/parameter/test_get_set_wrapping.py +++ b/tests/parameter/test_get_set_wrapping.py @@ -70,7 +70,9 @@ def test_gettable_settable_attributes_with_get_set_raw( listed as gettable/settable and reverse.""" class GetSetParam(baseclass): # type: ignore[valid-type,misc] - def __init__(self, *args: Any, initial_value: Any = None, **kwargs: Any): + def __init__( + self, *args: Any, initial_value: Any = None, **kwargs: Any + ) -> None: self._value = initial_value super().__init__(*args, **kwargs) diff --git a/tests/parameter/test_group_parameter.py b/tests/parameter/test_group_parameter.py index 9464b031e1c..d6152dbc7fb 100644 --- a/tests/parameter/test_group_parameter.py +++ b/tests/parameter/test_group_parameter.py @@ -8,11 +8,11 @@ from qcodes.parameters import Group, GroupParameter if TYPE_CHECKING: - from collections.abc import Callable + from collections.abc import Callable, Generator @pytest.fixture(autouse=True) -def close_all_instruments(): +def close_all_instruments() -> "Generator[None, None, None]": """Makes sure that after startup and teardown all instruments are closed""" Instrument.close_all() yield diff --git a/tests/parameter/test_issequence.py b/tests/parameter/test_issequence.py index 3e4bab334bc..769ca8bdd13 100644 --- a/tests/parameter/test_issequence.py +++ b/tests/parameter/test_issequence.py @@ -1,9 +1,14 @@ +from typing import TYPE_CHECKING, Any, NoReturn + import pytest from qcodes.parameters.sequence_helpers import is_sequence +if TYPE_CHECKING: + from collections.abc import Iterator, Sequence + -def a_func(): +def a_func() -> NoReturn: raise RuntimeError("this function shouldn't get called") @@ -40,12 +45,12 @@ class AClass: @pytest.mark.parametrize("val", yes_sequence) -def test_yes(val) -> None: +def test_yes(val: "Sequence[Any] | Iterator[Any]") -> None: assert is_sequence(val) @pytest.mark.parametrize("val", no_sequence) -def test_no(val) -> None: +def test_no(val: object) -> None: assert not is_sequence(val) diff --git a/tests/parameter/test_issequenceof.py b/tests/parameter/test_issequenceof.py index 7c710473dfe..2a16ac36e45 100644 --- a/tests/parameter/test_issequenceof.py +++ b/tests/parameter/test_issequenceof.py @@ -1,9 +1,12 @@ -from typing import Any +from typing import TYPE_CHECKING, Any import pytest from qcodes.parameters.sequence_helpers import is_sequence_of +if TYPE_CHECKING: + from collections.abc import Sequence + simple_good: list[tuple[Any, ...]] = [ # empty lists pass without even checking that we provided a # valid type spec @@ -20,7 +23,7 @@ @pytest.mark.parametrize("args", simple_good) -def test_simple_good(args) -> None: +def test_simple_good(args: tuple[Any, ...]) -> None: assert is_sequence_of(*args) @@ -28,7 +31,7 @@ def test_simple_good(args) -> None: @pytest.mark.parametrize("args", simple_bad) -def test_simple_bad(args) -> None: +def test_simple_bad(args: tuple[Any, ...]) -> None: assert not is_sequence_of(*args) # second arg, if provided, must be a type or tuple of types # failing this doesn't return False, it raises an error @@ -51,7 +54,9 @@ def test_examples_raises() -> None: @pytest.mark.parametrize("args", good_depth) -def test_depth_good(args) -> None: +def test_depth_good( + args: "tuple[Sequence[Any], type | tuple[type, ...] | None, int]", +) -> None: assert is_sequence_of(*args) @@ -59,7 +64,7 @@ def test_depth_good(args) -> None: @pytest.mark.parametrize("args", bad_depth) -def test_depth_bad(args) -> None: +def test_depth_bad(args: "tuple[Sequence[Any], type, int]") -> None: assert not is_sequence_of(*args) @@ -77,7 +82,9 @@ def test_depth_bad(args) -> None: @pytest.mark.parametrize("args", good_shapes) -def test_shape_good(args) -> None: +def test_shape_good( + args: "tuple[Sequence[Any], type | tuple[type, ...] | None, tuple[int, ...]]", +) -> None: obj, types, shape = args assert is_sequence_of(obj, types, shape=shape) @@ -93,7 +100,9 @@ def test_shape_good(args) -> None: @pytest.mark.parametrize("args", bad_shapes) -def test_shape_bad(args) -> None: +def test_shape_bad( + args: "tuple[Sequence[Any], type | tuple[type, ...], tuple[int, ...]]", +) -> None: obj, types, shape = args assert not is_sequence_of(obj, types, shape=shape) diff --git a/tests/parameter/test_multi_channel_instrument_parameter.py b/tests/parameter/test_multi_channel_instrument_parameter.py index c3b7d12c145..19f051668e6 100644 --- a/tests/parameter/test_multi_channel_instrument_parameter.py +++ b/tests/parameter/test_multi_channel_instrument_parameter.py @@ -19,7 +19,7 @@ def dummy_channel_instrument() -> "Generator[DummyChannelInstrument, None, None] def test_set_multi_channel_instrument_parameter( dummy_channel_instrument: DummyChannelInstrument, -): +) -> None: """Tests :class:`MultiChannelInstrumentParameter` set method.""" for name, param in dummy_channel_instrument.channels[0].parameters.items(): if not param.settable: diff --git a/tests/parameter/test_multi_parameter.py b/tests/parameter/test_multi_parameter.py index 5e60778a965..453e319872f 100644 --- a/tests/parameter/test_multi_parameter.py +++ b/tests/parameter/test_multi_parameter.py @@ -8,7 +8,7 @@ class SimpleMultiParam(MultiParameter): - def __init__(self, return_val: Any, *args: Any, **kwargs: Any): + def __init__(self, return_val: Any, *args: Any, **kwargs: Any) -> None: self._return_val = return_val self._get_count = 0 super().__init__(*args, **kwargs) diff --git a/tests/parameter/test_parameter_cache.py b/tests/parameter/test_parameter_cache.py index ca1220f063a..68d5954be05 100644 --- a/tests/parameter/test_parameter_cache.py +++ b/tests/parameter/test_parameter_cache.py @@ -256,7 +256,7 @@ def test_no_get_max_val_age_runtime_error( value = 1 class LocalParameter(ParameterBase): - def __init__(self, *args: Any, **kwargs: Any): + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) self.set_raw = lambda x: x # type: ignore[method-assign] self.set = self._wrap_set(self.set_raw) diff --git a/tests/parameter/test_parameter_context_manager.py b/tests/parameter/test_parameter_context_manager.py index 9f30bcc5838..f4cc7fa6f1b 100644 --- a/tests/parameter/test_parameter_context_manager.py +++ b/tests/parameter/test_parameter_context_manager.py @@ -11,7 +11,7 @@ class DummyTrackingInstrument(DummyInstrument): - def __init__(self, name: str): + def __init__(self, name: str) -> None: super().__init__(name) self.add_parameter("a", set_cmd=None, get_cmd=None) diff --git a/tests/parameter/test_parameter_on_set_callback.py b/tests/parameter/test_parameter_on_set_callback.py index d1a9cdee8b3..89bc5a7f4aa 100644 --- a/tests/parameter/test_parameter_on_set_callback.py +++ b/tests/parameter/test_parameter_on_set_callback.py @@ -269,12 +269,12 @@ def timing_callback(param: ParameterBase, value: Any) -> None: def test_set_callback_for_instance( basic_callback: "Callable[[ParameterBase, Any], None]", captured_params: list[tuple[ParameterBase, Any]], -): +) -> None: param_a = Parameter("test_param_a", set_cmd=None, get_cmd=None) param_b = Parameter("test_param_b", set_cmd=None, get_cmd=None) captured_instance_params = [] - def callback(param: ParameterBase, val): + def callback(param: ParameterBase, val: Any) -> None: if ParameterBase.global_on_set_callback: ParameterBase.global_on_set_callback(param, val) captured_instance_params.append(val) diff --git a/tests/parameter/test_parameter_override.py b/tests/parameter/test_parameter_override.py index a66914bf80d..3017b87c913 100644 --- a/tests/parameter/test_parameter_override.py +++ b/tests/parameter/test_parameter_override.py @@ -6,11 +6,14 @@ from qcodes.instrument import Instrument if TYPE_CHECKING: - from qcodes.parameters import Parameter + from typing import Unpack + + from qcodes.instrument import InstrumentBaseKWArgs + from qcodes.parameters import Parameter, ParameterBase class DummyOverrideInstr(Instrument): - def __init__(self, name, **kwargs): + def __init__(self, name: str, **kwargs: "Unpack[InstrumentBaseKWArgs]") -> None: """ This instrument errors because it tries to override an attribute with a parameter. Removing the parameter using `self.parameters.pop("voltage")` is not safe but @@ -25,19 +28,19 @@ def __init__(self, name, **kwargs): class DummyParameterIsAttrInstr(Instrument): - def __init__(self, name, **kwargs): + def __init__(self, name: str, **kwargs: "Unpack[InstrumentBaseKWArgs]") -> None: """ This instrument errors because it tries to override an attribute with a parameter. """ super().__init__(name, **kwargs) self.voltage = self.add_parameter("voltage", set_cmd=None, get_cmd=None) - def voltage(self): + def voltage(self) -> int: return 0 class DummyParameterIsPropertyInstr(Instrument): - def __init__(self, name, **kwargs): + def __init__(self, name: str, **kwargs: "Unpack[InstrumentBaseKWArgs]") -> None: """ We allow overriding a property since this pattern has been seen in the wild to define an interface for the instrument. @@ -46,7 +49,7 @@ def __init__(self, name, **kwargs): self.add_parameter("voltage", set_cmd=None, get_cmd=None) @property - def voltage(self): + def voltage(self) -> "ParameterBase": return self.parameters["voltage"] @@ -56,7 +59,7 @@ class DummyClassAttrInstr(Instrument): voltage: "Parameter | None" = None frequency: "Parameter | None" = None - def __init__(self, name, **kwargs): + def __init__(self, name: str, **kwargs: "Unpack[InstrumentBaseKWArgs]") -> None: """ We allow overriding a property since this pattern has been seen in the wild to define an interface for the instrument. @@ -68,7 +71,7 @@ def __init__(self, name, **kwargs): class DummyInstr(Instrument): - def __init__(self, name, **kwargs): + def __init__(self, name: str, **kwargs: "Unpack[InstrumentBaseKWArgs]") -> None: """ We allow overriding a property since this pattern has been seen in the wild to define an interface for the instrument. @@ -79,7 +82,7 @@ def __init__(self, name, **kwargs): self.some_attr = 1 -def test_overriding_parameter_attribute_with_parameter_raises(): +def test_overriding_parameter_attribute_with_parameter_raises() -> None: with pytest.raises( KeyError, match="Duplicate parameter name voltage on instrument", @@ -87,19 +90,21 @@ def test_overriding_parameter_attribute_with_parameter_raises(): DummyOverrideInstr("my_instr") -def test_overriding_attribute_with_parameter_raises(): +def test_overriding_attribute_with_parameter_raises() -> None: with pytest.warns( match="Parameter voltage overrides an attribute of the same name on instrument", ): DummyParameterIsAttrInstr("my_instr") -def test_overriding_property_with_parameter_works(request): +def test_overriding_property_with_parameter_works( + request: pytest.FixtureRequest, +) -> None: request.addfinalizer(DummyParameterIsPropertyInstr.close_all) DummyParameterIsPropertyInstr("my_instr") -def test_removing_parameter_works(request): +def test_removing_parameter_works(request: pytest.FixtureRequest) -> None: request.addfinalizer(DummyInstr.close_all) a = DummyInstr("my_instr") @@ -113,7 +118,9 @@ def test_removing_parameter_works(request): assert a.some_attr == 1 -def test_removed_parameter_from_prop_instrument_works(request): +def test_removed_parameter_from_prop_instrument_works( + request: pytest.FixtureRequest, +) -> None: request.addfinalizer(DummyParameterIsPropertyInstr.close_all) a = DummyParameterIsPropertyInstr("my_instr") a.remove_parameter("voltage") @@ -121,7 +128,9 @@ def test_removed_parameter_from_prop_instrument_works(request): a.voltage.set(1) -def test_remove_parameter_from_class_attr_works(request, caplog): +def test_remove_parameter_from_class_attr_works( + request: pytest.FixtureRequest, caplog: pytest.LogCaptureFixture +) -> None: request.addfinalizer(DummyClassAttrInstr.close_all) a = DummyClassAttrInstr("my_instr") diff --git a/tests/parameter/test_parameter_ramp.py b/tests/parameter/test_parameter_ramp.py index a1d49af003e..549f263c500 100644 --- a/tests/parameter/test_parameter_ramp.py +++ b/tests/parameter/test_parameter_ramp.py @@ -40,7 +40,7 @@ def test_step_ramp(caplog: LogCaptureFixture) -> None: @given(scale=hst.integers(1, 100), value=hst.floats(min_value=1e-9, max_value=10)) -def test_ramp_scaled(scale, value) -> None: +def test_ramp_scaled(scale: int, value: float) -> None: start_point = 0.0 p = MemoryParameter(name="p", scale=scale, initial_value=start_point) assert p() == start_point @@ -75,7 +75,7 @@ def test_ramp_scaled(scale, value) -> None: @given(value=hst.floats(min_value=1e-9, max_value=10)) -def test_ramp_parser(value) -> None: +def test_ramp_parser(value: float) -> None: start_point = 0.0 p = MemoryParameter( name="p", @@ -118,7 +118,7 @@ def test_ramp_parser(value) -> None: @given(scale=hst.integers(1, 100), value=hst.floats(min_value=1e-9, max_value=10)) @settings(deadline=None) -def test_ramp_parsed_scaled(scale, value) -> None: +def test_ramp_parsed_scaled(scale: int, value: float) -> None: start_point = 0.0 p = MemoryParameter( name="p", @@ -158,11 +158,11 @@ def test_ramp_parsed_scaled(scale, value) -> None: def test_stepping_from_invalid_starting_point() -> None: the_value = -10 - def set_function(value): + def set_function(value: float) -> None: nonlocal the_value the_value = value - def get_function(): + def get_function() -> float: return the_value a = Parameter( diff --git a/tests/parameter/test_parameter_registration.py b/tests/parameter/test_parameter_registration.py index 235bae32f1f..00f6920c267 100644 --- a/tests/parameter/test_parameter_registration.py +++ b/tests/parameter/test_parameter_registration.py @@ -20,7 +20,7 @@ class BrokenParameter(Parameter): def __init__( self, name: str, instrument: "InstrumentBase", *args: Any, **kwargs: Any - ): + ) -> None: super().__init__(name, *args, **kwargs) self._instrument = instrument @@ -30,7 +30,7 @@ class BrokenParameter2(Parameter): def __init__( self, name: str, instrument: "InstrumentBase", set_cmd: Any, get_cmd: Any - ): + ) -> None: super().__init__(name=name, instrument=instrument) diff --git a/tests/parameter/test_parameter_scale_offset.py b/tests/parameter/test_parameter_scale_offset.py index 7ba9533c668..0070e088f95 100644 --- a/tests/parameter/test_parameter_scale_offset.py +++ b/tests/parameter/test_parameter_scale_offset.py @@ -50,7 +50,13 @@ def test_scale_raw_value() -> None: # the following test stra @hst.composite -def iterable_or_number(draw, values, size, values_scalar, is_values): +def iterable_or_number( + draw: hst.DrawFn, + values: hst.SearchStrategy[float], + size: hst.SearchStrategy[int], + values_scalar: hst.SearchStrategy[bool], + is_values: bool, +) -> float | list[float]: if draw(values_scalar): # if parameter values are scalar, # return scalar for values and scale/offset @@ -74,7 +80,11 @@ def iterable_or_number(draw, values, size, values_scalar, is_values): offsets=iterable_or_number(TestFloats, SharedSize, ValuesScalar, False), scales=iterable_or_number(TestFloats, SharedSize, ValuesScalar, False), ) -def test_scale_and_offset_raw_value_iterable(values, offsets, scales) -> None: +def test_scale_and_offset_raw_value_iterable( + values: float | list[float], + offsets: float | list[float], + scales: float | list[float], +) -> None: p = Parameter(name="test_scale_and_offset_raw_value", set_cmd=None) # test that scale and offset does not change the default behaviour @@ -95,6 +105,9 @@ def test_scale_and_offset_raw_value_iterable(values, offsets, scales) -> None: # test set, only for scalar values if not isinstance(values, Iterable): + # scalar values imply scalar scale/offset (shared ``values_scalar`` draw) + assert not isinstance(scales, Iterable) + assert not isinstance(offsets, Iterable) p(values) # No set/get cmd performed np.testing.assert_allclose( @@ -138,7 +151,9 @@ def test_scale_and_offset_raw_value_iterable(values, offsets, scales) -> None: scales=iterable_or_number(TestFloats, SharedSize, ValuesScalar, False), ) def test_scale_and_offset_raw_value_iterable_for_set_cache( - values, offsets, scales + values: float | list[float], + offsets: float | list[float], + scales: float | list[float], ) -> None: p = Parameter(name="test_scale_and_offset_raw_value", set_cmd=None) @@ -199,7 +214,7 @@ def test_scale_and_offset_raw_value_iterable_for_set_cache( def test_numpy_array_valued_parameter_preserves_type_if_scale_and_offset_are_set() -> ( None ): - def rands(): + def rands() -> np.ndarray: return np.random.default_rng().standard_normal(5) param = Parameter(name="test_param", set_cmd=None, get_cmd=rands) diff --git a/tests/parameter/test_parameter_with_setpoints.py b/tests/parameter/test_parameter_with_setpoints.py index 497fe08a627..c16fe018d43 100644 --- a/tests/parameter/test_parameter_with_setpoints.py +++ b/tests/parameter/test_parameter_with_setpoints.py @@ -1,14 +1,23 @@ +from typing import TYPE_CHECKING + import numpy as np import pytest import qcodes.validators as vals from qcodes.parameters import Parameter, ParameterWithSetpoints, expand_setpoints_helper +if TYPE_CHECKING: + from collections.abc import Generator + _rng = np.random.default_rng() +type SetpointsParameters = tuple[ + Parameter, Parameter, Parameter, Parameter, Parameter, Parameter +] + @pytest.fixture() -def parameters(): +def parameters() -> "Generator[SetpointsParameters, None, None]": n_points_1 = Parameter("n_points_1", set_cmd=None, vals=vals.Ints()) n_points_2 = Parameter("n_points_2", set_cmd=None, vals=vals.Ints()) n_points_3 = Parameter("n_points_3", set_cmd=None, vals=vals.Ints()) @@ -370,7 +379,7 @@ def test_validation_one_sp_dim_missing() -> None: param_sp_without_shape.validate(param_sp_without_shape.get()) -def test_expand_setpoints_1c(parameters) -> None: +def test_expand_setpoints_1c(parameters: SetpointsParameters) -> None: """ Test that the setpoints expander helper function works correctly """ @@ -392,7 +401,7 @@ def test_expand_setpoints_1c(parameters) -> None: assert len(data[0][1]) == len(data[1][1]) -def test_expand_setpoints_2d(parameters) -> None: +def test_expand_setpoints_2d(parameters: SetpointsParameters) -> None: n_points_1, n_points_2, _n_points_3, setpoints_1, setpoints_2, _setpoints_3 = ( parameters ) @@ -420,7 +429,7 @@ def test_expand_setpoints_2d(parameters) -> None: np.testing.assert_array_equal(sp2[i, :], np.arange(sp1.shape[1])) -def test_expand_setpoints_3d(parameters) -> None: +def test_expand_setpoints_3d(parameters: SetpointsParameters) -> None: n_points_1, n_points_2, n_points_3, setpoints_1, setpoints_2, setpoints_3 = ( parameters ) diff --git a/tests/parameter/test_permissive_range.py b/tests/parameter/test_permissive_range.py index c0a5928a956..c4e511b3666 100644 --- a/tests/parameter/test_permissive_range.py +++ b/tests/parameter/test_permissive_range.py @@ -23,13 +23,13 @@ @pytest.mark.parametrize("args", bad_args) -def test_bad_calls(args) -> None: +def test_bad_calls(args: list[float | str | None]) -> None: with pytest.raises(TypeError): - permissive_range(*args) + permissive_range(*args) # pyright: ignore[reportArgumentType] @pytest.mark.parametrize("args,result", good_args) -def test_good_calls(args, result) -> None: +def test_good_calls(args: tuple[float, float, float], result: list[float]) -> None: # TODO(giulioungaretti) # not sure what we are testing here. # in python 1.0 and 1 are actually the same diff --git a/tests/parameter/test_scaled_parameter.py b/tests/parameter/test_scaled_parameter.py index 9c9a0092eb2..fb2357ce406 100644 --- a/tests/parameter/test_scaled_parameter.py +++ b/tests/parameter/test_scaled_parameter.py @@ -116,7 +116,7 @@ def test_wrapped_parameter(instrument: DummyInstrument) -> None: assert instrument.scaler.wrapped_parameter == instrument.target_parameter -def test_divider(instrument) -> None: +def test_divider(instrument: DummyInstrument) -> None: test_division = 10 test_value = 5 diff --git a/tests/parameter/test_validators.py b/tests/parameter/test_validators.py index 4d98bbfb786..338a9247755 100644 --- a/tests/parameter/test_validators.py +++ b/tests/parameter/test_validators.py @@ -6,7 +6,7 @@ from qcodes.validators import Ints, Numbers -def test_add_ints_validator_to_parameter(): +def test_add_ints_validator_to_parameter() -> None: p = Parameter("test_param", set_cmd=None, get_cmd=None) ints_val = Ints(min_value=0, max_value=10) p.add_validator(ints_val) @@ -15,7 +15,7 @@ def test_add_ints_validator_to_parameter(): assert p.vals is ints_val -def test_remove_ints_validator_from_parameter(): +def test_remove_ints_validator_from_parameter() -> None: p = Parameter("test_param", set_cmd=None, get_cmd=None) p.add_validator(Ints(min_value=0, max_value=10)) p.remove_validator() @@ -148,7 +148,7 @@ def test_validator_snapshot() -> None: assert "" == snapshot["vals"] -def test_replace_vals(): +def test_replace_vals() -> None: p = Parameter("test_param", set_cmd=None, get_cmd=None) val1 = Ints(min_value=0, max_value=10) p.add_validator(val1) From bbb961a25144a0b48d84048067db4c94fdfe866d Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Tue, 20 Oct 2026 07:30:44 +0200 Subject: [PATCH 08/20] Add type annotations to core tests and helpers --- tests/common.py | 2 +- tests/dataset_generators.py | 13 +++- tests/helpers/test_json_encoder.py | 12 +-- tests/helpers/test_strip_attrs.py | 6 +- .../test_parse_parameter_attr.py | 8 +- tests/test_abstract_instrument.py | 29 ++++---- tests/test_autoloadable_channels.py | 4 +- tests/test_channels.py | 8 +- tests/test_command.py | 8 +- tests/test_config.py | 57 +++++++++------ tests/test_field_vector.py | 23 +++--- tests/test_instrument.py | 30 +++++--- tests/test_interactive_widget.py | 36 ++++++--- tests/test_logger.py | 4 +- tests/test_metadata.py | 10 ++- tests/test_monitor.py | 27 +++++-- tests/test_plot_utils.py | 15 ++-- tests/test_snapshot.py | 4 +- tests/test_station.py | 73 ++++++++++++------- tests/test_sweep_values.py | 29 +++++--- tests/test_threading.py | 17 +++-- tests/test_visa.py | 26 ++++--- tests/utils/test_isfunction.py | 4 +- tests/utils/test_partial_with_docstring.py | 6 +- tests/validators/test_literal.py | 2 +- 25 files changed, 286 insertions(+), 167 deletions(-) diff --git a/tests/common.py b/tests/common.py index 11d4df019b2..0f3b7aff565 100644 --- a/tests/common.py +++ b/tests/common.py @@ -139,7 +139,7 @@ def skip_if_no_fixtures(dbname: str | Path) -> None: class DummyComponent(MetadatableWithName): """Docstring for DummyComponent.""" - def __init__(self, name: str): + def __init__(self, name: str) -> None: super().__init__() self.name = name diff --git a/tests/dataset_generators.py b/tests/dataset_generators.py index b54624cfa27..7f1e196514f 100644 --- a/tests/dataset_generators.py +++ b/tests/dataset_generators.py @@ -1,12 +1,21 @@ +from typing import TYPE_CHECKING + import numpy as np from qcodes.dataset.descriptions.dependencies import InterDependencies_ from qcodes.parameters import ParamSpecBase +if TYPE_CHECKING: + from qcodes.dataset.data_set import DataSet + def dataset_with_outliers_generator( - ds, data_offset=5, low_outlier=-3, high_outlier=1, background_noise=True -): + ds: "DataSet", + data_offset: float = 5, + low_outlier: float = -3, + high_outlier: float = 1, + background_noise: bool = True, +) -> "DataSet": x = ParamSpecBase("x", "numeric", label="Flux", unit="e^2/hbar") t = ParamSpecBase("t", "numeric", label="Time", unit="s") z = ParamSpecBase("z", "numeric", label="Majorana number", unit="Anyon") diff --git a/tests/helpers/test_json_encoder.py b/tests/helpers/test_json_encoder.py index 01a525e454f..01fc4f5c64a 100644 --- a/tests/helpers/test_json_encoder.py +++ b/tests/helpers/test_json_encoder.py @@ -89,7 +89,7 @@ def test_non_serializable() -> None: e = NumpyJSONEncoder() class Dummy: - def __str__(self): + def __str__(self) -> str: return "i am a dummy with \\ slashes /" assert e.encode(Dummy()) == '"i am a dummy with \\\\ slashes /"' @@ -103,13 +103,13 @@ def test_object_with_serialization_method() -> None: e = NumpyJSONEncoder() class Dummy: - def __init__(self): + def __init__(self) -> None: self.confession = "a_dict_addict" - def __str__(self): + def __str__(self) -> str: return "me as a string" - def _JSONEncoder(self): + def _JSONEncoder(self) -> dict[str, str]: return {"i_am_actually": self.confession} assert e.encode(Dummy()) == '{"i_am_actually": "a_dict_addict"}' @@ -166,7 +166,7 @@ def test_numpy_encoder_examplemetadata() -> None: @given(bytes_to_encode=hst.binary(max_size=100)) -def test_bytes(bytes_to_encode) -> None: +def test_bytes(bytes_to_encode: bytes) -> None: e = NumpyJSONEncoder() v = e.encode(bytes_to_encode) @@ -177,7 +177,7 @@ def test_bytes(bytes_to_encode) -> None: @given(bytes_to_encode=hst.binary(max_size=100)) -def test_bytes_array(bytes_to_encode) -> None: +def test_bytes_array(bytes_to_encode: bytes) -> None: e = NumpyJSONEncoder() value = bytearray(bytes_to_encode) v = e.encode(value) diff --git a/tests/helpers/test_strip_attrs.py b/tests/helpers/test_strip_attrs.py index 5dc8e39ddbd..e032ed292f1 100644 --- a/tests/helpers/test_strip_attrs.py +++ b/tests/helpers/test_strip_attrs.py @@ -1,4 +1,4 @@ -from typing import Any +from typing import Any, NoReturn import pytest @@ -11,12 +11,12 @@ class A: class BadKeysDict(dict[Any, Any]): - def keys(self): + def keys(self) -> NoReturn: raise RuntimeError("you can't have the keys!") class NoDelDict(dict[Any, Any]): - def __delitem__(self, item): + def __delitem__(self, item: Any) -> NoReturn: raise KeyError("get your hands off me!") diff --git a/tests/sphinx_extension/test_parse_parameter_attr.py b/tests/sphinx_extension/test_parse_parameter_attr.py index a19d457eb54..c0331a71519 100644 --- a/tests/sphinx_extension/test_parse_parameter_attr.py +++ b/tests/sphinx_extension/test_parse_parameter_attr.py @@ -1,3 +1,5 @@ +from typing import Any + import pytest from sphinx.util.inspect import safe_getattr from typing_extensions import deprecated @@ -15,7 +17,7 @@ class DummyTestClass(InstrumentBase): A class attribute """ - def __init__(self, *args, **kwargs): + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) self.other_attr = "InstanceAttribute" @@ -41,7 +43,7 @@ class DummyDecoratedInitTestClass(InstrumentBase): """ @deprecated("Deprecate to test that decorated init is handled correctly") - def __init__(self, *args, **kwargs): + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) self.other_attr = "InstanceAttribute" @@ -57,7 +59,7 @@ class DummyDecoratedClassTestClass(InstrumentBase): A class attribute """ - def __init__(self, *args, **kwargs): + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) self.other_attr = "InstanceAttribute" diff --git a/tests/test_abstract_instrument.py b/tests/test_abstract_instrument.py index 63a6bd729b3..5b32772d1a7 100644 --- a/tests/test_abstract_instrument.py +++ b/tests/test_abstract_instrument.py @@ -1,10 +1,13 @@ -from typing import Any +from typing import TYPE_CHECKING, Any import pytest from pytest import FixtureRequest from qcodes.instrument import ChannelList, Instrument, InstrumentBase, InstrumentChannel +if TYPE_CHECKING: + from collections.abc import Generator + class ExampleBaseVoltageSource(Instrument): """ @@ -13,7 +16,7 @@ class ExampleBaseVoltageSource(Instrument): allows us to enforce an interface. """ - def __init__(self, name: str): + def __init__(self, name: str) -> None: super().__init__(name) self.add_parameter( @@ -28,7 +31,7 @@ class VoltageSource(ExampleBaseVoltageSource): Make a specific implementation of the interface """ - def __init__(self, name: str): + def __init__(self, name: str) -> None: super().__init__(name) self.add_parameter("voltage", unit="V", get_cmd=None, set_cmd=None) @@ -45,7 +48,7 @@ class VoltageSourceBadUnit(ExampleBaseVoltageSource): The units must match between sub and base classes """ - def __init__(self, name: str): + def __init__(self, name: str) -> None: super().__init__(name) self.add_parameter( @@ -63,7 +66,7 @@ class VoltageSourceInitException(Instrument): is raised """ - def __init__(self, name: str, do_raise=True): + def __init__(self, name: str, do_raise: bool = True) -> None: super().__init__(name) if do_raise: @@ -75,7 +78,7 @@ class VoltageChannelBase(InstrumentChannel): Create a channel with an abstract parameter """ - def __init__(self, parent: InstrumentBase, name: str, **kwargs: Any): + def __init__(self, parent: InstrumentBase, name: str, **kwargs: Any) -> None: super().__init__(parent, name, **kwargs) self.add_parameter("voltage", unit="V", abstract=True) @@ -86,7 +89,7 @@ class VoltageChannel(VoltageChannelBase): Create an implementation of the abstract channel """ - def __init__(self, parent: InstrumentBase, name: str, **kwargs: Any): + def __init__(self, parent: InstrumentBase, name: str, **kwargs: Any) -> None: super().__init__(parent, name, **kwargs) self.add_parameter("voltage", unit="V", get_cmd=None, set_cmd=None) @@ -101,7 +104,7 @@ class VoltageAbstractChannelSource(Instrument): def __init__( self, name: str, - ): + ) -> None: super().__init__(name) channel = VoltageChannelBase(self, "voltage") self.add_submodule("voltage", channel) @@ -117,7 +120,7 @@ class VoltageChannelSource(Instrument): def __init__( self, name: str, - ): + ) -> None: super().__init__(name) channel = VoltageChannel(self, "voltage") self.add_submodule("voltage", channel) @@ -132,7 +135,7 @@ class VoltageAbstractChannelListSource(Instrument): def __init__( self, name: str, - ): + ) -> None: super().__init__(name) channel = VoltageChannelBase(self, "voltage") channellist = ChannelList(self, "cl", VoltageChannelBase, chan_list=[channel]) @@ -149,7 +152,7 @@ class VoltageChannelListSource(Instrument): def __init__( self, name: str, - ): + ) -> None: super().__init__(name) channel = VoltageChannel(self, "voltage") channellist = ChannelList(self, "cl", VoltageChannel, chan_list=[channel]) @@ -157,13 +160,13 @@ def __init__( @pytest.fixture(name="driver", scope="module") -def _driver(): +def _driver() -> "Generator[VoltageSource, None, None]": drvr = VoltageSource("abstract_instrument_driver") yield drvr drvr.close() -def test_sanity(driver) -> None: +def test_sanity(driver: VoltageSource) -> None: """ If all abstract parameters are implemented, we should be able to instantiate the instrument diff --git a/tests/test_autoloadable_channels.py b/tests/test_autoloadable_channels.py index 1a79cd142da..e2633a99a57 100644 --- a/tests/test_autoloadable_channels.py +++ b/tests/test_autoloadable_channels.py @@ -103,7 +103,7 @@ class SimpleTestChannel(AutoLoadableInstrumentChannel): @classmethod def _discover_from_instrument( - cls, parent: InstrumentBase, **kwargs + cls, parent: InstrumentBase, **kwargs: Any ) -> list[dict[Any, Any]]: """ New channels need `name` and `channel` keyword arguments. @@ -131,7 +131,7 @@ def _discover_from_instrument( @classmethod def _get_new_instance_kwargs( - cls, parent: InstrumentBase | None = None, **kwargs + cls, parent: InstrumentBase | None = None, **kwargs: Any ) -> dict[Any, Any]: """ Find the smallest channel number not yet occupied. An optional keyword diff --git a/tests/test_channels.py b/tests/test_channels.py index 910c9bec549..0537146357a 100644 --- a/tests/test_channels.py +++ b/tests/test_channels.py @@ -1,7 +1,7 @@ import logging import re from collections.abc import Generator, Sequence -from typing import TYPE_CHECKING, assert_type +from typing import TYPE_CHECKING, Any, assert_type import hypothesis.strategies as hst import numpy as np @@ -114,7 +114,7 @@ def test_channels_get(dci: DummyChannelInstrument) -> None: @settings(suppress_health_check=(HealthCheck.function_scoped_fixture,)) @given(value=hst.floats(0, 300), channel=hst.integers(0, 3)) def test_channel_access_is_identical( - dci: DummyChannelInstrument, value, channel + dci: DummyChannelInstrument, value: float, channel: int ) -> None: channel_to_label = {0: "A", 1: "B", 2: "C", 3: "D"} label = channel_to_label[channel] @@ -836,7 +836,7 @@ def test_multi_function_on_empty_channel_tuple_raises( channels.multi_function("temperature") -def _verify_multiparam_data(data) -> None: +def _verify_multiparam_data(data: Any) -> None: assert "multi_setpoint_param_this_setpoint_set" in data.arrays assert_array_equal( data.arrays["multi_setpoint_param_this_setpoint_set"].ndarray, @@ -856,7 +856,7 @@ def _verify_multiparam_data(data) -> None: ) -def _verify_array_data(data, channels=("A",)) -> None: +def _verify_array_data(data: Any, channels: Sequence[str] = ("A",)) -> None: assert "array_setpoint_param_this_setpoint_set" in data.arrays assert_array_equal( data.arrays["array_setpoint_param_this_setpoint_set"].ndarray, diff --git a/tests/test_command.py b/tests/test_command.py index 8db963dcb9a..7b9dbc34bfa 100644 --- a/tests/test_command.py +++ b/tests/test_command.py @@ -46,16 +46,16 @@ def no_cmd_function() -> NoReturn: def test_cmd_str() -> None: - def f_now(x): + def f_now(x: str) -> str: return x + " now" - def upper(s): + def upper(s: str) -> str: return s.upper() - def reversestr(s): + def reversestr(s: str) -> str: return s[::-1] - def swap(a, b): + def swap(a: str, b: str) -> tuple[str, str]: return b, a # basic exec_str diff --git a/tests/test_config.py b/tests/test_config.py index e85c10b7e57..f1cf2315411 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -2,6 +2,7 @@ import json import os from functools import partial +from typing import TYPE_CHECKING, Any from unittest.mock import PropertyMock, mock_open import jsonschema.exceptions @@ -10,6 +11,12 @@ import qcodes from qcodes.configuration import Config +if TYPE_CHECKING: + from collections.abc import Generator, Mapping + from pathlib import Path + + from pytest_mock import MockerFixture, MockType + VALID_JSON = "{}" ENV_KEY = "/dev/random" BAD_JSON = "{}" @@ -107,12 +114,12 @@ } -def side_effect(map, name): +def side_effect(map: "Mapping[str, dict[str, Any]]", name: str) -> dict[str, Any]: return map[name] @pytest.fixture(scope="function") -def path_to_config_file_on_disk(tmp_path): +def path_to_config_file_on_disk(tmp_path: "Path") -> "Generator[str, None, None]": contents = { "core": { "loglevel": "WARNING", @@ -137,13 +144,13 @@ def path_to_config_file_on_disk(tmp_path): @pytest.fixture(name="config") -def _make_config(): +def _make_config() -> "Generator[Config, None, None]": conf = Config() yield conf @pytest.fixture(name="load_config") -def _make_mock_config(mocker): +def _make_mock_config(mocker: "MockerFixture") -> "Generator[MockType, None, None]": schema = mocker.patch.object(Config, "current_schema", new_callable=PropertyMock) env = mocker.patch.object(Config, "env_file_name", new_callable=PropertyMock) load_config = mocker.patch.object(Config, "load_config") @@ -155,42 +162,46 @@ def _make_mock_config(mocker): yield load_config -def test_missing_config_file(config) -> None: +def test_missing_config_file(config: Config) -> None: with pytest.raises(FileNotFoundError): config.load_config("./missing.json") -def test_default_config_files(config, load_config) -> None: +def test_default_config_files(config: Config, load_config: "MockType") -> None: load_config.side_effect = partial(side_effect, GOOD_CONFIG_MAP) # don't try to load custom schemas - config.schema_cwd_file_name = None - config.schema_home_file_name = None - config.schema_env_file_name = None + config.schema_cwd_file_name = None # pyright: ignore[reportAttributeAccessIssue] + config.schema_home_file_name = None # pyright: ignore[reportAttributeAccessIssue] + config.schema_env_file_name = None # pyright: ignore[reportAttributeAccessIssue] config.defaults, _ = config.load_default() - config = config.update_config() - assert config == CONFIG + updated_config = config.update_config() + assert updated_config == CONFIG -def test_bad_config_files(config, load_config) -> None: +def test_bad_config_files(config: Config, load_config: "MockType") -> None: load_config.side_effect = partial(side_effect, BAD_CONFIG_MAP) # don't try to load custom schemas - config.schema_cwd_file_name = None - config.schema_home_file_name = None - config.schema_env_file_name = None + config.schema_cwd_file_name = None # pyright: ignore[reportAttributeAccessIssue] + config.schema_home_file_name = None # pyright: ignore[reportAttributeAccessIssue] + config.schema_env_file_name = None # pyright: ignore[reportAttributeAccessIssue] config.defaults, _ = config.load_default() with pytest.raises(jsonschema.exceptions.ValidationError): config.update_config() -def test_user_schema(config, load_config, mocker) -> None: +def test_user_schema( + config: Config, load_config: "MockType", mocker: "MockerFixture" +) -> None: mocker.patch("builtins.open", mock_open(read_data=USER_SCHEMA)) load_config.side_effect = partial(side_effect, GOOD_CONFIG_MAP) config.defaults, _ = config.load_default() - config = config.update_config() - assert config == CONFIG + updated_config = config.update_config() + assert updated_config == CONFIG -def test_bad_user_schema(config, load_config, mocker) -> None: +def test_bad_user_schema( + config: Config, load_config: "MockType", mocker: "MockerFixture" +) -> None: mocker.patch("builtins.open", mock_open(read_data=USER_SCHEMA)) load_config.side_effect = partial(side_effect, BAD_CONFIG_MAP) config.defaults, _ = config.load_default() @@ -198,7 +209,7 @@ def test_bad_user_schema(config, load_config, mocker) -> None: config.update_config() -def test_update_user_config(config, mocker) -> None: +def test_update_user_config(config: Config, mocker: "MockerFixture") -> None: myconfig = mocker.patch.object(Config, "current_config", new_callable=PropertyMock) # deep copy because we mutate state myconfig.return_value = copy.deepcopy(CONFIG) @@ -207,7 +218,9 @@ def test_update_user_config(config, mocker) -> None: assert config.current_config == UPDATED_CONFIG -def test_update_and_validate_user_config(config, mocker) -> None: +def test_update_and_validate_user_config( + config: Config, mocker: "MockerFixture" +) -> None: myconfig = mocker.patch.object(Config, "current_config", new_callable=PropertyMock) schema = mocker.patch.object(Config, "current_schema", new_callable=PropertyMock) schema.return_value = copy.deepcopy(SCHEMA) @@ -218,7 +231,7 @@ def test_update_and_validate_user_config(config, mocker) -> None: assert config.current_schema == UPDATED_SCHEMA -def test_update_from_path(path_to_config_file_on_disk) -> None: +def test_update_from_path(path_to_config_file_on_disk: str) -> None: cfg = qcodes.config # check that the default is still the default diff --git a/tests/test_field_vector.py b/tests/test_field_vector.py index fe3711ce32f..16cb6f2b376 100644 --- a/tests/test_field_vector.py +++ b/tests/test_field_vector.py @@ -38,13 +38,13 @@ @given(random_coordinates["spherical"]) @settings(max_examples=10) -def test_spherical_properties(spherical0) -> None: +def test_spherical_properties(spherical_coords: tuple[float, float, float]) -> None: """ If the cartesian to spherical transform has been correctly implemented then we expect certain symmetrical properties """ # Generate a random coordinate in spherical representation - spherical0 = dict(zip(["r", "theta", "phi"], spherical0)) + spherical0 = dict(zip(["r", "theta", "phi"], spherical_coords)) cartisian0 = FieldVector(**spherical0).get_components("x", "y", "z") # Mirror the theta angle in the xy-plane. @@ -80,13 +80,15 @@ def test_spherical_properties(spherical0) -> None: @given(random_coordinates["cylindrical"]) @settings(max_examples=10) -def test_cylindrical_properties(cylindrical0) -> None: +def test_cylindrical_properties( + cylindrical_coords: tuple[float, float, float], +) -> None: """ If the cartesian to cylindrical transform has been correctly implemented then we expect certain symmetrical properties """ # Generate a random coordinate in cylindrical representation - cylindrical0 = dict(zip(["rho", "phi", "z"], cylindrical0)) + cylindrical0 = dict(zip(["rho", "phi", "z"], cylindrical_coords)) cartisian0 = FieldVector(**cylindrical0).get_components("x", "y", "z") # If we flip the sign of the rho coordinate, we will flip the xy coordinate @@ -99,9 +101,12 @@ def test_cylindrical_properties(cylindrical0) -> None: @given(random_coordinates["cylindrical"], random_coordinates["spherical"]) @settings(max_examples=10) -def test_triangle_inequality(cylindrical0, spherical0) -> None: - cylindrical0 = FieldVector(**dict(zip(["rho", "phi", "z"], cylindrical0))) - spherical0 = FieldVector(**dict(zip(["r", "phi", "theta"], spherical0))) +def test_triangle_inequality( + cylindrical_coords: tuple[float, float, float], + spherical_coords: tuple[float, float, float], +) -> None: + cylindrical0 = FieldVector(**dict(zip(["rho", "phi", "z"], cylindrical_coords))) + spherical0 = FieldVector(**dict(zip(["r", "phi", "theta"], spherical_coords))) assert (cylindrical0 + spherical0).norm() <= ( cylindrical0.norm() + spherical0.norm() @@ -113,7 +118,7 @@ def test_triangle_inequality(cylindrical0, spherical0) -> None: @given(random_coordinates["cartesian"]) @settings(max_examples=10) -def test_homogeneous_roundtrip(cartesian0) -> None: +def test_homogeneous_roundtrip(cartesian0: tuple[float, float, float]) -> None: vec = FieldVector(**dict(zip("xyz", cartesian0))) h_vec = 13 * vec.as_homogeneous() @@ -125,7 +130,7 @@ def test_homogeneous_roundtrip(cartesian0) -> None: @given(random_coordinates["spherical"]) @settings(max_examples=10) -def test_json_dump(spherical0) -> None: +def test_json_dump(spherical0: tuple[float, float, float]) -> None: vec = FieldVector(**dict(zip(["r", "phi", "theta"], spherical0))) dump = json.dumps(vec, cls=NumpyJSONEncoder) diff --git a/tests/test_instrument.py b/tests/test_instrument.py index b2ba6dec987..a3a96126714 100644 --- a/tests/test_instrument.py +++ b/tests/test_instrument.py @@ -34,7 +34,9 @@ from qcodes.parameters import Function, Parameter if TYPE_CHECKING: - from collections.abc import Iterator + from collections.abc import Iterator, Sequence + + from qcodes.metadatable.metadatable_base import Snapshot, SnapshotUpdate @pytest.fixture(name="testdummy", scope="function") @@ -75,7 +77,7 @@ def _empty_instrument_with_empty_submodule() -> Iterator[Instrument]: @pytest.fixture(name="close_before_and_after", scope="function") -def _close_before_and_after(): +def _close_before_and_after() -> Iterator[None]: Instrument.close_all() try: yield @@ -266,7 +268,7 @@ def test_repr(testdummy: DummyInstrument) -> None: assert repr(testdummy) == "" -def test_add_remove_f_p(testdummy) -> None: +def test_add_remove_f_p(testdummy: DummyInstrument) -> None: with pytest.raises(KeyError, match="Duplicate parameter name dac1"): testdummy.add_parameter("dac1", get_cmd="foo") @@ -294,7 +296,7 @@ def test_add_remove_f_p(testdummy) -> None: assert isinstance(dac1, Parameter) -def test_instances(testdummy, parabola) -> None: +def test_instances(testdummy: DummyInstrument, parabola: MockParabola) -> None: instruments = [testdummy, parabola] for instrument in instruments: for other_instrument in instruments: @@ -312,13 +314,13 @@ def test_instances(testdummy, parabola) -> None: assert instrument is Instrument.find_instrument(instrument.name) -def test_is_valid(testdummy) -> None: +def test_is_valid(testdummy: DummyInstrument) -> None: assert Instrument.is_valid(testdummy) testdummy.close() assert not Instrument.is_valid(testdummy) -def test_snapshot_value(testdummy) -> None: +def test_snapshot_value(testdummy: DummyInstrument) -> None: testdummy.add_parameter( "has_snapshot_value", parameter_class=Parameter, @@ -346,7 +348,7 @@ def test_snapshot_value(testdummy) -> None: assert "value" not in snapshot["parameters"]["no_snapshot_value"] -def test_meta_instrument(parabola) -> None: +def test_meta_instrument(parabola: MockParabola) -> None: mock_instrument = MockMetaParabola("mock_parabola", parabola) # Check that the mock instrument can return values @@ -382,7 +384,7 @@ def test_empty_instrument(empty_instrument_with_empty_submodule: Instrument) -> empty_instrument_with_empty_submodule.print_readable_snapshot() -def test_find(testdummy) -> None: +def test_find(testdummy: DummyInstrument) -> None: """Test finding an existing instrument""" instr_2 = find_or_create_instrument( @@ -567,7 +569,7 @@ def test_instrumentbase_metadata() -> None: @pytest.mark.parametrize("cls", [(InstrumentBase), (Instrument)]) -def test_instrument_label(cls, request: FixtureRequest) -> None: +def test_instrument_label(cls: type[InstrumentBase], request: FixtureRequest) -> None: """Instrument uses nicely formatted label if available.""" instrument = cls(name="name") if isinstance(cls, type(Instrument)): @@ -585,7 +587,7 @@ def test_instrument_label(cls, request: FixtureRequest) -> None: assert instrument.label == label -def test_instrument_without_ref_is_gced(): +def test_instrument_without_ref_is_gced() -> None: # When there are no active references to an instrument and it is # gced there should be no live references to the instrument @@ -627,14 +629,18 @@ def __init__(self, sample_value: int) -> None: super().__init__() self.sample_value = sample_value - def snapshot_base(self, update=True, params_to_skip_update=None): + def snapshot_base( + self, + update: bool | SnapshotUpdate | None = True, + params_to_skip_update: Sequence[str] | None = None, + ) -> Snapshot: return {"sample_key": self.sample_value} class TestInstrument(InstrumentBase): __test__ = False - def __init__(self, name, label) -> None: + def __init__(self, name: str, label: str) -> None: super().__init__(name, label=label) self._meta_attrs.extend(["test_attribute"]) self._test_attribute = TestSnapshotType(12) diff --git a/tests/test_interactive_widget.py b/tests/test_interactive_widget.py index 283665bcf5a..287dc0dc012 100644 --- a/tests/test_interactive_widget.py +++ b/tests/test_interactive_widget.py @@ -1,4 +1,5 @@ import time +from typing import TYPE_CHECKING, Literal from unittest.mock import patch # importing ipykernel has the side effect @@ -17,12 +18,20 @@ from qcodes import interactive_widget +if TYPE_CHECKING: + from collections.abc import Callable, Generator + from unittest.mock import MagicMock + + from ipywidgets import Box # type: ignore[import-untyped] + + from qcodes.dataset.data_set import DataSet + # set matplotlib backend before importing pyplot matplotlib.use("Agg") @pytest.fixture(name="tab", scope="function") -def _create_tab(): +def _create_tab() -> "Generator[Tab, None, None]": yield interactive_widget.create_tab() @@ -53,7 +62,7 @@ def test_full_widget_on_one_dataset() -> None: def test_button_to_text( - standalone_parameters_dataset, + standalone_parameters_dataset: "DataSet", ) -> None: box = interactive_widget.button_to_text("title", "body") (button,) = box.children @@ -66,7 +75,7 @@ def test_button_to_text( assert len(box.children) == 1 -def test_snapshot_button(tab, standalone_parameters_dataset) -> None: +def test_snapshot_button(tab: Tab, standalone_parameters_dataset: "DataSet") -> None: ds = standalone_parameters_dataset snapshot_button = interactive_widget._get_snapshot_button(ds, tab) snapshot_button.click() @@ -75,7 +84,7 @@ def test_snapshot_button(tab, standalone_parameters_dataset) -> None: assert "snapshot" in tab.get_title(1) -def test_export_button(tab, standalone_parameters_dataset) -> None: +def test_export_button(tab: Tab, standalone_parameters_dataset: "DataSet") -> None: ds = standalone_parameters_dataset export_button = interactive_widget._get_export_button(ds, tab) export_button.click() @@ -83,7 +92,9 @@ def test_export_button(tab, standalone_parameters_dataset) -> None: @patch("matplotlib.pyplot.show") -def test_plot_button(tab, standalone_parameters_dataset) -> None: +def test_plot_button( + tab: "MagicMock", standalone_parameters_dataset: "DataSet" +) -> None: ds = standalone_parameters_dataset plot_button = interactive_widget._get_plot_button(ds, tab) plot_button.click() @@ -100,8 +111,8 @@ def test_plot_button(tab, standalone_parameters_dataset) -> None: ], ) def test_get_experiment_button( - get_button_function, - standalone_parameters_dataset, + get_button_function: "Callable[[DataSet], Box]", + standalone_parameters_dataset: "DataSet", ) -> None: ds = standalone_parameters_dataset box = get_button_function(ds) @@ -111,14 +122,14 @@ def test_get_experiment_button( assert len(box.children) == 2 -def test_get_parameters(standalone_parameters_dataset) -> None: +def test_get_parameters(standalone_parameters_dataset: "DataSet") -> None: parameters = interactive_widget._get_parameters(standalone_parameters_dataset) assert bool(parameters["dependent"]) # not empty assert bool(parameters["independent"]) # not empty def test_editable_metadata( - standalone_parameters_dataset, + standalone_parameters_dataset: "DataSet", ) -> None: ds = standalone_parameters_dataset box = interactive_widget.editable_metadata(ds) @@ -139,7 +150,7 @@ def test_editable_metadata( assert box.children[0].description == test_test -def test_experiments_widget(standalone_parameters_dataset) -> None: +def test_experiments_widget(standalone_parameters_dataset: "DataSet") -> None: dss = [standalone_parameters_dataset] widget = interactive_widget.experiments_widget(data_sets=dss) assert len(widget.children) == 3 @@ -151,7 +162,10 @@ def test_experiments_widget(standalone_parameters_dataset) -> None: @pytest.mark.parametrize("sort_by", [None, "run_id", "timestamp"]) -def test_experiments_widget_sorting(standalone_parameters_dataset, sort_by) -> None: +def test_experiments_widget_sorting( + standalone_parameters_dataset: "DataSet", + sort_by: Literal["timestamp", "run_id"] | None, +) -> None: dss = [standalone_parameters_dataset] widget = interactive_widget.experiments_widget(data_sets=dss, sort_by=sort_by) assert len(widget.children) == 3 diff --git a/tests/test_logger.py b/tests/test_logger.py index c70e0bf1e72..dfa00e32dbc 100644 --- a/tests/test_logger.py +++ b/tests/test_logger.py @@ -333,7 +333,9 @@ def test_instrument_connect_message(caplog: LogCaptureFixture) -> None: assert any(rec.msg == expected_con_mssg for rec in setup_records) -def test_instrument_logger_extra_info(awg5208, caplog: LogCaptureFixture) -> None: +def test_instrument_logger_extra_info( + awg5208: TektronixAWG5208, caplog: LogCaptureFixture +) -> None: """ Test that the connect_message method logs as expected diff --git a/tests/test_metadata.py b/tests/test_metadata.py index 87da1e55699..b608d7bd2b0 100644 --- a/tests/test_metadata.py +++ b/tests/test_metadata.py @@ -8,11 +8,17 @@ from qcodes.utils import diff_param_values if TYPE_CHECKING: - from qcodes.metadatable.metadatable_base import Snapshot + from collections.abc import Sequence + + from qcodes.metadatable.metadatable_base import Snapshot, SnapshotUpdate class HasSnapshotBase(Metadatable): - def snapshot_base(self, update=False, params_to_skip_update=None): + def snapshot_base( + self, + update: bool | SnapshotUpdate | None = False, + params_to_skip_update: Sequence[str] | None = None, + ) -> Snapshot: return {"cheese": "gruyere"} diff --git a/tests/test_monitor.py b/tests/test_monitor.py index b0772e63b67..523456e1be3 100644 --- a/tests/test_monitor.py +++ b/tests/test_monitor.py @@ -6,7 +6,7 @@ import json import random -from typing import cast +from typing import TYPE_CHECKING, cast import pytest import websockets @@ -19,11 +19,18 @@ from qcodes.monitor import monitor from qcodes.parameters import Parameter +if TYPE_CHECKING: + from collections.abc import Generator + monitor.WEBSOCKET_PORT = random.randint(50000, 60000) @pytest.fixture(name="inst_and_monitor") -def _make_inst_and_monitor(): +def _make_inst_and_monitor() -> Generator[ + tuple[DummyInstrument, monitor.Monitor, tuple[Parameter, ...], Parameter], + None, + None, +]: instr = DummyInstrument("MonitorDummy") param = Parameter("DummyParam", unit="V", get_cmd=None, set_cmd=None) param(1) @@ -39,7 +46,7 @@ def _make_inst_and_monitor(): @pytest.fixture(name="channel_instr") -def _make_channel_instr(): +def _make_channel_instr() -> Generator[DummyChannelInstrument, None, None]: instr = DummyChannelInstrument("MonitorDummy") try: yield instr @@ -48,7 +55,9 @@ def _make_channel_instr(): @pytest.fixture(name="channel_instr_monitor", params=[True, False]) -def _make_channel_instr_monitor(channel_instr, request: FixtureRequest): +def _make_channel_instr_monitor( + channel_instr: DummyChannelInstrument, request: FixtureRequest +) -> Generator[tuple[monitor.Monitor, bool], None, None]: m = monitor.Monitor( channel_instr.A.dummy_start, channel_instr.B.dummy_start, @@ -117,7 +126,11 @@ async def test_connection() -> None: @pytest.mark.asyncio -async def test_instrument_update(inst_and_monitor) -> None: +async def test_instrument_update( + inst_and_monitor: tuple[ + DummyInstrument, monitor.Monitor, tuple[Parameter, ...], Parameter + ], +) -> None: """ Test instrument updates """ @@ -164,7 +177,9 @@ async def test_instrument_update(inst_and_monitor) -> None: @pytest.mark.asyncio -async def test_monitor_root_instr(channel_instr_monitor) -> None: +async def test_monitor_root_instr( + channel_instr_monitor: tuple[monitor.Monitor, bool], +) -> None: _, use_root_instrument = channel_instr_monitor async with websockets.connect( f"ws://localhost:{monitor.WEBSOCKET_PORT}" diff --git a/tests/test_plot_utils.py b/tests/test_plot_utils.py index 5e07095be83..ea867e76503 100644 --- a/tests/test_plot_utils.py +++ b/tests/test_plot_utils.py @@ -2,6 +2,8 @@ Tests for `qcodes.utils.plotting`. """ +from typing import TYPE_CHECKING + import matplotlib from pytest import fixture @@ -10,6 +12,9 @@ from .dataset_generators import dataset_with_outliers_generator +if TYPE_CHECKING: + from qcodes.dataset.data_set import DataSet + # set matplotlib backend before importing pyplot matplotlib.use("Agg") @@ -17,27 +22,27 @@ @fixture(scope="function") -def dataset_with_data_outside_iqr_high_outlier(dataset): +def dataset_with_data_outside_iqr_high_outlier(dataset: "DataSet") -> "DataSet": return dataset_with_outliers_generator( dataset, data_offset=2, low_outlier=-2, high_outlier=3 ) @fixture(scope="function") -def dataset_with_data_outside_iqr_low_outlier(dataset): +def dataset_with_data_outside_iqr_low_outlier(dataset: "DataSet") -> "DataSet": return dataset_with_outliers_generator( dataset, data_offset=2, low_outlier=-2, high_outlier=3 ) @fixture(scope="function") -def dataset_with_outliers(dataset): +def dataset_with_outliers(dataset: "DataSet") -> "DataSet": return dataset_with_outliers_generator( dataset, low_outlier=-3, high_outlier=3, background_noise=False ) -def test_extend(dataset_with_outliers) -> None: +def test_extend(dataset_with_outliers: "DataSet") -> None: # this one should clipp the upper values run_id = dataset_with_outliers.run_id _, cb = plot_by_id(run_id, auto_color_scale=False) @@ -52,7 +57,7 @@ def test_extend(dataset_with_outliers) -> None: plt.close() -def test_defaults(dataset_with_outliers) -> None: +def test_defaults(dataset_with_outliers: "DataSet") -> None: run_id = dataset_with_outliers.run_id _, cb = plot_by_id(run_id) diff --git a/tests/test_snapshot.py b/tests/test_snapshot.py index 1b8c7031919..68ceef1428c 100644 --- a/tests/test_snapshot.py +++ b/tests/test_snapshot.py @@ -19,7 +19,7 @@ ], ) def test_snapshot_skip_params_update( - request: FixtureRequest, params, params_to_skip + request: FixtureRequest, params: list[str], params_to_skip: list[str] ) -> None: """ Test that params_to_skip_update works as expected, in particular that only @@ -63,7 +63,7 @@ def test_snapshot_skip_params_update( ], ) def test_snapshot_exclude_params( - request: FixtureRequest, params, params_to_exclude + request: FixtureRequest, params: list[str], params_to_exclude: list[str] ) -> None: """ Test that params_to_exclude works as expected, in particular that only diff --git a/tests/test_station.py b/tests/test_station.py index 4607199ebd1..0a10df3db04 100644 --- a/tests/test_station.py +++ b/tests/test_station.py @@ -6,6 +6,7 @@ from contextlib import contextmanager from io import StringIO from pathlib import Path +from typing import TYPE_CHECKING import pytest from ruamel.yaml import YAML @@ -25,9 +26,12 @@ from .common import DummyComponent +if TYPE_CHECKING: + from collections.abc import Generator + @pytest.fixture(autouse=True) -def set_default_station_to_none_automatically(): +def set_default_station_to_none_automatically() -> "Generator[None, None, None]": """Makes sure that after startup and teardown there is no default station""" Station.default = None yield @@ -35,7 +39,7 @@ def set_default_station_to_none_automatically(): @pytest.fixture(autouse=True) -def close_all_instruments(): +def close_all_instruments() -> "Generator[None, None, None]": """Makes sure that after startup and teardown all instruments are closed""" Instrument.close_all() yield @@ -43,7 +47,7 @@ def close_all_instruments(): @pytest.fixture(autouse=True) -def treat_validation_warning_as_error(): +def treat_validation_warning_as_error() -> "Generator[None, None, None]": warnings.simplefilter("error", ValidationWarning) yield warnings.simplefilter("default", ValidationWarning) @@ -252,7 +256,7 @@ def test_update_config_schema() -> None: @contextmanager -def config_file_context(file_content): +def config_file_context(file_content: str) -> "Generator[str, None, None]": with tempfile.TemporaryDirectory() as tmpdirname: filename = Path(tmpdirname, "station_config.yaml") with filename.open("w") as f: @@ -261,7 +265,9 @@ def config_file_context(file_content): @contextmanager -def config_files_context(file_content1, file_content2): +def config_files_context( + file_content1: str, file_content2: str +) -> "Generator[list[str], None, None]": with tempfile.TemporaryDirectory() as tmpdirname: filename1 = Path(tmpdirname, "station_config1.yaml") with filename1.open("w") as f: @@ -273,7 +279,7 @@ def config_files_context(file_content1, file_content2): @pytest.fixture(name="example_station_config") -def _make_example_station_config(): +def _make_example_station_config() -> "Generator[str, None, None]": """ Returns path to temp yaml file with station config. """ @@ -301,7 +307,7 @@ def _make_example_station_config(): yield filename -def test_dynamic_reload_of_file(example_station_config) -> None: +def test_dynamic_reload_of_file(example_station_config: str) -> None: st = Station(config_file=example_station_config) mock_dac = st.load_instrument("mock_dac") assert "ch1" in mock_dac.parameters @@ -325,12 +331,12 @@ def station_config_has_been_loaded(st: Station) -> bool: @pytest.fixture(name="example_station") -def _make_example_station(example_station_config): +def _make_example_station(example_station_config: str) -> Station: return Station(config_file=example_station_config) # instrument loading related tests -def test_station_config_path_resolution(example_station_config) -> None: +def test_station_config_path_resolution(example_station_config: str) -> None: config = qcodes.config["station"] # There is no default yaml file present that defines a station @@ -386,11 +392,13 @@ def test_station_config_path_resolution(example_station_config) -> None: assert station_config_has_been_loaded(Station(config_file=str(path))) -def test_station_configuration_is_a_component_of_station(example_station) -> None: +def test_station_configuration_is_a_component_of_station( + example_station: Station, +) -> None: assert station_config_has_been_loaded(example_station) -def test_station_config_can_be_loaded_from_snapshot(example_station) -> None: +def test_station_config_can_be_loaded_from_snapshot(example_station: Station) -> None: assert station_config_has_been_loaded(example_station) # ensure that we can correctly dump config which is a subclass of UserDict configdump = json.dumps(example_station.config, cls=NumpyJSONEncoder) @@ -410,7 +418,7 @@ def test_station_config_can_be_loaded_from_snapshot(example_station) -> None: @pytest.fixture -def simple_mock_station(): +def simple_mock_station() -> "Generator[Station, None, None]": yield station_from_config_str( """ instruments: @@ -420,7 +428,7 @@ def simple_mock_station(): ) -def test_simple_mock_config(simple_mock_station) -> None: +def test_simple_mock_config(simple_mock_station: Station) -> None: st = simple_mock_station assert station_config_has_been_loaded(st) assert hasattr(st, "load_mock") @@ -429,10 +437,11 @@ def test_simple_mock_config(simple_mock_station) -> None: mock_snapshot["type"] == "qcodes.instrument_drivers.mock_instruments.DummyInstrument" ) + assert st.config is not None assert "mock" in st.config["instruments"] -def test_simple_mock_load_mock(simple_mock_station) -> None: +def test_simple_mock_load_mock(simple_mock_station: Station) -> None: st = simple_mock_station mock = st.load_mock() assert isinstance(mock, DummyInstrument) @@ -440,7 +449,7 @@ def test_simple_mock_load_mock(simple_mock_station) -> None: assert st.components["mock"] is mock -def test_simple_mock_load_instrument(simple_mock_station) -> None: +def test_simple_mock_load_instrument(simple_mock_station: Station) -> None: st = simple_mock_station mock = st.load_instrument("mock") assert isinstance(mock, DummyInstrument) @@ -551,7 +560,7 @@ def test_init_parameters() -> None: st.load_instrument("lakeshore") -def test_name_init_kwarg(simple_mock_station) -> None: +def test_name_init_kwarg(simple_mock_station: Station) -> None: # special case of `name` as kwarg st = simple_mock_station mock = st.load_instrument("mock", name="test") @@ -577,7 +586,7 @@ def test_name_specified_in_init_in_yaml_is_used() -> None: class InstrumentWithNameAsNotFirstArgument(Instrument): - def __init__(self, first_arg, name): + def __init__(self, first_arg: int, name: str) -> None: super().__init__(name) self._first_arg = first_arg @@ -998,14 +1007,14 @@ def test_nested_config_scalar_submodule_value_runtime_error() -> None: st.load_instrument("mock") -def test_monitor_not_loaded_by_default(example_station_config) -> None: +def test_monitor_not_loaded_by_default(example_station_config: str) -> None: st = Station(config_file=example_station_config) st.load_instrument("mock_dac") assert Monitor.running is None def test_monitor_loaded_if_specified( - example_station_config, request: pytest.FixtureRequest + example_station_config: str, request: pytest.FixtureRequest ) -> None: st = Station(config_file=example_station_config, use_monitor=True) st.load_instrument("mock_dac") @@ -1016,7 +1025,7 @@ def test_monitor_loaded_if_specified( def test_monitor_loaded_by_default_if_in_config( - example_station_config, request: pytest.FixtureRequest + example_station_config: str, request: pytest.FixtureRequest ) -> None: qcodes.config["station"]["use_monitor"] = True st = Station(config_file=example_station_config) @@ -1027,7 +1036,7 @@ def test_monitor_loaded_by_default_if_in_config( assert Monitor.running._parameters[0].name == "ch1" -def test_monitor_not_loaded_if_specified(example_station_config) -> None: +def test_monitor_not_loaded_if_specified(example_station_config: str) -> None: st = Station(config_file=example_station_config, use_monitor=False) st.load_instrument("mock_dac") assert Monitor.running is None @@ -1070,7 +1079,7 @@ def test_config_validation_comprehensive_config() -> None: def test_load_all_instruments_raises_on_both_only_names_and_only_types_passed( - example_station, + example_station: Station, ) -> None: with pytest.raises( ValueError, @@ -1081,10 +1090,12 @@ def test_load_all_instruments_raises_on_both_only_names_and_only_types_passed( example_station.load_all_instruments(only_names=(), only_types=()) -def test_load_all_instruments_no_args(example_station) -> None: +def test_load_all_instruments_no_args(example_station: Station) -> None: all_instruments_in_config = {"lakeshore", "mock_dac", "mock_dac2"} - loaded_instruments = example_station.load_all_instruments() + # TODO: the load_all_instruments overloads in qcodes.station lack None + # defaults so calls omitting only_names/only_types match no overload. + loaded_instruments = example_station.load_all_instruments() # pyright: ignore[reportCallIssue] assert set(loaded_instruments) == all_instruments_in_config @@ -1093,10 +1104,12 @@ def test_load_all_instruments_no_args(example_station) -> None: assert Instrument.exist(instrument) -def test_load_all_instruments_only_types(example_station) -> None: +def test_load_all_instruments_only_types(example_station: Station) -> None: all_dummy_instruments = {"mock_dac", "mock_dac2"} - loaded_instruments = example_station.load_all_instruments( + # TODO: the load_all_instruments overloads in qcodes.station lack None + # defaults so calls omitting only_names/only_types match no overload. + loaded_instruments = example_station.load_all_instruments( # pyright: ignore[reportCallIssue] only_types=("DummyInstrument",) ) @@ -1106,6 +1119,7 @@ def test_load_all_instruments_only_types(example_station) -> None: assert instrument in example_station.components assert Instrument.exist(instrument) + assert example_station.config is not None other_instruments = ( set(example_station.config["instruments"].keys()) - all_dummy_instruments ) @@ -1115,10 +1129,12 @@ def test_load_all_instruments_only_types(example_station) -> None: assert not Instrument.exist(instrument) -def test_load_all_instruments_only_names(example_station) -> None: +def test_load_all_instruments_only_names(example_station: Station) -> None: instruments_to_load = {"lakeshore", "mock_dac"} - loaded_instruments = example_station.load_all_instruments( + # TODO: the load_all_instruments overloads in qcodes.station lack None + # defaults so calls omitting only_names/only_types match no overload. + loaded_instruments = example_station.load_all_instruments( # pyright: ignore[reportCallIssue] only_names=instruments_to_load ) @@ -1128,6 +1144,7 @@ def test_load_all_instruments_only_names(example_station) -> None: assert instrument in example_station.components assert Instrument.exist(instrument) + assert example_station.config is not None other_instruments = ( set(example_station.config["instruments"].keys()) - instruments_to_load ) diff --git a/tests/test_sweep_values.py b/tests/test_sweep_values.py index a7b4a517852..d01002af449 100644 --- a/tests/test_sweep_values.py +++ b/tests/test_sweep_values.py @@ -1,29 +1,34 @@ +from typing import TYPE_CHECKING + import pytest from qcodes.parameters import Parameter from qcodes.parameters.sweep_values import SweepValues from qcodes.validators import Numbers +if TYPE_CHECKING: + from collections.abc import Generator + @pytest.fixture(name="c0") -def _make_c0(): +def _make_c0() -> "Generator[Parameter, None, None]": c0 = Parameter("c0", vals=Numbers(-10, 10), get_cmd=None, set_cmd=None) yield c0 @pytest.fixture(name="c1") -def _make_c1(): +def _make_c1() -> "Generator[Parameter, None, None]": c1 = Parameter("c1", get_cmd=None, set_cmd=None) yield c1 @pytest.fixture(name="c2") -def _make_c2(): +def _make_c2() -> "Generator[Parameter, None, None]": c2 = Parameter("c2", get_cmd=lambda: 42) yield c2 -def test_errors(c0, c1, c2) -> None: +def test_errors(c0: Parameter, c1: Parameter, c2: Parameter) -> None: # only complete 3-part slices are valid with pytest.raises(TypeError): c0[1:2] # For Int params this could be defined as step=1 @@ -56,20 +61,21 @@ def test_errors(c0, c1, c2) -> None: # improper use of extend with pytest.raises(TypeError): - c0[0.1].extend(5) + c0[0.1].extend(5) # pyright: ignore[reportArgumentType] # SweepValue object has no getter, even if the parameter does with pytest.raises(AttributeError): - _ = c0[0.1].get + _ = c0[0.1].get # pyright: ignore[reportAttributeAccessIssue] -def test_valid(c0) -> None: +def test_valid(c0: Parameter) -> None: c0_sv = c0[1] # setter gets mapped assert c0_sv.set == c0.set # normal sequence operations access values assert list(c0_sv) == [1] - assert c0_sv[0] == 1 + # TODO: SweepFixedValues.__getitem__ only accepts slice but int keys work + assert c0_sv[0] == 1 # pyright: ignore[reportArgumentType] assert 1 in c0_sv assert 2 not in c0_sv @@ -98,7 +104,8 @@ def test_valid(c0) -> None: # in-place and copying reverse c0_sv.reverse() - c0_sv5 = reversed(c0_sv) + # TODO: SweepFixedValues.__reversed__ returns Self, which is not an Iterator + c0_sv5 = reversed(c0_sv) # pyright: ignore[reportCallIssue, reportArgumentType] assert list(c0_sv) == [1.7, 1.6, 1.5, 1] assert list(c0_sv5) == [1, 1.5, 1.6, 1.7] @@ -117,7 +124,7 @@ def test_base() -> None: iter(SweepValues(p)) -def test_snapshot(c0) -> None: +def test_snapshot(c0: Parameter) -> None: assert c0[0].snapshot() == {"parameter": c0.snapshot(), "values": [{"item": 0}]} assert c0[0:5:0.3].snapshot()["values"] == [ # type: ignore[misc] @@ -143,7 +150,7 @@ def test_snapshot(c0) -> None: ] -def test_repr(c0) -> None: +def test_repr(c0: Parameter) -> None: sv = c0[0] assert repr(sv) == ( f"" diff --git a/tests/test_threading.py b/tests/test_threading.py index 95175a549a1..506d1ee7f75 100644 --- a/tests/test_threading.py +++ b/tests/test_threading.py @@ -5,7 +5,7 @@ import threading import time from collections import defaultdict -from typing import Any +from typing import TYPE_CHECKING, Any import pytest @@ -14,6 +14,9 @@ from qcodes.parameters import Parameter, ParamRawDataType from qcodes.utils.threading_utils import RespondingThread, thread_map +if TYPE_CHECKING: + from collections.abc import Generator + class ParameterWithThreadKnowledge(Parameter): def __init__(self, *args: Any, **kwargs: Any) -> None: @@ -25,7 +28,7 @@ def get_raw(self) -> ParamRawDataType: @pytest.fixture(name="dummy_1", scope="function") -def _dummy_dac1(): +def _dummy_dac1() -> "Generator[DummyInstrument, None, None]": instrument = DummyInstrument(name="dummy_1", gates=["ch1"]) instrument.add_parameter( @@ -43,7 +46,7 @@ def _dummy_dac1(): @pytest.fixture(name="dummy_2", scope="function") -def _dummy_dac2(): +def _dummy_dac2() -> "Generator[DummyInstrument, None, None]": instrument = DummyInstrument(name="dummy_2", gates=["ch1"]) instrument.add_parameter( @@ -60,7 +63,9 @@ def _dummy_dac2(): instrument.close() -def test_call_params_threaded(dummy_1, dummy_2) -> None: +def test_call_params_threaded( + dummy_1: DummyInstrument, dummy_2: DummyInstrument +) -> None: params_output = call_params_threaded( (dummy_1.voltage_1, dummy_1.voltage_2, dummy_2.voltage_1, dummy_2.voltage_2) ) @@ -79,7 +84,9 @@ def test_call_params_threaded(dummy_1, dummy_2) -> None: } == expected_params_per_thread -def test_thread_pool_params_caller(dummy_1, dummy_2) -> None: +def test_thread_pool_params_caller( + dummy_1: DummyInstrument, dummy_2: DummyInstrument +) -> None: params = ( dummy_1.voltage_1, dummy_1.voltage_2, diff --git a/tests/test_visa.py b/tests/test_visa.py index c3f42b56f8f..0466891ce6b 100644 --- a/tests/test_visa.py +++ b/tests/test_visa.py @@ -17,13 +17,21 @@ if TYPE_CHECKING: from collections.abc import Generator + from typing import Unpack from pytest_mock import MockerFixture + from qcodes.instrument import VisaInstrumentKWArgs + class MockVisa(VisaInstrument): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) + def __init__( + self, + name: str, + address: str | None = None, + **kwargs: "Unpack[VisaInstrumentKWArgs]", + ) -> None: + super().__init__(name, address, **kwargs) self.state = self.add_parameter( "state", get_cmd="STAT?", @@ -58,15 +66,15 @@ class MockVisaHandle(pyvisa.resources.MessageBasedResource): resource_name: str = "MOCK::INSTR" # pyright: ignore[reportIncompatibleVariableOverride] - def __init__(self): + def __init__(self) -> None: self.state = 0 self.closed = False self._session = None # required for session property - def clear(self): + def clear(self) -> None: self.state = 0 - def close(self): + def close(self) -> None: # make it an error to ask or write after close self.closed = True @@ -89,7 +97,7 @@ def write( return len(message) - def ask(self, cmd): + def ask(self, cmd: str) -> float: if self.closed: raise RuntimeError("Trying to ask a closed instrument") if self.state > 10: @@ -107,7 +115,7 @@ def set_visa_attribute( setattr(self, str(name), state) return pyvisa.constants.StatusCode.success - def __del__(self): + def __del__(self) -> None: pass @@ -199,7 +207,7 @@ def test_ask_write_local(mock_visa: MockVisa) -> None: def test_visa_backend(mocker: "MockerFixture", request: FixtureRequest) -> None: rm_mock = mocker.patch("qcodes.instrument.visa.pyvisa.ResourceManager") - address_opened = [None] + address_opened: list[str | None] = [None] class MockBackendVisaInstrument(VisaInstrument): _visa_handle = MockVisaHandle() @@ -209,7 +217,7 @@ def visabackend(self) -> str: return "sim" class MockRM: - def open_resource(self, address): + def open_resource(self, address: str) -> MockVisaHandle: address_opened[0] = address return MockVisaHandle() diff --git a/tests/utils/test_isfunction.py b/tests/utils/test_isfunction.py index 81bc0268ab8..4088c798619 100644 --- a/tests/utils/test_isfunction.py +++ b/tests/utils/test_isfunction.py @@ -51,14 +51,14 @@ def f0(one_arg: int) -> int: def test_function_varargs() -> None: - def f(*args) -> None: + def f(*args: object) -> None: return None assert is_function(f, 0) assert is_function(f, 1) assert is_function(f, 100) - def g(a, b=1, *args) -> None: + def g(a: object, b: object = 1, *args: object) -> None: return None assert not is_function(g, 0) diff --git a/tests/utils/test_partial_with_docstring.py b/tests/utils/test_partial_with_docstring.py index c7159d91b56..4e39ec3087e 100644 --- a/tests/utils/test_partial_with_docstring.py +++ b/tests/utils/test_partial_with_docstring.py @@ -2,7 +2,7 @@ def test_partial_with_docstring() -> None: - def f(): + def f() -> None: pass docstring = "some docstring" @@ -18,7 +18,7 @@ def test_partial_with_docstring_pass_args() -> None: functools.partial """ - def f(a: int, b: int): + def f(a: int, b: int) -> int: return a + b docstring = "some docstring" @@ -29,7 +29,7 @@ def f(a: int, b: int): def test_partial_with_docstring_returns_value() -> None: - def f(a: int, b: int): + def f(a: int, b: int) -> int: return a + b docstring = "some docstring" diff --git a/tests/validators/test_literal.py b/tests/validators/test_literal.py index c549e8003af..cb625d1d122 100644 --- a/tests/validators/test_literal.py +++ b/tests/validators/test_literal.py @@ -41,7 +41,7 @@ def test_valid_values() -> None: assert a123_val.valid_values == (1, 2, 3) -def test_missing_generic_arg_raises_at_runtime(): +def test_missing_generic_arg_raises_at_runtime() -> None: wrong_validator = LiteralValidator() with pytest.raises( From 73d649a50e8ff7d505725b40c02317773b0ba6cf Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Tue, 20 Oct 2026 21:20:34 +0200 Subject: [PATCH 09/20] Fix load_all_instruments overloads for default args None of the overloads gave only_names/only_types a None default, so calling with no arguments or with only one of them as a keyword matched no overload. --- src/qcodes/station.py | 16 ++++++++++++---- tests/test_station.py | 14 ++++---------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/qcodes/station.py b/src/qcodes/station.py index cfc7d18b857..fd52e9b80da 100644 --- a/src/qcodes/station.py +++ b/src/qcodes/station.py @@ -847,22 +847,30 @@ def update_monitor() -> None: @overload def load_all_instruments( self, - only_names: None, - only_types: Iterable[str], + only_names: None = None, + only_types: None = None, ) -> tuple[str, ...]: ... @overload def load_all_instruments( self, only_names: Iterable[str], - only_types: None, + only_types: None = None, + ) -> tuple[str, ...]: ... + + @overload + def load_all_instruments( + self, + only_names: None = None, + *, + only_types: Iterable[str], ) -> tuple[str, ...]: ... @overload def load_all_instruments( self, only_names: None, - only_types: None, + only_types: Iterable[str], ) -> tuple[str, ...]: ... @overload diff --git a/tests/test_station.py b/tests/test_station.py index 0a10df3db04..0c633b325f0 100644 --- a/tests/test_station.py +++ b/tests/test_station.py @@ -1093,9 +1093,7 @@ def test_load_all_instruments_raises_on_both_only_names_and_only_types_passed( def test_load_all_instruments_no_args(example_station: Station) -> None: all_instruments_in_config = {"lakeshore", "mock_dac", "mock_dac2"} - # TODO: the load_all_instruments overloads in qcodes.station lack None - # defaults so calls omitting only_names/only_types match no overload. - loaded_instruments = example_station.load_all_instruments() # pyright: ignore[reportCallIssue] + loaded_instruments = example_station.load_all_instruments() assert set(loaded_instruments) == all_instruments_in_config @@ -1107,9 +1105,7 @@ def test_load_all_instruments_no_args(example_station: Station) -> None: def test_load_all_instruments_only_types(example_station: Station) -> None: all_dummy_instruments = {"mock_dac", "mock_dac2"} - # TODO: the load_all_instruments overloads in qcodes.station lack None - # defaults so calls omitting only_names/only_types match no overload. - loaded_instruments = example_station.load_all_instruments( # pyright: ignore[reportCallIssue] + loaded_instruments = example_station.load_all_instruments( only_types=("DummyInstrument",) ) @@ -1132,9 +1128,7 @@ def test_load_all_instruments_only_types(example_station: Station) -> None: def test_load_all_instruments_only_names(example_station: Station) -> None: instruments_to_load = {"lakeshore", "mock_dac"} - # TODO: the load_all_instruments overloads in qcodes.station lack None - # defaults so calls omitting only_names/only_types match no overload. - loaded_instruments = example_station.load_all_instruments( # pyright: ignore[reportCallIssue] + loaded_instruments = example_station.load_all_instruments( only_names=instruments_to_load ) @@ -1157,7 +1151,7 @@ def test_load_all_instruments_only_names(example_station: Station) -> None: def test_load_all_instruments_without_config_raises() -> None: station = Station() with pytest.raises(ValueError, match="Station has no config"): - station.load_all_instruments() # type: ignore[call-overload] + station.load_all_instruments() def test_station_config_created_with_multiple_config_files() -> None: From 6f0845b9d1476e2c4a5d3bb4ba07c52211cd3bab Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Tue, 20 Oct 2026 21:20:35 +0200 Subject: [PATCH 10/20] Make interactive widget button tests meaningful The plot button test received the plt.show mock as its tab fixture and neither test asserted anything, while the widget callback swallows all exceptions. Assert on the created tab, the plt.show call and the exported file, and export into tmp_path rather than the cwd. --- tests/test_interactive_widget.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/tests/test_interactive_widget.py b/tests/test_interactive_widget.py index 287dc0dc012..85a7eeb9e6f 100644 --- a/tests/test_interactive_widget.py +++ b/tests/test_interactive_widget.py @@ -1,4 +1,5 @@ import time +from pathlib import Path from typing import TYPE_CHECKING, Literal from unittest.mock import patch @@ -84,21 +85,36 @@ def test_snapshot_button(tab: Tab, standalone_parameters_dataset: "DataSet") -> assert "snapshot" in tab.get_title(1) -def test_export_button(tab: Tab, standalone_parameters_dataset: "DataSet") -> None: +def test_export_button( + tab: Tab, + standalone_parameters_dataset: "DataSet", + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + # the export button writes to the current working directory + monkeypatch.chdir(tmp_path) ds = standalone_parameters_dataset export_button = interactive_widget._get_export_button(ds, tab) export_button.click() - time.sleep(0.5) # after click + + export_path = Path(ds.export_info.export_paths["csv"]) + assert export_path.parent == tmp_path / "export" + assert export_path.is_file() @patch("matplotlib.pyplot.show") def test_plot_button( - tab: "MagicMock", standalone_parameters_dataset: "DataSet" + mock_show: "MagicMock", tab: Tab, standalone_parameters_dataset: "DataSet" ) -> None: ds = standalone_parameters_dataset plot_button = interactive_widget._get_plot_button(ds, tab) plot_button.click() - time.sleep(0.5) # after click + + assert len(tab.children) == 2 + assert tab.get_title(1) == f"RID #{ds.captured_run_id} plot" + # plt.show is only reached if plot_dataset did not raise; errors inside the + # widget callback are caught and printed rather than propagated + mock_show.assert_called_once() @pytest.mark.parametrize( From cd707a86ea923d7ffd97909cd1d26479e1362d1f Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Tue, 20 Oct 2026 21:38:16 +0200 Subject: [PATCH 11/20] Add remaining type annotations to dataset and delegate tests --- tests/dataset/conftest.py | 16 ++++---- tests/dataset/dond/test_dond.py | 6 +-- tests/dataset/test_dataset_basic.py | 14 ++++--- tests/dataset/test_dataset_in_mem_import.py | 37 +++++++++++++++---- ...st_parameter_with_setpoints_has_control.py | 6 ++- tests/dataset/test_paramspec.py | 17 +++++++-- tests/dataset/test_subscribing.py | 2 +- tests/delegate/test_delegate_instrument.py | 17 +++++++-- tests/delegate/test_device.py | 20 +++++++--- 9 files changed, 95 insertions(+), 40 deletions(-) diff --git a/tests/dataset/conftest.py b/tests/dataset/conftest.py index f075014c49a..f6a65fc2693 100644 --- a/tests/dataset/conftest.py +++ b/tests/dataset/conftest.py @@ -5,7 +5,7 @@ import tempfile from contextlib import contextmanager from enum import StrEnum -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Any import numpy as np import pytest @@ -94,7 +94,7 @@ def two_empty_temp_db_connections( @contextmanager def temporarily_copied_DB( - filepath: str, **kwargs + filepath: str, **kwargs: Any ) -> Generator[AtomicConnection, None, None]: """ Make a temporary copy of a db-file and delete it after use. Meant to be @@ -683,8 +683,8 @@ def __init__( self, name: str, instrument: InstrumentBase, - **kwargs, - ): + **kwargs: Any, + ) -> None: super().__init__( name=name, shape=(1,), # this attribute should be removed @@ -716,8 +716,8 @@ def __init__( self, name: str, instrument: InstrumentBase, - **kwargs, - ): + **kwargs: Any, + ) -> None: self.start = 0 self.stop = 2e6 self.npts = (100, 50, 20) @@ -820,8 +820,8 @@ class ArrayshapedParam(Parameter): def __init__( self, *args: str, - **kwargs, - ): + **kwargs: Any, + ) -> None: super().__init__(*args, **kwargs) self._rng = np.random.default_rng() diff --git a/tests/dataset/dond/test_dond.py b/tests/dataset/dond/test_dond.py index 0d1a7949bfa..973e90cb7bb 100644 --- a/tests/dataset/dond/test_dond.py +++ b/tests/dataset/dond/test_dond.py @@ -5,7 +5,7 @@ import logging import re from functools import partial -from typing import TYPE_CHECKING, assert_type +from typing import TYPE_CHECKING, Any, assert_type import hypothesis.strategies as hst import matplotlib.axes @@ -54,7 +54,7 @@ class TrackingParameter(Parameter): """Parameter that keeps track of number of get and set operations""" - def __init__(self, *args: str, **kwargs): + def __init__(self, *args: str, **kwargs: Any) -> None: self.set_count = 0 self.get_count = 0 super().__init__(*args, **kwargs) @@ -77,7 +77,7 @@ class GetReturnsCountParameter(Parameter): Allows you to set a value but returns the get count rather than the value""" - def __init__(self, *args: str, **kwargs): + def __init__(self, *args: str, **kwargs: Any) -> None: self.set_count = 0 self.get_count = 0 super().__init__(*args, **kwargs) diff --git a/tests/dataset/test_dataset_basic.py b/tests/dataset/test_dataset_basic.py index 615cc38f619..1af0d9af4b2 100644 --- a/tests/dataset/test_dataset_basic.py +++ b/tests/dataset/test_dataset_basic.py @@ -630,7 +630,7 @@ def test_numpy_floats(dataset: DataSet) -> None: assert np.allclose(data, expected_result, atol=1e-8) -def test_numpy_complex(dataset) -> None: +def test_numpy_complex(dataset: DataSet) -> None: """ Test that we can insert numpy complex in the data set """ @@ -640,7 +640,9 @@ def test_numpy_complex(dataset) -> None: dataset.mark_started() results = [{"x": tp(2.0 + 3.0j)} for tp in numpy_complex] - dataset.add_results(results) + # TODO: VALUE in qcodes.dataset.sqlite.query_helpers does not include + # numpy scalar types although add_results supports them + dataset.add_results(results) # pyright: ignore[reportArgumentType] expected_result = np.array([tp(2.0 + 3.0j) for tp in numpy_complex]) result = dataset.get_parameter_data() np.testing.assert_array_equal(result["x"]["x"], expected_result) @@ -1438,8 +1440,8 @@ def limit_data_to_start_end( end: int | None, input_names: list[str], expected_names: dict[str, list[str]], - expected_shapes, - expected_values, + expected_shapes: dict[str, list[tuple[int, ...]]], + expected_values: dict[str, list[np.ndarray]], ) -> tuple[int | None, int | None]: if not (start is None and end is None): if start is None: @@ -1450,8 +1452,8 @@ def limit_data_to_start_end( assert start is not None and end is not None if end < start: for name in input_names: - expected_shapes[name] = () - expected_values[name] = {} + expected_shapes[name] = [] + expected_values[name] = [] else: for name in input_names: new_shapes = [] diff --git a/tests/dataset/test_dataset_in_mem_import.py b/tests/dataset/test_dataset_in_mem_import.py index 4d04d4df7e4..a9e65dc439e 100644 --- a/tests/dataset/test_dataset_in_mem_import.py +++ b/tests/dataset/test_dataset_in_mem_import.py @@ -16,11 +16,12 @@ from pathlib import Path from qcodes.dataset.data_set_protocol import DataSetProtocol + from qcodes.dataset.descriptions.dependencies import InterDependencies_ def test_basic_roundtrip( two_empty_temp_db_connections: tuple[AtomicConnection, AtomicConnection], - some_interdeps, + some_interdeps: list[InterDependencies_], tmp_path: Path, ) -> None: """ @@ -49,8 +50,12 @@ def test_basic_roundtrip( source_dataset._perform_start_actions() for val in range(10): + # TODO: no data is actually added here: str keys never match the + # ParamSpecBase keys _enqueue_results expects, and the cache is + # never prepared (use ds.prepare(...) instead of + # _set_interdependencies/_perform_start_actions). source_dataset._enqueue_results( - {name: np.array([val]) for name in some_interdeps[1].names} + {name: np.array([val]) for name in some_interdeps[1].names} # pyright: ignore[reportArgumentType] ) source_dataset.mark_completed() source_dataset.export(export_type="netcdf", path=str(tmp_path)) @@ -80,7 +85,7 @@ def test_basic_roundtrip( def test_roundtrip_to_db_with_existing_meas( two_empty_temp_db_connections: tuple[AtomicConnection, AtomicConnection], - some_interdeps, + some_interdeps: list[InterDependencies_], tmp_path: Path, ) -> None: """ @@ -118,8 +123,12 @@ def test_roundtrip_to_db_with_existing_meas( source_dataset._perform_start_actions() for val in range(10): + # TODO: no data is actually added here: str keys never match the + # ParamSpecBase keys _enqueue_results expects, and the cache is + # never prepared (use ds.prepare(...) instead of + # _set_interdependencies/_perform_start_actions). source_dataset._enqueue_results( - {name: np.array([val]) for name in some_interdeps[1].names} + {name: np.array([val]) for name in some_interdeps[1].names} # pyright: ignore[reportArgumentType] ) source_dataset.mark_completed() source_dataset.export(export_type="netcdf", path=str(tmp_path)) @@ -135,8 +144,12 @@ def test_roundtrip_to_db_with_existing_meas( extra_dataset.mark_started() for val in range(10): + # TODO: no data is actually added here: str keys never match the + # ParamSpecBase keys _enqueue_results expects, and the cache is + # never prepared (use ds.prepare(...) instead of + # _set_interdependencies/_perform_start_actions). extra_dataset._enqueue_results( - {name: np.array([val]) for name in some_interdeps[1].names} + {name: np.array([val]) for name in some_interdeps[1].names} # pyright: ignore[reportArgumentType] ) extra_dataset.mark_completed() @@ -166,7 +179,7 @@ def test_roundtrip_to_db_with_existing_meas( def test_roundtrip_to_db_with_existing_meas_in_same_exp( two_empty_temp_db_connections: tuple[AtomicConnection, AtomicConnection], - some_interdeps, + some_interdeps: list[InterDependencies_], tmp_path: Path, ) -> None: """ @@ -201,8 +214,12 @@ def test_roundtrip_to_db_with_existing_meas_in_same_exp( source_dataset._perform_start_actions() for val in range(10): + # TODO: no data is actually added here: str keys never match the + # ParamSpecBase keys _enqueue_results expects, and the cache is + # never prepared (use ds.prepare(...) instead of + # _set_interdependencies/_perform_start_actions). source_dataset._enqueue_results( - {name: np.array([val]) for name in some_interdeps[1].names} + {name: np.array([val]) for name in some_interdeps[1].names} # pyright: ignore[reportArgumentType] ) source_dataset.mark_completed() source_dataset.export(export_type="netcdf", path=str(tmp_path)) @@ -218,8 +235,12 @@ def test_roundtrip_to_db_with_existing_meas_in_same_exp( extra_dataset.mark_started() for val in range(10): + # TODO: no data is actually added here: str keys never match the + # ParamSpecBase keys _enqueue_results expects, and the cache is + # never prepared (use ds.prepare(...) instead of + # _set_interdependencies/_perform_start_actions). extra_dataset._enqueue_results( - {name: np.array([val]) for name in some_interdeps[1].names} + {name: np.array([val]) for name in some_interdeps[1].names} # pyright: ignore[reportArgumentType] ) extra_dataset.mark_completed() diff --git a/tests/dataset/test_parameter_with_setpoints_has_control.py b/tests/dataset/test_parameter_with_setpoints_has_control.py index dcbb25fcad2..430a0b68fa9 100644 --- a/tests/dataset/test_parameter_with_setpoints_has_control.py +++ b/tests/dataset/test_parameter_with_setpoints_has_control.py @@ -13,7 +13,9 @@ if TYPE_CHECKING: import pytest + from qcodes.dataset.data_set_protocol import ValuesType from qcodes.dataset.experiment_container import Experiment + from qcodes.parameters import ParameterBase def _make_controlled_setpoints( @@ -24,7 +26,9 @@ def _make_controlled_setpoints( """Create a ParameterWithSetpoints that infers ``controlled`` via unpack_self.""" class _ControlledSetpoints(ParameterWithSetpoints): - def unpack_self(self, value): # type: ignore[override] + def unpack_self( + self, value: "ValuesType" + ) -> "list[tuple[ParameterBase, ValuesType]]": res = super().unpack_self(value) res.append((controlled, controlled())) return res diff --git a/tests/dataset/test_paramspec.py b/tests/dataset/test_paramspec.py index 89a6b1fadb7..7ad5d1a286e 100644 --- a/tests/dataset/test_paramspec.py +++ b/tests/dataset/test_paramspec.py @@ -1,6 +1,6 @@ from keyword import iskeyword from numbers import Number -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, TypedDict import hypothesis.strategies as hst import pytest @@ -26,6 +26,15 @@ def valid_identifier(**kwargs: "int") -> "SearchStrategy[str]": ).filter(lambda x: x[0].isalpha() and x.isidentifier() and not (iskeyword(x))) +class _ParamSpecKwargs(TypedDict): + name: str + paramtype: str + label: str | None + unit: str | None + depends_on: list[ParamSpec | str] + inferred_from: list[ParamSpec | str] + + # This strategy generates a dict of kwargs needed to instantiate a valid # ParamSpec object valid_paramspec_kwargs = hst.fixed_dictionaries( @@ -287,7 +296,7 @@ def test_from_dict( @given(paramspecs=hst.lists(valid_paramspec_kwargs, min_size=2, max_size=2)) -def test_hash(paramspecs) -> None: +def test_hash(paramspecs: "list[_ParamSpecKwargs]") -> None: p1 = ParamSpec(**paramspecs[0]) p2 = ParamSpec(**paramspecs[1]) @@ -315,7 +324,7 @@ def test_hash(paramspecs) -> None: add_to_2_dep=hst.booleans(), ) def test_hash_with_deferred_and_inferred_as_paramspecs( - paramspecs, + paramspecs: "list[_ParamSpecKwargs]", add_to_1_inf: "bool", add_to_1_dep: "bool", add_to_2_inf: "bool", @@ -358,7 +367,7 @@ def test_hash_with_deferred_and_inferred_as_paramspecs( @given(paramspecs=hst.lists(valid_paramspec_kwargs, min_size=1, max_size=1)) -def test_base_version(paramspecs) -> None: +def test_base_version(paramspecs: "list[_ParamSpecKwargs]") -> None: kwargs = paramspecs[0] ps = ParamSpec(**kwargs) diff --git a/tests/dataset/test_subscribing.py b/tests/dataset/test_subscribing.py index 2bb73dae895..2a4c9dcd4e0 100644 --- a/tests/dataset/test_subscribing.py +++ b/tests/dataset/test_subscribing.py @@ -36,7 +36,7 @@ class MockSubscriber: dataset! """ - def __init__(self, ds: "DataSet", lg: "bool"): + def __init__(self, ds: "DataSet", lg: "bool") -> None: self.lg = lg self.ds = ds diff --git a/tests/delegate/test_delegate_instrument.py b/tests/delegate/test_delegate_instrument.py index a712f01fad6..b221757afa1 100644 --- a/tests/delegate/test_delegate_instrument.py +++ b/tests/delegate/test_delegate_instrument.py @@ -1,3 +1,4 @@ +from typing import TYPE_CHECKING from unittest.mock import patch import pytest @@ -7,14 +8,20 @@ from qcodes.instrument.delegate import DelegateInstrument from qcodes.instrument_drivers.mock_instruments import MockField +if TYPE_CHECKING: + from qcodes.instrument_drivers.mock_instruments import MockDAC + from qcodes.station import Station -def test_mock_dac(dac) -> None: + +def test_mock_dac(dac: "MockDAC") -> None: assert dac.ch01.voltage() == 0.0 dac.ch01.voltage(1.0) assert dac.ch01.voltage() == 1.0 -def test_mock_field_delegate(station, field_x, chip_config) -> None: +def test_mock_field_delegate( + station: "Station", field_x: MockField, chip_config: str +) -> None: with patch.object( MockField, "set_field", wraps=field_x.set_field ) as mock_set_field: @@ -38,7 +45,7 @@ def test_mock_field_delegate(station, field_x, chip_config) -> None: assert_almost_equal(field.ramp_X_field(), 0.0) -def test_delegate_channel_instrument(station, chip_config) -> None: +def test_delegate_channel_instrument(station: "Station", chip_config: str) -> None: station.load_config_file(chip_config) switch = station.load_switch(station=station) @@ -56,7 +63,9 @@ def test_delegate_channel_instrument(station, chip_config) -> None: assert state.gnd == "off" -def test_invalid_parameter_paths_raise(station, request: pytest.FixtureRequest) -> None: +def test_invalid_parameter_paths_raise( + station: "Station", request: pytest.FixtureRequest +) -> None: """A parameter must map to either a single path or a sequence of paths.""" name = "delegate_with_invalid_paths" diff --git a/tests/delegate/test_device.py b/tests/delegate/test_device.py index 60e005fda83..c94faa2c610 100644 --- a/tests/delegate/test_device.py +++ b/tests/delegate/test_device.py @@ -1,4 +1,5 @@ import os +from typing import TYPE_CHECKING import numpy as np import pytest @@ -6,8 +7,15 @@ from qcodes.dataset import Measurement from qcodes.instrument_drivers.mock_instruments import DummyChannel, MockCustomChannel +if TYPE_CHECKING: + from qcodes.instrument.delegate import InstrumentGroup + from qcodes.instrument_drivers.mock_instruments import MockDAC, MockLockin + from qcodes.station import Station -def test_device(station, chip_config, dac, lockin) -> None: + +def test_device( + station: "Station", chip_config: str, dac: "MockDAC", lockin: "MockLockin" +) -> None: assert os.path.exists(chip_config) station.load_config_file(chip_config) chip = station.load_MockChip_123(station=station) @@ -23,7 +31,7 @@ def test_device(station, chip_config, dac, lockin) -> None: @pytest.mark.usefixtures("experiment") -def test_device_meas(station, chip) -> None: +def test_device_meas(station: "Station", chip: "InstrumentGroup") -> None: meas = Measurement(station=station) device = chip.device1 meas.register_parameter(device.gate) @@ -39,7 +47,7 @@ def test_device_meas(station, chip) -> None: assert len(datasaver.dataset.to_pandas_dataframe_dict()["device1_drain"]) == 10 -def test_device_with_channels(chip, station) -> None: +def test_device_with_channels(chip: "InstrumentGroup", station: "Station") -> None: device = chip.channel_device assert device.gate_1 == station.dac.ch01 @@ -59,7 +67,9 @@ def test_device_with_channels(chip, station) -> None: assert device.readout() == 30 -def test_device_with_custom_channels(chip, station) -> None: +def test_device_with_custom_channels( + chip: "InstrumentGroup", station: "Station" +) -> None: device = chip.channel_device_custom assert device.gate_1._dac_channel == station.dac.ch01 @@ -71,7 +81,7 @@ def test_device_with_custom_channels(chip, station) -> None: assert isinstance(device.fast_gate, DummyChannel) -def test_chip_definition(chip_config_typo, station) -> None: +def test_chip_definition(chip_config_typo: str, station: "Station") -> None: station.load_config_file(chip_config_typo) with pytest.raises(KeyError): _ = station.load_MockChip_123(station=station) From 7e4c729dba62e6512376f0e8d4ad24674a928bdf Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Tue, 20 Oct 2026 21:44:22 +0200 Subject: [PATCH 12/20] Make in-memory dataset import tests use real data The datasets were never prepared and results were keyed by name rather than ParamSpecBase, so no data was ever added and the tests only round-tripped empty datasets. Also assert that the data survives the round trip. --- tests/dataset/test_dataset_in_mem_import.py | 59 +++++++++------------ 1 file changed, 25 insertions(+), 34 deletions(-) diff --git a/tests/dataset/test_dataset_in_mem_import.py b/tests/dataset/test_dataset_in_mem_import.py index a9e65dc439e..ceb664b72c6 100644 --- a/tests/dataset/test_dataset_in_mem_import.py +++ b/tests/dataset/test_dataset_in_mem_import.py @@ -45,17 +45,11 @@ def test_basic_roundtrip( path_to_db=source_path, ) source_datasets.append(source_dataset) - source_dataset._set_interdependencies(some_interdeps[1]) - - source_dataset._perform_start_actions() + source_dataset.prepare(interdeps=some_interdeps[1], snapshot={}) for val in range(10): - # TODO: no data is actually added here: str keys never match the - # ParamSpecBase keys _enqueue_results expects, and the cache is - # never prepared (use ds.prepare(...) instead of - # _set_interdependencies/_perform_start_actions). source_dataset._enqueue_results( - {name: np.array([val]) for name in some_interdeps[1].names} # pyright: ignore[reportArgumentType] + {param: np.array([val]) for param in some_interdeps[1].paramspecs} ) source_dataset.mark_completed() source_dataset.export(export_type="netcdf", path=str(tmp_path)) @@ -118,17 +112,11 @@ def test_roundtrip_to_db_with_existing_meas( path_to_db=source_path, ) source_datasets.append(source_dataset) - source_dataset._set_interdependencies(some_interdeps[1]) - - source_dataset._perform_start_actions() + source_dataset.prepare(interdeps=some_interdeps[1], snapshot={}) for val in range(10): - # TODO: no data is actually added here: str keys never match the - # ParamSpecBase keys _enqueue_results expects, and the cache is - # never prepared (use ds.prepare(...) instead of - # _set_interdependencies/_perform_start_actions). source_dataset._enqueue_results( - {name: np.array([val]) for name in some_interdeps[1].names} # pyright: ignore[reportArgumentType] + {param: np.array([val]) for param in some_interdeps[1].paramspecs} ) source_dataset.mark_completed() source_dataset.export(export_type="netcdf", path=str(tmp_path)) @@ -144,12 +132,8 @@ def test_roundtrip_to_db_with_existing_meas( extra_dataset.mark_started() for val in range(10): - # TODO: no data is actually added here: str keys never match the - # ParamSpecBase keys _enqueue_results expects, and the cache is - # never prepared (use ds.prepare(...) instead of - # _set_interdependencies/_perform_start_actions). extra_dataset._enqueue_results( - {name: np.array([val]) for name in some_interdeps[1].names} # pyright: ignore[reportArgumentType] + {param: np.array([val]) for param in some_interdeps[1].paramspecs} ) extra_dataset.mark_completed() @@ -209,17 +193,11 @@ def test_roundtrip_to_db_with_existing_meas_in_same_exp( path_to_db=source_path, ) source_datasets.append(source_dataset) - source_dataset._set_interdependencies(some_interdeps[1]) - - source_dataset._perform_start_actions() + source_dataset.prepare(interdeps=some_interdeps[1], snapshot={}) for val in range(10): - # TODO: no data is actually added here: str keys never match the - # ParamSpecBase keys _enqueue_results expects, and the cache is - # never prepared (use ds.prepare(...) instead of - # _set_interdependencies/_perform_start_actions). source_dataset._enqueue_results( - {name: np.array([val]) for name in some_interdeps[1].names} # pyright: ignore[reportArgumentType] + {param: np.array([val]) for param in some_interdeps[1].paramspecs} ) source_dataset.mark_completed() source_dataset.export(export_type="netcdf", path=str(tmp_path)) @@ -235,12 +213,8 @@ def test_roundtrip_to_db_with_existing_meas_in_same_exp( extra_dataset.mark_started() for val in range(10): - # TODO: no data is actually added here: str keys never match the - # ParamSpecBase keys _enqueue_results expects, and the cache is - # never prepared (use ds.prepare(...) instead of - # _set_interdependencies/_perform_start_actions). extra_dataset._enqueue_results( - {name: np.array([val]) for name in some_interdeps[1].names} # pyright: ignore[reportArgumentType] + {param: np.array([val]) for param in some_interdeps[1].paramspecs} ) extra_dataset.mark_completed() @@ -268,10 +242,24 @@ def test_roundtrip_to_db_with_existing_meas_in_same_exp( ) +def _assert_same_data(expected: DataSetProtocol, actual: DataSetProtocol) -> None: + expected_data = expected.get_parameter_data() + actual_data = actual.get_parameter_data() + assert expected_data.keys() == actual_data.keys() + for tree_name, expected_tree in expected_data.items(): + actual_tree = actual_data[tree_name] + assert expected_tree.keys() == actual_tree.keys() + for param_name, expected_values in expected_tree.items(): + np.testing.assert_array_equal(actual_tree[param_name], expected_values) + + def _assert_before_writing_metadata( source_datasets: Sequence[DataSetInMem], loaded_datasets: Sequence[DataSetInMem] ) -> None: for ds, lds in zip(source_datasets, loaded_datasets): + assert ds.number_of_results > 0 + _assert_same_data(ds, lds) + # these should always be equal assert ds.guid == lds.guid @@ -311,6 +299,9 @@ def _assert_after_writing_metadata( offset_exps: int, ) -> None: for ds, lds, rlds in zip(source_datasets, loaded_datasets, reloaded_ds): + _assert_same_data(ds, lds) + _assert_same_data(ds, rlds) + # these should always be equal assert ds.guid == lds.guid assert ds.guid == rlds.guid From 1f1fcbba01ee16f39f44a189ba29420b3cd3f9a7 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Tue, 20 Oct 2026 21:48:58 +0200 Subject: [PATCH 13/20] Add type annotations to Keysight B1500 tests --- .../keysight_b1500/test_commandlist.py | 13 +- .../keysight_b1500/test_messagebuilder.py | 574 +++++++++--------- 2 files changed, 300 insertions(+), 287 deletions(-) diff --git a/tests/drivers/keysight_b1500/test_commandlist.py b/tests/drivers/keysight_b1500/test_commandlist.py index 5076a1dd7c2..0663abd4dcc 100644 --- a/tests/drivers/keysight_b1500/test_commandlist.py +++ b/tests/drivers/keysight_b1500/test_commandlist.py @@ -1,10 +1,15 @@ +from typing import TYPE_CHECKING + import pytest from qcodes.instrument_drivers.Keysight.keysightb1500.message_builder import CommandList +if TYPE_CHECKING: + from collections.abc import Generator + @pytest.fixture -def uut(): +def uut() -> "Generator[CommandList, None, None]": yield CommandList() @@ -15,7 +20,7 @@ def test_append(uut: CommandList) -> None: assert ["a", "b"] == uut -def test_set_final(uut) -> None: +def test_set_final(uut: CommandList) -> None: uut.append("a") uut.set_final() @@ -24,7 +29,7 @@ def test_set_final(uut) -> None: uut.append("b") -def test_clear(uut) -> None: +def test_clear(uut: CommandList) -> None: uut.append("a") uut.set_final() @@ -34,7 +39,7 @@ def test_clear(uut) -> None: assert ["b"] == uut -def test_string_representation(uut) -> None: +def test_string_representation(uut: CommandList) -> None: uut.append("a") uut.append("b") diff --git a/tests/drivers/keysight_b1500/test_messagebuilder.py b/tests/drivers/keysight_b1500/test_messagebuilder.py index bc394357d07..5e8182f1dfd 100644 --- a/tests/drivers/keysight_b1500/test_messagebuilder.py +++ b/tests/drivers/keysight_b1500/test_messagebuilder.py @@ -1,4 +1,5 @@ import re +from typing import TYPE_CHECKING, NoReturn import pytest @@ -8,9 +9,12 @@ as_csv, ) +if TYPE_CHECKING: + from collections.abc import Generator + @pytest.fixture -def mb(): +def mb() -> "Generator[MessageBuilder, None, None]": yield MessageBuilder() @@ -22,21 +26,23 @@ def test_as_csv() -> None: assert "1,2" == as_csv([c.ChNr.SLOT_01_CH1, c.ChNr.SLOT_02_CH1]) -def _skip(): +def _skip() -> NoReturn: pytest.skip("not implemented yet") -def test_cmd(mb) -> None: +def test_cmd(mb: MessageBuilder) -> None: cmd = mb.aad(1, 0).ach(1, 5).ab() assert "AAD 1,0;ACH 1,5;AB" == cmd.message -def test_raise_error_on_appending_command_after_final_command(mb) -> None: +def test_raise_error_on_appending_command_after_final_command( + mb: MessageBuilder, +) -> None: with pytest.raises(ValueError): mb.aad(1, 0).ab().ach(1, 5) -def test_exception_on_too_long_message(mb) -> None: +def test_exception_on_too_long_message(mb: MessageBuilder) -> None: length = 0 while length < 250: mb.os() @@ -52,7 +58,7 @@ def test_exception_on_too_long_message(mb) -> None: _ = mb.message -def test_clear_message_queue(mb) -> None: +def test_clear_message_queue(mb: MessageBuilder) -> None: mb.aad(1, 0) assert mb.message == "AAD 1,0" @@ -61,7 +67,7 @@ def test_clear_message_queue(mb) -> None: assert mb.message == "" -def test_aad(mb) -> None: +def test_aad(mb: MessageBuilder) -> None: assert "AAD 1,0" == mb.aad(c.ChNr.SLOT_01_CH1, c.AAD.Type.HIGH_SPEED).message mb.clear_message_queue() @@ -72,11 +78,11 @@ def test_aad(mb) -> None: ) -def test_ab(mb) -> None: +def test_ab(mb: MessageBuilder) -> None: assert "AB" == mb.ab().message -def test_ach(mb) -> None: +def test_ach(mb: MessageBuilder) -> None: assert "ACH 1,5" == mb.ach(c.ChNr.SLOT_01_CH1, c.ChNr.SLOT_05_CH1).message mb.clear_message_queue() @@ -88,7 +94,7 @@ def test_ach(mb) -> None: assert "ACH" == mb.ach().message -def test_act(mb) -> None: +def test_act(mb: MessageBuilder) -> None: assert "ACT 0,1" == mb.act(c.ACT.Mode.AUTO, 1).message mb.clear_message_queue() @@ -96,15 +102,15 @@ def test_act(mb) -> None: assert "ACT 2" == mb.act(c.ACT.Mode.PLC).message -def test_acv(mb) -> None: +def test_acv(mb: MessageBuilder) -> None: assert "ACV 7,0.01" == mb.acv(c.ChNr.SLOT_07_CH1, 0.01).message -def test_adj(mb) -> None: +def test_adj(mb: MessageBuilder) -> None: assert "ADJ 9,1" == mb.adj(c.ChNr.SLOT_09_CH1, c.ADJ.Mode.MANUAL).message -def test_adj_query(mb) -> None: +def test_adj_query(mb: MessageBuilder) -> None: assert "ADJ? 1" == mb.adj_query(c.ChNr.SLOT_01_CH1).message mb.clear_message_queue() @@ -114,7 +120,7 @@ def test_adj_query(mb) -> None: ) -def test_ait(mb) -> None: +def test_ait(mb: MessageBuilder) -> None: assert ( "AIT 2,3,0.001" == mb.ait( @@ -128,7 +134,7 @@ def test_ait(mb) -> None: ) -def test_aitm(mb) -> None: +def test_aitm(mb: MessageBuilder) -> None: assert "AITM 0" == mb.aitm(c.APIVersion.B1500).message @@ -136,25 +142,25 @@ def test_aitm_query(mb: MessageBuilder) -> None: assert "AITM?" == mb.aitm_query().message -def test_als(mb) -> None: +def test_als(mb: MessageBuilder) -> None: with pytest.raises(NotImplementedError): mb.als(c.ChNr.SLOT_01_CH1, 1, b"a") -def test_als_query(mb) -> None: +def test_als_query(mb: MessageBuilder) -> None: assert "ALS? 1" == mb.als_query(c.ChNr.SLOT_01_CH1).message -def test_alw(mb) -> None: +def test_alw(mb: MessageBuilder) -> None: with pytest.raises(NotImplementedError): mb.alw(c.ChNr.SLOT_01_CH1, 1, b"a") -def test_alw_query(mb) -> None: +def test_alw_query(mb: MessageBuilder) -> None: assert "ALW? 1" == mb.alw_query(c.ChNr.SLOT_01_CH1).message -def test_av(mb) -> None: +def test_av(mb: MessageBuilder) -> None: assert "AV 10" == mb.av(10).message mb.clear_message_queue() assert "AV -50" == mb.av(-50).message @@ -162,13 +168,13 @@ def test_av(mb) -> None: assert "AV 100,1" == mb.av(100, c.AV.Mode.MANUAL).message -def test_az(mb) -> None: +def test_az(mb: MessageBuilder) -> None: assert "AZ 0" == mb.az(False).message mb.clear_message_queue() assert "AZ 1" == mb.az(True).message -def test_bc(mb) -> None: +def test_bc(mb: MessageBuilder) -> None: assert "BC" == mb.bc().message @@ -180,7 +186,7 @@ def test_bdt(mb: MessageBuilder) -> None: assert "BDT 0.1,0.001" == mb.bdt(hold=0.1, delay=1e-3).message -def test_bdv(mb) -> None: +def test_bdv(mb: MessageBuilder) -> None: assert ( "BDV 1,0,0,100,0.01" == mb.bdv( @@ -193,7 +199,7 @@ def test_bdv(mb) -> None: ) -def test_bgi(mb) -> None: +def test_bgi(mb: MessageBuilder) -> None: assert ( "BGI 1,0,1e-08,14,1e-06" == mb.bgi( @@ -206,96 +212,96 @@ def test_bgi(mb) -> None: ) -def test_bgv(mb) -> None: +def test_bgv(mb: MessageBuilder) -> None: assert "BGV 1,0,0.1,12,5" == mb.bgv(1, 0, 0.1, 12, 5).message -def test_bsi(mb) -> None: +def test_bsi(mb: MessageBuilder) -> None: assert "BSI 1,0,1e-12,1e-06,10" == mb.bsi(1, 0, 1e-12, 1e-6, 10).message -def test_bsm(mb) -> None: +def test_bsm(mb: MessageBuilder) -> None: assert "BSM 1,2,3" == mb.bsm(1, 2, 3).message -def test_bssi(mb) -> None: +def test_bssi(mb: MessageBuilder) -> None: assert "BSSI 1,0,1e-06,10" == mb.bssi(1, 0, 1e-6, 10).message -def test_bssv(mb) -> None: +def test_bssv(mb: MessageBuilder) -> None: assert "BSSV 1,0,5,1e-06" == mb.bssv(1, 0, 5, 1e-6).message -def test_bst(mb) -> None: +def test_bst(mb: MessageBuilder) -> None: assert "BST 5,0.1" == mb.bst(5, 0.1).message -def test_bsv(mb) -> None: +def test_bsv(mb: MessageBuilder) -> None: assert "BSV 1,0,0,20,1e-06" == mb.bsv(1, 0, 0, 20, 1e-6).message -def test_bsvm(mb) -> None: +def test_bsvm(mb: MessageBuilder) -> None: assert "BSVM 1" == mb.bsvm(1).message -def test_ca(mb) -> None: +def test_ca(mb: MessageBuilder) -> None: assert "CA" == mb.ca().message -def test_cal_query(mb) -> None: +def test_cal_query(mb: MessageBuilder) -> None: assert "*CAL?" == mb.cal_query().message -def test_cl(mb) -> None: +def test_cl(mb: MessageBuilder) -> None: assert "CL" == mb.cl().message mb.clear_message_queue() assert "CL 1,2,3,5" == mb.cl([1, 2, 3, 5]).message -def test_clcorr(mb) -> None: +def test_clcorr(mb: MessageBuilder) -> None: assert "CLCORR 9,1" == mb.clcorr(9, 1).message -def test_cm(mb) -> None: +def test_cm(mb: MessageBuilder) -> None: assert "CM 0" == mb.cm(False).message -def test_cmm(mb) -> None: +def test_cmm(mb: MessageBuilder) -> None: assert "CMM 1,2" == mb.cmm(1, 2).message -def test_cn(mb) -> None: +def test_cn(mb: MessageBuilder) -> None: assert "CN" == mb.cn().message mb.clear_message_queue() assert "CN 1,2,3,5" == mb.cn([1, 2, 3, 5]).message -def test_cnx(mb) -> None: +def test_cnx(mb: MessageBuilder) -> None: assert "CNX" == mb.cnx().message mb.clear_message_queue() assert "CNX 1,2,3,5" == mb.cnx([1, 2, 3, 5]).message -def test_corr_query(mb) -> None: +def test_corr_query(mb: MessageBuilder) -> None: assert "CORR? 9,3" == mb.corr_query(9, 3).message -def test_corrdt(mb) -> None: +def test_corrdt(mb: MessageBuilder) -> None: assert ( "CORRDT 9,3000000,0,0,0,0,0,0" == mb.corrdt(9, 3000000, 0, 0, 0, 0, 0, 0).message ) -def test_corrdt_query(mb) -> None: +def test_corrdt_query(mb: MessageBuilder) -> None: assert "CORRDT? 9,1" == mb.corrdt_query(9, 1).message -def test_corrl(mb) -> None: +def test_corrl(mb: MessageBuilder) -> None: assert "CORRL 9,3000000" == mb.corrl(9, 3000000).message -def test_corrl_query(mb) -> None: +def test_corrl_query(mb: MessageBuilder) -> None: assert "CORRL? 9" == mb.corrl_query(9).message mb.clear_message_queue() assert "CORRL? 9,2" == mb.corrl_query(9, 2).message @@ -308,382 +314,382 @@ def test_corrser_query(mb: MessageBuilder) -> None: ) -def test_corrst(mb) -> None: - assert "CORRST 3,1,1" == mb.corrst(3, 1, 1).message +def test_corrst(mb: MessageBuilder) -> None: + assert "CORRST 3,1,1" == mb.corrst(3, 1, True).message -def test_corrst_query(mb) -> None: +def test_corrst_query(mb: MessageBuilder) -> None: assert "CORRST? 1,2" == mb.corrst_query(1, 2).message -def test_dcorr(mb) -> None: +def test_dcorr(mb: MessageBuilder) -> None: assert "DCORR 3,1,100,0.1,0.2" == mb.dcorr(3, 1, 100, 0.1, 0.2).message -def test_dcorr_query(mb) -> None: +def test_dcorr_query(mb: MessageBuilder) -> None: assert "DCORR? 3,1" == mb.dcorr_query(3, 1).message -def test_dcv(mb) -> None: +def test_dcv(mb: MessageBuilder) -> None: assert "DCV 3,2.5" == mb.dcv(3, 2.5).message -def test_di(mb) -> None: +def test_di(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_diag_query(mb) -> None: +def test_diag_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_do(mb) -> None: +def test_do(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_dsmplarm(mb) -> None: +def test_dsmplarm(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_dsmplflush(mb) -> None: +def test_dsmplflush(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_dsmplsetup(mb) -> None: +def test_dsmplsetup(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_dv(mb) -> None: +def test_dv(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_dz(mb) -> None: +def test_dz(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_emg_query(mb) -> None: +def test_emg_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_end(mb) -> None: +def test_end(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erc(mb) -> None: +def test_erc(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_ercmaa(mb) -> None: +def test_ercmaa(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_ercmaa_query(mb) -> None: +def test_ercmaa_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_ercmagrd(mb) -> None: +def test_ercmagrd(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_ercmagrd_query(mb) -> None: +def test_ercmagrd_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_ercmaio(mb) -> None: +def test_ercmaio(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_ercmaio_query(mb) -> None: +def test_ercmaio_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_ercmapfgd(mb) -> None: +def test_ercmapfgd(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erhpa(mb) -> None: +def test_erhpa(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erhpa_query(mb) -> None: +def test_erhpa_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erhpe(mb) -> None: +def test_erhpe(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erhpe_query(mb) -> None: +def test_erhpe_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erhpl(mb) -> None: +def test_erhpl(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erhpl_query(mb) -> None: +def test_erhpl_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erhpp(mb) -> None: +def test_erhpp(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erhpp_query(mb) -> None: +def test_erhpp_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erhpqg(mb) -> None: +def test_erhpqg(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erhpqg_query(mb) -> None: +def test_erhpqg_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erhpr(mb) -> None: +def test_erhpr(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erhpr_query(mb) -> None: +def test_erhpr_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erhps(mb) -> None: +def test_erhps(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erhps_query(mb) -> None: +def test_erhps_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erhvca(mb) -> None: +def test_erhvca(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erhvca_query(mb) -> None: +def test_erhvca_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erhvctst_query(mb) -> None: +def test_erhvctst_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erhvp(mb) -> None: +def test_erhvp(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erhvp_query(mb) -> None: +def test_erhvp_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erhvpv(mb) -> None: +def test_erhvpv(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erhvs(mb) -> None: +def test_erhvs(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erhvs_query(mb) -> None: +def test_erhvs_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erm(mb) -> None: +def test_erm(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_ermod(mb) -> None: +def test_ermod(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_ermod_query(mb) -> None: +def test_ermod_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erpfda(mb) -> None: +def test_erpfda(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erpfda_query(mb) -> None: +def test_erpfda_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erpfdp(mb) -> None: +def test_erpfdp(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erpfdp_query(mb) -> None: +def test_erpfdp_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erpfds(mb) -> None: +def test_erpfds(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erpfds_query(mb) -> None: +def test_erpfds_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erpfga(mb) -> None: +def test_erpfga(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erpfga_query(mb) -> None: +def test_erpfga_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erpfgp(mb) -> None: +def test_erpfgp(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erpfgp_query(mb) -> None: +def test_erpfgp_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erpfgr(mb) -> None: +def test_erpfgr(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erpfgr_query(mb) -> None: +def test_erpfgr_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erpfqg(mb) -> None: +def test_erpfqg(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erpfqg_query(mb) -> None: +def test_erpfqg_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erpftemp_query(mb) -> None: +def test_erpftemp_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erpfuhca(mb) -> None: +def test_erpfuhca(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erpfuhca_query(mb) -> None: +def test_erpfuhca_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erpfuhccal_query(mb) -> None: +def test_erpfuhccal_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erpfuhcmax_query(mb) -> None: +def test_erpfuhcmax_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erpfuhctst(mb) -> None: +def test_erpfuhctst(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_err_query(mb) -> None: +def test_err_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_errx_query(mb) -> None: +def test_errx_query(mb: MessageBuilder) -> None: assert "ERRX?" == mb.errx_query().message mb.clear_message_queue() assert "ERRX? 0" == mb.errx_query(mode=c.ERRX.Mode.CODE_AND_MESSAGE).message -def test_ers_query(mb) -> None: +def test_ers_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erssp(mb) -> None: +def test_erssp(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_erssp_query(mb) -> None: +def test_erssp_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_eruhva(mb) -> None: +def test_eruhva(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_eruhva_query(mb) -> None: +def test_eruhva_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_fc(mb) -> None: +def test_fc(mb: MessageBuilder) -> None: assert "FC 3,1000" == mb.fc(3, 1000).message -def test_fl(mb) -> None: +def test_fl(mb: MessageBuilder) -> None: assert "FL 1" == mb.fl(True).message mb.clear_message_queue() assert "FL 0,1,3,5" == mb.fl(False, [1, 3, 5]).message @@ -697,7 +703,7 @@ def test_fl(mb) -> None: _ = mb.fl(False, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]).message -def test_fmt(mb) -> None: +def test_fmt(mb: MessageBuilder) -> None: assert "FMT 1" == mb.fmt(c.FMT.Format.ASCII_12_DIGITS_WITH_HEADER_CRLF_EOI).message mb.clear_message_queue() assert ( @@ -709,160 +715,160 @@ def test_fmt(mb) -> None: ) -def test_hvsmuop(mb) -> None: +def test_hvsmuop(mb: MessageBuilder) -> None: _skip() -def test_hvsmuop_query(mb) -> None: +def test_hvsmuop_query(mb: MessageBuilder) -> None: assert "HVSMUOP?" == mb.hvsmuop_query().message -def test_idn_query(mb) -> None: +def test_idn_query(mb: MessageBuilder) -> None: assert "IN" == mb.in_().message mb.clear_message_queue() assert "IN 1,2,3,5,6" == mb.in_([1, 2, 3, 5, 6]).message -def test_imp(mb) -> None: +def test_imp(mb: MessageBuilder) -> None: assert "IMP 10" == mb.imp(c.IMP.MeasurementMode.Z_THETA_RAD).message -def test_in_(mb) -> None: +def test_in_(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_intlkvth(mb) -> None: +def test_intlkvth(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_intlkvth_query(mb) -> None: +def test_intlkvth_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_lgi(mb) -> None: +def test_lgi(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_lgv(mb) -> None: +def test_lgv(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_lim(mb) -> None: +def test_lim(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_lim_query(mb) -> None: +def test_lim_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_lmn(mb) -> None: +def test_lmn(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_lop_query(mb) -> None: +def test_lop_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_lrn_query(mb) -> None: +def test_lrn_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_lsi(mb) -> None: +def test_lsi(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_lsm(mb) -> None: +def test_lsm(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_lssi(mb) -> None: +def test_lssi(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_lssv(mb) -> None: +def test_lssv(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_lst_query(mb) -> None: +def test_lst_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_lstm(mb) -> None: +def test_lstm(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_lsv(mb) -> None: +def test_lsv(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_lsvm(mb) -> None: +def test_lsvm(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_mcc(mb) -> None: +def test_mcc(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_mcpnt(mb) -> None: +def test_mcpnt(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_mcpnx(mb) -> None: +def test_mcpnx(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_mcpt(mb) -> None: +def test_mcpt(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_mcpws(mb) -> None: +def test_mcpws(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_mcpwnx(mb) -> None: +def test_mcpwnx(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_mdcv(mb) -> None: +def test_mdcv(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_mi(mb) -> None: +def test_mi(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_ml(mb) -> None: +def test_ml(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_mm(mb) -> None: +def test_mm(mb: MessageBuilder) -> None: assert "MM 1,1" == mb.mm(mode=c.MM.Mode.SPOT, channels=[c.ChNr.SLOT_01_CH1]).message mb.clear_message_queue() assert ( @@ -874,183 +880,183 @@ def test_mm(mb) -> None: ) -def test_msc(mb) -> None: +def test_msc(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_msp(mb) -> None: +def test_msp(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_mt(mb) -> None: +def test_mt(mb: MessageBuilder) -> None: assert "MT 0.0,0.42,32" == mb.mt(0.0, 0.42, 32).message mb.clear_message_queue() assert "MT 0.0,0.42,32,0.12" == mb.mt(0.0, 0.42, 32, 0.12).message -def test_mtdcv(mb) -> None: +def test_mtdcv(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_mv(mb) -> None: +def test_mv(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_nub_query(mb) -> None: +def test_nub_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_odsw(mb) -> None: +def test_odsw(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_odsw_query(mb) -> None: +def test_odsw_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_opc_query(mb) -> None: +def test_opc_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_os(mb) -> None: +def test_os(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_osx(mb) -> None: +def test_osx(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_pa(mb) -> None: +def test_pa(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_pad(mb) -> None: +def test_pad(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_pax(mb) -> None: +def test_pax(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_pch(mb) -> None: +def test_pch(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_pch_query(mb) -> None: +def test_pch_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_pdcv(mb) -> None: +def test_pdcv(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_pi(mb) -> None: +def test_pi(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_pt(mb) -> None: +def test_pt(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_ptdcv(mb) -> None: +def test_ptdcv(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_pv(mb) -> None: +def test_pv(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_pwdcv(mb) -> None: +def test_pwdcv(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_pwi(mb) -> None: +def test_pwi(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_pwv(mb) -> None: +def test_pwv(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_qsc(mb) -> None: +def test_qsc(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_qsl(mb) -> None: +def test_qsl(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_qsm(mb) -> None: +def test_qsm(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_qso(mb) -> None: +def test_qso(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_qsr(mb) -> None: +def test_qsr(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_qst(mb) -> None: +def test_qst(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_qsv(mb) -> None: +def test_qsv(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_qsz(mb) -> None: +def test_qsz(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_rc(mb) -> None: +def test_rc(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_rcv(mb) -> None: +def test_rcv(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_ri(mb) -> None: +def test_ri(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_rm(mb) -> None: +def test_rm(mb: MessageBuilder) -> None: assert "RM 1,2" == mb.rm(1, c.RM.Mode.AUTO_UP).message mb.clear_message_queue() assert "RM 2,3,60" == mb.rm(2, c.RM.Mode.AUTO_UP_DOWN, 60).message @@ -1059,450 +1065,452 @@ def test_rm(mb) -> None: mb.rm(c.ChNr.SLOT_01_CH1, c.RM.Mode.DEFAULT, 22) -def test_rst(mb) -> None: +def test_rst(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_ru(mb) -> None: +def test_ru(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_rv(mb) -> None: +def test_rv(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_rz(mb) -> None: +def test_rz(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_sal(mb) -> None: +def test_sal(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_sap(mb) -> None: +def test_sap(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_sar(mb) -> None: +def test_sar(mb: MessageBuilder) -> None: assert "SAR 1,0" == mb.sar(1, enable_picoamp_autoranging=True).message -def test_scr(mb) -> None: +def test_scr(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_ser(mb) -> None: +def test_ser(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_ser_query(mb) -> None: +def test_ser_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_sim(mb) -> None: +def test_sim(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_sim_query(mb) -> None: +def test_sim_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_sopc(mb) -> None: +def test_sopc(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_sopc_query(mb) -> None: +def test_sopc_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_sovc(mb) -> None: +def test_sovc(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_sovc_query(mb) -> None: +def test_sovc_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_spm(mb) -> None: +def test_spm(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_spm_query(mb) -> None: +def test_spm_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_spp(mb) -> None: +def test_spp(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_spper(mb) -> None: +def test_spper(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_spper_query(mb) -> None: +def test_spper_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_sprm(mb) -> None: +def test_sprm(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_sprm_query(mb) -> None: +def test_sprm_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_spst_query(mb) -> None: +def test_spst_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_spt(mb) -> None: +def test_spt(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_spt_query(mb) -> None: +def test_spt_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_spupd(mb) -> None: +def test_spupd(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_spv(mb) -> None: +def test_spv(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_spv_query(mb) -> None: +def test_spv_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_sre(mb) -> None: +def test_sre(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_sre_query(mb) -> None: +def test_sre_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_srp(mb) -> None: +def test_srp(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_ssl(mb) -> None: +def test_ssl(mb: MessageBuilder) -> None: assert "SSL 9,0" == mb.ssl(9, enable_indicator_led=False).message -def test_ssp(mb) -> None: +def test_ssp(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_ssr(mb) -> None: +def test_ssr(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_st(mb) -> None: +def test_st(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_stb_query(mb) -> None: +def test_stb_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_stgp(mb) -> None: +def test_stgp(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_stgp_query(mb) -> None: +def test_stgp_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_tacv(mb) -> None: +def test_tacv(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_tc(mb) -> None: +def test_tc(mb: MessageBuilder) -> None: assert "TC 3,0" == mb.tc(3, 0).message -def test_tdcv(mb) -> None: +def test_tdcv(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_tdi(mb) -> None: +def test_tdi(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_tdv(mb) -> None: +def test_tdv(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_tgmo(mb) -> None: +def test_tgmo(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_tgp(mb) -> None: +def test_tgp(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_tgpc(mb) -> None: +def test_tgpc(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_tgsi(mb) -> None: +def test_tgsi(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_tgso(mb) -> None: +def test_tgso(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_tgxo(mb) -> None: +def test_tgxo(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_ti(mb) -> None: +def test_ti(mb: MessageBuilder) -> None: assert "TI 1" == mb.ti(chnum=1).message mb.clear_message_queue() assert "TI 1,-14" == mb.ti(chnum=1, i_range=c.IMeasRange.FIX_1uA).message -def test_tiv(mb) -> None: +def test_tiv(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_tm(mb) -> None: +def test_tm(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_tmacv(mb) -> None: +def test_tmacv(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_tmdcv(mb) -> None: +def test_tmdcv(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_tsc(mb) -> None: +def test_tsc(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_tsq(mb) -> None: +def test_tsq(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_tsr(mb) -> None: +def test_tsr(mb: MessageBuilder) -> None: assert "TSR" == mb.tsr().message mb.clear_message_queue() assert "TSR 1" == mb.tsr(c.ChNr.SLOT_01_CH1).message -def test_tst(mb) -> None: +def test_tst(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_ttc(mb) -> None: +def test_ttc(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_tti(mb) -> None: +def test_tti(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_ttiv(mb) -> None: +def test_ttiv(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_ttv(mb) -> None: +def test_ttv(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_tv(mb) -> None: +def test_tv(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_unt_query(mb) -> None: +def test_unt_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_var(mb) -> None: +def test_var(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_var_query(mb) -> None: +def test_var_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_wacv(mb) -> None: +def test_wacv(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_wat(mb) -> None: +def test_wat(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_wdcv(mb) -> None: +def test_wdcv(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_wfc(mb) -> None: +def test_wfc(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_wi(mb) -> None: +def test_wi(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_wm(mb) -> None: +def test_wm(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_wmacv(mb) -> None: +def test_wmacv(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_wmdcv(mb) -> None: +def test_wmdcv(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_wmfc(mb) -> None: +def test_wmfc(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_wncc(mb) -> None: +def test_wncc(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_wnu_query(mb) -> None: +def test_wnu_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_wnx(mb) -> None: +def test_wnx(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_ws(mb) -> None: +def test_ws(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_wsi(mb) -> None: +def test_wsi(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_wsv(mb) -> None: +def test_wsv(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_wt(mb) -> None: +def test_wt(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_wtacv(mb) -> None: +def test_wtacv(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_wtdcv(mb) -> None: +def test_wtdcv(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_wtfc(mb) -> None: +def test_wtfc(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_wv(mb) -> None: +def test_wv(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_wz_query(mb) -> None: +def test_wz_query(mb: MessageBuilder) -> None: # assert '' == mb.().message _skip() -def test_xe(mb) -> None: +def test_xe(mb: MessageBuilder) -> None: assert "XE" == mb.xe().message -def test_nplc_setting_for_high_speed_vs_high_resolution_mode(mb) -> None: +def test_nplc_setting_for_high_speed_vs_high_resolution_mode( + mb: MessageBuilder, +) -> None: msg = ( mb.ait(adc_type=c.AIT.Type.HIGH_SPEED, mode=c.AIT.Mode.NPLC, coeff=3) .ait(adc_type=c.AIT.Type.HIGH_RESOLUTION, mode=c.AIT.Mode.NPLC, coeff=8) @@ -1511,7 +1519,7 @@ def test_nplc_setting_for_high_speed_vs_high_resolution_mode(mb) -> None: assert msg == "AIT 0,2,3;AIT 1,2,8" -def test_set_resolution_mode_for_each_smu(mb) -> None: +def test_set_resolution_mode_for_each_smu(mb: MessageBuilder) -> None: msg = ( mb.aad(chnum=1, adc_type=c.AAD.Type.HIGH_SPEED) .aad(chnum=2, adc_type=c.AAD.Type.HIGH_RESOLUTION) From 90cbdacf6427b12a5b213db79b0e3aef8b6a30cf Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Tue, 20 Oct 2026 21:52:42 +0200 Subject: [PATCH 14/20] Add missing type annotations to B1500 MessageBuilder --- pyproject.toml | 1 - .../Keysight/keysightb1500/message_builder.py | 28 +++++++++++++++---- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index e9b1bbca5ad..12cb48b7fdd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -163,7 +163,6 @@ strict_equality = true module = [ "tests.*", "qcodes.instrument_drivers.Harvard.*", - "qcodes.instrument_drivers.Keysight.keysightb1500.message_builder.*", ] disallow_untyped_defs = false disallow_incomplete_defs = false diff --git a/src/qcodes/instrument_drivers/Keysight/keysightb1500/message_builder.py b/src/qcodes/instrument_drivers/Keysight/keysightb1500/message_builder.py index cb8bbabd26e..1baf36c672d 100644 --- a/src/qcodes/instrument_drivers/Keysight/keysightb1500/message_builder.py +++ b/src/qcodes/instrument_drivers/Keysight/keysightb1500/message_builder.py @@ -1587,7 +1587,13 @@ def ercmagrd_query(self) -> "MessageBuilder": self._msg.append(cmd) return self - def ercmaio(self, cmhl=None, acgs=None, bias=None, corr=None) -> "MessageBuilder": + def ercmaio( + self, + cmhl: int | None = None, + acgs: int | None = None, + bias: int | None = None, + corr: int | None = None, + ) -> "MessageBuilder": cmd = "ERCMAIO" if cmhl is not None: @@ -2191,7 +2197,12 @@ def lssv( return self @final_command - def lst_query(self, pnum=None, index=None, size=None) -> "MessageBuilder": + def lst_query( + self, + pnum: int | None = None, + index: int | None = None, + size: int | None = None, + ) -> "MessageBuilder": cmd = "LST?" if pnum is not None: @@ -2593,7 +2604,7 @@ def pch( return self @final_command - def pch_query(self, master=None) -> "MessageBuilder": + def pch_query(self, master: constants.ChNr | int | None = None) -> "MessageBuilder": cmd = "PCH" if master is not None: @@ -3031,7 +3042,9 @@ def spper_query(self) -> "MessageBuilder": self._msg.append(cmd) return self - def sprm(self, mode: constants.SPRM.Mode | int, condition=None) -> "MessageBuilder": + def sprm( + self, mode: constants.SPRM.Mode | int, condition: float | None = None + ) -> "MessageBuilder": if condition is None: cmd = f"SPRM {mode}" else: @@ -3198,7 +3211,7 @@ def tc( self, chnum: constants.ChNr | int, mode: constants.RangingMode | int, - ranging_type=None, + ranging_type: int | None = None, ) -> "MessageBuilder": cmd = f"TC {chnum},{mode}" @@ -3558,7 +3571,10 @@ def wacv( return self def wat( - self, wait_time_type: constants.WAT.Type | int, coeff: float, offset=None + self, + wait_time_type: constants.WAT.Type | int, + coeff: float, + offset: float | None = None, ) -> "MessageBuilder": cmd = f"WAT {wait_time_type},{coeff}" From 5ec4d1687661530f5b167cc3a378ebdee23f6469 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Tue, 20 Oct 2026 22:13:15 +0200 Subject: [PATCH 15/20] Accept any AcquisitionInterface in Alazar acquire acquire only uses AcquisitionInterface methods but required an AcquisitionController. AcquisitionController also subclassed AcquisitionInterface[Any], so its type parameter never reached the return type of acquire. --- src/qcodes/instrument_drivers/AlazarTech/ATS.py | 4 ++-- tests/mockers/test_simulated_ats_api.py | 9 +-------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/qcodes/instrument_drivers/AlazarTech/ATS.py b/src/qcodes/instrument_drivers/AlazarTech/ATS.py index f23d2cf03fc..a1e64776c0b 100644 --- a/src/qcodes/instrument_drivers/AlazarTech/ATS.py +++ b/src/qcodes/instrument_drivers/AlazarTech/ATS.py @@ -322,7 +322,7 @@ def acquire[OutputType]( # noqa: D417 (missing args documentation) get_processed_data: str | None = None, allocated_buffers: int | None = None, buffer_timeout: int | None = None, - acquisition_controller: AcquisitionController[OutputType] | None = None, + acquisition_controller: AcquisitionInterface[OutputType] | None = None, ) -> OutputType: """ Perform a single acquisition with the Alazar board, and set certain @@ -897,7 +897,7 @@ def buffer_done_callback(self, buffers_completed: int) -> None: """ -class AcquisitionController[OutputType](Instrument, AcquisitionInterface[Any]): +class AcquisitionController[OutputType](Instrument, AcquisitionInterface[OutputType]): """ Compatibility class. The methods of :class:`AcquisitionController` have been extracted. This class is the base class fro AcquisitionInterfaces diff --git a/tests/mockers/test_simulated_ats_api.py b/tests/mockers/test_simulated_ats_api.py index 165575a029e..98ba3ef03dc 100644 --- a/tests/mockers/test_simulated_ats_api.py +++ b/tests/mockers/test_simulated_ats_api.py @@ -71,14 +71,7 @@ def test_simulated_alazar( get_processed_data=None, allocated_buffers=4, buffer_timeout=None, - # TODO: AlazarTechATS.acquire annotates acquisition_controller as - # AcquisitionController[OutputType] but only uses methods from - # AcquisitionInterface, so any AcquisitionInterface subclass works - # at runtime. AcquisitionController also subclasses - # AcquisitionInterface[Any], so OutputType is never bound to the - # return type. Widen the annotation to - # AcquisitionInterface[OutputType] and remove this ignore. - acquisition_controller=alazar_ctrl, # pyright: ignore[reportArgumentType] + acquisition_controller=alazar_ctrl, ) assert "tot acquire time" in caplog.records[-1].msg assert len(data) == buffers_per_acquisition From 780831e262d32e88dbe41cd425b67d248b14c8dd Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Tue, 20 Oct 2026 22:16:29 +0200 Subject: [PATCH 16/20] Add newsfragments for src typing fixes --- docs/changes/newsfragments/0000.improved | 3 +++ docs/changes/newsfragments/0000.improved_driver | 6 ++++++ docs/changes/newsfragments/0000.improved_driver.1 | 3 +++ 3 files changed, 12 insertions(+) create mode 100644 docs/changes/newsfragments/0000.improved create mode 100644 docs/changes/newsfragments/0000.improved_driver create mode 100644 docs/changes/newsfragments/0000.improved_driver.1 diff --git a/docs/changes/newsfragments/0000.improved b/docs/changes/newsfragments/0000.improved new file mode 100644 index 00000000000..361030988fe --- /dev/null +++ b/docs/changes/newsfragments/0000.improved @@ -0,0 +1,3 @@ +The type annotations of ``Station.load_all_instruments`` now accept calling it without +arguments or with only one of ``only_names`` and ``only_types``. Previously these valid +calls matched none of the overloads and were rejected by static type checkers. diff --git a/docs/changes/newsfragments/0000.improved_driver b/docs/changes/newsfragments/0000.improved_driver new file mode 100644 index 00000000000..34dad960ed0 --- /dev/null +++ b/docs/changes/newsfragments/0000.improved_driver @@ -0,0 +1,6 @@ +``AlazarTechATS.acquire`` is now annotated to accept any ``AcquisitionInterface`` as +``acquisition_controller``, not only an ``AcquisitionController``, matching what it supports +at runtime. ``AcquisitionController`` now passes its type parameter on to +``AcquisitionInterface``, so the return type of ``acquire`` is inferred from the +``post_acquire`` return type of the controller. Subclasses of ``AcquisitionController[T]`` +whose ``post_acquire`` does not return ``T`` will now be reported by static type checkers. diff --git a/docs/changes/newsfragments/0000.improved_driver.1 b/docs/changes/newsfragments/0000.improved_driver.1 new file mode 100644 index 00000000000..835b4cd0dfa --- /dev/null +++ b/docs/changes/newsfragments/0000.improved_driver.1 @@ -0,0 +1,3 @@ +Added missing type annotations to the optional arguments of the Keysight B1500 +``MessageBuilder`` methods ``ercmaio``, ``lst_query``, ``pch_query``, ``sprm``, ``tc`` +and ``wat``. From 04e306d070317acefd724d2eb8cf8bc407e775a0 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Wed, 21 Oct 2026 22:30:19 +0200 Subject: [PATCH 17/20] Use PR number in newsfragments --- docs/changes/newsfragments/{0000.improved => 8644.improved} | 0 .../newsfragments/{0000.improved_driver => 8644.improved_driver} | 0 .../{0000.improved_driver.1 => 8644.improved_driver.1} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename docs/changes/newsfragments/{0000.improved => 8644.improved} (100%) rename docs/changes/newsfragments/{0000.improved_driver => 8644.improved_driver} (100%) rename docs/changes/newsfragments/{0000.improved_driver.1 => 8644.improved_driver.1} (100%) diff --git a/docs/changes/newsfragments/0000.improved b/docs/changes/newsfragments/8644.improved similarity index 100% rename from docs/changes/newsfragments/0000.improved rename to docs/changes/newsfragments/8644.improved diff --git a/docs/changes/newsfragments/0000.improved_driver b/docs/changes/newsfragments/8644.improved_driver similarity index 100% rename from docs/changes/newsfragments/0000.improved_driver rename to docs/changes/newsfragments/8644.improved_driver diff --git a/docs/changes/newsfragments/0000.improved_driver.1 b/docs/changes/newsfragments/8644.improved_driver.1 similarity index 100% rename from docs/changes/newsfragments/0000.improved_driver.1 rename to docs/changes/newsfragments/8644.improved_driver.1 From 46cf260fe93a57a9bb81ef2c342cd962e6d230b0 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Wed, 21 Oct 2026 22:40:34 +0200 Subject: [PATCH 18/20] Defer annotation evaluation in dond tests With the minimum supported typing_extensions, Parameter[int] fails at runtime since TypeVar defaults are not applied, which broke test collection. --- tests/dataset/dond/conftest.py | 6 ++++-- tests/dataset/dond/test_do1d.py | 4 +++- tests/dataset/dond/test_do2d.py | 4 +++- tests/dataset/dond/test_dond.py | 12 +++++++----- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/tests/dataset/dond/conftest.py b/tests/dataset/dond/conftest.py index 5e368610bed..c4343a05c8b 100644 --- a/tests/dataset/dond/conftest.py +++ b/tests/dataset/dond/conftest.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from typing import TYPE_CHECKING import matplotlib.pyplot as plt @@ -13,7 +15,7 @@ @pytest.fixture(autouse=True) -def set_tmp_output_dir(tmpdir: "LocalPath") -> "Generator[None, None, None]": +def set_tmp_output_dir(tmpdir: LocalPath) -> Generator[None, None, None]: old_config = config.user.mainfolder try: config.user.mainfolder = str(tmpdir) @@ -23,7 +25,7 @@ def set_tmp_output_dir(tmpdir: "LocalPath") -> "Generator[None, None, None]": @pytest.fixture() -def plot_close() -> "Generator[None, None, None]": +def plot_close() -> Generator[None, None, None]: yield plt.close("all") diff --git a/tests/dataset/dond/test_do1d.py b/tests/dataset/dond/test_do1d.py index 342c73874be..fc6072af156 100644 --- a/tests/dataset/dond/test_do1d.py +++ b/tests/dataset/dond/test_do1d.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import logging from typing import TYPE_CHECKING @@ -209,7 +211,7 @@ def test_do1d_parameter_with_array_vals(_param_set: Parameter[float | None]) -> def test_do1d_explicit_experiment( _param_set: Parameter[float | None], _param: Parameter[int], - experiment: "Experiment", + experiment: Experiment, ) -> None: start = 0 stop = 1 diff --git a/tests/dataset/dond/test_do2d.py b/tests/dataset/dond/test_do2d.py index 6202deb81c3..a3a708c602d 100644 --- a/tests/dataset/dond/test_do2d.py +++ b/tests/dataset/dond/test_do2d.py @@ -2,6 +2,8 @@ These are the basic black box tests for the doNd functions. """ +from __future__ import annotations + from typing import TYPE_CHECKING import hypothesis.strategies as hst @@ -415,7 +417,7 @@ def test_do2d_explicit_experiment( _param_set: Parameter[float | None], _param_set_2: Parameter[float | None], _param: Parameter[int], - experiment: "Experiment", + experiment: Experiment, ) -> None: start_p1 = 0 stop_p1 = 0.5 diff --git a/tests/dataset/dond/test_dond.py b/tests/dataset/dond/test_dond.py index 973e90cb7bb..dc12841c423 100644 --- a/tests/dataset/dond/test_dond.py +++ b/tests/dataset/dond/test_dond.py @@ -2,6 +2,8 @@ These are the basic black box tests for the doNd functions. """ +from __future__ import annotations + import logging import re from functools import partial @@ -260,7 +262,7 @@ def test_array_sweep_parameter_class( def test_dond_explicit_exp_meas_sample( - _param: Parameter[int], experiment: "Experiment" + _param: Parameter[int], experiment: Experiment ) -> None: experiment_2 = new_experiment("new-exp", "no-sample") @@ -286,7 +288,7 @@ def test_dond_explicit_exp_meas_sample( def test_dond_multi_datasets_explicit_exp_meas_sample( - _param: Parameter[int], _param_complex: Parameter[complex], experiment: "Experiment" + _param: Parameter[int], _param_complex: Parameter[complex], experiment: Experiment ) -> None: experiment_2 = new_experiment("new-exp", "no-sample") @@ -318,7 +320,7 @@ def test_dond_multi_datasets_explicit_exp_meas_sample( def test_dond_multi_datasets_explicit_meas_names( - _param: Parameter[int], _param_complex: Parameter[complex], experiment: "Experiment" + _param: Parameter[int], _param_complex: Parameter[complex], experiment: Experiment ) -> None: data1 = dond( [_param], @@ -334,7 +336,7 @@ def test_dond_multi_datasets_explicit_meas_names( def test_dond_multi_datasets_meas_names_len_mismatch( - _param: Parameter[int], experiment: "Experiment" + _param: Parameter[int], experiment: Experiment ) -> None: with pytest.raises( ValueError, @@ -1876,7 +1878,7 @@ def test_error_measured_grouped_and_not_grouped() -> None: @pytest.mark.usefixtures("plot_close", "experiment") -def test_post_action(mocker: "MockerFixture") -> None: +def test_post_action(mocker: MockerFixture) -> None: param_1 = ManualParameter("param_1", initial_value=0.0) param_2 = ManualParameter("param_2", initial_value=0.0) From 326d2e0a7b94da0b98f3b0aff64abb7e4c81849c Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Thu, 22 Oct 2026 07:11:20 +0200 Subject: [PATCH 19/20] Require typing_extensions 4.12 or later Older versions do not apply TypeVar defaults, so Parameter[int] fails at runtime on Python 3.12. --- docs/changes/newsfragments/8644.underthehood | 3 +++ pyproject.toml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 docs/changes/newsfragments/8644.underthehood diff --git a/docs/changes/newsfragments/8644.underthehood b/docs/changes/newsfragments/8644.underthehood new file mode 100644 index 00000000000..45edb21b0cf --- /dev/null +++ b/docs/changes/newsfragments/8644.underthehood @@ -0,0 +1,3 @@ +The minimum supported version of ``typing_extensions`` is now 4.12.0. Older versions do not +apply the default of a ``TypeVar``, so subscripting a parameter with only its value type at +runtime, e.g. ``Parameter[int]``, failed with ``TypeError: Too few arguments`` on Python 3.12. diff --git a/pyproject.toml b/pyproject.toml index 12cb48b7fdd..3faa6801513 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,7 +38,7 @@ dependencies = [ "pyvisa>=1.12.0, <1.17.0", "ruamel.yaml>=0.16.0,!=0.16.6", "tabulate>=0.9.0", - "typing_extensions>=4.6.0", + "typing_extensions>=4.12.0", "tqdm>=4.59.0", "uncertainties>=3.2.0", "versioningit>=2.2.1", From 9c3186ac0caf63e1a89732cc454dddd4f21f4832 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Thu, 22 Oct 2026 13:40:09 +0200 Subject: [PATCH 20/20] Enforce types across the code base --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 3faa6801513..1053246c244 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -201,7 +201,6 @@ reportPrivateUsage = "none" reportUnknownMemberType = "none" reportUnknownParameterType = "none" reportMissingTypeArgument = "none" -reportMissingParameterType = "none" reportUnknownArgumentType = "none" reportUnusedFunction = "none" reportUnusedImport = "none"