Repository navigation
QCoDeS 0.61.0
QCoDeS 0.61.0 (2026-10-08)
Breaking Changes:
- Remove the deprecated TypeVars and type aliases that were retained as runtime
compatibility imports after the conversion to PEP 695 type parameter syntax.
Imports oftyping.TypeVarandtyping.ParamSpecare now banned: use PEP
695 type parameter syntax unless a default value requires the corresponding
typing_extensionstype on Python 3.12. #8543
Improved:
-
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 casesnake_case, e.g.weinschel_8320.py. Vendor and
model capitalization belongs in the class name (Weinschel8320) and not in the module name. #8356 -
Exceptions raised by QCoDeS drivers are now more specific.
The ruff rule
TRY002(raise-vanilla-class) has been enabled. A number of drivers raised a
bareException; these now raise a specific builtin exception instead. Invalid arguments raise
ValueError, using the instrument in an unsupported state raisesRuntimeErrorand selecting an
unimplemented acquisition mode on the AlazarTech ATS raisesNotImplementedError. Code that
catchesExceptionis unaffected since all of these are subclasses ofException.In addition, two catch-all exception handlers have been narrowed to the specific exceptions that the
guarded code can actually raise: reading a screenshot from the Keysight Infiniium now only handles
OSErrorandVisaIOError, and waiting for the Tektronix AWG5014 to become ready only handles
VisaIOError. Both previously swallowed unrelated errors.Finally, the coding style section of the contributor documentation now points at
ruff checkand
ruff format, configured inpyproject.toml, rather than the long removedpycodestyleand
flake8based workflow. #8368 -
Adding metadata that SQLite cannot store in a column now fails with a message naming the tag and its type.
A nested dict or a sequence previously surfaced as
sqlite3.ProgrammingError: Error binding parameter 1: type 'list' is not supported, which said nothing about which tag was at fault.validate_dynamic_column_data
now rejects such values with aTypeErrorsuggesting serialization, alongside its existing checks for invalid
tags andNone. Values that NumPy registers a SQLite adapter for, such as scalars and arrays, are unaffected. #8518 -
Fixed NetCDF export of measurements without setpoint dependencies, including
scalar and array-valued parameters measured withdo0dordond.
The exported data retains its shape using parameter-specific dimensions. #8570 -
A second interrupt during a database write rolls the open transaction back instead of leaving the file locked.
Improved Drivers:
- The Infiniium driver now tests explicitly for the type of parent instrument when required and
usedcastwherepyvisatypes the
return ofread_binary_values/query_binary_valuesas aSequence[float]
regardless of the requestedcontainer. This replaces five type checker
suppressions. #8457
New:
-
Added split raw data storage for the DataSet: an opt-in results
backend selected via thedataset.raw_data_backendconfig option
(set it to"sqlite_per_dataset_db") that writes raw measurement
data into individual per-dataset SQLite files while keeping all metadata
in the main database. This prevents the main DB file from growing
excessively large and allows users to have a single main SQLite QCoDeS
database on their PC as opposed to many that grow infinitely. When
selected, no results table is created in the main database at all - it
holds only metadata - and all publicDataSetAPIs continue to work
unchanged. Backend-specific settings (such as the per-dataset file
folderraw_data_path) live underdataset.raw_data_backend_config.Management helpers included:
update_raw_data_paths(): update stored paths after raw data files
have been moved to a new folder.purge_orphaned_datasets(): find and remove dataset records whose
raw data files no longer exist on disk (dry_run=Trueby default).cleanup_datasets(): remove datasets (DB records and raw data files)
by age, sample name, or file size (dry_run=Trueby default). #8219
-
The method for setting a parameter's cache from a raw value as reported by the
instrument is now public asParameter.cache.set_from_raw_value. It is the
raw-side counterpart ofParameter.cache.setand applies the same
conversions as aget(get_parser,scale,offsetand
val_mapping). This is useful for drivers that populate the caches of many
parameters from a single bulk instrument reply without issuing agetper
parameter. The previous private_set_from_raw_valueis kept as a
backwards-compatible alias. #8484 -
Instruments can now opt in to a logger of their own, rather than sharing a
single logger with every other instrument in the process. Setting the class
attributedefault_logger_scopeto"instrument"makesInstrument.log
(andVisaInstrument.visa_log) use a logger named after the driver class and
the instrument'sname_parts, so that levels, handlers andpropagatecan
be configured per instrument::class MyDriver(VisaInstrument): default_logger_scope = "instrument"The scoped loggers mirror the instrument hierarchy below the module-qualified
driver class. For an instrumentmyinstofMyDriver, defined in the
modulevendor.driver, the loggers are named::vendor.driver.MyDriver the driver class vendor.driver.MyDriver.myinst Instrument.log vendor.driver.MyDriver.myinst.my_module log of a submodule vendor.driver.MyDriver.myinst.com.visa VisaInstrument.visa_logA submodule is named after the key it is added with in
add_submodule, for
examplemy_moduleforself.add_submodule("my_module", MyModule(self)),
and not after its own name or class. A submodule that is not added as an
individual submodule, such as a channel that only exists in a channel list, is
named after its own name.Each logger is a child of the previous one, so a level or handler can be set
for a whole driver class, for everything that belongs to one instrument
(its own messages, its submodules and its VISA traffic), or for a single
submodule or the VISA traffic of one instrument. Setting the level for a whole
driver also works before any of its instruments exist, so it can be configured
vialogger_levelsinqcodesrc.json.Because module-qualified scoped loggers are no longer below
qcodes.instrument.instrument_base, opted-in drivers do not inherit levels
configured on that shared logger or onqcodes.instrument.instrument_base.com.visa.
Configure the module-qualified driver class logger instead.The default is unchanged: without opting in, all instruments keep sharing one
logger exactly as before. #8523
Under the hood:
-
Enable the ruff rule
N999(invalid-module-name). Instrument driver modules that are part of the
publicqcodesnamespace are exempted vialint.pep8-naming.extend-ignore-namesinpyproject.toml
so that no public module names change. Test modules with invalid names have been renamed to lower case. #8356 -
CombinedParameter.parameteris now a small dataclass holding thename,
full_name,labelandunitof the combined parameter, replacing a
lambda that had those attributes attached to it. It remains callable, and
calling it returnsNoneas before. #8364 -
Two additional ruff lint rules have been enabled.
TRY004(type-check-without-type-error) ensures that new code raisesTypeErrorwhen
rejecting a value because of its type. Existing type checks that raiseValueErroror
RuntimeErrorare unchanged and carry an explicitnoqacomment, sinceTypeErroris not a
subclass of either and changing them would break user code that catches the current exception.S110(try-except-pass) flags exception handlers that silently pass. No behaviour changed as a
result: the only remaining silent handlers are in teardown paths that must never raise or log, and
they are now explicitly marked as intentional. #8368 -
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
usingLogger.exceptionrather than only the exception message. The private module level loggers
named_LOGhave been renamed to_LOGGER. #8498 -
The test suite no longer depends on
deepdiff. The tests that used it now compare the data directly
usingInterDependenciesequality andnumpy.testing.assert_array_equal. #8552