Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions CONTRIBUTING.rst
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,20 @@ Coding Style
- Use `PEP8 <http://legacy.python.org/dev/peps/pep-0008/>`__ style. Not
only is this style good for readability in an absolute sense, but
consistent styling helps us all read each other's code.
- This includes module names: all modules, including instrument driver
modules, should be named using lower case ``snake_case``, e.g.
``weinschel_8320.py`` and not ``Weinschel_8320.py``. Vendor and model
capitalization belongs in the class name (``Weinschel8320``), not in the
module name. This is enforced by the ruff rule ``N999``
(``invalid-module-name``). A number of existing driver modules predate this
rule and are exempted in ``pyproject.toml`` because renaming them would
break user code; new modules should not be added to that exemption list.
- Raise a specific exception rather than a bare ``Exception``, and raise
``TypeError`` when rejecting a value because of its type. These are enforced
by the ruff rules ``TRY002`` and ``TRY004``. Some pre-existing type checks
raise ``ValueError`` or ``RuntimeError`` instead; those carry an explicit
``# noqa: TRY004`` because ``TypeError`` is not a subclass of either, so
changing them would break user code that catches the current exception.
- There is a command-line tool (``pip install pycodestyle``) you can run after
writing code to validate its style.
- A lot of editors have plugins that will check this for you
Expand Down
4 changes: 4 additions & 0 deletions docs/changes/newsfragments/8356.improved
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
The documentation on naming instrument drivers (in the Contributor guide and in the
"Creating Instrument Drivers" example notebook) has been updated to state that instrument driver
modules should be named using lower case ``snake_case``, e.g. ``weinschel_8320.py``. Vendor and
model capitalization belongs in the class name (``Weinschel8320``) and not in the module name.
5 changes: 5 additions & 0 deletions docs/changes/newsfragments/8356.improved.1
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Enable the ruff rule ``TRY002`` (``raise-vanilla-class``). A number of drivers raised a bare
``Exception``; these now raise a specific builtin exception instead. Invalid arguments raise
``ValueError``, using the instrument in an unsupported state raises ``RuntimeError`` and selecting an
unimplemented acquisition mode on the AlazarTech ATS raises ``NotImplementedError``. Code that
catches ``Exception`` is unaffected since all of these are subclasses of ``Exception``.
4 changes: 4 additions & 0 deletions docs/changes/newsfragments/8356.improved.2
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Several catch-all exception handlers have been narrowed to the specific exceptions that the guarded
code can actually raise. Notably reading a screenshot from the Keysight Infiniium, aborting an
acquisition on the SignalHound USB SA124B, waiting for the Tektronix AWG5014 to become ready and
querying pip in :func:`qcodes.utils.is_qcodes_installed_editably` no longer swallow unrelated errors.
3 changes: 3 additions & 0 deletions docs/changes/newsfragments/8356.underthehood
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Enable the ruff rule ``N999`` (``invalid-module-name``). Instrument driver modules that are part of the
public ``qcodes`` namespace are exempted via ``lint.pep8-naming.extend-ignore-names`` in ``pyproject.toml``
so that no public module names change. Test modules with invalid names have been renamed to lower case.
4 changes: 4 additions & 0 deletions docs/changes/newsfragments/8356.underthehood.1
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Enable the ruff rule ``BLE001`` (``blind-except``). Places where QCoDeS intentionally catches a
broad exception are now explicitly marked, and several catch-all handlers now log the full traceback
using ``Logger.exception`` rather than only the exception message. The private module level loggers
named ``_LOG`` have been renamed to ``_LOGGER``.
4 changes: 4 additions & 0 deletions docs/changes/newsfragments/8356.underthehood.2
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Enable the ruff rule ``S110`` (``try-except-pass``). ``Station.add_component`` no longer silently
swallows an error raised while snapshotting a component but logs it including the traceback. The
remaining silent handlers are in teardown paths that must never raise or log and are explicitly
marked as intentional.
5 changes: 5 additions & 0 deletions docs/changes/newsfragments/8356.underthehood.3
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Enable the ruff rule ``TRY004`` (``type-check-without-type-error``) so that new code raises
``TypeError`` when rejecting a value because of its type. Existing type checks that raise
``ValueError`` or ``RuntimeError`` are unchanged and carry an explicit ``noqa`` comment, since
``TypeError`` is not a subclass of either and changing them would break user code that catches the
current exception.
23 changes: 14 additions & 9 deletions docs/examples/writing_drivers/Creating-Instrument-Drivers.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -124,13 +124,13 @@
"The same rules should apply for QCoDeS-contrib-drivers with the exception that all drivers are stored in subfolders of the drivers folder. \n",
"\n",
"### Naming the Instrument class\n",
"A driver for an instrument with model `Model` and from the vendor `Vendor` should be stored in the file:\n",
"A driver for an instrument with model `Model` and from the vendor `Vendor` should be stored in the module:\n",
"\n",
"```\n",
"qcodes\\instrument_drivers\\{Vendor}\\{Vendor}_{Model}.py \n",
"qcodes\\instrument_drivers\\{Vendor}\\{vendor}_{model}.py \n",
"```\n",
"using snake case with an underscore between the vendor and model name but starting the\n",
"vendor name with upper case.\n",
"i.e. the module (file) name should be all lower case snake case with an underscore between the\n",
"vendor name and the model name.\n",
"\n",
"The primary instrument class should be named as follows:\n",
"```\n",
Expand All @@ -139,12 +139,17 @@
"```\n",
"E.g Vendor followed by Model number in CamelCase.\n",
"\n",
"Note that we use vendor names starting with upper case for both folders and file names.\n",
"Note that we use vendor names starting with upper case for folders but all lower case for module\n",
"(file) names. Module names must be valid lower case snake case identifiers; this is enforced by the\n",
"`N999` (`invalid-module-name`) ruff rule. A number of existing driver modules predate this rule and\n",
"are still named using `Vendor_Model` capitalization. Those are exempted via\n",
"`lint.pep8-naming.extend-ignore-names` in `pyproject.toml`, since renaming them would break user\n",
"code, but no new exemptions should be added.\n",
"\n",
"It is also fine to use an acronym for instrument vendors when there are well established. E.g. drivers for `American Magnetics Inc.` instruments\n",
"may use the acronym `AMI` to refer to the vendor.\n",
"\n",
"As an example the driver for the Weinschel 8320 should be stored in the file `qcodes\\instrument_drivers\\Weinschel\\Weinschel_8320.py` and the \n",
"As an example the driver for the Weinschel 8320 should be stored in the module `qcodes\\instrument_drivers\\Weinschel\\weinschel_8320.py` and the \n",
"class named `Weinschel8320` \n",
"\n",
"### Naming InstrumentModule classes\n",
Expand All @@ -161,11 +166,11 @@
"\n",
"As an example have a look at the Keysight 344xxA series of digital multi meters. To implement drivers for such instruments it is preferable\n",
"to implement a private base class such as `_Keysight344xxA`. This class should be stored either in a `private` subfolder of the Vendor folder or\n",
"in a file starting with an underscore i.e. `_Keysight344xxA.py`. If possible, we prefer a format where `x` is used to signal the parts of the model numbers that \n",
"in a module starting with an underscore i.e. `_keysight_344xxa.py`. If possible, we prefer a format where `x` is used to signal the parts of the model numbers that \n",
"may change. Along with this class subclasses for each of the supported models should be implemented. These may either make small modifications to the baseclass as needed \n",
"or be empty subclasses if no modifications are needed. \n",
"\n",
"E.g. subclasses of the Keysight 344xxA driver for the specific model `34410A` should be named as `Keysight34410A` and stored in `Keysight34410A.py`.\n",
"E.g. subclasses of the Keysight 344xxA driver for the specific model `34410A` should be named as `Keysight34410A` and stored in `keysight_34410a.py`.\n",
"\n"
]
},
Expand Down Expand Up @@ -465,7 +470,7 @@
"\n",
" self._handle = self._ATS_dll.AlazarGetBoardBySystemID(system_id, board_id)\n",
" if not self._handle:\n",
" raise Exception(\n",
" raise RuntimeError(\n",
" f\"AlazarTech_ATS not found at system {system_id}, board {board_id}\"\n",
" )\n",
"\n",
Expand Down
166 changes: 163 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -257,8 +257,14 @@ extend-select = [
# PLxxxx are pylint lints that generate a fair amount of warnings
# it may be worth fixing some or these in the future
# PYI036 disable until https://github.com/astral-sh/ruff/issues/9794 is fixed
ignore = ["E501", "G004", "PLR2004", "PLR0913", "PLR0911", "PLR0912", "PLR0915", "PLW0602", "PLW0603", "PLW2901", "PYI036", "PLR0917",
"N999", "BLE001", "TRY004", "TRY002", "S110"] # new defaults of 0.16.0 that we don't want to enable yet
ignore = ["E501", "G004", "PLR2004", "PLR0913", "PLR0911", "PLR0912", "PLR0915", "PLW0602", "PLW0603", "PLW2901", "PYI036", "PLR0917"]

# TRY004 (type-check-without-type-error) is enabled but a number of pre-existing
# type checks raise ValueError or RuntimeError rather than TypeError. Since
# TypeError is not a subclass of those, changing them would break user code that
# catches the current exception, so those call sites carry an explicit
# `# noqa: TRY004` instead. New code should raise TypeError for an invalid type.
# RUF100 will flag the noqa as unused if such a call site is ever changed.

# we want to explicitly use the micro symbol
# not the greek letter
Expand All @@ -272,7 +278,9 @@ known-first-party = ["qcodes"]
[tool.ruff.lint.per-file-ignores]
# TID253 these imports are fine at module level
# in tests and examples
"docs/*" = ["TID253"]
# BLE001 the example notebooks deliberately catch broad exceptions
# to demonstrate that an operation is rejected by QCoDeS
"docs/*" = ["TID253", "BLE001"]
"tests/*" = ["TID253"]

[tool.ruff.lint.flake8-tidy-imports]
Expand All @@ -283,6 +291,158 @@ banned-module-level-imports = [
"xarray", "cf_xarray","pandas", "opencensus", "tqdm.dask", "dask",
"matplotlib", "IPython", "ruamel", "tabulate", "h5netcdf", "PIL", "qcodes_loop"]


[tool.ruff.lint.pep8-naming]
# N999 (invalid-module-name): these instrument driver modules are part of
# the public qcodes namespace so they cannot be renamed to lower case
# without breaking user code. New modules should use lower case names.
extend-ignore-names = [
"_Agilent_344xxA",
"_AimTTi_PL_P",
"_Keithley_2600",
"_Keysight_N5232B",
"_M5065",
"_M5180",
"_M5xxx",
"_TM620",
"Agilent_34401A",
"Agilent_34410A",
"Agilent_34411A",
"Agilent_E8257D",
"Agilent_E8267C",
"Aim_TTi_PL068_P",
"Aim_TTi_PL155_P",
"Aim_TTi_PL303_P",
"Aim_TTi_PL303QMD_P",
"Aim_TTi_PL303QMT_P",
"Aim_TTi_PL601_P",
"Aim_TTi_QL355_TP",
"AMI430_visa",
"ATS",
"ATS_acquisition_controllers",
"ATS9360",
"ATS9373",
"ATS9440",
"ATS9870",
"AWG5014",
"AWG5208",
"AWG70000A",
"AWG70002A",
"AWGFileParser",
"Base_SPDT",
"BaselSP983",
"BaselSP983a",
"BaselSP983c",
"Decadac",
"DP8xx",
"DPO7200xx",
"DynaCool",
"HP_8133A",
"HP_83650A",
"HP_8753D",
"Infiniium",
"Ithaco_1211",
"Keithley_2000",
"Keithley_2400",
"Keithley_2450",
"Keithley_2601B",
"Keithley_2602A",
"Keithley_2602B",
"Keithley_2604B",
"Keithley_2611B",
"Keithley_2612B",
"Keithley_2614B",
"Keithley_2634B",
"Keithley_2635B",
"Keithley_2636B",
"Keithley_3706A",
"Keithley_6500",
"Keithley_7510",
"Keithley_s46",
"Keysight_33210a",
"Keysight_33250a",
"Keysight_33510b",
"Keysight_33511b",
"Keysight_33512b",
"Keysight_33521b",
"Keysight_33522b",
"Keysight_33611a",
"Keysight_33612a",
"Keysight_33621a",
"Keysight_33622a",
"Keysight_34410A_submodules",
"Keysight_34411A_submodules",
"Keysight_34460A_submodules",
"Keysight_34461A_submodules",
"Keysight_34465A_submodules",
"Keysight_34470A_submodules",
"Keysight_344xxA_submodules",
"Keysight_B2962A",
"Keysight_N5173B",
"Keysight_N5183B",
"Keysight_N5222B",
"Keysight_N5230C",
"Keysight_N5245A",
"Keysight_N6705B",
"Keysight_N9030B",
"Keysight_P5002B",
"Keysight_P5004B",
"Keysight_P9374A",
"KeysightAgilent_33XXX",
"KeysightB1500_base",
"KeysightB1500_module",
"KeysightB1500_sampling_measurement",
"KeysightB1511B",
"KeysightB1517A",
"KeysightB1520A",
"KeysightB1530A",
"KtM960x",
"KtM960xDefs",
"KtMAwg",
"KtMAwgDefs",
"Lakeshore_model_325",
"Lakeshore_model_336",
"Lakeshore_model_372",
"MercuryiPS_VISA",
"N51x1",
"N52xx",
"QDac_channels",
"Rigol_DG1062",
"Rigol_DG4000",
"Rigol_DP821",
"Rigol_DP831",
"Rigol_DP832",
"Rigol_DS1074Z",
"Rigol_DS4000",
"Rohde_Schwarz_ZNB20",
"Rohde_Schwarz_ZNB8",
"RTO1000",
"SG384",
"SGS100A",
"SignalHound_USB_SA124B",
"SR560",
"SR830",
"SR860",
"SR865",
"SR865A",
"SR86x",
"Tektronix_70001A",
"Tektronix_70001B",
"Tektronix_70002B",
"Tektronix_DPO5000",
"Tektronix_DPO7000",
"Tektronix_DPO70000",
"Tektronix_DSA70000",
"Tektronix_MSO5000",
"Tektronix_MSO70000",
"TPS2012",
"USBHIDMixin",
"Weinschel_8320",
"Yokogawa_GS200",
"ZNB",
"ZNB20",
]

[tool.ruff.lint.pydocstyle]
convention = "google"

Expand Down
6 changes: 3 additions & 3 deletions src/qcodes/dataset/data_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -1452,11 +1452,11 @@ def _flush_data_to_database(self, block: bool = False) -> None:
else:
log.debug("Successfully wrote result to disk")
self._results = []
except Exception as e:
except Exception:
if writer_status.write_in_background:
log.warning(f"Could not enqueue result; {e}")
log.exception("Could not enqueue result")
else:
log.warning(f"Could not commit to database; {e}")
log.exception("Could not commit to database")
else:
log.debug("No results to flush")

Expand Down
Loading
Loading