Skip to content
Merged
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
19 changes: 19 additions & 0 deletions docs/changes/newsfragments/8354.breaking
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
The default value of the ``update`` argument of ``snapshot``, ``snapshot_base``
and ``print_readable_snapshot`` (on every :class:`.Metadatable`, including
instruments, channels and parameters) is now ``"Only_invalid"``. Previously
calling e.g. ``instrument.snapshot()`` or ``parameter.snapshot()`` without an
argument effectively defaulted to the equivalent of ``"Never"`` and never
refreshed anything. As a result, ``snapshot()`` (and ``print_readable_snapshot()``)
will now call ``get()`` on parameters whose cache is invalid, which may query the
underlying instrument. Pass ``update="Never"`` explicitly to restore the previous
"do not update" behavior, or ``update="All"`` to force a full update.

Relatedly, the station snapshot that :class:`.Measurement` stores before a
measurement starts is now taken with ``update="Only_invalid"`` (previously the
equivalent of ``"Never"``), so parameters with an invalid cache are refreshed via
a single ``get`` while parameters with a valid cache keep their cached value.

The legacy ``True`` / ``None`` / ``False`` values of the ``update`` argument are
now deprecated aliases for ``"All"`` / ``"Only_invalid"`` / ``"Never"``. They keep
working (no runtime warning is raised), but type checkers will flag their use;
prefer the string values instead.
7 changes: 7 additions & 0 deletions docs/changes/newsfragments/8354.improved_driver
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
The QDev QDac driver now honors the ``"Only_invalid"`` snapshot ``update`` mode:
when snapshotting, the bulk status read that refreshes the channel
``v``/``i``/``irange``/``vrange`` caches is only performed for ``update="All"``,
or for ``update="Only_invalid"`` when one of those channel caches is actually
invalid. Previously the bulk read was performed on every snapshot that was not
``"Never"``, which made repeated snapshots unnecessarily expensive even when the
channel caches were already valid.
13 changes: 13 additions & 0 deletions docs/changes/newsfragments/8354.new
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
The ``update`` argument of ``snapshot`` and ``snapshot_base`` (available on every
:class:`.Metadatable`, including instruments, channels and parameters) now accepts
the explicit string values ``"All"``, ``"Only_invalid"`` and ``"Never"``:

* ``"All"`` forces an update of every value (calls ``get()`` on each parameter).
* ``"Only_invalid"`` only refreshes parameters whose cache is invalid, via a
single ``get`` (``cache.get(get_if_invalid=True)``), and uses the cached value
for everything else.
* ``"Never"`` never updates and uses the latest values already in memory.

The new public helper :func:`qcodes.metadatable.normalize_snapshot_update` and the
:data:`qcodes.metadatable.SnapshotUpdate` type are exported for drivers that
override ``snapshot_base`` and need to interpret the ``update`` argument.
30 changes: 29 additions & 1 deletion docs/examples/DataSet/Working with snapshots.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,34 @@
"pprint(snapshot_of_station)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Controlling what gets updated: the `update` argument\n",
"\n",
"`snapshot()` (and `snapshot_base()`) accept an `update` argument that controls whether parameter values are refreshed from the instruments while the snapshot is taken. It takes one of three string values:\n",
"\n",
"- `\"All\"`: force an update of every value by calling `get()` on each parameter (unless the parameter has `snapshot_get=False`).\n",
"- `\"Only_invalid\"` (the default): only call `get()` for parameters whose cache is invalid, and use the latest cached value for everything else. This keeps snapshotting fast while making sure stale values are refreshed.\n",
"- `\"Never\"`: never call `get()`, always use the latest values already in memory.\n",
"\n",
"The legacy boolean/`None` values (`True`/`None`/`False`) are deprecated aliases for `\"All\"`/`\"Only_invalid\"`/`\"Never\"` respectively and should no longer be used.\n",
"\n",
"For example, to force a full update:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"snapshot_of_p = p.snapshot(update=\"All\")\n",
"\n",
"pprint(snapshot_of_p)"
]
},
{
"cell_type": "markdown",
"metadata": {},
Expand All @@ -468,7 +496,7 @@
"\n",
"With the power of the station object, it is now possible to conveniently associate the snapshot information with the measured data.\n",
"\n",
"In order to do so, a station needs to be created, and then that station needs to be provided to the `Measurement` object. If no station is explicitly provided, the `Measurement` object will use the default station, `Station.default` (refer to `Measurement` and `Station` objects docstrings for more information). At the moment the new measurement run is started, a snapshot of the whole station will be taken, and added next to the measured data.\n",
"In order to do so, a station needs to be created, and then that station needs to be provided to the `Measurement` object. If no station is explicitly provided, the `Measurement` object will use the default station, `Station.default` (refer to `Measurement` and `Station` objects docstrings for more information). At the moment the new measurement run is started, a snapshot of the whole station will be taken (with `update=\"Only_invalid\"`, so that parameters with an invalid cache are refreshed while the rest use their cached values), and added next to the measured data.\n",
"\n",
"The measured dataset also automatically snapshots the parameters involved in the measurement and stores it alongside the station snapshot.\n",
"\n",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@
],
"source": [
"# Let's look at all parameters\n",
"vna.print_readable_snapshot(update=True)"
"vna.print_readable_snapshot(update=\"All\")"
]
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@
}
],
"source": [
"dynacool.print_readable_snapshot(update=True)"
"dynacool.print_readable_snapshot(update=\"All\")"
]
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@
}
],
"source": [
"vna.print_readable_snapshot(update=True)"
"vna.print_readable_snapshot(update=\"All\")"
]
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@
}
],
"source": [
"dmm.print_readable_snapshot(update=True)"
"dmm.print_readable_snapshot(update=\"All\")"
]
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@
}
],
"source": [
"mips.print_readable_snapshot(update=True)"
"mips.print_readable_snapshot(update=\"All\")"
]
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@
"outputs": [],
"source": [
"# To get an overview of all instrument settings, print_readable_sanpshot is great\n",
"rto.print_readable_snapshot(update=True)"
"rto.print_readable_snapshot(update=\"All\")"
]
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
}
],
"source": [
"sgsa.print_readable_snapshot(update=True)"
"sgsa.print_readable_snapshot(update=\"All\")"
]
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1622,7 +1622,7 @@
}
],
"source": [
"vna.channels.S11.print_readable_snapshot(update=True)"
"vna.channels.S11.print_readable_snapshot(update=\"All\")"
]
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@
"source": [
"# Let's have a look at the available parameters\n",
"\n",
"awg.print_readable_snapshot(update=True)"
"awg.print_readable_snapshot(update=\"All\")"
]
},
{
Expand Down
6 changes: 3 additions & 3 deletions src/qcodes/dataset/measurements.py
Original file line number Diff line number Diff line change
Expand Up @@ -663,17 +663,17 @@ def __enter__(self) -> DataSaver:
station = self.station

if station is not None:
snapshot = {"station": station.snapshot()}
snapshot = {"station": station.snapshot(update="Only_invalid")}
else:
snapshot = {}
if self._registered_parameters is not None:
parameter_snapshot = {
param.short_name: param.snapshot()
param.short_name: param.snapshot(update="Never")
for param in self._registered_parameters
}
parameter_snapshot.update(
{
param.register_name: param.snapshot()
param.register_name: param.snapshot(update="Never")
for param in self._registered_parameters
}
)
Expand Down
23 changes: 14 additions & 9 deletions src/qcodes/instrument/channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from typing_extensions import TypeVar

from qcodes.metadatable import MetadatableWithName
from qcodes.metadatable import MetadatableWithName, normalize_snapshot_update
from qcodes.parameters import (
ArrayParameter,
MultiChannelInstrumentParameter,
Expand All @@ -23,6 +23,8 @@
if TYPE_CHECKING:
from typing import Unpack

from qcodes.metadatable import SnapshotUpdate

from .instrument_base import InstrumentBaseKWArgs


Expand Down Expand Up @@ -380,7 +382,7 @@ def get_validator(self) -> ChannelTupleValidator:

def snapshot_base(
self,
update: bool | None = True,
update: bool | SnapshotUpdate | None = "Only_invalid",
params_to_skip_update: Sequence[str] | None = None,
) -> dict[Any, Any]:
"""
Expand All @@ -389,13 +391,13 @@ def snapshot_base(
:class:`.NumpyJSONEncoder` supports).

Args:
update: If True, update the state by querying the
instrument. If None only update if the state is known to be
invalid. If False, just use the latest values in memory
and never update.
update: If ``"All"``, update the state by querying the instrument.
If ``"Only_invalid"`` (the default) only update values whose
cache is invalid. If ``"Never"``, just use the latest values in
memory and never update.
params_to_skip_update: List of parameter names that will be skipped
in update even if update is True. This is useful if you have
parameters that are slow to update but can be updated in a
in update even if update is ``"All"``. This is useful if you
have parameters that are slow to update but can be updated in a
different way (as in the qdac). If you want to skip the
update of certain parameters in all snapshots, use the
``snapshot_get`` attribute of those parameters instead.
Expand All @@ -404,6 +406,7 @@ def snapshot_base(
dict: base snapshot

"""
update = normalize_snapshot_update(update)
if self._snapshotable:
snap = {
"channels": {
Expand Down Expand Up @@ -611,7 +614,9 @@ def __dir__(self) -> list[Any]:
return sorted(set(names))

def print_readable_snapshot(
self, update: bool = False, max_chars: int = 80
self,
update: bool | SnapshotUpdate | None = "Only_invalid",
max_chars: int = 80,
) -> None:
if self._snapshotable:
for channel in self._channels:
Expand Down
40 changes: 25 additions & 15 deletions src/qcodes/instrument/instrument_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@
from typing_extensions import TypedDict, TypeVar, deprecated

from qcodes.logger import get_instrument_logger
from qcodes.metadatable import Metadatable, MetadatableWithName
from qcodes.metadatable import (
Metadatable,
MetadatableWithName,
normalize_snapshot_update,
)
from qcodes.parameters import Function, Parameter, ParameterBase
from qcodes.utils import DelegateAttributes, full_class

Expand All @@ -21,6 +25,7 @@

from qcodes.instrument.channel import ChannelTuple, InstrumentModule
from qcodes.logger.instrument_logger import InstrumentLoggerAdapter
from qcodes.metadatable import SnapshotUpdate

from qcodes.utils import QCoDeSDeprecationWarning

Expand Down Expand Up @@ -407,7 +412,7 @@ def _get_component_by_name(

def snapshot_base(
self,
update: bool | None = False,
update: bool | SnapshotUpdate | None = "Only_invalid",
params_to_skip_update: Sequence[str] | None = None,
) -> dict[Any, Any]:
"""
Expand All @@ -417,13 +422,13 @@ def snapshot_base(
supports).

Args:
update: If ``True``, update the state by querying the
instrument. If None update the state if known to be invalid.
If ``False``, just use the latest values in memory and never
update state.
update: If ``"All"``, update the state by querying the instrument.
If ``"Only_invalid"`` (the default) update the state only for
values whose cache is invalid. If ``"Never"``, just use the
latest values in memory and never update state.
params_to_skip_update: List of parameter names that will be skipped
in update even if update is True. This is useful if you have
parameters that are slow to update but can be updated in a
in update even if update is ``"All"``. This is useful if you
have parameters that are slow to update but can be updated in a
different way (as in the qdac). If you want to skip the
update of certain parameters in all snapshots, use the
``snapshot_get`` attribute of those parameters instead.
Expand All @@ -432,6 +437,7 @@ def snapshot_base(
dict: base snapshot

"""
update = normalize_snapshot_update(update)

if params_to_skip_update is None:
params_to_skip_update = []
Expand All @@ -453,7 +459,7 @@ def snapshot_base(
if param.snapshot_exclude:
continue
if params_to_skip_update and name in params_to_skip_update:
update_par: bool | None = False
update_par: SnapshotUpdate = "Never"
else:
update_par = update
try:
Expand All @@ -463,7 +469,7 @@ def snapshot_base(
# at lower level with more info for file based loggers
self.log.warning("Snapshot: Could not update parameter: %s", name)
self.log.info("Details for Snapshot:", exc_info=True)
snap["parameters"][name] = param.snapshot(update=False)
snap["parameters"][name] = param.snapshot(update="Never")

for attr in set(self._meta_attrs):
val = getattr(self, attr, None)
Expand All @@ -476,7 +482,9 @@ def snapshot_base(
return snap

def print_readable_snapshot(
self, update: bool = False, max_chars: int = 80
self,
update: bool | SnapshotUpdate | None = "Only_invalid",
max_chars: int = 80,
) -> None:
"""
Prints a readable version of the snapshot.
Expand All @@ -486,16 +494,18 @@ def print_readable_snapshot(
status of an instrument.

Args:
update: If ``True``, update the state by querying the
instrument. If ``False``, just use the latest values in memory.
This argument gets passed to the snapshot function.
update: What to do about the values in the snapshot. ``"All"``
updates every value by querying the instrument,
``"Only_invalid"`` (the default) only updates values whose
cache is invalid, and ``"Never"`` just uses the latest values
in memory. This argument gets passed to the snapshot function.
max_chars: the maximum number of characters per line. The
readable snapshot will be cropped if this value is exceeded.
Defaults to 80 to be consistent with default terminal width.

"""
floating_types = (float, np.integer, np.floating)
snapshot = self.snapshot(update=update)
snapshot = self.snapshot(update=normalize_snapshot_update(update))

par_lengths = [len(p) for p in snapshot["parameters"]]
# handle the case of no parameters
Expand Down
14 changes: 8 additions & 6 deletions src/qcodes/instrument/ip.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
from types import TracebackType
from typing import Unpack

from qcodes.metadatable import SnapshotUpdate

from .instrument_base import InstrumentBaseKWArgs

log = logging.getLogger(__name__)
Expand Down Expand Up @@ -213,7 +215,7 @@ def ask_raw(self, cmd: str) -> str:

def snapshot_base(
self,
update: bool | None = False,
update: bool | SnapshotUpdate | None = "Only_invalid",
params_to_skip_update: Sequence[str] | None = None,
) -> dict[Any, Any]:
"""
Expand All @@ -223,12 +225,12 @@ def snapshot_base(
supports).

Args:
update: If True, update the state by querying the
instrument. If None only update if the state is known to be
invalid. If False, just use the latest values in memory and
never update.
update: If ``"All"``, update the state by querying the instrument.
If ``"Only_invalid"`` (the default) only update values whose
cache is invalid. If ``"Never"``, just use the latest values in
memory and never update.
params_to_skip_update: List of parameter names that will be
skipped in update even if update is True. This is useful
skipped in update even if update is ``"All"``. This is useful
if you have parameters that are slow to update but can
be updated in a different way (as in the qdac). If you
want to skip the update of certain parameters in all
Expand Down
Loading
Loading