From 04e6d957049f0856caea94fb6e9e6ba2cb988bdf Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Mon, 22 Jun 2026 08:28:55 +0200 Subject: [PATCH 1/3] Fix deprecation warnings in test suite Replace in-place ndarray shape assignment with np.reshape (deprecated in NumPy 2.5) and pass an explicit dtype to h5py create_dataset to preserve the prior float32 default. --- src/qcodes_loop/data/hdf5_format.py | 4 +++- src/qcodes_loop/measure.py | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/qcodes_loop/data/hdf5_format.py b/src/qcodes_loop/data/hdf5_format.py index 58be4fd3..814da1d3 100644 --- a/src/qcodes_loop/data/hdf5_format.py +++ b/src/qcodes_loop/data/hdf5_format.py @@ -285,7 +285,9 @@ def _create_dataarray_dset(self, array, group): name = array.array_id # Create the hdf5 dataset - dset = group.create_dataset(array.array_id, (0, 1), maxshape=(None, 1)) + dset = group.create_dataset( + array.array_id, (0, 1), maxshape=(None, 1), dtype="f4" + ) dset.attrs["label"] = _encode_to_utf8(str(label)) dset.attrs["name"] = _encode_to_utf8(str(name)) dset.attrs["unit"] = _encode_to_utf8(str(array.unit or "")) diff --git a/src/qcodes_loop/measure.py b/src/qcodes_loop/measure.py index 1c93e808..d9d27cb0 100644 --- a/src/qcodes_loop/measure.py +++ b/src/qcodes_loop/measure.py @@ -2,6 +2,7 @@ from datetime import datetime from typing import Optional +import numpy as np from qcodes.metadatable import Metadatable from qcodes.parameters import Parameter from qcodes.utils import full_class @@ -101,7 +102,9 @@ def run(self, use_threads=False, quiet=False, station=None, **kwargs): # The original return was an array, so take off the extra dim. # (This ensures the outer dim length was 1, otherwise this # will raise a ValueError.) - array.ndarray.shape = array.ndarray.shape[1:] + array.ndarray = np.reshape( + array.ndarray, array.ndarray.shape[1:], copy=False + ) # TODO: DataArray.shape masks ndarray.shape, and a user *could* # change it, thinking they were reshaping the underlying array, From 434bde86c26c4e6ab9159350e4179a454ec43c49 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Mon, 22 Jun 2026 08:31:30 +0200 Subject: [PATCH 2/3] Avoid matplotlib warning when reusing figure window clear() reinitializes the plot with an existing figure number. Passing figsize to plt.subplots for an existing figure is ignored and warns, so apply the size via set_size_inches instead. --- src/qcodes_loop/plots/qcmatplotlib.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/qcodes_loop/plots/qcmatplotlib.py b/src/qcodes_loop/plots/qcmatplotlib.py index 32c40f5b..dff36c4d 100644 --- a/src/qcodes_loop/plots/qcmatplotlib.py +++ b/src/qcodes_loop/plots/qcmatplotlib.py @@ -84,11 +84,21 @@ def __getitem__(self, key): def _init_plot(self, subplots=None, figsize=None, num=None): import matplotlib.pyplot as plt + # When reusing an existing figure window (e.g. via clear()), matplotlib + # ignores figure-level kwargs such as figsize and emits a warning. In + # that case we leave figsize out of the plt.subplots call and apply it + # to the figure directly afterwards. + reuse_figure = num is not None and plt.fignum_exists(num) + if isinstance(subplots, Mapping): if figsize is None: figsize = (6, 4) self.fig, self.subplots = plt.subplots( - figsize=figsize, num=num, squeeze=False, clear=True, **subplots + figsize=None if reuse_figure else figsize, + num=num, + squeeze=False, + clear=True, + **subplots, ) else: # Format subplots as tuple (nrows, ncols) @@ -106,9 +116,16 @@ def _init_plot(self, subplots=None, figsize=None, num=None): figsize = self.default_figsize(subplots) self.fig, self.subplots = plt.subplots( - *subplots, num=num, figsize=figsize, squeeze=False, clear=True + *subplots, + num=num, + figsize=None if reuse_figure else figsize, + squeeze=False, + clear=True, ) + if reuse_figure and figsize is not None: + self.fig.set_size_inches(figsize) + # squeeze=False ensures that subplots is always a 2D array independent # of the number of subplots. # However the qcodes api assumes that subplots is always a 1D array From f7b06868714aa1c38c1a869bb178b61c670af452 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Mon, 22 Jun 2026 08:38:39 +0200 Subject: [PATCH 3/3] Shut down pyqtgraph remote process in QtPlot test teardown The remote process forwards its stdout/stderr via daemon threads. Left running until interpreter exit, they wrote to a stdout pytest had already closed, raising 'ValueError: I/O operation on closed file'. Join the process in tearDownClass while stdout is still valid. --- src/qcodes_loop/tests/test_plots.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/qcodes_loop/tests/test_plots.py b/src/qcodes_loop/tests/test_plots.py index afe2757a..83e86a68 100644 --- a/src/qcodes_loop/tests/test_plots.py +++ b/src/qcodes_loop/tests/test_plots.py @@ -37,6 +37,19 @@ def setUp(self): def tearDown(self): pass + @classmethod + def tearDownClass(cls): + # The remote pyqtgraph process spawns daemon threads that forward the + # child's stdout/stderr to sys.stdout/stderr. If the process is left + # running until interpreter shutdown, those threads can try to write to + # a stdout that pytest has already closed, raising + # "ValueError: I/O operation on closed file". Shut the process down here, + # while stdout is still valid. + if not noQtPlot and QtPlot.proc is not None: + QtPlot.proc.join() + QtPlot.proc = None + QtPlot.rpg = None + def test_creation(self): """ Simple test function which created a QtPlot window