diff --git a/qcodes/parameters/combined_parameter.py b/qcodes/parameters/combined_parameter.py index 93e4f5ed1ea..ed688ebbf47 100644 --- a/qcodes/parameters/combined_parameter.py +++ b/qcodes/parameters/combined_parameter.py @@ -23,7 +23,7 @@ def combine( label: str | None = None, unit: str | None = None, units: str | None = None, - aggregator: Callable[[Sequence[Any]], Any] | None = None, + aggregator: Callable[..., Any] | None = None, ) -> CombinedParameter: """ Combine parameters into one sweepable parameter diff --git a/qcodes/parameters/val_mapping.py b/qcodes/parameters/val_mapping.py index 992e65cfd91..4510144607a 100644 --- a/qcodes/parameters/val_mapping.py +++ b/qcodes/parameters/val_mapping.py @@ -1,12 +1,13 @@ from __future__ import annotations from collections import OrderedDict -from typing import Any +from typing import TypeVar +T = TypeVar("T") def create_on_off_val_mapping( - on_val: Any = True, off_val: Any = False -) -> dict[str | bool, Any]: + on_val: T | bool = True, off_val: T | bool = False +) -> OrderedDict[str | bool, T | bool]: """ Returns a value mapping which maps inputs which reasonably mean "on"/"off" to the specified ``on_val``/``off_val`` which are to be sent to the diff --git a/qcodes/tests/parameter/conftest.py b/qcodes/tests/parameter/conftest.py index ea8e0f803e8..e95556edaea 100644 --- a/qcodes/tests/parameter/conftest.py +++ b/qcodes/tests/parameter/conftest.py @@ -1,45 +1,49 @@ from __future__ import annotations from collections import namedtuple -from typing import Any, Generator +from typing import Any, Callable, Generator, Literal, TypeVar import pytest import qcodes.validators as vals from qcodes.instrument import InstrumentBase -from qcodes.parameters import Parameter +from qcodes.parameters import ParamDataType, Parameter, ParamRawDataType from qcodes.tests.instrument_mocks import DummyChannelInstrument -NOT_PASSED = 'NOT_PASSED' +T = TypeVar("T") + +NOT_PASSED: Literal["NOT_PASSED"] = "NOT_PASSED" @pytest.fixture(params=(True, False, NOT_PASSED)) -def snapshot_get(request: pytest.FixtureRequest) -> Any: +def snapshot_get(request: pytest.FixtureRequest) -> bool | Literal["NOT_PASSED"]: return request.param @pytest.fixture(params=(True, False, NOT_PASSED)) -def snapshot_value(request: pytest.FixtureRequest) -> Any: +def snapshot_value(request: pytest.FixtureRequest) -> bool | Literal["NOT_PASSED"]: return request.param @pytest.fixture(params=(None, False, NOT_PASSED)) -def get_cmd(request: pytest.FixtureRequest) -> Any: +def get_cmd( + request: pytest.FixtureRequest, +) -> None | Literal[False] | Literal["NOT_PASSED"]: return request.param @pytest.fixture(params=(True, False, NOT_PASSED)) -def get_if_invalid(request: pytest.FixtureRequest) -> Any: +def get_if_invalid(request: pytest.FixtureRequest) -> bool | Literal["NOT_PASSED"]: return request.param @pytest.fixture(params=(True, False, None, NOT_PASSED)) -def update(request: pytest.FixtureRequest) -> Any: +def update(request: pytest.FixtureRequest) -> bool | None | Literal["NOT_PASSED"]: return request.param @pytest.fixture(params=(True, False)) -def cache_is_valid(request: pytest.FixtureRequest) -> Any: +def cache_is_valid(request: pytest.FixtureRequest) -> bool: return request.param @@ -52,11 +56,11 @@ def _make_dummy_instrument() -> Generator[DummyChannelInstrument, None, None]: class GettableParam(Parameter): """ Parameter that keeps track of number of get operations""" - def __init__(self, *args, **kwargs): + def __init__(self, *args: Any, **kwargs: Any): super().__init__(*args, **kwargs) self._get_count = 0 - def get_raw(self): + def get_raw(self) -> int: self._get_count += 1 return 42 @@ -64,7 +68,7 @@ def get_raw(self): class BetterGettableParam(Parameter): """ Parameter that keeps track of number of get operations, But can actually store values""" - def __init__(self, *args, **kwargs): + def __init__(self, *args: Any, **kwargs: Any): super().__init__(*args, **kwargs) self._get_count = 0 @@ -75,77 +79,81 @@ def get_raw(self) -> Any: class SettableParam(Parameter): """ Parameter that keeps track of number of set operations""" - def __init__(self, *args, **kwargs): + def __init__(self, *args: Any, **kwargs: Any): self._set_count = 0 super().__init__(*args, **kwargs) - def set_raw(self, value): + def set_raw(self, value: Any) -> None: self._set_count += 1 class OverwriteGetParam(Parameter): """ Parameter that overwrites get.""" - def __init__(self, *args, **kwargs): + def __init__(self, *args: Any, **kwargs: Any): super().__init__(*args, **kwargs) self._value = 42 self.set_count = 0 self.get_count = 0 - def get(self): + def get(self) -> int: self.get_count += 1 return self._value class OverwriteSetParam(Parameter): """ Parameter that overwrites set.""" - def __init__(self, *args, **kwargs): + def __init__(self, *args: Any, **kwargs: Any): super().__init__(*args, **kwargs) self._value = 42 self.set_count = 0 self.get_count = 0 - def set(self, value): + def set(self, value: Any) -> None: self.set_count += 1 self._value = value class GetSetRawParameter(Parameter): """ Parameter that implements get and set raw""" - def __init__(self, *args, **kwargs): + def __init__(self, *args: Any, **kwargs: Any): super().__init__(*args, **kwargs) - def get_raw(self): + def get_raw(self) -> ParamRawDataType: return self.cache.raw_value - def set_raw(self, value): + def set_raw(self, value: ParamRawDataType) -> None: pass -class BookkeepingValidator(vals.Validator[Any]): +class BookkeepingValidator(vals.Validator[T]): """ Validator that keeps track of what it validates """ - def __init__(self, min_value=-float("inf"), max_value=float("inf")): - self.values_validated = [] - def validate(self, value, context=''): + def __init__( + self, min_value: float = -float("inf"), max_value: float = float("inf") + ): + self.values_validated: list[T] = [] + + def validate(self, value: T, context: str = "") -> None: self.values_validated.append(value) is_numeric = True - class MemoryParameter(Parameter): - def __init__(self, get_cmd=None, **kwargs): - self.set_values = [] - self.get_values = [] + def __init__(self, get_cmd: None | Callable[[], Any] = None, **kwargs: Any): + self.set_values: list[Any] = [] + self.get_values: list[Any] = [] super().__init__(set_cmd=self.add_set_value, get_cmd=self.create_get_func(get_cmd), **kwargs) - def add_set_value(self, value): + def add_set_value(self, value: ParamDataType) -> None: self.set_values.append(value) - def create_get_func(self, func): - def get_func(): + def create_get_func( + self, func: None | Callable[[], ParamDataType] + ) -> Callable[[], ParamDataType]: + def get_func() -> ParamDataType: if func is not None: val = func() else: @@ -156,7 +164,7 @@ def get_func(): class VirtualParameter(Parameter): - def __init__(self, name: str, param: Parameter, **kwargs): + def __init__(self, name: str, param: Parameter, **kwargs: Any): self._param = param super().__init__(name=name, **kwargs) @@ -164,7 +172,7 @@ def __init__(self, name: str, param: Parameter, **kwargs): def underlying_instrument(self) -> InstrumentBase | None: return self._param.instrument - def get_raw(self): + def get_raw(self) -> ParamRawDataType: return self._param.get() @@ -178,22 +186,22 @@ def get_raw(self): class ParameterMemory: - def __init__(self): - self._value = None + def __init__(self) -> None: + self._value: Any | None = None - def get(self): + def get(self) -> ParamDataType: return self._value - def set(self, value): + def set(self, value: ParamDataType) -> None: self._value = value - def set_p_prefixed(self, val): + def set_p_prefixed(self, val: int) -> None: self._value = f'PVAL: {val:d}' @staticmethod - def parse_set_p(val): + def parse_set_p(val: int) -> str: return f'{val:d}' @staticmethod - def strip_prefix(val): + def strip_prefix(val: str) -> int: return int(val[6:]) diff --git a/qcodes/tests/parameter/test_array_parameter.py b/qcodes/tests/parameter/test_array_parameter.py index effc9f9e186..b83fbc91d9c 100644 --- a/qcodes/tests/parameter/test_array_parameter.py +++ b/qcodes/tests/parameter/test_array_parameter.py @@ -1,28 +1,30 @@ +from typing import Any + import pytest -from qcodes.parameters import ArrayParameter +from qcodes.parameters import ArrayParameter, ParamRawDataType from .conftest import blank_instruments, named_instrument class SimpleArrayParam(ArrayParameter): - def __init__(self, return_val, *args, **kwargs): + def __init__(self, return_val: ParamRawDataType, *args: Any, **kwargs: Any): self._return_val = return_val self._get_count = 0 super().__init__(*args, **kwargs) - def get_raw(self): + def get_raw(self) -> ParamRawDataType: self._get_count += 1 return self._return_val class SettableArray(SimpleArrayParam): # this is not allowed - just created to raise an error in the test below - def set_raw(self, value): + def set_raw(self, value: Any) -> None: self.v = value -def test_default_attributes(): +def test_default_attributes() -> None: name = 'array_param' shape = (2, 3) p = SimpleArrayParam([[1, 2, 3], [4, 5, 6]], name, shape) @@ -52,10 +54,11 @@ def test_default_attributes(): assert 'raw_value' not in snap assert snap['ts'] is None + assert p.__doc__ is not None assert name in p.__doc__ -def test_explicit_attributes(): +def test_explicit_attributes() -> None: name = 'tiny_array' shape = (2,) label = 'it takes two to tango' @@ -98,11 +101,12 @@ def test_explicit_attributes(): assert snap[k] == v assert snap['ts'] is not None + assert p.__doc__ is not None assert name in p.__doc__ assert docstring in p.__doc__ -def test_has_set_get(): +def test_has_set_get() -> None: name = 'array_param' shape = (3,) with pytest.raises(AttributeError): @@ -128,27 +132,30 @@ def test_has_set_get(): SettableArray([1, 2, 3], name, shape) -def test_full_name(): +def test_full_name() -> None: # three cases where only name gets used for full_name for instrument in blank_instruments: p = SimpleArrayParam([6, 7], 'fred', (2,), setpoint_names=('barney',)) - p._instrument = instrument + # this is not allowed since instrument + # here is not actually an instrument + # but useful for testing + p._instrument = instrument # type: ignore[assignment] assert str(p) == 'fred' assert p.setpoint_full_names == ('barney',) # and then an instrument that really has a name - p = SimpleArrayParam([6, 7], 'wilma', (2,), - setpoint_names=('betty',)) - p._instrument = named_instrument - assert str(p) == 'astro_wilma' - assert p.setpoint_full_names == ('astro_betty',) + p = SimpleArrayParam([6, 7], "wilma", (2,), setpoint_names=("betty",)) + p._instrument = named_instrument # type: ignore[assignment] + assert str(p) == "astro_wilma" + assert p.setpoint_full_names == ("astro_betty",) # and with a 2d parameter to test mixed setpoint_names - p = SimpleArrayParam([[6, 7, 8], [1, 2, 3]], 'wilma', (3, 2), - setpoint_names=('betty', None)) - p._instrument = named_instrument - assert p.setpoint_full_names == ('astro_betty', None) + p = SimpleArrayParam( + [[6, 7, 8], [1, 2, 3]], "wilma", (3, 2), setpoint_names=("betty", None) + ) + p._instrument = named_instrument # type: ignore[assignment] + assert p.setpoint_full_names == ("astro_betty", None) @pytest.mark.parametrize("constructor", [ @@ -158,6 +165,6 @@ def test_full_name(): {'shape': [3], 'setpoint_labels': 'the index'}, # ['the index'] {'shape': [3], 'setpoint_names': [None, 'index2']} ]) -def test_constructor_errors(constructor): +def test_constructor_errors(constructor: dict) -> None: with pytest.raises(ValueError): SimpleArrayParam([1, 2, 3], 'p', **constructor) diff --git a/qcodes/tests/parameter/test_combined_par.py b/qcodes/tests/parameter/test_combined_par.py index 39faab50330..4d20642fea9 100644 --- a/qcodes/tests/parameter/test_combined_par.py +++ b/qcodes/tests/parameter/test_combined_par.py @@ -1,37 +1,39 @@ +from __future__ import annotations + from collections import OrderedDict +from typing import TYPE_CHECKING, Generator import hypothesis.strategies as hst import numpy as np import pytest from hypothesis import HealthCheck, given, settings -from qcodes.parameters import combine +from qcodes.parameters import ManualParameter, combine from qcodes.utils import full_class -from ..common import DumyPar - +if TYPE_CHECKING: + from pytest_mock import MockerFixture @pytest.fixture() -def parameters(): - parameters = [DumyPar(name) for name in ["X", "Y", "Z"]] +def parameters() -> Generator[list[ManualParameter], None, None]: + parameters = [ManualParameter(name) for name in ["X", "Y", "Z"]] yield parameters -def testCombine(parameters): +def test_combine(parameters: list[ManualParameter]) -> None: multipar = combine(*parameters, name="combined") assert multipar.dimensionality == len(parameters) -def testSweepBadSetpoints(parameters): +def test_sweep_bad_setpoints(parameters: list[ManualParameter]) -> None: with pytest.raises(ValueError): combine(*parameters, name="fail").sweep(np.array([[1, 2]])) -def testSweep(parameters): +def test_sweep(parameters: list[ManualParameter]) -> None: setpoints = np.array([[1, 1, 1], [1, 1, 1]]) - sweep_values = combine(*parameters, - name="combined").sweep(setpoints) + sweep_values = combine(*parameters, name="combined").sweep(setpoints) res = [] for i in sweep_values: @@ -44,31 +46,38 @@ def testSweep(parameters): assert res == expected -def testSet(parameters, mocker): +def test_set(parameters: list[ManualParameter], mocker: MockerFixture) -> None: setpoints = np.array([[1, 1, 1], [1, 1, 1]]) - sweep_values = combine(*parameters, - name="combined").sweep(setpoints) + sweep_values = combine(*parameters, name="combined").sweep(setpoints) mock_method = mocker.patch.object(sweep_values, 'set') for i in sweep_values: sweep_values.set(i) - mock_method.assert_has_calls([ - mocker.call(0), mocker.call(1) - ] - ) + mock_method.assert_has_calls([mocker.call(0), mocker.call(1)]) # pyright: ignore @settings(suppress_health_check=(HealthCheck.function_scoped_fixture,)) @given( npoints=hst.integers(1, 100), - x_start_stop=hst.lists(hst.integers(), min_size=2, max_size=2).map(sorted), # type: ignore[arg-type] - y_start_stop=hst.lists(hst.integers(), min_size=2, max_size=2).map(sorted), # type: ignore[arg-type] - z_start_stop=hst.lists(hst.integers(), min_size=2, max_size=2).map(sorted), # type: ignore[arg-type] + x_start_stop=hst.lists(hst.integers(), min_size=2, max_size=2).map( + sorted # type: ignore[arg-type] + ), + y_start_stop=hst.lists(hst.integers(), min_size=2, max_size=2).map( + sorted # type: ignore[arg-type] + ), + z_start_stop=hst.lists(hst.integers(), min_size=2, max_size=2).map( + sorted # type: ignore[arg-type] + ), ) -def testAggregator(parameters, npoints, x_start_stop, y_start_stop, z_start_stop): - +def test_aggregator( + parameters: list[ManualParameter], + npoints: int, + x_start_stop: list[int], + y_start_stop: list[int], + z_start_stop: list[int], +) -> None: x_set = np.linspace(x_start_stop[0], x_start_stop[1], npoints).reshape(npoints, 1) y_set = np.linspace(y_start_stop[0], y_start_stop[1], npoints).reshape(npoints, 1) z_set = np.linspace(z_start_stop[0], z_start_stop[1], npoints).reshape(npoints, 1) @@ -86,7 +95,7 @@ def testAggregator(parameters, npoints, x_start_stop, y_start_stop, z_start_stop assert results == expected_results -def testMeta(parameters): +def test_meta(parameters: list[ManualParameter]) -> None: name = "combined" label = "Linear Combination" unit = "a.u" @@ -105,11 +114,11 @@ def testMeta(parameters): out["full_name"] = name out["aggregator"] = repr(linear) for param in sweep_values.parameters: - out[param.full_name] = {} + out[param.full_name] = param.snapshot() # type: ignore[assignment] assert out == snap -def testMutable(parameters): +def test_mutable(parameters: list[ManualParameter]) -> None: setpoints = np.array([[1, 1, 1], [1, 1, 1]]) sweep_values = combine(*parameters, @@ -120,7 +129,7 @@ def testMutable(parameters): assert a != b -def testArrays(parameters): +def test_arrays(parameters: list[ManualParameter]) -> None: x_vals = np.linspace(1, 1, 2) y_vals = np.linspace(1, 1, 2) z_vals = np.linspace(1, 1, 2) @@ -138,7 +147,7 @@ def testArrays(parameters): assert res == expected -def testWrongLen(parameters): +def test_wrong_len(parameters: list[ManualParameter]) -> None: x_vals = np.linspace(1, 1, 2) y_vals = np.linspace(1, 1, 2) z_vals = np.linspace(1, 1, 3) @@ -147,7 +156,7 @@ def testWrongLen(parameters): name="combined").sweep(x_vals, y_vals, z_vals) -def testInvalidName(parameters): +def test_invalid_name(parameters: list[ManualParameter]) -> None: x_vals = np.linspace(1, 1, 2) y_vals = np.linspace(1, 1, 2) z_vals = np.linspace(1, 1, 2) @@ -156,7 +165,7 @@ def testInvalidName(parameters): name="combined with spaces").sweep(x_vals, y_vals, z_vals) -def testLen(parameters): +def test_len(parameters: list[ManualParameter]) -> None: x_vals = np.linspace(1, 1, 2) y_vals = np.linspace(1, 1, 2) z_vals = np.linspace(1, 0, 2) @@ -165,5 +174,5 @@ def testLen(parameters): assert len(x_vals) == len(sweep_values.setpoints) -def linear(x, y, z): +def linear(x: float, y: float, z: float) -> float: return x+y+z diff --git a/qcodes/tests/parameter/test_delegate_parameter.py b/qcodes/tests/parameter/test_delegate_parameter.py index cc71295bb23..d7b7b8febdb 100644 --- a/qcodes/tests/parameter/test_delegate_parameter.py +++ b/qcodes/tests/parameter/test_delegate_parameter.py @@ -1,7 +1,9 @@ """ Test suite for DelegateParameter """ -from typing import cast +from __future__ import annotations + +from typing import Any, Callable, Generator, Literal, cast import hypothesis.strategies as hst import pytest @@ -16,38 +18,40 @@ # pylint: disable=redefined-outer-name -@pytest.fixture() -def numeric_val(): +@pytest.fixture(name="numeric_val") +def _make_numeric_val() -> Generator[int, None, None]: yield 1 -@pytest.fixture() -def simple_param(numeric_val): +@pytest.fixture(name="simple_param") +def _make_simple_param(numeric_val: int) -> Generator[Parameter, None, None]: yield Parameter('testparam', set_cmd=None, get_cmd=None, scale=2, offset=17, label='Test Parameter', unit='V', initial_value=numeric_val) +class ObservableParam(Parameter): + def __init__(self, *args: Any, **kwargs: Any): + self.instr_val = None + super().__init__(*args, **kwargs) -@pytest.fixture(params=[True, False]) -def make_observable_parameter(request): - def make_parameter(*args, override_getset: bool = True, **kwargs): - class ObservableParam(Parameter): - def __init__(self, *args, **kwargs): - self.instr_val = None - super().__init__(*args, **kwargs) + def set_raw(self, value: ParamRawDataType) -> None: # pylint: disable=method-hidden + self.instr_val = value - def set_raw( # pylint: disable=method-hidden - self, value: ParamRawDataType) -> None: - self.instr_val = value + def get_raw(self) -> ParamRawDataType: # pylint: disable=method-hidden + return self.instr_val - def get_raw( # pylint: disable=method-hidden - self) -> ParamRawDataType: - return self.instr_val + def get_instr_val(self) -> ParamRawDataType: + return self.instr_val - def get_instr_val(self): - return self.instr_val +@pytest.fixture(params=[True, False]) +def make_observable_parameter( + request: pytest.FixtureRequest, +) -> Generator[Callable[..., ObservableParam], None, None]: + def make_parameter( + *args: Any, override_getset: bool = True, **kwargs: Any + ) -> ObservableParam: if request.param: if not override_getset: pytest.skip() @@ -55,11 +59,11 @@ def get_instr_val(self): else: val = None - def set_cmd(value): + def set_cmd(value: Any) -> None: nonlocal val val = value - def get_cmd(): + def get_cmd() -> Any: nonlocal val return val @@ -71,25 +75,27 @@ def get_cmd(): yield make_parameter -def test_observable_parameter(make_observable_parameter, numeric_val): - p = make_observable_parameter('testparam') +def test_observable_parameter( + make_observable_parameter: Callable[..., ObservableParam], numeric_val: int +) -> None: + p = make_observable_parameter("testparam") p(numeric_val) assert p.get_instr_val() == numeric_val -def test_observable_parameter_initial_value(make_observable_parameter, - numeric_val): - t = make_observable_parameter( - 'observable_parameter', initial_value=numeric_val) +def test_observable_parameter_initial_value( + make_observable_parameter: Callable[..., ObservableParam], numeric_val: int +) -> None: + t = make_observable_parameter("observable_parameter", initial_value=numeric_val) assert t.get_instr_val() == numeric_val -def test_same_value(simple_param): +def test_same_value(simple_param: Parameter) -> None: d = DelegateParameter('test_delegate_parameter', simple_param) assert d() == simple_param() -def test_same_label_and_unit_on_init(simple_param): +def test_same_label_and_unit_on_init(simple_param: Parameter) -> None: """ Test that the label and unit get used from source parameter if not specified otherwise. @@ -99,14 +105,14 @@ def test_same_label_and_unit_on_init(simple_param): assert d.unit == simple_param.unit -def test_overwritten_unit_on_init(simple_param): +def test_overwritten_unit_on_init(simple_param: Parameter) -> None: d = DelegateParameter('test_delegate_parameter', simple_param, unit='Ohm') assert d.label == simple_param.label assert not d.unit == simple_param.unit assert d.unit == 'Ohm' -def test_overwritten_label_on_init(simple_param): +def test_overwritten_label_on_init(simple_param: Parameter) -> None: d = DelegateParameter('test_delegate_parameter', simple_param, label='Physical parameter') assert d.unit == simple_param.unit @@ -114,7 +120,7 @@ def test_overwritten_label_on_init(simple_param): assert d.label == 'Physical parameter' -def test_get_set_raises(simple_param): +def test_get_set_raises(simple_param: Parameter) -> None: """ Test that providing a get/set_cmd kwarg raises an error. """ @@ -124,7 +130,7 @@ def test_get_set_raises(simple_param): assert str(e.value).startswith('\'It is not allowed to set') -def test_scaling(simple_param, numeric_val): +def test_scaling(simple_param: Parameter, numeric_val: int) -> None: scale = 5 offset = 3 d = DelegateParameter( @@ -137,7 +143,9 @@ def test_scaling(simple_param, numeric_val): assert simple_param() == numeric_val * scale + offset -def test_scaling_delegate_initial_value(simple_param, numeric_val): +def test_scaling_delegate_initial_value( + simple_param: Parameter, numeric_val: int +) -> None: scale = 5 offset = 3 DelegateParameter( @@ -147,7 +155,7 @@ def test_scaling_delegate_initial_value(simple_param, numeric_val): assert simple_param() == numeric_val * scale + offset -def test_scaling_initial_value(simple_param, numeric_val): +def test_scaling_initial_value(simple_param: Parameter) -> None: scale = 5 offset = 3 d = DelegateParameter( @@ -155,7 +163,7 @@ def test_scaling_initial_value(simple_param, numeric_val): assert d() == (simple_param() - offset) / scale -def test_snapshot(): +def test_snapshot() -> None: p = Parameter('testparam', set_cmd=None, get_cmd=None, offset=1, scale=2, initial_value=1) d = DelegateParameter('test_delegate_parameter', p, offset=3, scale=5, @@ -168,7 +176,7 @@ def test_snapshot(): assert source_snapshot['value'] == 13 -def test_set_source_cache_changes_delegate_cache(simple_param): +def test_set_source_cache_changes_delegate_cache(simple_param: Parameter) -> None: """ Setting the cached value of the source parameter changes the delegate parameter cache accordingly. @@ -182,7 +190,7 @@ def test_set_source_cache_changes_delegate_cache(simple_param): assert d.cache.get() == (new_source_value - offset) / scale -def test_set_source_cache_changes_delegate_get(simple_param): +def test_set_source_cache_changes_delegate_get(simple_param: Parameter) -> None: """ When the delegate parameter's ``get`` is called, the new value of the source propagates. @@ -197,7 +205,7 @@ def test_set_source_cache_changes_delegate_get(simple_param): assert d.get() == (new_source_value - offset) / scale -def test_set_delegate_cache_changes_source_cache(simple_param): +def test_set_delegate_cache_changes_source_cache(simple_param: Parameter) -> None: offset = 4 scale = 5 d = DelegateParameter('d', simple_param, offset=offset, scale=scale) @@ -208,7 +216,7 @@ def test_set_delegate_cache_changes_source_cache(simple_param): assert simple_param.cache.get() == (new_delegate_value * scale + offset) -def test_set_delegate_cache_with_raw_value(simple_param): +def test_set_delegate_cache_with_raw_value(simple_param: Parameter) -> None: offset = 4 scale = 5 d = DelegateParameter('d', simple_param, offset=offset, scale=scale) @@ -221,7 +229,8 @@ def test_set_delegate_cache_with_raw_value(simple_param): def test_instrument_val_invariant_under_delegate_cache_set( - make_observable_parameter, numeric_val): + make_observable_parameter: Callable[..., ObservableParam], numeric_val: int +) -> None: """ Setting the cached value of the source parameter changes the delegate parameter. But it has no impact on the instrument value. @@ -234,14 +243,16 @@ def test_instrument_val_invariant_under_delegate_cache_set( assert t.get_instr_val() == initial_value -def test_delegate_cache_pristine_if_not_set(): +def test_delegate_cache_pristine_if_not_set() -> None: p = Parameter('test') d = DelegateParameter('delegate', p) gotten_delegate_cache = d.cache.get(get_if_invalid=False) assert gotten_delegate_cache is None -def test_delegate_get_updates_cache(make_observable_parameter, numeric_val): +def test_delegate_get_updates_cache( + make_observable_parameter: Callable[..., ObservableParam], numeric_val: int +) -> None: initial_value = numeric_val t = make_observable_parameter( 'observable_parameter', initial_value=initial_value) @@ -252,7 +263,7 @@ def test_delegate_get_updates_cache(make_observable_parameter, numeric_val): assert t.get_instr_val() == initial_value -def test_delegate_parameter_get_and_snapshot_with_none_source(): +def test_delegate_parameter_get_and_snapshot_with_none_source() -> None: """ Test that a delegate parameter returns None on get and snapshot if the source has a value of None and an offset or scale is used. @@ -270,14 +281,15 @@ def test_delegate_parameter_get_and_snapshot_with_none_source(): assert delegate_param.get() is None assert delegate_param.snapshot()['value'] is None - assert delegate_param.cache._parameter.source.cache is none_param.cache + parameter = delegate_param.cache._parameter # type: ignore[attr-defined] + assert parameter.source.cache is none_param.cache delegate_param.source = source_param assert delegate_param.get() == 1 assert delegate_param.snapshot()['value'] == 1 - assert delegate_param.cache._parameter.source.cache is source_param.cache + assert parameter.source.cache is source_param.cache -def test_raw_value_scaling(make_observable_parameter): +def test_raw_value_scaling() -> None: """ The :attr:`raw_value` will be deprecated soon, so other tests should not use it. @@ -296,7 +308,7 @@ def test_raw_value_scaling(make_observable_parameter): assert d.raw_value == p() -def test_setting_initial_value_delegate_parameter(): +def test_setting_initial_value_delegate_parameter() -> None: value = 10 p = Parameter('testparam', set_cmd=None, get_cmd=None) d = DelegateParameter('test_delegate_parameter', p, @@ -305,7 +317,7 @@ def test_setting_initial_value_delegate_parameter(): assert d.cache.get(get_if_invalid=False) == value -def test_setting_initial_cache_delegate_parameter(): +def test_setting_initial_cache_delegate_parameter() -> None: value = 10 p = Parameter('testparam', set_cmd=None, get_cmd=None) d = DelegateParameter('test_delegate_parameter', p, @@ -314,26 +326,26 @@ def test_setting_initial_cache_delegate_parameter(): assert d.cache.get(get_if_invalid=False) == value -def test_delegate_parameter_with_none_source_works_as_expected(): +def test_delegate_parameter_with_none_source_works_as_expected() -> None: delegate_param = DelegateParameter(name='delegate', source=None, scale=2, offset=1) _assert_none_source_is_correct(delegate_param) -@given(hst.floats(allow_nan=False, allow_infinity=False), - hst.floats(allow_nan=False, allow_infinity=False).filter(lambda x: x != 0), - hst.floats(allow_nan=False, allow_infinity=False)) -def test_delegate_parameter_with_changed_source_snapshot_matches_value(value, - scale, - offset): - delegate_param = DelegateParameter(name="delegate", - source=None, - scale=scale, - offset=offset) - source_parameter = Parameter(name="source", - get_cmd=None, - set_cmd=None, - initial_value=value) +@given( + hst.floats(allow_nan=False, allow_infinity=False), + hst.floats(allow_nan=False, allow_infinity=False).filter(lambda x: x != 0), + hst.floats(allow_nan=False, allow_infinity=False), +) +def test_delegate_parameter_with_changed_source_snapshot_matches_value( + value: float, scale: float, offset: float +) -> None: + delegate_param = DelegateParameter( + name="delegate", source=None, scale=scale, offset=offset + ) + source_parameter = Parameter( + name="source", get_cmd=None, set_cmd=None, initial_value=value + ) _assert_none_source_is_correct(delegate_param) delegate_param.source = source_parameter calc_value = (value - offset) / scale @@ -353,7 +365,7 @@ def test_delegate_parameter_with_changed_source_snapshot_matches_value(value, _assert_delegate_cache_none_source(delegate_param) -def _assert_none_source_is_correct(delegate_param): +def _assert_none_source_is_correct(delegate_param: DelegateParameter) -> None: with pytest.raises(TypeError): delegate_param.get() with pytest.raises(TypeError): @@ -367,7 +379,7 @@ def _assert_none_source_is_correct(delegate_param): assert snapshot == updated_snapshot -def _assert_delegate_cache_none_source(delegate_param): +def _assert_delegate_cache_none_source(delegate_param: DelegateParameter) -> None: with pytest.raises(TypeError): delegate_param.cache.set(1) with pytest.raises(TypeError): @@ -381,9 +393,13 @@ def _assert_delegate_cache_none_source(delegate_param): @pytest.mark.parametrize("snapshot_value", [True, False]) @pytest.mark.parametrize("gettable,get_cmd", [(True, None), (False, False)]) @pytest.mark.parametrize("settable,set_cmd", [(True, None), (False, False)]) -def test_gettable_settable_snapshotget_delegate_parameter(gettable, get_cmd, - settable, set_cmd, - snapshot_value): +def test_gettable_settable_snapshotget_delegate_parameter( + gettable: bool, + get_cmd: Literal[False] | None, + settable: bool, + set_cmd: Literal[False] | None, + snapshot_value: bool, +) -> None: """ Test that gettable, settable and snapshot_get are correctly reflected in the DelegateParameter @@ -399,9 +415,13 @@ def test_gettable_settable_snapshotget_delegate_parameter(gettable, get_cmd, @pytest.mark.parametrize("snapshot_value", [True, False]) @pytest.mark.parametrize("gettable,get_cmd", [(True, None), (False, False)]) @pytest.mark.parametrize("settable,set_cmd", [(True, None), (False, False)]) -def test_gettable_settable_snapshotget_delegate_parameter_2(gettable, get_cmd, - settable, set_cmd, - snapshot_value): +def test_gettable_settable_snapshotget_delegate_parameter_2( + gettable: bool, + get_cmd: Literal[False] | None, + settable: bool, + set_cmd: Literal[False] | None, + snapshot_value: bool, +) -> None: """ Test that gettable/settable and snapshot_get are updated correctly when source changes @@ -415,7 +435,7 @@ def test_gettable_settable_snapshotget_delegate_parameter_2(gettable, get_cmd, assert delegate_param._snapshot_value is snapshot_value -def test_initial_value_and_none_source_raises(): +def test_initial_value_and_none_source_raises() -> None: with pytest.raises(KeyError, match="It is not allowed to supply" " 'initial_value' or" " 'initial_cache_value'"): @@ -426,7 +446,7 @@ def test_initial_value_and_none_source_raises(): DelegateParameter("delegate", source=None, initial_cache_value=1) -def test_delegate_parameter_change_source_reflected_in_label_and_unit(): +def test_delegate_parameter_change_source_reflected_in_label_and_unit() -> None: delegate_param = DelegateParameter("delegate", source=None) source_param_1 = Parameter("source1", label="source 1", unit="unit1") source_param_2 = Parameter("source2", label="source 2", unit="unit2") @@ -444,7 +464,7 @@ def test_delegate_parameter_change_source_reflected_in_label_and_unit(): assert delegate_param.unit == "" -def test_delegate_parameter_fixed_label_unit_unchanged(): +def test_delegate_parameter_fixed_label_unit_unchanged() -> None: delegate_param = DelegateParameter("delegate", label="delegatelabel", unit="delegateunit", @@ -465,7 +485,7 @@ def test_delegate_parameter_fixed_label_unit_unchanged(): assert delegate_param.unit == "delegateunit" -def test_cache_invalidation(): +def test_cache_invalidation() -> None: value = 10 p = BetterGettableParam('testparam', set_cmd=None, get_cmd=None) d = DelegateParameter('test_delegate_parameter', p, @@ -489,7 +509,7 @@ def test_cache_invalidation(): assert p.cache.valid is True -def test_cache_no_source(): +def test_cache_no_source() -> None: d = DelegateParameter('test_delegate_parameter', source=None) assert d.cache.valid is False @@ -505,7 +525,7 @@ def test_cache_no_source(): d.cache.invalidate() -def test_underlying_instrument_property_for_delegate_parameter(): +def test_underlying_instrument_property_for_delegate_parameter() -> None: p = BetterGettableParam('testparam', set_cmd=None, get_cmd=None) d = DelegateParameter('delegate_parameter_with_source', p) @@ -515,7 +535,7 @@ def test_underlying_instrument_property_for_delegate_parameter(): assert d.underlying_instrument is None -def test_value_validation(): +def test_value_validation() -> None: source_param = Parameter("source", set_cmd=None, get_cmd=None) delegate_param = DelegateParameter("delegate", source=source_param) @@ -540,7 +560,7 @@ def test_value_validation(): delegate_param.validate(11) -def test_value_validation_with_offset_and_scale(): +def test_value_validation_with_offset_and_scale() -> None: source_param = Parameter( "source", set_cmd=None, get_cmd=None, vals=vals.Numbers(-5, 5) ) diff --git a/qcodes/tests/parameter/test_elapsed_time_parameter.py b/qcodes/tests/parameter/test_elapsed_time_parameter.py new file mode 100644 index 00000000000..3add38e4515 --- /dev/null +++ b/qcodes/tests/parameter/test_elapsed_time_parameter.py @@ -0,0 +1,53 @@ +""" +Tests for the specialized_parameters module +""" + +from time import sleep + +import pytest + +from qcodes.parameters import ElapsedTimeParameter + + +def test_elapsed_time_parameter_init() -> None: + tp1 = ElapsedTimeParameter("time1") + sleep(0.01) + tp2 = ElapsedTimeParameter("time2") + + assert tp1() > tp2() + + +def test_elapsed_time_parameter_monotonic() -> None: + tp = ElapsedTimeParameter("time") + + times = [tp() for _ in range(25)] + + assert sorted(times) == times + + +def test_elapsed_time_parameter_reset_clock() -> None: + tp = ElapsedTimeParameter("time") + + sleep(0.01) + t1 = tp() + + tp.reset_clock() + t2 = tp() + + assert t1 > t2 + + +def test_elapsed_time_parameter_not_settable() -> None: + tp = ElapsedTimeParameter("time") + + with pytest.raises(NotImplementedError): + tp(0) + + +def test_elapsed_time_parameter_forbidden_kwargs() -> None: + forbidden_kwargs = ["unit", "get_cmd", "set_cmd"] + + for fb_kwarg in forbidden_kwargs: + match = f'Can not set "{fb_kwarg}" for an ElapsedTimeParameter' + with pytest.raises(ValueError, match=match): + ElapsedTimeParameter("time", **{fb_kwarg: None}) # type: ignore[arg-type] diff --git a/qcodes/tests/parameter/test_get_latest.py b/qcodes/tests/parameter/test_get_latest.py index 7c13eef0b56..049b3c9ea51 100644 --- a/qcodes/tests/parameter/test_get_latest.py +++ b/qcodes/tests/parameter/test_get_latest.py @@ -1,5 +1,6 @@ import time from datetime import datetime, timedelta +from typing import Any import pytest @@ -8,7 +9,7 @@ from .conftest import BetterGettableParam -def test_get_latest(): +def test_get_latest() -> None: time_resolution = time.get_clock_info('time').resolution sleep_delta = 2 * time_resolution @@ -22,16 +23,20 @@ def test_get_latest(): # Check we return last set value, with the correct timestamp assert local_parameter.get_latest() == 1 - assert before_set < local_parameter.get_latest.get_timestamp() < after_set + get_timestamp = local_parameter.get_latest.get_timestamp() + assert get_timestamp is not None + assert before_set < get_timestamp < after_set # Check that updating the value updates the timestamp time.sleep(sleep_delta) local_parameter.set(2) + get_timestamp = local_parameter.get_latest.get_timestamp() + assert get_timestamp is not None assert local_parameter.get_latest() == 2 - assert local_parameter.get_latest.get_timestamp() > after_set + assert get_timestamp > after_set -def test_get_latest_raw_value(): +def test_get_latest_raw_value() -> None: # To have a simple distinction between raw value and value of the # parameter lets create a parameter with an offset p = Parameter('p', set_cmd=None, get_cmd=None, offset=42) @@ -50,7 +55,7 @@ def test_get_latest_raw_value(): assert p.get_latest.get_raw_value() == 3 + 42 -def test_get_latest_unknown(): +def test_get_latest_unknown() -> None: """ Test that get latest on a parameter that has not been acquired will trigger a get @@ -60,8 +65,8 @@ def test_get_latest_unknown(): get_cmd=None) # fake a parameter that has a value but never been get/set to mock # an instrument. - local_parameter.cache._value = value - local_parameter.cache._raw_value = value + local_parameter.cache._value = value # type: ignore[attr-defined] + local_parameter.cache._raw_value = value # type: ignore[attr-defined] assert local_parameter.get_latest.get_timestamp() is None before_get = datetime.now() assert local_parameter._get_count == 0 @@ -69,14 +74,15 @@ def test_get_latest_unknown(): assert local_parameter._get_count == 1 # calling get_latest above will call get since TS is None # and the TS will therefore no longer be None - assert local_parameter.get_latest.get_timestamp() is not None - assert local_parameter.get_latest.get_timestamp() >= before_get + get_timestamp = local_parameter.get_latest.get_timestamp() + assert get_timestamp is not None + assert get_timestamp >= before_get # calling get_latest now will not trigger get assert local_parameter.get_latest() == value assert local_parameter._get_count == 1 -def test_get_latest_known(): +def test_get_latest_known() -> None: """ Test that get latest on a parameter that has a known value will not trigger a get @@ -98,7 +104,7 @@ def test_get_latest_known(): assert local_parameter.get_latest.get_timestamp() == set_time -def test_get_latest_no_get(): +def test_get_latest_no_get() -> None: """ Test that get_latest on a parameter that does not have get is handled correctly. @@ -122,7 +128,7 @@ def test_get_latest_no_get(): assert local_parameter2.get_latest() == value -def test_max_val_age(): +def test_max_val_age() -> None: value = 1 start = datetime.now() local_parameter = BetterGettableParam('test_param', @@ -141,10 +147,12 @@ def test_max_val_age(): assert local_parameter.get_latest.get_timestamp() == set_time assert local_parameter.get_latest() == value assert local_parameter._get_count == 1 - assert local_parameter.get_latest.get_timestamp() >= start + new_get_time_stamp = local_parameter.get_latest.get_timestamp() + assert new_get_time_stamp is not None + assert new_get_time_stamp >= start -def test_no_get_max_val_age(): +def test_no_get_max_val_age() -> None: """ Test that get_latest on a parameter with max_val_age set and no get cmd raises correctly. @@ -159,9 +167,9 @@ def test_no_get_max_val_age(): # 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, **kwargs): + def __init__(self, *args: Any, **kwargs: Any): super().__init__(*args, **kwargs) - self.set_raw = lambda x: x + self.set_raw = lambda x: x # type: ignore[assignment] self.set = self._wrap_set(self.set_raw) localparameter = LocalParameter('test_param', diff --git a/qcodes/tests/parameter/test_get_set_parser.py b/qcodes/tests/parameter/test_get_set_parser.py index 3b1394961c1..6afa12bb429 100644 --- a/qcodes/tests/parameter/test_get_set_parser.py +++ b/qcodes/tests/parameter/test_get_set_parser.py @@ -3,7 +3,7 @@ from .conftest import ParameterMemory -def test_param_cmd_with_parsing(): +def test_param_cmd_with_parsing() -> None: mem = ParameterMemory() diff --git a/qcodes/tests/parameter/test_get_set_wrapping.py b/qcodes/tests/parameter/test_get_set_wrapping.py index e1514ee2945..b5a84c4d6e3 100644 --- a/qcodes/tests/parameter/test_get_set_wrapping.py +++ b/qcodes/tests/parameter/test_get_set_wrapping.py @@ -1,3 +1,7 @@ +from __future__ import annotations + +from typing import Any, Callable, Literal + import pytest from qcodes.parameters import Parameter, ParameterBase @@ -10,7 +14,7 @@ ) -def test_parameter_with_overwritten_get_raises(): +def test_parameter_with_overwritten_get_raises() -> None: """ Test that creating a parameter that overwrites get and set raises runtime errors """ @@ -22,7 +26,7 @@ def test_parameter_with_overwritten_get_raises(): ) -def test_parameter_with_overwritten_set_raises(): +def test_parameter_with_overwritten_set_raises() -> None: """ Test that creating a parameter that overwrites get and set raises runtime errors """ @@ -33,12 +37,20 @@ def test_parameter_with_overwritten_set_raises(): ) -@pytest.mark.parametrize("get_cmd, set_cmd", [(False, False), (False, None), (None, None), (None, False), - (lambda: 1, lambda x: x)]) -def test_gettable_settable_attributes_with_get_set_cmd(get_cmd, set_cmd): - a = Parameter(name='foo', - get_cmd=get_cmd, - set_cmd=set_cmd) +@pytest.mark.parametrize( + "get_cmd, set_cmd", + [ + (False, False), + (False, None), + (None, None), + (None, False), + (lambda: 1, lambda x: x), + ], +) +def test_gettable_settable_attributes_with_get_set_cmd( + get_cmd: Literal[False] | None | Callable, set_cmd: Literal[False] | None | Callable +) -> None: + a = Parameter(name="foo", get_cmd=get_cmd, set_cmd=set_cmd) expected_gettable = get_cmd is not False expected_settable = set_cmd is not False @@ -47,19 +59,21 @@ def test_gettable_settable_attributes_with_get_set_cmd(get_cmd, set_cmd): @pytest.mark.parametrize("baseclass", [ParameterBase, Parameter]) -def test_gettable_settable_attributes_with_get_set_raw(baseclass): +def test_gettable_settable_attributes_with_get_set_raw( + baseclass: type[ParameterBase], +) -> None: """Test that parameters that have get_raw,set_raw are listed as gettable/settable and reverse.""" - class GetSetParam(baseclass): - def __init__(self, *args, initial_value=None, **kwargs): + class GetSetParam(baseclass): # type: ignore[valid-type,misc] + def __init__(self, *args: Any, initial_value: Any = None, **kwargs: Any): self._value = initial_value super().__init__(*args, **kwargs) - def get_raw(self): + def get_raw(self) -> Any: return self._value - def set_raw(self, value): + def set_raw(self, value: Any) -> Any: self._value = value a = GetSetParam('foo', instrument=None, initial_value=1) @@ -75,32 +89,42 @@ def set_raw(self, value): @pytest.mark.parametrize("working_get_cmd", (False, None)) @pytest.mark.parametrize("working_set_cmd", (False, None)) -def test_get_raw_and_get_cmd_raises(working_get_cmd, working_set_cmd): +def test_get_raw_and_get_cmd_raises( + working_get_cmd: Literal[False] | None, working_set_cmd: Literal[False] | None +) -> None: with pytest.raises(TypeError, match="get_raw"): - GetSetRawParameter(name="param1", get_cmd="GiveMeTheValue", set_cmd=working_set_cmd) + GetSetRawParameter( + name="param1", get_cmd="GiveMeTheValue", set_cmd=working_set_cmd + ) with pytest.raises(TypeError, match="set_raw"): - GetSetRawParameter(name="param2", set_cmd="HereIsTheValue {}", get_cmd=working_get_cmd) + GetSetRawParameter( + name="param2", set_cmd="HereIsTheValue {}", get_cmd=working_get_cmd + ) GetSetRawParameter("param3", get_cmd=working_get_cmd, set_cmd=working_set_cmd) -def test_get_on_parameter_marked_as_non_gettable_raises(): +def test_get_on_parameter_marked_as_non_gettable_raises() -> None: a = Parameter("param") a._gettable = False - with pytest.raises(TypeError, match="Trying to get a parameter that is not gettable."): + with pytest.raises( + TypeError, match="Trying to get a parameter that is not gettable." + ): a.get() -def test_set_on_parameter_marked_as_non_settable_raises(): +def test_set_on_parameter_marked_as_non_settable_raises() -> None: a = Parameter("param", set_cmd=None) a.set(2) assert a.get() == 2 a._settable = False - with pytest.raises(TypeError, match="Trying to set a parameter that is not settable."): + with pytest.raises( + TypeError, match="Trying to set a parameter that is not settable." + ): a.set(1) assert a.get() == 2 -def test_settable(): +def test_settable() -> None: mem = ParameterMemory() p = Parameter('p', set_cmd=mem.set, get_cmd=False) @@ -121,7 +145,7 @@ def test_settable(): assert p.get_latest() == 7 -def test_gettable(): +def test_gettable() -> None: mem = ParameterMemory() p = Parameter('p', get_cmd=mem.get) mem.set(21) diff --git a/qcodes/tests/parameter/test_instrument_ref_parameter.py b/qcodes/tests/parameter/test_instrument_ref_parameter.py index 7a4764144ca..f0ef71d7e22 100644 --- a/qcodes/tests/parameter/test_instrument_ref_parameter.py +++ b/qcodes/tests/parameter/test_instrument_ref_parameter.py @@ -1,11 +1,13 @@ +from typing import Generator + import pytest from qcodes.parameters import InstrumentRefParameter from qcodes.tests.instrument_mocks import DummyInstrument -@pytest.fixture() -def instrument_a(): +@pytest.fixture(name="instrument_a") +def _make_instrument_a() -> Generator[DummyInstrument, None, None]: a = DummyInstrument('dummy_holder') try: yield a @@ -13,8 +15,8 @@ def instrument_a(): a.close() -@pytest.fixture() -def instrument_d(): +@pytest.fixture(name="instrument_d") +def _make_instrument_d() -> Generator[DummyInstrument, None, None]: d = DummyInstrument('dummy') try: yield d @@ -22,8 +24,10 @@ def instrument_d(): d.close() -def test_get_instr(instrument_a, instrument_d): - instrument_a.add_parameter('test', parameter_class=InstrumentRefParameter) +def test_get_instr( + instrument_a: DummyInstrument, instrument_d: DummyInstrument +) -> None: + instrument_a.add_parameter("test", parameter_class=InstrumentRefParameter) instrument_a.test.set(instrument_d.name) diff --git a/qcodes/tests/parameter/test_multi_parameter.py b/qcodes/tests/parameter/test_multi_parameter.py index b3df57c683a..2bd45ff8fe4 100644 --- a/qcodes/tests/parameter/test_multi_parameter.py +++ b/qcodes/tests/parameter/test_multi_parameter.py @@ -1,28 +1,30 @@ +from typing import Any + import pytest -from qcodes.parameters import MultiParameter +from qcodes.parameters import MultiParameter, ParamRawDataType from .conftest import blank_instruments, named_instrument class SimpleMultiParam(MultiParameter): - def __init__(self, return_val, *args, **kwargs): + def __init__(self, return_val: Any, *args: Any, **kwargs: Any): self._return_val = return_val self._get_count = 0 super().__init__(*args, **kwargs) - def get_raw(self): + def get_raw(self) -> ParamRawDataType: self._get_count += 1 return self._return_val class SettableMulti(SimpleMultiParam): - def set_raw(self, value): + def set_raw(self, value: ParamRawDataType) -> None: print("Calling set") self._return_val = value -def test_default_attributes(): +def test_default_attributes() -> None: name = 'mixed_dimensions' names = ('0D', '1D', '2D') shapes = ((), (3,), (2, 2)) @@ -56,6 +58,7 @@ def test_default_attributes(): assert 'value' not in snap assert 'raw_value' not in snap + assert p.__doc__ is not None assert name in p.__doc__ # only in simple parameters @@ -63,7 +66,7 @@ def test_default_attributes(): assert not hasattr(p, 'unit') -def test_explicit_attributes(): +def test_explicit_attributes() -> None: name = 'mixed_dimensions' names = ('0D', '1D', '2D') shapes = ((), (3,), (2, 2)) @@ -113,11 +116,12 @@ def test_explicit_attributes(): assert snap[k] == v assert snap['ts'] is not None + assert p.__doc__ is not None assert name in p.__doc__ assert docstring in p.__doc__ -def test_has_set_get(): +def test_has_set_get() -> None: name = 'mixed_dimensions' names = ['0D', '1D', '2D'] shapes = ((), (3,), (2, 2)) @@ -157,7 +161,7 @@ def test_has_set_get(): assert p.get() == value_to_set -def test_full_name_s(): +def test_full_name_s() -> None: name = 'mixed_dimensions' names = ('0D', '1D', '2D') setpoint_names = ((), @@ -168,20 +172,29 @@ def test_full_name_s(): # three cases where only name gets used for full_name for instrument in blank_instruments: - p = SimpleMultiParam([0, [1, 2, 3], [[4, 5], [6, 7]]], - name, names, shapes, - setpoint_names=setpoint_names) - p._instrument = instrument + p = SimpleMultiParam( + [0, [1, 2, 3], [[4, 5], [6, 7]]], + name, + names, + shapes, + setpoint_names=setpoint_names, + ) + p._instrument = instrument # type: ignore[assignment] assert str(p) == name assert p.full_names == names assert p.setpoint_full_names == \ ((), ('setpoints_1D',), ('setpoints_2D_1', None)) # and finally an instrument that really has a name - p = SimpleMultiParam([0, [1, 2, 3], [[4, 5], [6, 7]]], - name, names, shapes, setpoint_names=setpoint_names) - p._instrument = named_instrument - assert str(p) == 'astro_mixed_dimensions' + p = SimpleMultiParam( + [0, [1, 2, 3], [[4, 5], [6, 7]]], + name, + names, + shapes, + setpoint_names=setpoint_names, + ) + p._instrument = named_instrument # type: ignore[assignment] + assert str(p) == "astro_mixed_dimensions" assert p.full_names == ('astro_0D', 'astro_1D', 'astro_2D') assert p.setpoint_full_names == \ @@ -198,6 +211,6 @@ def test_full_name_s(): 'setpoint_names': (None, ('index',))}, {'names': ('a', 'b'), 'shapes': ((3,), ()), 'setpoint_labels': (None, None, None)}]) -def test_constructor_errors(constructor): +def test_constructor_errors(constructor: dict) -> None: with pytest.raises(ValueError): SimpleMultiParam([1, 2, 3], 'p', **constructor) diff --git a/qcodes/tests/parameter/test_non_gettable_parameter.py b/qcodes/tests/parameter/test_non_gettable_parameter.py index 4ecadaec006..13812c2b075 100644 --- a/qcodes/tests/parameter/test_non_gettable_parameter.py +++ b/qcodes/tests/parameter/test_non_gettable_parameter.py @@ -1,12 +1,20 @@ +from __future__ import annotations + import logging +from typing import TYPE_CHECKING import numpy as np from numpy.testing import assert_array_almost_equal +if TYPE_CHECKING: + from pytest import LogCaptureFixture + from qcodes.parameters import Parameter -def test_setting_non_gettable_parameter_with_finite_step(caplog): +def test_setting_non_gettable_parameter_with_finite_step( + caplog: LogCaptureFixture, +) -> None: initial_value = 0 step_size = 0.1 set_value = 1.0 @@ -25,8 +33,10 @@ def test_setting_non_gettable_parameter_with_finite_step(caplog): # afterwards the stepping should work as expected. with caplog.at_level(logging.WARNING): caplog.clear() - assert_array_almost_equal(np.array(x.get_ramp_values(set_value, step_size)), - (np.arange(initial_value+step_size, set_value+step_size, step_size))) + assert_array_almost_equal( + np.array(x.get_ramp_values(set_value, step_size)), + (np.arange(initial_value + step_size, set_value + step_size, step_size)), + ) x.set(set_value) assert x.cache.get() == set_value assert len(caplog.records) == 0 diff --git a/qcodes/tests/parameter/test_on_off_mapping.py b/qcodes/tests/parameter/test_on_off_mapping.py index 57c363dc90e..190ef33dde2 100644 --- a/qcodes/tests/parameter/test_on_off_mapping.py +++ b/qcodes/tests/parameter/test_on_off_mapping.py @@ -1,15 +1,17 @@ +from __future__ import annotations + import pytest from qcodes.parameters import create_on_off_val_mapping, invert_val_mapping -def test_values_of_mapping_are_only_the_given_two(): +def test_values_of_mapping_are_only_the_given_two() -> None: val_mapping = create_on_off_val_mapping(on_val="666", off_val="000") values_set = set(list(val_mapping.values())) assert values_set == {"000", "666"} -def test_its_inverse_maps_only_to_booleans(): +def test_its_inverse_maps_only_to_booleans() -> None: inverse = invert_val_mapping(create_on_off_val_mapping(on_val="666", off_val="000")) assert inverse == {"666": True, "000": False} @@ -18,7 +20,9 @@ def test_its_inverse_maps_only_to_booleans(): @pytest.mark.parametrize( ("on_val", "off_val"), ((1, 0), (1.0, 0.0), ("1", "0"), (True, False)) ) -def test_create_on_off_val_mapping_for(on_val, off_val): +def test_create_on_off_val_mapping_for( + on_val: str | float | bool, off_val: str | float | bool +) -> None: """ Explicitly test ``create_on_off_val_mapping`` function by covering some of the edge cases of ``on_val`` and ``off_val`` @@ -31,14 +35,16 @@ def test_create_on_off_val_mapping_for(on_val, off_val): assert on_val in values_list assert off_val in values_list - assert val_mapping[1] is on_val + # this does not type check. However, hash(1) == hash(True) + # so 1/0 behaves like True and False at runtime + assert val_mapping[1] is on_val # type: ignore[index] assert val_mapping[True] is on_val assert val_mapping["1"] is on_val assert val_mapping["ON"] is on_val assert val_mapping["On"] is on_val assert val_mapping["on"] is on_val - assert val_mapping[0] is off_val + assert val_mapping[0] is off_val # type: ignore[index] assert val_mapping[False] is off_val assert val_mapping["0"] is off_val assert val_mapping["OFF"] is off_val diff --git a/qcodes/tests/parameter/test_parameter_basics.py b/qcodes/tests/parameter/test_parameter_basics.py index 836d46e16de..21802638af9 100644 --- a/qcodes/tests/parameter/test_parameter_basics.py +++ b/qcodes/tests/parameter/test_parameter_basics.py @@ -11,12 +11,12 @@ ) -def test_no_name(): +def test_no_name() -> None: with pytest.raises(TypeError): - Parameter() + Parameter() # type: ignore[call-arg] -def test_default_attributes(): +def test_default_attributes() -> None: # Test the default attributes, providing only a name name = 'repetitions' p = GettableParam(name, vals=vals.Numbers()) @@ -31,6 +31,7 @@ def test_default_attributes(): p.validate('not a number') # docstring exists, even without providing one explicitly + assert p.__doc__ is not None assert name in p.__doc__ # test snapshot_get by looking at _get_count @@ -51,7 +52,7 @@ def test_default_attributes(): assert snap['ts'] is not None -def test_explicit_attributes(): +def test_explicit_attributes() -> None: # Test the explicit attributes, providing everything we can name = 'volt' label = 'Voltage' @@ -73,6 +74,7 @@ def test_explicit_attributes(): with pytest.raises(TypeError): p.validate('not a number') + assert p.__doc__ is not None assert name in p.__doc__ assert docstring in p.__doc__ @@ -100,7 +102,7 @@ def test_explicit_attributes(): assert not hasattr(p, attr) -def test_has_set_get(): +def test_has_set_get() -> None: # Create parameter that has no set_cmd, and get_cmd returns last value gettable_parameter = Parameter('one', set_cmd=False, get_cmd=None) assert hasattr(gettable_parameter, 'get') @@ -135,20 +137,20 @@ def test_has_set_get(): assert settable_gettable_parameter() == 22 -def test_str_representation(): +def test_str_representation() -> None: # three cases where only name gets used for full_name for instrument in blank_instruments: p = Parameter(name='fred') - p._instrument = instrument + p._instrument = instrument # type: ignore[assignment] assert str(p) == 'fred' # and finally an instrument that really has a name p = Parameter(name='wilma') - p._instrument = named_instrument + p._instrument = named_instrument # type: ignore[assignment] assert str(p) == 'astro_wilma' -def test_bad_name(): +def test_bad_name() -> None: with pytest.raises(ValueError): Parameter('p with space') with pytest.raises(ValueError): @@ -157,11 +159,11 @@ def test_bad_name(): Parameter('1') -def test_set_via_function(): +def test_set_via_function() -> None: # not a use case we want to promote, but it's there... p = Parameter('test', get_cmd=None, set_cmd=None) - def doubler(x): + def doubler(x: float) -> None: p.set(x * 2) f = Function('f', call_cmd=doubler, args=[vals.Numbers(-10, 10)]) @@ -172,30 +174,32 @@ def doubler(x): f(20) -def test_unknown_args_to_baseparameter_raises(): +def test_unknown_args_to_baseparameter_raises() -> None: """ Passing an unknown kwarg to ParameterBase should trigger a TypeError """ with pytest.raises(TypeError): - _ = ParameterBase(name="Foo", instrument=None, snapshotable=False) + _ = ParameterBase( + name="Foo", instrument=None, snapshotable=False # type: ignore[call-arg] + ) -def test_underlying_instrument_for_virtual_parameter(): - p = GettableParam('base_param', vals=vals.Numbers()) - p._instrument = named_instrument - vp = VirtualParameter('test_param', param=p) +def test_underlying_instrument_for_virtual_parameter() -> None: + p = GettableParam("base_param", vals=vals.Numbers()) + p._instrument = named_instrument # type: ignore[assignment] + vp = VirtualParameter("test_param", param=p) - assert vp.underlying_instrument is named_instrument + assert vp.underlying_instrument is named_instrument # type: ignore[comparison-overlap] -def test_get_cmd_str_no_instrument_raises(): +def test_get_cmd_str_no_instrument_raises() -> None: with pytest.raises( TypeError, match="Cannot use a str get_cmd without binding to an instrument." ): Parameter(name="test", instrument=None, get_cmd="get_me") -def test_set_cmd_str_no_instrument_raises(): +def test_set_cmd_str_no_instrument_raises() -> None: with pytest.raises( TypeError, match="Cannot use a str set_cmd without binding to an instrument." ): diff --git a/qcodes/tests/parameter/test_parameter_cache.py b/qcodes/tests/parameter/test_parameter_cache.py index bc1aaba0733..2f99c14be27 100644 --- a/qcodes/tests/parameter/test_parameter_cache.py +++ b/qcodes/tests/parameter/test_parameter_cache.py @@ -1,15 +1,19 @@ +from __future__ import annotations + import time from datetime import datetime, timedelta +from typing import Any, Literal import pytest import qcodes.validators as vals from qcodes.parameters import Parameter, ParameterBase +from qcodes.tests.instrument_mocks import DummyChannelInstrument from .conftest import NOT_PASSED, BetterGettableParam, SettableParam -def test_get_from_cache_does_not_trigger_real_get_if_get_if_invalid_false(): +def test_get_from_cache_does_not_trigger_real_get_if_get_if_invalid_false() -> None: """ assert that calling get on the cache with get_if_invalid=False does not trigger a get of the parameter when parameter @@ -24,7 +28,7 @@ def test_get_from_cache_does_not_trigger_real_get_if_get_if_invalid_false(): assert param._get_count == 1 -def test_initial_set_with_without_cache(): +def test_initial_set_with_without_cache() -> None: value = 43 # setting the initial value triggers a set param1 = SettableParam(name="param", initial_value=value) @@ -36,13 +40,13 @@ def test_initial_set_with_without_cache(): assert param2.cache.get(get_if_invalid=False) == value -def test_set_initial_and_initial_cache_raises(): +def test_set_initial_and_initial_cache_raises() -> None: with pytest.raises(SyntaxError, match="`initial_value` and `initial_cache_value`"): Parameter(name="param", initial_value=1, initial_cache_value=2) -def test_get_cache(): +def test_get_cache() -> None: time_resolution = time.get_clock_info('time').resolution sleep_delta = 2 * time_resolution @@ -56,16 +60,20 @@ def test_get_cache(): # Check we return last set value, with the correct timestamp assert local_parameter.cache.get() == 1 - assert before_set < local_parameter.cache.timestamp < after_set + before_timestamp = local_parameter.cache.timestamp + assert before_timestamp is not None + assert before_set < before_timestamp < after_set # Check that updating the value updates the timestamp time.sleep(sleep_delta) local_parameter.set(2) assert local_parameter.cache.get() == 2 - assert local_parameter.cache.timestamp > after_set + after_timestamp = local_parameter.cache.timestamp + assert after_timestamp is not None + assert after_timestamp > after_set -def test_get_cache_raw_value(): +def test_get_cache_raw_value() -> None: # To have a simple distinction between raw value and value of the # parameter lets create a parameter with an offset p = Parameter('p', set_cmd=None, get_cmd=None, offset=42) @@ -82,7 +90,7 @@ def test_get_cache_raw_value(): assert p.cache.raw_value == 3 + 42 -def test_get_cache_unknown(): +def test_get_cache_unknown() -> None: """ Test that cache get on a parameter that has not been acquired will trigger a get @@ -92,8 +100,8 @@ def test_get_cache_unknown(): get_cmd=None) # fake a parameter that has a value but never been get/set to mock # an instrument. - local_parameter.cache._value = value - local_parameter.cache._raw_value = value + local_parameter.cache._value = value # type: ignore[attr-defined] + local_parameter.cache._raw_value = value # type: ignore[attr-defined] assert local_parameter.cache.timestamp is None before_get = datetime.now() assert local_parameter._get_count == 0 @@ -108,7 +116,7 @@ def test_get_cache_unknown(): assert local_parameter._get_count == 1 -def test_get_cache_known(): +def test_get_cache_known() -> None: """ Test that cache.get on a parameter that has a known value will not trigger a get @@ -130,7 +138,7 @@ def test_get_cache_known(): assert local_parameter.cache.timestamp == set_time -def test_get_cache_no_get(): +def test_get_cache_no_get() -> None: """ Test that cache.get on a parameter that does not have get is handled correctly. @@ -154,7 +162,7 @@ def test_get_cache_no_get(): assert local_parameter2.cache.get() == value -def test_set_raw_value_on_cache(): +def test_set_raw_value_on_cache() -> None: value = 1 scale = 10 local_parameter = BetterGettableParam('test_param', @@ -165,11 +173,13 @@ def test_set_raw_value_on_cache(): after = datetime.now() assert local_parameter.cache.get(get_if_invalid=False) == value assert local_parameter.cache.raw_value == value * scale - assert local_parameter.cache.timestamp >= before - assert local_parameter.cache.timestamp <= after + timestamp = local_parameter.cache.timestamp + assert timestamp is not None + assert timestamp >= before + assert timestamp <= after -def test_max_val_age(): +def test_max_val_age() -> None: value = 1 start = datetime.now() local_parameter = BetterGettableParam('test_param', @@ -188,10 +198,12 @@ def test_max_val_age(): assert local_parameter.cache.timestamp == set_time assert local_parameter.cache.get() == value assert local_parameter._get_count == 1 - assert local_parameter.cache.timestamp >= start + timestamp = local_parameter.cache.timestamp + assert timestamp is not None + assert timestamp >= start -def test_no_get_max_val_age(): +def test_no_get_max_val_age() -> None: """ Test that cache.get on a parameter with max_val_age set and no get cmd raises correctly. @@ -203,7 +215,9 @@ def test_no_get_max_val_age(): max_val_age=1, initial_value=value) -def test_no_get_max_val_age_runtime_error(get_if_invalid): +def test_no_get_max_val_age_runtime_error( + get_if_invalid: bool | Literal["NOT_PASSED"], +) -> None: """ ParameterBase does not have a check on creation time that no get_cmd is mixed with max_val_age since get_cmd could be added @@ -213,9 +227,9 @@ def test_no_get_max_val_age_runtime_error(get_if_invalid): value = 1 class LocalParameter(ParameterBase): - def __init__(self, *args, **kwargs): + def __init__(self, *args: Any, **kwargs: Any): super().__init__(*args, **kwargs) - self.set_raw = lambda x: x + self.set_raw = lambda x: x # type: ignore[assignment] self.set = self._wrap_set(self.set_raw) local_parameter = LocalParameter('test_param', @@ -236,7 +250,9 @@ def __init__(self, *args, **kwargs): assert local_parameter.cache.get(get_if_invalid=get_if_invalid) == 1 -def test_no_get_timestamp_none_runtime_error(get_if_invalid): +def test_no_get_timestamp_none_runtime_error( + get_if_invalid: bool | Literal["NOT_PASSED"], +) -> None: """ Test that a parameter that has never been set, cannot be get and does not support @@ -254,16 +270,16 @@ def test_no_get_timestamp_none_runtime_error(get_if_invalid): assert local_parameter.cache.get(get_if_invalid=get_if_invalid) is None -def test_latest_dictionary_gets_updated_upon_set_of_memory_parameter(): - p = Parameter('p', set_cmd=None, get_cmd=None) - assert p.cache._value is None - assert p.cache._raw_value is None +def test_latest_dictionary_gets_updated_upon_set_of_memory_parameter() -> None: + p = Parameter("p", set_cmd=None, get_cmd=None) + assert p.cache._value is None # type: ignore[attr-defined] + assert p.cache.raw_value is None assert p.cache.timestamp is None p(42) - assert p.cache._value == 42 - assert p.cache._raw_value == 42 + assert p.cache._value == 42 # type: ignore[attr-defined] + assert p.cache.raw_value == 42 assert p.cache.timestamp is not None @@ -299,7 +315,9 @@ def test_latest_dictionary_gets_updated_upon_set_of_memory_parameter(): 'with_scale_and_offset', ) ) -def test_set_latest_works_for_plain_memory_parameter(p, value, raw_value): +def test_set_latest_works_for_plain_memory_parameter( + p: Parameter, value: str | int, raw_value: str | int +) -> None: # Set latest value of the parameter p.cache.set(value) @@ -308,8 +326,8 @@ def test_set_latest_works_for_plain_memory_parameter(p, value, raw_value): assert p.raw_value == raw_value # Assert latest value and raw_value via private attributes for strictness - assert p.cache._value == value - assert p.cache._raw_value == raw_value + assert p.cache._value == value # type: ignore[attr-defined] + assert p.cache.raw_value == raw_value # Now let's get the value of the parameter to ensure that the value that # is set above gets picked up from the `_latest` dictionary (due to @@ -329,11 +347,11 @@ def test_set_latest_works_for_plain_memory_parameter(p, value, raw_value): assert p.raw_value == raw_value # Assert latest value and raw_value via private attributes for strictness - assert p.cache._value == value - assert p.cache._raw_value == raw_value + assert p.cache._value == value # type: ignore[attr-defined] + assert p.cache.raw_value == raw_value -def test_get_from_cache_marked_invalid(): +def test_get_from_cache_marked_invalid() -> None: param = BetterGettableParam(name="param") param.get() assert param._get_count == 1 @@ -360,8 +378,10 @@ def test_get_from_cache_marked_invalid(): assert param._get_count == 2 -def test_marking_invalid_via_instrument(dummy_instrument): - def _assert_cache_status(valid: bool): +def test_marking_invalid_via_instrument( + dummy_instrument: DummyChannelInstrument, +) -> None: + def _assert_cache_status(valid: bool) -> None: for param in dummy_instrument.parameters.values(): assert param.cache.valid is valid, param.full_name diff --git a/qcodes/tests/parameter/test_parameter_context_manager.py b/qcodes/tests/parameter/test_parameter_context_manager.py index f8be62dd626..e4a1657dc80 100644 --- a/qcodes/tests/parameter/test_parameter_context_manager.py +++ b/qcodes/tests/parameter/test_parameter_context_manager.py @@ -1,13 +1,15 @@ +from typing import Any, Generator + import pytest import qcodes.validators as vals -from qcodes.parameters import Parameter +from qcodes.parameters import Parameter, ParamRawDataType from qcodes.tests.instrument_mocks import DummyInstrument class DummyTrackingInstrument(DummyInstrument): - def __init__(self, name): + def __init__(self, name: str): super().__init__(name) self.add_parameter("a", set_cmd=None, @@ -43,28 +45,28 @@ def __init__(self, name): self._cp_counter = 0 self._cp_get_counter = 0 - def _vp_getter(self): + def _vp_getter(self) -> str: return self._vp_value - def _vp_setter(self, value): + def _vp_setter(self, value: str) -> None: self._vp_value = value - def _pp_getter(self): + def _pp_getter(self) -> ParamRawDataType: return self._pp_value - def _pp_setter(self, value): + def _pp_setter(self, value: ParamRawDataType) -> None: self._pp_value = value - def _cp_setter(self, value): + def _cp_setter(self, value: ParamRawDataType) -> None: self._cp_counter += 1 - def _cp_getter(self): + def _cp_getter(self) -> ParamRawDataType: self._cp_get_counter += 1 return self.counting_parameter.cache._value -@pytest.fixture() -def instrument(): +@pytest.fixture(name="instrument") +def _make_instrument() -> Generator[DummyTrackingInstrument, None, None]: instrument = DummyTrackingInstrument('dummy_holder') try: yield instrument @@ -72,7 +74,9 @@ def instrument(): instrument.close() -def test_set_to_none_when_parameter_is_not_captured_yet(instrument): +def test_set_to_none_when_parameter_is_not_captured_yet( + instrument: DummyTrackingInstrument, +) -> None: counting_parameter = instrument.counting_parameter # Pre-conditions: assert instrument._cp_counter == 0 @@ -100,12 +104,12 @@ def test_set_to_none_when_parameter_is_not_captured_yet(instrument): assert instrument._cp_get_counter == 1 -def test_set_to_none_for_not_captured_parameter_but_instrument_has_value(): +def test_set_to_none_for_not_captured_parameter_but_instrument_has_value() -> None: # representing instrument here instr_value = 'something' set_counter = 0 - def set_instr_value(value): + def set_instr_value(value: Any) -> None: nonlocal instr_value, set_counter instr_value = value set_counter += 1 @@ -115,28 +119,28 @@ def set_instr_value(value): val_mapping={'foo': 'something', None: 'nothing'}) # pre-conditions - assert p.cache._value is None - assert p.cache._raw_value is None + assert p.cache._value is None # type: ignore[attr-defined] + assert p.cache.raw_value is None assert p.cache.timestamp is None assert set_counter == 0 with p.set_to(None): # assertions after entering the context assert set_counter == 1 - assert instr_value == 'nothing' - assert p.cache._value is None - assert p.cache._raw_value == 'nothing' + assert instr_value == "nothing" + assert p.cache._value is None # type: ignore[attr-defined] + assert p.cache.raw_value == "nothing" assert p.cache.timestamp is not None # assertions after exiting the context assert set_counter == 2 - assert instr_value == 'something' - assert p.cache._value == 'foo' - assert p.cache._raw_value == 'something' + assert instr_value == "something" + assert p.cache._value == "foo" # pyright: ignore + assert p.cache.raw_value == "something" assert p.cache.timestamp is not None -def test_none_value(instrument): +def test_none_value(instrument: DummyTrackingInstrument) -> None: with instrument.a.set_to(3): assert instrument.a.get_latest.get_timestamp() is not None assert instrument.a.get() == 3 @@ -144,7 +148,7 @@ def test_none_value(instrument): assert instrument.a.get_latest.get_timestamp() is not None -def test_context(instrument): +def test_context(instrument: DummyTrackingInstrument) -> None: instrument.a.set(2) with instrument.a.set_to(3): @@ -152,7 +156,7 @@ def test_context(instrument): assert instrument.a.get() == 2 -def test_validated_param(instrument): +def test_validated_param(instrument: DummyTrackingInstrument) -> None: assert instrument.parsed_param.cache._value is None assert instrument.validated_param.get_latest() == "foo" with instrument.validated_param.set_to("bar"): @@ -161,7 +165,7 @@ def test_validated_param(instrument): assert instrument.validated_param.get() == "foo" -def test_parsed_param(instrument): +def test_parsed_param(instrument: DummyTrackingInstrument) -> None: assert instrument.parsed_param.cache._value is None assert instrument.parsed_param.get_latest() == 42 with instrument.parsed_param.set_to(1): @@ -170,7 +174,7 @@ def test_parsed_param(instrument): assert instrument.parsed_param.get() == 42 -def test_number_of_set_calls(instrument): +def test_number_of_set_calls(instrument: DummyTrackingInstrument) -> None: """ Test that with param.set_to(X) does not perform any calls to set if the parameter already had the value X @@ -188,7 +192,9 @@ def test_number_of_set_calls(instrument): assert instrument._cp_counter == 3 -def test_value_modified_between_context_create_and_enter(instrument): +def test_value_modified_between_context_create_and_enter( + instrument: DummyTrackingInstrument, +) -> None: p = instrument.a p.set(2) ctx = p.set_to(5) @@ -200,7 +206,7 @@ def test_value_modified_between_context_create_and_enter(instrument): assert p() == 3 -def test_disallow_changes(instrument): +def test_disallow_changes(instrument: DummyTrackingInstrument) -> None: instrument.a.set(2) with instrument.a.set_to(3, allow_changes=False): @@ -213,7 +219,7 @@ def test_disallow_changes(instrument): assert instrument.a() == 2 -def test_allow_changes(instrument): +def test_allow_changes(instrument: DummyTrackingInstrument) -> None: p = instrument.a p.set(2) with p.set_to(3, allow_changes=True): @@ -236,7 +242,7 @@ def test_allow_changes(instrument): assert p() == 2 -def test_reset_at_exit(instrument): +def test_reset_at_exit(instrument: DummyTrackingInstrument) -> None: p = instrument.a p.set(2) with p.restore_at_exit(): @@ -244,7 +250,9 @@ def test_reset_at_exit(instrument): assert p() == 2 -def test_reset_at_exit_with_allow_changes_false(instrument): +def test_reset_at_exit_with_allow_changes_false( + instrument: DummyTrackingInstrument, +) -> None: p = instrument.a p.set(2) with p.restore_at_exit(allow_changes=False): diff --git a/qcodes/tests/parameter/test_parameter_registration.py b/qcodes/tests/parameter/test_parameter_registration.py index 3d4ab14217e..2e8214dc592 100644 --- a/qcodes/tests/parameter/test_parameter_registration.py +++ b/qcodes/tests/parameter/test_parameter_registration.py @@ -1,5 +1,8 @@ +from typing import Any, Generator + import pytest +from qcodes.instrument import InstrumentBase from qcodes.parameters import Parameter from qcodes.tests.instrument_mocks import DummyAttrInstrument from qcodes.utils import QCoDeSDeprecationWarning @@ -11,7 +14,9 @@ class BrokenParameter(Parameter): instead, it should forward it via ``instrument`` argument """ - def __init__(self, name, instrument, *args, **kwargs): + def __init__( + self, name: str, instrument: InstrumentBase, *args: Any, **kwargs: Any + ): super().__init__(name, *args, **kwargs) self._instrument = instrument @@ -19,18 +24,20 @@ def __init__(self, name, instrument, *args, **kwargs): class BrokenParameter2(Parameter): """A parameter that does not pass kwargs to the ParameterBase class""" - def __init__(self, name, instrument, set_cmd, get_cmd): + def __init__( + self, name: str, instrument: InstrumentBase, set_cmd: Any, get_cmd: Any + ): super().__init__(name=name, instrument=instrument) @pytest.fixture(name="dummy_attr_instr") -def _make_dummy_attr_instr(): +def _make_dummy_attr_instr() -> Generator[DummyAttrInstrument, None, None]: dummy_attr_instr = DummyAttrInstrument("dummy_attr_instr") yield dummy_attr_instr dummy_attr_instr.close() -def test_parameter_registration_on_instr(dummy_attr_instr): +def test_parameter_registration_on_instr(dummy_attr_instr: DummyAttrInstrument) -> None: """Test that an instrument that have parameters defined as attrs""" assert dummy_attr_instr.ch1.instrument is dummy_attr_instr assert dummy_attr_instr.parameters["ch1"] is dummy_attr_instr.ch1 @@ -41,10 +48,13 @@ def test_parameter_registration_on_instr(dummy_attr_instr): ) -def test_parameter_registration_with_non_instr_passing_parameter(dummy_attr_instr): +def test_parameter_registration_with_non_instr_passing_parameter( + dummy_attr_instr: DummyAttrInstrument, +) -> None: with pytest.warns( QCoDeSDeprecationWarning, - match="Parameter brokenparameter did not correctly register itself on instrument dummy_attr_instr", + match="Parameter brokenparameter did not correctly " + "register itself on instrument dummy_attr_instr", ): dummy_attr_instr.add_parameter( name="brokenparameter", @@ -57,7 +67,9 @@ def test_parameter_registration_with_non_instr_passing_parameter(dummy_attr_inst assert "brokenparameter" in dummy_attr_instr.parameters.keys() -def test_parameter_registration_with_non_kwargs_passing_parameter(dummy_attr_instr): +def test_parameter_registration_with_non_kwargs_passing_parameter( + dummy_attr_instr: DummyAttrInstrument, +) -> None: with pytest.warns( QCoDeSDeprecationWarning, match="does not correctly pass kwargs to its baseclass", @@ -68,6 +80,7 @@ def test_parameter_registration_with_non_kwargs_passing_parameter(dummy_attr_ins set_cmd=None, get_cmd=None, ) - # test that even if the parameter does not pass kwargs (bind_to_instrument specifically) + # test that even if the parameter does not pass kwargs + # (bind_to_instrument specifically) # to the baseclass it will still be registered on the instr assert "brokenparameter2" in dummy_attr_instr.parameters.keys() diff --git a/qcodes/tests/parameter/test_parameter_validation.py b/qcodes/tests/parameter/test_parameter_validation.py index ac48b5cc921..4b5f967194c 100644 --- a/qcodes/tests/parameter/test_parameter_validation.py +++ b/qcodes/tests/parameter/test_parameter_validation.py @@ -7,13 +7,14 @@ from .conftest import BookkeepingValidator -def test_number_of_validations(): +def test_number_of_validations() -> None: p = Parameter('p', set_cmd=None, initial_value=0, vals=BookkeepingValidator()) # in the set wrapper the final value is validated # and then subsequently each step is validated. # in this case there is one step so the final value # is validated twice. + assert isinstance(p.vals, BookkeepingValidator) assert p.vals.values_validated == [0, 0] p.step = 1 @@ -21,9 +22,10 @@ def test_number_of_validations(): assert p.vals.values_validated == [0, 0, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] -def test_number_of_validations_for_set_cache(): +def test_number_of_validations_for_set_cache() -> None: p = Parameter('p', set_cmd=None, vals=BookkeepingValidator()) + assert isinstance(p.vals, BookkeepingValidator) assert p.vals.values_validated == [] p.cache.set(1) @@ -37,12 +39,12 @@ def test_number_of_validations_for_set_cache(): assert p.vals.values_validated == [1, 4, 10] -def test_bad_validator(): +def test_bad_validator() -> None: with pytest.raises(TypeError): - Parameter('p', vals=[1, 2, 3]) + Parameter("p", vals=[1, 2, 3]) # type:ignore[arg-type] -def test_setting_int_with_float(): +def test_setting_int_with_float() -> None: parameter = Parameter(name='foobar', set_cmd=None, get_cmd=None, set_parser=lambda x: int(round(x)), vals=vals.PermissiveInts(0)) @@ -56,7 +58,7 @@ def test_setting_int_with_float(): assert isinstance(a, int) -def test_setting_int_with_float_not_close(): +def test_setting_int_with_float_not_close() -> None: parameter = Parameter(name='foobar', set_cmd=None, get_cmd=None, set_parser=lambda x: int(round(x)), vals=vals.PermissiveInts(0)) diff --git a/qcodes/tests/parameter/test_snapshot.py b/qcodes/tests/parameter/test_snapshot.py index 71697e0585e..9da1c505814 100644 --- a/qcodes/tests/parameter/test_snapshot.py +++ b/qcodes/tests/parameter/test_snapshot.py @@ -1,45 +1,54 @@ +from __future__ import annotations + from datetime import datetime, timedelta -from typing import Any, Callable, Dict, Optional, Union +from typing import Any, Callable, Literal, TypeVar + +from typing_extensions import ParamSpec from qcodes.parameters import Parameter from .conftest import NOT_PASSED +T = TypeVar("T") +P = ParamSpec("P") -def create_parameter(snapshot_get: bool, - snapshot_value: bool, - cache_is_valid: bool, - get_cmd: Optional[Union[Callable[..., Any], bool]], - offset: Union[str, float] = NOT_PASSED): - kwargs: Dict[str, Any] = dict(set_cmd=None, - label='Parameter', - unit='a.u.', - docstring='some docs') + +def create_parameter( + snapshot_get: bool | Literal["NOT_PASSED"], + snapshot_value: bool | Literal["NOT_PASSED"], + cache_is_valid: bool, + get_cmd: Callable[..., Any] | bool | None | Literal["NOT_PASSED"], + offset: float | Literal["NOT_PASSED"] = NOT_PASSED, +) -> Parameter: + kwargs: dict[str, Any] = dict( + set_cmd=None, label="Parameter", unit="a.u.", docstring="some docs" + ) if offset != NOT_PASSED: kwargs.update(offset=offset) - if snapshot_get != NOT_PASSED: # type: ignore[comparison-overlap] + if snapshot_get != NOT_PASSED: kwargs.update(snapshot_get=snapshot_get) - if snapshot_value != NOT_PASSED: # type: ignore[comparison-overlap] + if snapshot_value != NOT_PASSED: kwargs.update(snapshot_value=snapshot_value) - if get_cmd != NOT_PASSED: # type: ignore[comparison-overlap] + if get_cmd != NOT_PASSED: kwargs.update(get_cmd=get_cmd) p = Parameter('p', **kwargs) if get_cmd is not False: - def wrap_in_call_counter(get_func): + + def wrap_in_call_counter(get_func: Callable[P, T]) -> Callable[P, T]: call_count = 0 - def wrapped_func(*args, **kwargs): + def wrapped_func(*args: P.args, **kwargs: P.kwargs) -> T: nonlocal call_count call_count += 1 return get_func(*args, **kwargs) - wrapped_func.call_count = lambda: call_count + wrapped_func.call_count = lambda: call_count # type: ignore[attr-defined] return wrapped_func @@ -58,7 +67,12 @@ def wrapped_func(*args, **kwargs): def test_snapshot_contains_parameter_attributes( - snapshot_get, snapshot_value, get_cmd, cache_is_valid, update): + snapshot_get: bool | Literal["NOT_PASSED"], + snapshot_value: bool | Literal["NOT_PASSED"], + get_cmd: None | Literal[False] | Literal["NOT_PASSED"], + cache_is_valid: bool, + update: bool | Literal["NOT_PASSED"] | None, +) -> None: p = create_parameter(snapshot_get, snapshot_value, cache_is_valid, get_cmd) if update != NOT_PASSED: @@ -96,9 +110,12 @@ def test_snapshot_contains_parameter_attributes( def test_snapshot_timestamp_of_non_gettable_depends_only_on_cache_validity( - snapshot_get, snapshot_value, update, cache_is_valid): - p = create_parameter(snapshot_get, snapshot_value, cache_is_valid, - get_cmd=False) + snapshot_get: bool | Literal["NOT_PASSED"], + snapshot_value: bool | Literal["NOT_PASSED"], + update: bool | Literal["NOT_PASSED"] | None, + cache_is_valid: bool, +) -> None: + p = create_parameter(snapshot_get, snapshot_value, cache_is_valid, get_cmd=False) t0 = p.cache.timestamp @@ -108,23 +125,31 @@ def test_snapshot_timestamp_of_non_gettable_depends_only_on_cache_validity( s = p.snapshot() if cache_is_valid: + assert t0 is not None ts = datetime.strptime(s['ts'], '%Y-%m-%d %H:%M:%S') t0_up_to_seconds = t0.replace(microsecond=0) assert ts >= t0_up_to_seconds else: + assert t0 is None assert s['ts'] is None def test_snapshot_timestamp_for_valid_cache_depends_on_cache_update( - snapshot_get, snapshot_value, update): - - p = create_parameter(snapshot_get, snapshot_value, - get_cmd=lambda: 69, cache_is_valid=True) + snapshot_get: bool | Literal["NOT_PASSED"], + snapshot_value: bool | Literal["NOT_PASSED"], + update: bool | Literal["NOT_PASSED"] | None, +) -> None: + p = create_parameter( + snapshot_get, snapshot_value, get_cmd=lambda: 69, cache_is_valid=True + ) # Hack cache's timestamp to simplify this test - p.cache._timestamp = p.cache.timestamp - timedelta(days=31) + timestamp = p.cache.timestamp + assert timestamp is not None + p.cache._timestamp = timestamp - timedelta(days=31) # type: ignore[attr-defined] tu = datetime.now() + assert p.cache.timestamp is not None assert p.cache.timestamp < tu # pre-condition if update != NOT_PASSED: s = p.snapshot(update=update) @@ -147,10 +172,13 @@ def test_snapshot_timestamp_for_valid_cache_depends_on_cache_update( def test_snapshot_timestamp_for_invalid_cache_depends_only_on_snapshot_flags( - snapshot_get, snapshot_value, update): - - p = create_parameter(snapshot_get, snapshot_value, - get_cmd=lambda: 69, cache_is_valid=False) + snapshot_get: bool | Literal["NOT_PASSED"], + snapshot_value: bool | Literal["NOT_PASSED"], + update: bool | Literal["NOT_PASSED"] | None, +) -> None: + p = create_parameter( + snapshot_get, snapshot_value, get_cmd=lambda: 69, cache_is_valid=False + ) cache_gets_updated_on_snapshot_call = ( snapshot_value is not False @@ -161,6 +189,8 @@ def test_snapshot_timestamp_for_invalid_cache_depends_only_on_snapshot_flags( if cache_gets_updated_on_snapshot_call: tu = datetime.now() + else: + tu = None if update != NOT_PASSED: s = p.snapshot(update=update) @@ -169,6 +199,7 @@ def test_snapshot_timestamp_for_invalid_cache_depends_only_on_snapshot_flags( if cache_gets_updated_on_snapshot_call: ts = datetime.strptime(s['ts'], '%Y-%m-%d %H:%M:%S') + assert tu is not None tu_up_to_seconds = tu.replace(microsecond=0) assert ts >= tu_up_to_seconds else: @@ -176,8 +207,11 @@ def test_snapshot_timestamp_for_invalid_cache_depends_only_on_snapshot_flags( def test_snapshot_when_snapshot_value_is_false( - snapshot_get, get_cmd, cache_is_valid, update): - + snapshot_get: bool | Literal["NOT_PASSED"], + get_cmd: Literal[False, "NOT_PASSED"] | None, + cache_is_valid: bool, + update: bool | Literal["NOT_PASSED"] | None, +) -> None: p = create_parameter( snapshot_get=snapshot_get, snapshot_value=False, get_cmd=get_cmd, cache_is_valid=cache_is_valid) @@ -191,10 +225,13 @@ def test_snapshot_when_snapshot_value_is_false( assert 'raw_value' not in s if get_cmd is not False: - assert p.get.call_count() == 0 + assert p.get.call_count() == 0 # type: ignore[attr-defined] -def test_snapshot_value_is_true_by_default(snapshot_get, get_cmd): +def test_snapshot_value_is_true_by_default( + snapshot_get: bool | Literal["NOT_PASSED"], + get_cmd: Literal[False, "NOT_PASSED"] | None, +) -> None: p = create_parameter( snapshot_value=NOT_PASSED, snapshot_get=snapshot_get, @@ -204,7 +241,10 @@ def test_snapshot_value_is_true_by_default(snapshot_get, get_cmd): assert p._snapshot_value is True -def test_snapshot_get_is_true_by_default(snapshot_value, get_cmd): +def test_snapshot_get_is_true_by_default( + snapshot_value: bool | Literal["NOT_PASSED"], + get_cmd: Literal[False, "NOT_PASSED"] | None, +) -> None: p = create_parameter( snapshot_get=NOT_PASSED, snapshot_value=snapshot_value, @@ -214,7 +254,11 @@ def test_snapshot_get_is_true_by_default(snapshot_value, get_cmd): assert p._snapshot_get is True -def test_snapshot_when_snapshot_get_is_false(get_cmd, update, cache_is_valid): +def test_snapshot_when_snapshot_get_is_false( + get_cmd: Literal[False, "NOT_PASSED"] | None, + update: bool | Literal["NOT_PASSED"] | None, + cache_is_valid: bool, +) -> None: p = create_parameter( snapshot_get=False, snapshot_value=True, @@ -235,11 +279,12 @@ def test_snapshot_when_snapshot_get_is_false(get_cmd, update, cache_is_valid): assert s['raw_value'] is None if get_cmd is not False: - assert p.get.call_count() == 0 + assert p.get.call_count() == 0 # type: ignore[attr-defined] def test_snapshot_of_non_gettable_parameter_mirrors_cache( - update, cache_is_valid): + update: bool | Literal["NOT_PASSED"] | None, cache_is_valid: bool +) -> None: p = create_parameter( snapshot_get=True, snapshot_value=True, get_cmd=False, cache_is_valid=cache_is_valid, offset=4) @@ -257,8 +302,9 @@ def test_snapshot_of_non_gettable_parameter_mirrors_cache( assert s['raw_value'] is None -def test_snapshot_of_gettable_parameter_depends_on_update(update, - cache_is_valid): +def test_snapshot_of_gettable_parameter_depends_on_update( + update: bool | Literal["NOT_PASSED"] | None, cache_is_valid: bool +) -> None: p = create_parameter( snapshot_get=True, snapshot_value=True, get_cmd=lambda: 69, cache_is_valid=cache_is_valid, offset=4) @@ -271,18 +317,18 @@ def test_snapshot_of_gettable_parameter_depends_on_update(update, if update is not True and cache_is_valid: assert s['value'] == 42 assert s['raw_value'] == 46 - assert p.get.call_count() == 0 + assert p.get.call_count() == 0 # type: ignore[attr-defined] elif update is False or update == NOT_PASSED: assert s['value'] is None assert s['raw_value'] is None - assert p.get.call_count() == 0 + assert p.get.call_count() == 0 # type: ignore[attr-defined] else: assert s['value'] == 65 assert s['raw_value'] == 69 - assert p.get.call_count() == 1 + assert p.get.call_count() == 1 # type: ignore[attr-defined] -def test_snapshot_value(): +def test_snapshot_value() -> None: p_snapshot = Parameter('no_snapshot', set_cmd=None, get_cmd=None, snapshot_value=True) p_snapshot(42) diff --git a/qcodes/tests/parameter/test_specialized_parameters.py b/qcodes/tests/parameter/test_specialized_parameters.py deleted file mode 100644 index fa525062607..00000000000 --- a/qcodes/tests/parameter/test_specialized_parameters.py +++ /dev/null @@ -1,58 +0,0 @@ -""" -Tests for the specialized_parameters module -""" - -from time import sleep - -import pytest - -from qcodes.parameters import ElapsedTimeParameter - - -def test_elapsed_time_parameter_init(): - - tp1 = ElapsedTimeParameter('time1') - sleep(0.01) - tp2 = ElapsedTimeParameter('time2') - - assert tp1() > tp2() - - -def test_elapsed_time_parameter_monotonic(): - - tp = ElapsedTimeParameter('time') - - times = [tp() for _ in range(25)] - - assert sorted(times) == times - - -def test_elapsed_time_parameter_reset_clock(): - - tp = ElapsedTimeParameter('time') - - sleep(0.01) - t1 = tp() - - tp.reset_clock() - t2 = tp() - - assert t1 > t2 - - -def test_elapsed_time_parameter_not_settable(): - - tp = ElapsedTimeParameter('time') - - with pytest.raises(NotImplementedError): - tp(0) - - -def test_elapsed_time_parameter_forbidden_kwargs(): - - forbidden_kwargs = ['unit', 'get_cmd', 'set_cmd'] - - for fb_kwarg in forbidden_kwargs: - match = f'Can not set "{fb_kwarg}" for an ElapsedTimeParameter' - with pytest.raises(ValueError, match=match): - ElapsedTimeParameter('time', **{fb_kwarg: None}) diff --git a/qcodes/tests/parameter/test_val_mapping.py b/qcodes/tests/parameter/test_val_mapping.py index 4e1bccdd033..7c1c1287348 100644 --- a/qcodes/tests/parameter/test_val_mapping.py +++ b/qcodes/tests/parameter/test_val_mapping.py @@ -1,3 +1,5 @@ +from typing import Generator + import pytest import qcodes.validators as vals @@ -8,7 +10,7 @@ @pytest.fixture(name="dummyinst") -def _make_dummy_inst(): +def _make_dummy_inst() -> Generator[DummyInstrument, None, None]: inst = DummyInstrument('dummy_holder') try: yield inst @@ -16,7 +18,7 @@ def _make_dummy_inst(): inst.close() -def test_val_mapping_basic(): +def test_val_mapping_basic() -> None: # We store value external to cache # to allow testing of interaction with cache mem = ParameterMemory() @@ -53,7 +55,7 @@ def test_val_mapping_basic(): assert p.get_latest() == 'on' -def test_val_mapping_with_parsers(): +def test_val_mapping_with_parsers() -> None: # We store value external to cache # to allow testing of interaction with cache mem = ParameterMemory() @@ -90,7 +92,7 @@ def test_val_mapping_with_parsers(): assert p.cache.get() == 'on' -def test_on_off_val_mapping(): +def test_on_off_val_mapping() -> None: instrument_value_for_on = 'on_' instrument_value_for_off = 'off_' @@ -124,7 +126,7 @@ def test_on_off_val_mapping(): assert p() == parameter_return_value -def test_val_mapping_on_instrument(dummyinst): +def test_val_mapping_on_instrument(dummyinst: DummyInstrument) -> None: dummyinst.add_parameter('myparameter', set_cmd=None, get_cmd=None, val_mapping={'A': 0, 'B': 1}) diff --git a/qcodes/tests/validators/test_arrays.py b/qcodes/tests/validators/test_arrays.py index 5e19d34c36b..4fecbff89fb 100644 --- a/qcodes/tests/validators/test_arrays.py +++ b/qcodes/tests/validators/test_arrays.py @@ -15,25 +15,25 @@ from qcodes.validators import Arrays -def test_type(): +def test_type() -> None: m = Arrays(min_value=0.0, max_value=3.2, shape=(2, 2)) for v in ['somestring', 4, 2, [[2, 0], [1, 2]]]: with pytest.raises(TypeError): - m.validate(v) + m.validate(v) # type: ignore[arg-type] -def test_complex_min_max_raises(): +def test_complex_min_max_raises() -> None: """ Min max is not implemented for complex types """ with pytest.raises(TypeError, match=r"min_value must be a real number\." r" It is \(1\+1j\) of type " r""): - Arrays(min_value=1 + 1j) + Arrays(min_value=1 + 1j) # type: ignore[arg-type] with pytest.raises(TypeError, match=r"max_value must be a real number. " r"It is \(1\+1j\) of type " r""): - Arrays(max_value=1 + 1j) + Arrays(max_value=1 + 1j) # type: ignore[arg-type] with pytest.raises(TypeError, match=r'Setting min_value or max_value is ' r'not supported for complex ' r'validators'): @@ -41,12 +41,12 @@ def test_complex_min_max_raises(): @given(dtype=complex_number_dtypes()) -def test_complex(dtype): +def test_complex(dtype: np.dtype) -> None: a = Arrays(valid_types=(np.complexfloating,)) a.validate(np.arange(10, dtype=dtype)) -def test_complex_subtypes(): +def test_complex_subtypes() -> None: """Test that specifying a specific complex subtype works as expected""" a = Arrays(valid_types=(np.complex64,)) @@ -64,7 +64,7 @@ def test_complex_subtypes(): a.validate(np.arange(10, dtype=np.complex64)) -def test_min_max_real_ints_raises(): +def test_min_max_real_ints_raises() -> None: with pytest.raises(TypeError, match="min_value must be an instance " "of valid_types."): Arrays(valid_types=(np.integer,), min_value=1.0) @@ -73,7 +73,7 @@ def test_min_max_real_ints_raises(): Arrays(valid_types=(np.integer,), max_value=6.0) -def test_min_max_ints_real_raises(): +def test_min_max_ints_real_raises() -> None: with pytest.raises(TypeError, match="min_value must be an instance " "of valid_types."): Arrays(valid_types=(np.floating,), min_value=1) @@ -82,7 +82,7 @@ def test_min_max_ints_real_raises(): Arrays(valid_types=(np.floating,), max_value=6) -def test_real_subtypes(): +def test_real_subtypes() -> None: """ Test that validating a concrete real type into an array that only support other concrete types raises as expected @@ -98,7 +98,7 @@ def test_real_subtypes(): a.validate(np.arange(10, dtype=mytype)) -def test_complex_default_raises(): +def test_complex_default_raises() -> None: """Complex types should not validate by default""" a = Arrays() for dtype in concrete_complex_types: @@ -109,7 +109,7 @@ def test_complex_default_raises(): a.validate(np.arange(10, dtype=dtype)) -def test_text_type_raises(): +def test_text_type_raises() -> None: """Text types are not supported """ with pytest.raises(TypeError, match="Arrays validator only supports " "numeric types: None: """Test that an array of text raises""" a = Arrays() with pytest.raises(TypeError, @@ -127,7 +127,7 @@ def test_text_array_raises(): a.validate(np.array(['A', 'BC', 'CDF'])) -def test_default_types(): +def test_default_types() -> None: """Arrays constructed with all concrete and abstract real number types should validate by default""" a = Arrays() @@ -148,7 +148,7 @@ def test_default_types(): a.validate(np.arange(10, dtype=mytype)) -def test_min_max(): +def test_min_max() -> None: m = Arrays(min_value=-5, max_value=50, shape=(2, 2)) v = np.array([[2, 0], [1, 2]]) m.validate(v) @@ -164,13 +164,13 @@ def test_min_max(): m.validate(v * 100) -def test_max_smaller_min_raises(): +def test_max_smaller_min_raises() -> None: with pytest.raises(TypeError, match='max_value must be ' 'bigger than min_value'): Arrays(min_value=10, max_value=-10) -def test_shape(): +def test_shape() -> None: m = Arrays(min_value=-5, max_value=50, shape=(2, 2)) v1 = np.array([[2, 0], [1, 2]]) @@ -186,7 +186,7 @@ def test_shape(): m.validate(v2) -def test_shape_defered(): +def test_shape_defered() -> None: m = Arrays(min_value=-5, max_value=50, shape=(lambda: 2, lambda: 2)) v1 = np.array([[2, 5], [3, 7]]) m.validate(v1) @@ -195,26 +195,26 @@ def test_shape_defered(): m.validate(v2) -def test_valid_values_with_shape(): +def test_valid_values_with_shape() -> None: val = Arrays(min_value=-5, max_value=50, shape=(2, 2)) for vval in val.valid_values: val.validate(vval) -def test_valid_values(): +def test_valid_values() -> None: val = Arrays(min_value=-5, max_value=50) for vval in val.valid_values: val.validate(vval) -def test_shape_non_sequence_raises(): +def test_shape_non_sequence_raises() -> None: with pytest.raises(ValueError): - _ = Arrays(shape=5) + _ = Arrays(shape=5) # type: ignore[arg-type] with pytest.raises(ValueError): - _ = Arrays(shape=lambda: 10) + _ = Arrays(shape=lambda: 10) # type: ignore[arg-type] -def test_repr(): +def test_repr() -> None: a = Arrays() assert str(a) == '' b = Arrays(min_value=1, max_value=2) diff --git a/qcodes/tests/validators/test_basic.py b/qcodes/tests/validators/test_basic.py index d265198c4c4..f2f94afb75b 100644 --- a/qcodes/tests/validators/test_basic.py +++ b/qcodes/tests/validators/test_basic.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from typing import Any import pytest @@ -9,31 +11,52 @@ class BrokenValidator(Validator[Any]): - def __init__(self): + def __init__(self) -> None: pass -def test_broken(): +def test_broken() -> None: # nor can you call validate without overriding it in a subclass b = BrokenValidator() with pytest.raises(NotImplementedError): b.validate(0) -def test_real_anything(): +def test_real_anything() -> None: a = Anything() - for v in [None, 0, 1, 0.0, 1.2, '', 'hi!', [1, 2, 3], [], - {'a': 1, 'b': 2}, {}, {1, 2, 3}, a, range(10), - True, False, float("nan"), float("inf"), b'good', - AClass, AClass(), a_func]: + anything: list[Any] = [ + None, + 0, + 1, + 0.0, + 1.2, + "", + "hi!", + [1, 2, 3], + [], + {"a": 1, "b": 2}, + {}, + {1, 2, 3}, + a, + range(10), + True, + False, + float("nan"), + float("inf"), + b"good", + AClass, + AClass(), + a_func, + ] + for v in anything: a.validate(v) assert repr(a) == '' -def test_failed_anything(): +def test_failed_anything() -> None: with pytest.raises(TypeError): - Anything(1) + Anything(1) # type: ignore[call-arg] with pytest.raises(TypeError): - Anything(values=[1, 2, 3]) + Anything(values=[1, 2, 3]) # type: ignore[call-arg] diff --git a/qcodes/tests/validators/test_bool.py b/qcodes/tests/validators/test_bool.py index 9fbadbc376e..a25690c523d 100644 --- a/qcodes/tests/validators/test_bool.py +++ b/qcodes/tests/validators/test_bool.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import math import numpy as np @@ -7,7 +9,7 @@ from .conftest import AClass, a_func -BOOLS = [True, False, np.bool_(True), np.bool_(False)] +BOOLS: list[bool | np.bool_] = [True, False, np.bool_(True), np.bool_(False)] NOTBOOLS = [0, 1, 10, -1, 100, 1000000, int(-1e15), int(1e15), 0.1, -0.1, 1.0, 3.5, -2.3e6, 5.5e15, 1.34e-10, -2.5e-5, math.pi, math.e, '', None, float("nan"), float("inf"), @@ -15,20 +17,20 @@ AClass, AClass(), a_func] -def test_bool(): +def test_bool() -> None: b = Bool() for v in BOOLS: b.validate(v) - for v in NOTBOOLS: + for vv in NOTBOOLS: with pytest.raises(TypeError): - b.validate(v) + b.validate(vv) # type: ignore[arg-type] assert repr(b) == '' -def test_valid_bool_values(): +def test_valid_bool_values() -> None: val = Bool() for vval in val.valid_values: val.validate(vval) diff --git a/qcodes/tests/validators/test_callable.py b/qcodes/tests/validators/test_callable.py index c5644d6cb23..02a75a59cb6 100644 --- a/qcodes/tests/validators/test_callable.py +++ b/qcodes/tests/validators/test_callable.py @@ -3,19 +3,19 @@ from qcodes.validators import Callable -def test_callable(): +def test_callable() -> None: c = Callable() - def test_func(): + def test_func() -> bool: return True c.validate(test_func) test_int = 5 with pytest.raises(TypeError): - c.validate(test_int) + c.validate(test_int) # type: ignore[arg-type] -def test_valid_values(): +def test_valid_values() -> None: val = Callable() for vval in val.valid_values: val.validate(vval) diff --git a/qcodes/tests/validators/test_complex.py b/qcodes/tests/validators/test_complex.py index 5d4155013be..c8a372e1373 100644 --- a/qcodes/tests/validators/test_complex.py +++ b/qcodes/tests/validators/test_complex.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import hypothesis.strategies as hst import pytest from hypothesis import given @@ -7,7 +9,7 @@ @given(complex_val=hst.complex_numbers()) -def test_complex(complex_val): +def test_complex(complex_val: complex) -> None: n = ComplexNumbers() assert str(n) == '' @@ -19,9 +21,9 @@ def test_complex(complex_val): @given(val=hst.one_of(hst.floats(), hst.integers(), hst.characters())) -def test_complex_raises(val): +def test_complex_raises(val: float | int | str) -> None: n = ComplexNumbers() with pytest.raises(TypeError, match=r"is not complex;"): - n.validate(val) + n.validate(val) # type: ignore[arg-type] diff --git a/qcodes/tests/validators/test_dict.py b/qcodes/tests/validators/test_dict.py index 3de8b4795b6..76746374ba7 100644 --- a/qcodes/tests/validators/test_dict.py +++ b/qcodes/tests/validators/test_dict.py @@ -1,18 +1,22 @@ +from __future__ import annotations + +from typing import Any + import pytest -from qcodes.validators import Dict +import qcodes.validators as vals -def test_dict(): - d = Dict() - my_dict = {} +def test_dict() -> None: + d = vals.Dict() + my_dict: dict[Any, Any] = {} d.validate(my_dict) my_int = 5 with pytest.raises(TypeError): - d.validate(my_int) + d.validate(my_int) # type: ignore[arg-type] -def test_valid_values(): - val = Dict() +def test_valid_values() -> None: + val = vals.Dict() for vval in val.valid_values: val.validate(vval) diff --git a/qcodes/tests/validators/test_enum.py b/qcodes/tests/validators/test_enum.py index 51ae010e227..ca58bf02716 100644 --- a/qcodes/tests/validators/test_enum.py +++ b/qcodes/tests/validators/test_enum.py @@ -1,20 +1,24 @@ +from __future__ import annotations + +from typing import Any + import pytest -from qcodes.validators import Enum +import qcodes.validators as vals -enums = [ +enums: list[list[Any]] = [ [True, False], [1, 2, 3], [True, None, 1, 2.3, 'Hi!', b'free', (1, 2), float("inf")] ] # enum items must be immutable - tuple OK, list bad. -not_enums = [[], [[1, 2], [3, 4]]] +not_enums: list[list[list[int]]] = [[], [[1, 2], [3, 4]]] -def test_good(): +def test_good() -> None: for enum in enums: - e = Enum(*enum) + e = vals.Enum(*enum) for v in enum: e.validate(v) @@ -30,14 +34,14 @@ def test_good(): assert not e.is_numeric -def test_bad(): +def test_bad() -> None: for enum in not_enums: with pytest.raises(TypeError): - Enum(*enum) + vals.Enum(*enum) # type: ignore[arg-type] -def test_valid_values(): +def test_valid_values() -> None: for enum in enums: - e = Enum(*enum) + e = vals.Enum(*enum) for val in e._valid_values: e.validate(val) diff --git a/qcodes/tests/validators/test_ints.py b/qcodes/tests/validators/test_ints.py index 44f98b82739..162533bc586 100644 --- a/qcodes/tests/validators/test_ints.py +++ b/qcodes/tests/validators/test_ints.py @@ -1,4 +1,7 @@ +from __future__ import annotations + import math +from typing import Any, Callable import numpy as np import pytest @@ -7,31 +10,88 @@ from .conftest import AClass, a_func -ints = [0, 1, 10, -1, 100, 1000000, int(-1e15), int(1e15), - # warning: True==1 and False==0 - we *could* prohibit these, using - # isinstance(v, bool) - True, False, - # np scalars - np.int64(3)] -not_ints = [0.1, -0.1, 1.0, 3.5, -2.3e6, 5.5e15, 1.34e-10, -2.5e-5, - math.pi, math.e, '', None, float("nan"), float("inf"), - -float("inf"), '1', [], {}, [1, 2], {1: 1}, b'good', - AClass, AClass(), a_func] - - -def test_unlimited(): +ints: tuple[int | bool | np.int64, ...] = ( + 0, + 1, + 10, + -1, + 100, + 1000000, + int(-1e15), + int(1e15), + # warning: True==1 and False==0 - we *could* prohibit these, using + # isinstance(v, bool) + True, + False, + # np scalars + np.int64(3), +) +not_ints: tuple[ + float, + float, + float, + float, + float, + float, + float, + float, + float, + float, + str, + None, + float, + float, + float, + str, + list[Any], + dict[Any, Any], + list[int], + dict[int, int], + bytes, + type[AClass], + AClass, + Callable, +] = ( + 0.1, + -0.1, + 1.0, + 3.5, + -2.3e6, + 5.5e15, + 1.34e-10, + -2.5e-5, + math.pi, + math.e, + "", + None, + float("nan"), + float("inf"), + -float("inf"), + "1", + [], + {}, + [1, 2], + {1: 1}, + b"good", + AClass, + AClass(), + a_func, +) + + +def test_unlimited() -> None: n = Ints() n.validate(n.valid_values[0]) for v in ints: n.validate(v) - for v in not_ints: + for vv in not_ints: with pytest.raises(TypeError): - n.validate(v) + n.validate(vv) # type: ignore[arg-type] -def test_min(): +def test_min() -> None: for min_val in ints: n = Ints(min_value=min_val) for v in ints: @@ -41,12 +101,15 @@ def test_min(): with pytest.raises(ValueError): n.validate(v) + +def test_min_raises() -> None: + n = Ints(min_value=ints[-1]) for v in not_ints: with pytest.raises(TypeError): - n.validate(v) + n.validate(v) # type: ignore[arg-type] -def test_max(): +def test_max() -> None: for max_val in ints: n = Ints(max_value=max_val) for v in ints: @@ -56,12 +119,15 @@ def test_max(): with pytest.raises(ValueError): n.validate(v) + +def test_max_raises() -> None: + n = Ints(max_value=ints[-1]) for v in not_ints: with pytest.raises(TypeError): - n.validate(v) + n.validate(v) # type: ignore[arg-type] -def test_range(): +def test_range() -> None: n = Ints(0, 10) for v in ints: @@ -71,30 +137,30 @@ def test_range(): with pytest.raises(ValueError): n.validate(v) - for v in not_ints: + for vv in not_ints: with pytest.raises(TypeError): - n.validate(v) + n.validate(vv) # type: ignore[arg-type] assert repr(n) == '' assert n.is_numeric -def test_failed_numbers(): +def test_failed_numbers() -> None: with pytest.raises(TypeError): - Ints(1, 2, 3) + Ints(1, 2, 3) # type: ignore[call-arg] with pytest.raises(TypeError): Ints(1, 1) # min >= max for val in not_ints: with pytest.raises((TypeError, OverflowError)): - Ints(max_value=val) + Ints(max_value=val) # type: ignore[arg-type] with pytest.raises((TypeError, OverflowError)): - Ints(min_value=val) + Ints(min_value=val) # type: ignore[arg-type] -def test_valid_values(): +def test_valid_values() -> None: val = Ints() for vval in val.valid_values: val.validate(vval) diff --git a/qcodes/tests/validators/test_lists.py b/qcodes/tests/validators/test_lists.py index 61fb75ec9c2..a5f93501d3a 100644 --- a/qcodes/tests/validators/test_lists.py +++ b/qcodes/tests/validators/test_lists.py @@ -1,29 +1,32 @@ +from typing import Any, List, Union + +import numpy as np import pytest from qcodes.validators import Ints, Lists -def test_type(): - l = Lists() +def test_type() -> None: + l: Lists[Any] = Lists() v1 = ['a', 'b', 5] l.validate(v1) v2 = 234 with pytest.raises(TypeError): - l.validate(v2) + l.validate(v2) # type: ignore[arg-type] -def test_elt_vals(): +def test_elt_vals() -> None: l = Lists(Ints(max_value=10)) - v1 = [0, 1, 5] + v1: List[Union[int, np.integer, bool]] = [0, 1, 5] l.validate(v1) v2 = [0, 1, 11] with pytest.raises(ValueError): - l.validate(v2) + l.validate(v2) # type: ignore[arg-type] -def test_valid_values(): +def test_valid_values() -> None: val = Lists(Ints(max_value=10)) for vval in val.valid_values: val.validate(vval) diff --git a/qcodes/tests/validators/test_multi_type.py b/qcodes/tests/validators/test_multi_type.py index 480aaa320c8..905ebd3e28d 100644 --- a/qcodes/tests/validators/test_multi_type.py +++ b/qcodes/tests/validators/test_multi_type.py @@ -13,7 +13,7 @@ from .conftest import AClass, a_func -def test_good_or(): +def test_good_or() -> None: # combiner == 'OR' m = MultiType(Strings(2, 4), Ints(10, 1000)) @@ -30,7 +30,7 @@ def test_good_or(): assert not MultiType(Strings(), Enum(1, 2)).is_numeric -def test_good_and(): +def test_good_and() -> None: # combiner == 'AND' m = MultiType( Numbers(min_value=2e-3, max_value=5e4), @@ -42,7 +42,7 @@ def test_good_and(): m.validate(v) # in py True == 1, so valid in this construction - for v in [ + for vv in [ 0, 0.001, 50000.1, @@ -59,7 +59,7 @@ def test_good_and(): False, ]: with pytest.raises(ValueError): - m.validate(v) + m.validate(vv) assert ( repr(m) @@ -69,22 +69,22 @@ def test_good_and(): assert not MultiType(Strings(), Enum(1, 2), combiner="AND").is_numeric -def test_bad(): +def test_bad() -> None: # combiner == 'OR' for args in [[], [1], [Strings(), True]]: with pytest.raises(TypeError): - MultiType(*args) + MultiType(*args) # type: ignore[misc] # combiner == 'OR' for args in [[], [1], [Strings(), True]]: with pytest.raises(TypeError): - MultiType(*args, combiner="OR") + MultiType(*args, combiner="OR") # type: ignore[misc] # combiner == 'AND' for args in [[], [1], [Strings(), True]]: with pytest.raises(TypeError): - MultiType(*args, combiner="AND") + MultiType(*args, combiner="AND") # type: ignore[misc] -def test_valid_values(): +def test_valid_values() -> None: # combiner == 'OR' ms = [MultiType(Strings(2, 4), Ints(10, 32)), MultiType(Ints(), Lists(Ints())), diff --git a/qcodes/tests/validators/test_multi_type_and.py b/qcodes/tests/validators/test_multi_type_and.py index 03fa4caed53..8d323ea1453 100644 --- a/qcodes/tests/validators/test_multi_type_and.py +++ b/qcodes/tests/validators/test_multi_type_and.py @@ -5,7 +5,7 @@ from .conftest import AClass, a_func -def test_good(): +def test_good() -> None: m = MultiTypeAnd( Numbers(min_value=2e-3, max_value=5e4), PermissiveMultiples(divisor=1e-3) ) @@ -14,7 +14,7 @@ def test_good(): m.validate(v) # in py True == 1, so valid in this construction - for v in [ + for vv in [ 0, 0.001, 50000.1, @@ -31,17 +31,17 @@ def test_good(): False, ]: with pytest.raises(ValueError): - m.validate(v) + m.validate(vv) assert ( - repr(m) - == "" + repr(m) == "" ) assert m.is_numeric assert not MultiTypeAnd(Strings(), Enum(1, 2)).is_numeric -def test_bad(): - for args in [[], [1], [Strings(), True]]: +def test_bad() -> None: + for args in ([], [1], [Strings(), True]): with pytest.raises(TypeError): - MultiTypeAnd(*args) + MultiTypeAnd(*args) # type: ignore[misc] diff --git a/qcodes/tests/validators/test_multi_type_or.py b/qcodes/tests/validators/test_multi_type_or.py index 95cc4cbff12..52ac0c1ced7 100644 --- a/qcodes/tests/validators/test_multi_type_or.py +++ b/qcodes/tests/validators/test_multi_type_or.py @@ -5,7 +5,7 @@ from .conftest import AClass, a_func -def test_good(): +def test_good() -> None: m = MultiTypeOr(Strings(2, 4), Ints(10, 1000)) for v in [10, 11, 123, 1000, "aa", "mop", "FRED"]: @@ -35,13 +35,13 @@ def test_good(): assert not MultiTypeOr(Strings(), Enum(1, 2)).is_numeric -def test_bad(): - for args in [[], [1], [Strings(), True]]: +def test_bad() -> None: + for args in ([], [1], [Strings(), True]): with pytest.raises(TypeError): - MultiTypeOr(*args) + MultiTypeOr(*args) # type: ignore[misc] -def test_valid_values(): +def test_valid_values() -> None: ms = [ MultiTypeOr(Strings(2, 4), Ints(10, 32)), MultiTypeOr(Ints(), Lists(Ints())), diff --git a/qcodes/tests/validators/test_multiples.py b/qcodes/tests/validators/test_multiples.py index 97b8a68d607..bf4f66d1647 100644 --- a/qcodes/tests/validators/test_multiples.py +++ b/qcodes/tests/validators/test_multiples.py @@ -1,4 +1,7 @@ +from __future__ import annotations + import math +from typing import Callable, Literal import numpy as np import pytest @@ -7,49 +10,121 @@ from .conftest import AClass, a_func -divisors = [3, 7, 11, 13] -not_divisors = [0, -1, -5, -1e15, 0.1, -0.1, 1.0, 3.5, - -2.3e6, 5.5e15, 1.34e-10, -2.5e-5, - math.pi, math.e, '', None, float("nan"), float("inf"), - -float("inf"), '1', [], {}, [1, 2], {1: 1}, b'good', - AClass, AClass(), a_func] -multiples = [0, 1, 10, -1, 100, 1000000, int(-1e15), int(1e15), - # warning: True==1 and False==0 - we *could* prohibit these, using - # isinstance(v, bool) - True, False, - # numpy scalars - np.int64(2)] -not_multiples = [0.1, -0.1, 1.0, 3.5, -2.3e6, 5.5e15, 1.34e-10, -2.5e-5, - math.pi, math.e, '', None, float("nan"), float("inf"), - -float("inf"), '1', [], {}, [1, 2], {1: 1}, b'good', - AClass, AClass(), a_func] - - -def test_divisors(): +divisors = (3, 7, 11, 13) +not_divisors: tuple[ + int | float | str | None | bytes | list | dict | type[AClass] | AClass | Callable, + ..., +] = ( + 0, + -1, + -5, + -1e15, + 0.1, + -0.1, + 1.0, + 3.5, + -2.3e6, + 5.5e15, + 1.34e-10, + -2.5e-5, + math.pi, + math.e, + "", + None, + float("nan"), + float("inf"), + -float("inf"), + "1", + [], + {}, + [1, 2], + {1: 1}, + b"good", + AClass, + AClass(), + a_func, +) +multiples: list[int | np.integer | bool] = [ + 0, + 1, + 10, + -1, + 100, + 1000000, + int(-1e15), + int(1e15), + # warning: True==1 and False==0 - we *could* prohibit these, using + # isinstance(v, bool) + True, + False, + # numpy scalars + np.int64(2), +] +not_multiples: tuple[ + float + | Literal[""] + | None + | str + | list + | dict + | bytes + | type[AClass] + | AClass + | Callable, + ..., +] = ( + 0.1, + -0.1, + 1.0, + 3.5, + -2.3e6, + 5.5e15, + 1.34e-10, + -2.5e-5, + math.pi, + math.e, + "", + None, + float("nan"), + float("inf"), + -float("inf"), + "1", + [], + {}, + [1, 2], + {1: 1}, + b"good", + AClass, + AClass(), + a_func, +) + + +def test_divisors() -> None: for d in divisors: n = Multiples(divisor=d) for v in [d * e for e in multiples]: n.validate(v) - for v in multiples: - if v == 0: + for vv in multiples: + if vv == 0: continue with pytest.raises(ValueError): - n.validate(v) + n.validate(vv) - for v in not_multiples: + for vvv in not_multiples: with pytest.raises(TypeError): - n.validate(v) + n.validate(vvv) # type:ignore[arg-type] - for d in not_divisors: + for dd in not_divisors: with pytest.raises(TypeError): - n = Multiples(divisor=d) + n = Multiples(divisor=dd) # type:ignore[arg-type] n = Multiples(divisor=3, min_value=1, max_value=10) assert repr(n) == '' -def test_valid_values(): +def test_valid_values() -> None: for d in divisors: n = Multiples(divisor=d) for num in n.valid_values: diff --git a/qcodes/tests/validators/test_numbers.py b/qcodes/tests/validators/test_numbers.py index e4f011cdc20..84cf80750bf 100644 --- a/qcodes/tests/validators/test_numbers.py +++ b/qcodes/tests/validators/test_numbers.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import math from typing import Any, List @@ -8,20 +10,48 @@ from .conftest import AClass, a_func -numbers = [0, 1, -1, 0.1, -0.1, 100, 1000000, 1.0, 3.5, -2.3e6, 5.5e15, - 1.34e-10, -2.5e-5, math.pi, math.e, - # warning: True==1 and False==0 - True, False, - # warning: +/- inf are allowed if max & min are not specified! - -float("inf"), float("inf"), - # numpy scalars - np.int64(36), np.float32(-1.123) - ] -not_numbers: List[Any] = ['', None, '1', [], {}, [1, 2], {1: 1}, - b'good', AClass, AClass(), a_func] - - -def test_unlimited(): +numbers: list[float | bool | np.integer | np.floating] = [ + 0, + 1, + -1, + 0.1, + -0.1, + 100, + 1000000, + 1.0, + 3.5, + -2.3e6, + 5.5e15, + 1.34e-10, + -2.5e-5, + math.pi, + math.e, + # warning: True==1 and False==0 + True, + False, + # warning: +/- inf are allowed if max & min are not specified! + -float("inf"), + float("inf"), + # numpy scalars + np.int64(36), + np.float32(-1.123), +] +not_numbers: list[Any] = [ + "", + None, + "1", + [], + {}, + [1, 2], + {1: 1}, + b"good", + AClass, + AClass(), + a_func, +] + + +def test_unlimited() -> None: n = Numbers() for v in numbers: @@ -38,8 +68,9 @@ def test_unlimited(): n.validate(n.valid_values[0]) -def test_min(): - for min_val in [-1e20, -1, -0.1, 0, 0.1, 10]: +def test_min() -> None: + values: list[float] = [-1e20, -1, -0.1, 0, 0.1, 10] + for min_val in values: n = Numbers(min_value=min_val) n.validate(n.valid_values[0]) @@ -50,6 +81,10 @@ def test_min(): with pytest.raises(ValueError): n.validate(v) + +def test_min_raises() -> None: + n = Numbers(min_value=10) + for v in not_numbers: with pytest.raises(TypeError): n.validate(v) @@ -58,7 +93,7 @@ def test_min(): n.validate(float('nan')) -def test_max(): +def test_max() -> None: for max_val in [-1e20, -1, -0.1, 0, 0.1, 10]: n = Numbers(max_value=max_val) @@ -70,6 +105,10 @@ def test_max(): with pytest.raises(ValueError): n.validate(v) + +def test_max_raises() -> None: + n = Numbers(max_value=10) + for v in not_numbers: with pytest.raises(TypeError): n.validate(v) @@ -78,7 +117,7 @@ def test_max(): n.validate(float('nan')) -def test_range(): +def test_range() -> None: n = Numbers(0.1, 3.5) for v in numbers: @@ -98,9 +137,9 @@ def test_range(): assert repr(n) == '' -def test_failed_numbers(): +def test_failed_numbers() -> None: with pytest.raises(TypeError): - Numbers(1, 2, 3) + Numbers(1, 2, 3) # type: ignore[call-arg] with pytest.raises(TypeError): Numbers(1, 1) # min >= max @@ -113,7 +152,7 @@ def test_failed_numbers(): Numbers(min_value=val) -def test_valid_values(): +def test_valid_values() -> None: val = Numbers() for vval in val.valid_values: val.validate(vval) diff --git a/qcodes/tests/validators/test_permissive_ints.py b/qcodes/tests/validators/test_permissive_ints.py index dd2a0eba55d..d55c9ed3d44 100644 --- a/qcodes/tests/validators/test_permissive_ints.py +++ b/qcodes/tests/validators/test_permissive_ints.py @@ -4,7 +4,7 @@ from qcodes.validators import PermissiveInts -def test_close_to_ints(): +def test_close_to_ints() -> None: validator = PermissiveInts() validator.validate(validator.valid_values[0]) @@ -15,7 +15,7 @@ def test_close_to_ints(): validator.validate(i) -def test_bad_values(): +def test_bad_values() -> None: validator = PermissiveInts(0, 10) validator.validate(validator.valid_values[0]) @@ -30,7 +30,7 @@ def test_bad_values(): validator.validate(i) -def test_valid_values(): +def test_valid_values() -> None: val = PermissiveInts() for vval in val.valid_values: val.validate(vval) diff --git a/qcodes/tests/validators/test_permissive_multiples.py b/qcodes/tests/validators/test_permissive_multiples.py index 232743e1e0b..244bf35a5e9 100644 --- a/qcodes/tests/validators/test_permissive_multiples.py +++ b/qcodes/tests/validators/test_permissive_multiples.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import numpy as np import pytest @@ -5,11 +7,13 @@ divisors = [40e-9, -1, 0.2225, 1 / 3, np.pi / 2] -multiples = [[800e-9, -40e-9, 0, 1], - [3, -4, 0, -1, 1], - [1.5575, -167.9875, 0], - [2 / 3, 3, 1, 0, -5 / 3, 1e4], - [np.pi, 5 * np.pi / 2, 0, -np.pi / 2]] +multiples: list[list[float | np.floating]] = [ + [800e-9, -40e-9, 0, 1], + [3, -4, 0, -1, 1], + [1.5575, -167.9875, 0], + [2 / 3, 3, 1, 0, -5 / 3, 1e4], + [np.pi, 5 * np.pi / 2, 0, -np.pi / 2], +] not_multiples = [[801e-9, 4.002e-5], [1.5, 0.9999999], @@ -18,14 +22,14 @@ [3 * np.pi / 4]] -def test_passing(): - for divind, div in enumerate(divisors): +def test_passing() -> None: + for multiple, div in zip(multiples, divisors): val = PermissiveMultiples(div) - for mult in multiples[divind]: + for mult in multiple: val.validate(mult) -def test_not_passing(): +def test_not_passing() -> None: for divind, div in enumerate(divisors): val = PermissiveMultiples(div) for mult in not_multiples[divind]: @@ -34,7 +38,7 @@ def test_not_passing(): # finally, a quick test that the precision is indeed setable -def test_precision(): +def test_precision() -> None: pm_lax = PermissiveMultiples(35e-9, precision=3e-9) pm_lax.validate(72e-9) pm_strict = PermissiveMultiples(35e-9, precision=1e-10) @@ -42,7 +46,7 @@ def test_precision(): pm_strict.validate(70.2e-9) -def test_valid_values(): +def test_valid_values() -> None: for div in divisors: pm = PermissiveMultiples(div) for val in pm.valid_values: diff --git a/qcodes/tests/validators/test_sequence.py b/qcodes/tests/validators/test_sequence.py index 0aa6d254348..e52dec45d1f 100644 --- a/qcodes/tests/validators/test_sequence.py +++ b/qcodes/tests/validators/test_sequence.py @@ -3,7 +3,7 @@ from qcodes.validators import Ints, Sequence -def test_type(): +def test_type() -> None: l = Sequence() v1 = ['a', 'b', 5] l.validate(v1) @@ -12,10 +12,10 @@ def test_type(): v2 = 234 with pytest.raises(TypeError): - l.validate(v2) + l.validate(v2) # type: ignore[arg-type] -def test_elt_vals(): +def test_elt_vals() -> None: l = Sequence(Ints(max_value=10)) v1 = [0, 1, 5] l.validate(v1) @@ -25,13 +25,13 @@ def test_elt_vals(): l.validate(v2) -def test_valid_values(): +def test_valid_values() -> None: val = Sequence(Ints(max_value=10)) for vval in val.valid_values: val.validate(vval) -def test_length(): +def test_length() -> None: l = Sequence(length=3) v1 = [0, 1, 5] l.validate(v1) @@ -45,7 +45,7 @@ def test_length(): l.validate(v3) -def test_sorted(): +def test_sorted() -> None: l = Sequence(length=3, require_sorted=True) v1 = [0, 1, 5] diff --git a/qcodes/utils/types.py b/qcodes/utils/types.py index bf39784039f..cac86766f16 100644 --- a/qcodes/utils/types.py +++ b/qcodes/utils/types.py @@ -1,10 +1,17 @@ """ Useful collections of types used around QCoDeS """ -from typing import Tuple, Union +from __future__ import annotations + +from typing import Union import numpy as np +complex_type_union = Union[ + np.complex64, np.complex128, np.complex_, np.complexfloating, complex +] + + numpy_concrete_ints = (np.int8, np.int16, np.int32, np.int64, np.uint8, np.uint16, np.uint32, np.uint64) """ @@ -21,7 +28,7 @@ Default integer types. The size may be platform dependent. """ -numpy_ints: Tuple[type, ...] = ( +numpy_ints: tuple[type, ...] = ( numpy_concrete_ints + numpy_c_ints + numpy_non_concrete_ints_instantiable @@ -44,7 +51,7 @@ Default floating point types. The size may be platform dependent. """ -numpy_floats: Tuple[type, ...] = ( +numpy_floats: tuple[type, ...] = ( numpy_concrete_floats + numpy_c_floats + numpy_non_concrete_floats_instantiable @@ -66,7 +73,7 @@ Default complex types. The size may be platform dependent. """ -numpy_complex = ( +numpy_complex: tuple[type[complex_type_union], ...] = ( numpy_concrete_complex + numpy_c_complex + numpy_non_concrete_complex_instantiable @@ -81,8 +88,3 @@ numpy_non_concrete_complex_instantiable + (complex,) ) - -# These are the same types as a above unfortunately there does not seem to be -# a good way to convert a tuple of types to a Union -complex_type_union = Union[np.complex64, np.complex128, - np.complex_, np.complexfloating, complex] diff --git a/qcodes/validators/validators.py b/qcodes/validators/validators.py index 909ea6f39f5..4813af3db36 100644 --- a/qcodes/validators/validators.py +++ b/qcodes/validators/validators.py @@ -2,19 +2,22 @@ Provides validators for different types of values. Validator validates if the value belongs to the given type and is in the provided range. """ -import collections.abc -import math +from __future__ import annotations -# rename on import since this file implements its own classes -# with these names. -from typing import Any -from typing import Callable as TCallable -from typing import Dict as TDict -from typing import Generic, Hashable -from typing import List as TList -from typing import Literal, Optional -from typing import Sequence as TSequence -from typing import Set, Tuple, TypeVar, Union, cast +import math +import typing +from collections import abc +from typing import ( + Any, + Generic, + Hashable, + Literal, + Optional, + Tuple, + TypeVar, + Union, + cast, +) import numpy as np @@ -22,11 +25,11 @@ BIGINT = int(1e18) numbertypes = Union[float, int, np.floating, np.integer] -shape_type = Union[int, TCallable[[], int]] +shape_type = Union[int, typing.Callable[[], int]] shape_tuple_type = Optional[Tuple[shape_type, ...]] -def validate_all(*args: Tuple["Validator[Any]", Any], context: str = "") -> None: +def validate_all(*args: tuple[Validator[Any], Any], context: str = "") -> None: """ Takes a list of (validator, value) couplets and tests whether they are all valid, raising ValueError otherwise. @@ -43,8 +46,8 @@ def validate_all(*args: Tuple["Validator[Any]", Any], context: str = "") -> None def range_str( - min_val: Optional[Union[float, "np.floating[Any]", "np.integer[Any]"]], - max_val: Optional[Union[float, "np.floating[Any]", "np.integer[Any]"]], + min_val: float | np.floating[Any] | np.integer[Any] | None, + max_val: float | np.floating[Any] | np.integer[Any] | None, name: str, ) -> str: """ @@ -102,14 +105,14 @@ class Validator(Generic[T]): implementation of getting valid values. """ - _valid_values: Tuple[T, ...] = () + _valid_values: tuple[T, ...] = () is_numeric = False # is this a numeric type (so it can be swept)? def validate(self, value: T, context: str = "") -> None: raise NotImplementedError @property - def valid_values(self) -> Tuple[T, ...]: + def valid_values(self) -> tuple[T, ...]: return self._valid_values @@ -157,7 +160,7 @@ def reason(self, reason: str) -> None: self._reason = reason -class Bool(Validator[bool]): +class Bool(Validator[Union[bool, np.bool_]]): """ Requires a boolean. """ @@ -165,7 +168,7 @@ class Bool(Validator[bool]): def __init__(self) -> None: self._valid_values = (True, False) - def validate(self, value: bool, context: str = "") -> None: + def validate(self, value: bool | np.bool_, context: str = "") -> None: """ Validates if bool else raises error. @@ -320,7 +323,7 @@ def max_value(self) -> float: return float(self._max_value) -class Ints(Validator[Union[int, "np.integer[Any]"]]): +class Ints(Validator[Union[int, "np.integer[Any]", bool]]): """ Requires an integer. Optional parameters min_value and max_value, enforce @@ -415,7 +418,7 @@ def validate(self, value: numbertypes, context: str = "") -> None: Raises: TypeError: If not an int or close to it. """ - castvalue: Union[int, "np.integer[Any]"] + castvalue: int | np.integer[Any] if isinstance(value, (float, np.floating)): intrepr = int(np.round(value)) remainder = np.abs(value - intrepr) @@ -443,7 +446,7 @@ def __init__(self) -> None: self._valid_values = ((1 + 1j),) def validate( - self, value: Union[complex, "np.complexfloating[Any,Any]"], context: str = "" + self, value: complex | np.complexfloating[Any, Any], context: str = "" ) -> None: """ Validates if complex number else raises error. @@ -475,7 +478,7 @@ class Enum(Validator[Hashable]): TypeError: If no value provided """ - def __init__(self, *values: Optional[Hashable]) -> None: + def __init__(self, *values: Hashable | None) -> None: if not len(values): raise TypeError("Enum needs at least one value") @@ -503,7 +506,7 @@ def __repr__(self) -> str: return f"" @property - def values(self) -> Set[Hashable]: + def values(self) -> set[Hashable]: return self._values.copy() @@ -542,7 +545,7 @@ def __init__(self, divisor: int = 1, **kwargs: Any) -> None: self._divisor = divisor self._valid_values = (divisor,) - def validate(self, value: Union[int, "np.integer[Any]"], context: str = "") -> None: + def validate(self, value: int | np.integer[Any], context: str = "") -> None: """ Validates if the value is a integer multiple of divisor else raises error. @@ -592,7 +595,7 @@ class PermissiveMultiples(Validator[numbertypes]): """ def __init__(self, divisor: numbertypes, precision: float = 1e-9) -> None: - self._mulval: Optional[Multiples] = None + self._mulval: Multiples | None = None self._precision = precision self._numval = Numbers() self.divisor = divisor @@ -710,7 +713,7 @@ def __init__( ) def validate(self, value: Any, context: str = "") -> None: - args: TList[str] = [] + args: list[str] = [] for v in self._validators: try: v.validate(value, context) @@ -735,7 +738,7 @@ def combiner(self) -> Literal["OR", "AND"]: return self._combiner @property - def validators(self) -> Tuple[Validator[Any], ...]: + def validators(self) -> tuple[Validator[Any], ...]: return self._validators @@ -823,10 +826,10 @@ class Arrays(Validator[np.ndarray]): def __init__( self, - min_value: Optional[numbertypes] = None, - max_value: Optional[numbertypes] = None, - shape: Optional[TSequence[shape_type]] = None, - valid_types: Optional[TSequence[type]] = None, + min_value: numbertypes | None = None, + max_value: numbertypes | None = None, + shape: abc.Sequence[shape_type] | None = None, + valid_types: abc.Sequence[type] | None = None, ) -> None: if valid_types is not None: @@ -913,7 +916,7 @@ def __init__( if not valuesok: raise TypeError("max_value must be bigger than min_value") - if not isinstance(shape, collections.abc.Sequence) and shape is not None: + if not isinstance(shape, abc.Sequence) and shape is not None: raise ValueError( f"Shape must be a sequence (List, Tuple ...) " f"got a {type(shape)}" ) @@ -922,7 +925,7 @@ def __init__( self._shape = tuple(shape) @property - def valid_values(self) -> Tuple[np.ndarray]: + def valid_values(self) -> tuple[np.ndarray]: valid_type = self.valid_types[0] if valid_type == np.integer: valid_type = np.int32 @@ -943,7 +946,7 @@ def shape_unevaluated(self) -> shape_tuple_type: return self._shape @property - def shape(self) -> Optional[Tuple[int, ...]]: + def shape(self) -> tuple[int, ...] | None: if self._shape is None: return None shape_array = [] @@ -1014,15 +1017,15 @@ def __repr__(self) -> str: ) @property - def min_value(self) -> Optional[float]: + def min_value(self) -> float | None: return float(self._min_value) if self._min_value is not None else None @property - def max_value(self) -> Optional[float]: + def max_value(self) -> float | None: return float(self._max_value) if self._max_value is not None else None -class Lists(Validator[TList[Any]]): +class Lists(Validator[typing.List[T]]): """ Validator for lists @@ -1030,7 +1033,7 @@ class Lists(Validator[TList[Any]]): elt_validator: Used to validate the individual elements of the list. """ - def __init__(self, elt_validator: Validator[Any] = Anything()) -> None: + def __init__(self, elt_validator: Validator[T] = Anything()) -> None: self._elt_validator = elt_validator self._valid_values = ([vval for vval in elt_validator._valid_values],) @@ -1039,7 +1042,7 @@ def __repr__(self) -> str: msg += self._elt_validator.__repr__() + ">" return msg - def validate(self, value: TList[Anything], context: str = "") -> None: + def validate(self, value: list[T], context: str = "") -> None: """ Validate if list else raises error. @@ -1062,7 +1065,7 @@ def elt_validator(self) -> Validator[Any]: return self._elt_validator -class Sequence(Validator[TSequence[Any]]): +class Sequence(Validator[typing.Sequence[Any]]): """ Validator for Sequences. @@ -1076,7 +1079,7 @@ class Sequence(Validator[TSequence[Any]]): def __init__( self, elt_validator: Validator[Any] = Anything(), - length: Optional[int] = None, + length: int | None = None, require_sorted: bool = False, ) -> None: self._elt_validator = elt_validator @@ -1091,7 +1094,7 @@ def __repr__(self) -> str: msg += self._elt_validator.__repr__() + ">" return msg - def validate(self, value: TSequence[Any], context: str = "") -> None: + def validate(self, value: abc.Sequence[Any], context: str = "") -> None: """ Validates if sequence else raise typeerror. @@ -1103,7 +1106,7 @@ def validate(self, value: TSequence[Any], context: str = "") -> None: TypeError: If not a sequence. ValueError: If not of given length or if not sorted. """ - if not isinstance(value, collections.abc.Sequence): + if not isinstance(value, abc.Sequence): raise TypeError(f"{repr(value)} is not a sequence; {context}") if self._length and not len(value) == self._length: raise ValueError( @@ -1121,7 +1124,7 @@ def elt_validator(self) -> Validator[Any]: return self._elt_validator @property - def length(self) -> Optional[int]: + def length(self) -> int | None: return self._length @property @@ -1129,7 +1132,7 @@ def require_sorted(self) -> bool: return self._require_sorted -class Callable(Validator[TCallable[..., Any]]): +class Callable(Validator[typing.Callable[..., Any]]): """ Validator for callables such as functions. """ @@ -1137,7 +1140,7 @@ class Callable(Validator[TCallable[..., Any]]): def __init__(self) -> None: self._valid_values = (lambda: 0,) - def validate(self, value: TCallable[..., Any], context: str = "") -> None: + def validate(self, value: abc.Callable[..., Any], context: str = "") -> None: """ Validates if callable else raise typeerror. @@ -1155,12 +1158,12 @@ def __repr__(self) -> str: return "" -class Dict(Validator[TDict[Hashable, Any]]): +class Dict(Validator[typing.Dict[Hashable, Any]]): """ Validator for dictionaries. """ - def __init__(self, allowed_keys: Optional[TSequence[Hashable]] = None) -> None: + def __init__(self, allowed_keys: abc.Sequence[Hashable] | None = None) -> None: """ Validator for dictionary keys @@ -1170,7 +1173,7 @@ def __init__(self, allowed_keys: Optional[TSequence[Hashable]] = None) -> None: self._allowed_keys = allowed_keys self._valid_values = ({0: 1},) - def validate(self, value: TDict[Hashable, Any], context: str = "") -> None: + def validate(self, value: dict[Hashable, Any], context: str = "") -> None: """ Validates dictionary keys else raise typeerror. @@ -1201,9 +1204,9 @@ def __repr__(self) -> str: return f"" @property - def allowed_keys(self) -> Optional[TSequence[Hashable]]: + def allowed_keys(self) -> abc.Sequence[Hashable] | None: return self._allowed_keys @allowed_keys.setter - def allowed_keys(self, keys: Optional[TSequence[Hashable]]) -> None: + def allowed_keys(self, keys: abc.Sequence[Hashable] | None) -> None: self._allowed_keys = keys