diff --git a/.github/workflows/pytest.yaml b/.github/workflows/pytest.yaml index 29ce5c05dce..ae61c95e365 100644 --- a/.github/workflows/pytest.yaml +++ b/.github/workflows/pytest.yaml @@ -59,6 +59,9 @@ jobs: - name: install qcodes run: | pip install .[test] -c requirements.txt + - uses: jakebailey/pyright-action@v1.4.2 + with: + version: 1.1.289 - name: Run Mypy run: mypy -p qcodes - name: Run parallel tests diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 90cd072f1ad..d3bc3d38844 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -306,9 +306,9 @@ Our required checks consists of a number of jobs that performs the following act on Linux and on Windows. - Run our test suite using pytest as described above. -- Perform type checking of the code in QCoDeS using MyPy. For many of the modules we enforce that the code must be +- Perform type checking of the code in QCoDeS using MyPy and Pyright. For many of the modules we enforce that the code must be type annotated. We encourage all contributors to type annotate any contribution to QCoDeS. If you need help with this - please feel free to reach out. + please feel free to reach out. Pyright typechecks can be performed inline within VC-code using the Pylance extension. - Build the documentation using Sphinx with Sphinx warnings as errors. This includes execution of all example notebooks that are not explicitly marked as not to be executed. Please see here_ for information on how to disable execution of a notebook. diff --git a/docs/changes/newsfragments/4938.improved b/docs/changes/newsfragments/4938.improved new file mode 100644 index 00000000000..475f45d57e5 --- /dev/null +++ b/docs/changes/newsfragments/4938.improved @@ -0,0 +1,2 @@ +QcoDeS is now typechecked with Pyright in addition to mypy. This should give a significantly +better user experience when working in VS Code. diff --git a/pyproject.toml b/pyproject.toml index a0600f79934..81a4b40b11a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -181,8 +181,6 @@ module = [ "qcodes.instrument.mockers.ami430", "qcodes.instrument_drivers.Harvard.*", "qcodes.instrument_drivers.Keysight.keysightb1500.message_builder.*", - "qcodes.instrument_drivers.oxford.mercuryiPS", - "qcodes.instrument_drivers.test", "qcodes.loops", "qcodes.math_utils.*", "qcodes.measure", @@ -233,10 +231,8 @@ ignore_missing_imports = true include = ["qcodes"] ignore = [ "qcodes/tests", - "qcodes/instrument_drivers/test.py", "qcodes/instrument_drivers/zurich_instruments", "qcodes/instrument_drivers/Harvard/Decadac.py", - "qcodes/instrument_drivers/oxford/mercuryiPS.py", "qcodes/actions.py", "qcodes/data", "qcodes/loops.py", diff --git a/qcodes/instrument/instrument.py b/qcodes/instrument/instrument.py index a5136b1c127..75504db1d5b 100644 --- a/qcodes/instrument/instrument.py +++ b/qcodes/instrument/instrument.py @@ -5,7 +5,7 @@ import time import weakref from collections.abc import Mapping -from typing import TYPE_CHECKING, Any, Protocol, TypeVar, cast +from typing import TYPE_CHECKING, Any, Protocol, TypeVar, cast, overload from qcodes.utils import strip_attrs from qcodes.validators import Anything @@ -259,6 +259,16 @@ def remove_instance(cls, instance: Instrument) -> None: if ref is instance: del all_ins[name] + @overload + @classmethod + def find_instrument(cls, name: str, instrument_class: None = None) -> Instrument: + ... + + @overload + @classmethod + def find_instrument(cls, name: str, instrument_class: type[T]) -> T: + ... + @classmethod def find_instrument(cls, name: str, instrument_class: type[T] | None = None) -> T: """ diff --git a/qcodes/instrument_drivers/Keithley/_Keithley_2600.py b/qcodes/instrument_drivers/Keithley/_Keithley_2600.py index 409c9a8bd7f..e7a9dde915c 100644 --- a/qcodes/instrument_drivers/Keithley/_Keithley_2600.py +++ b/qcodes/instrument_drivers/Keithley/_Keithley_2600.py @@ -237,14 +237,14 @@ class _ParameterWithStatus(Parameter): def __init__(self, *args: Any, **kwargs: Any): super().__init__(*args, **kwargs) - self._measurement_status: Optional[MeasurementStatus] = None + self._measurement_status: Optional[Keithley2600MeasurementStatus] = None @property - def measurement_status(self) -> Optional[MeasurementStatus]: + def measurement_status(self) -> Optional[Keithley2600MeasurementStatus]: return self._measurement_status @staticmethod - def _parse_response(data: str) -> Tuple[float, MeasurementStatus]: + def _parse_response(data: str) -> Tuple[float, Keithley2600MeasurementStatus]: value, meas_status = data.split("\t") status_bits = [