You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Add parameter observers notified of every get and set - #8684
Add qcodes.parameters.add_parameter_observer, remove_parameter_observer and the observe_parameters context manager. An observer is called with a ParameterAccess after every get and set of every parameter, describing the parameter, value, raw value, start time, duration and any exception. A set that ramps in steps is reported once per step. Any number of observers can be registered, independently of on_set_callback and global_on_set_callback.
This makes it possible to record all instrument communication made by a piece of code without changing it or the drivers it uses.
Add qcodes.parameters.add_parameter_observer, remove_parameter_observer and
the observe_parameters context manager. An observer is called with a
ParameterAccess after every get and set of every parameter, describing the
parameter, value, raw value, start time, duration and any exception. A set
that ramps in steps is reported once per step. Any number of observers can be
registered, independently of on_set_callback and global_on_set_callback.
This makes it possible to record all instrument communication made by a piece
of code without changing it or the drivers it uses.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
❌ Patch coverage is 98.60140% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 75.15%. Comparing base (18fcd29) to head (a82e215). ⚠️ Report is 54 commits behind head on main.
Show how to record the parameter accesses of a block of code with
observe_parameters, what a ParameterAccess contains, that ramped sets are
reported per step and failed accesses with their exception, how to see what a
measurement did to the instruments, and how to register an observer for longer
with add_parameter_observer.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
If you're worried about making this too general and abusable, one thing you could do is for now restrict the observer to be a concrete type that just appends to a list. Or maybe that and logging, to avoid the full general callback-like behavior.
Replace the parameter observer API (add_parameter_observer and friends) with
recording that can only store what happened, so that it cannot be used as a
callback, for example to set one parameter in reaction to another:
- record_parameter_accesses() appends each access to a plain list (subclasses
are refused, so append cannot be overridden); log_parameter_accesses() logs
them to the qcodes.parameters.parameter_access logger instead. No user code
runs while a parameter is accessed.
- A ParameterAccess holds data only: the names of the parameter and its root
instrument, the value, raw value, unit, timing and a description of any
error, but not the parameter, instrument or exception.
- Recording is local to the context it was started in (thread or asyncio
task), held in a ContextVar, and can be nested.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
Access-log dispatch can recurse or propagate handler failures into parameter operations, while inherited thread and task contexts allow recording beyond the documented scope and lifetime.
This PR adds parameter-access recording to QCoDeS, allowing callers to inspect parameter activity without modifying measurement code or instrument drivers.
Changes:
Introduces ParameterAccess records and context managers for list recording and logging.
Instruments get/set wrappers to capture values, timing, errors, and individual ramp steps.
Adds automated tests, an example notebook, and a release note.
File
Description
tests/parameter/test_parameter_access.py
Tests recording, logging, failures, nesting, and context behavior.
src/qcodes/parameters/parameter_base.py
Records accesses through get/set wrappers.
src/qcodes/parameters/parameter_access.py
Defines access records and recording/logging context managers.
Address the review of the parameter access recording:
- An access made by a logging handler or filter while another access is
logged is recorded in lists but not logged, so it cannot recurse.
- A sink that raises no longer changes the outcome of the access: the
error is logged to qcodes.parameters.parameter_access_errors, the access
returns or raises its original error, a ramp continues and later sinks
still receive the access. An exception whose str() fails is described
as traceback does.
- Recording ends when its block exits, also for asyncio tasks that
inherited it, and accesses from other threads are never recorded, even
from a copy of the recording context such as asyncio.to_thread.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
Recording can trigger instrument I/O when reading unit metadata, allowing timeouts to turn successful accesses into failures or replace their original exceptions.
A get can obtain and convert a value successfully, then fail validation—for example, a ParameterWithSetpoints returning an array with the wrong shape. This branch always records value=None, discarding the converted reading despite ParameterAccess.value documenting None for failures before a value was obtained. Initialize value before the try block and record its current value so post-conversion failures retain the reading alongside the error.
Address the review of the parameter access recording:
- Drop the unit from ParameterAccess. unit is a property that drivers may
implement by querying the instrument (e.g. the Infiniium DSOTraceParam),
so recording could add communication of its own. Building the record is
now also isolated, so a failure there never affects the access.
- Append an access to every list before logging it, so a logging handler
that accesses a parameter cannot make simultaneous recordings see the
accesses in different orders.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Handler-created tasks can incorrectly suppress parameter-access logging, and recorded set durations omit conversion time required by the documented contract.
No open issues 🎉
2 previously missed issues
In code that hasn't changed since last review
Handler-created tasks remain excluded from parameter logging
src/qcodes/parameters/parameter_access.py:283
A task created by a logging handler or filter inherits _recording=True, so its parameter accesses remain excluded from logging after dispatch returns, even while the logging block is still active. Resetting the token only restores the parent's context; list recording still receives the child's accesses. Use a thread-local reentrancy guard or shared state that is deactivated when dispatch ends, and add a regression test for a handler-created task.
Set duration omits value-conversion time
src/qcodes/parameters/parameter_base.py:1112
The recorded set duration excludes all value conversion: _from_value_to_raw_value() runs before t0, while ParameterAccess.duration explicitly promises to include conversions. This matters for existing drivers such as SR830._set_sensitivity, whose set parser queries another instrument parameter; its query time is omitted from the set's duration, whereas get-parser time is included for gets. Measure conversion time separately and include it in the access duration, keeping the existing delay timer unchanged so inter/post delays remain excluded.
Address the review of the parameter access recording:
- Guard against recursive logging with a thread-local flag instead of a
context variable. A task that a logging handler starts copied the flag
and so was never logged, even after the dispatch had ended.
- Include the time spent converting a set value to its raw value, such as
in a set_parser, in the duration of the access, as for gets. The delay
timers are unchanged, so inter_delay and post_delay stay excluded.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Human assessment is needed because subtle failure-path interactions can lose diagnostic values, omit completed writes, or allow reporting failures to alter get/set outcomes.
Accept any MutableSequence rather than only a plain list, as suggested in
review, for example a deque with a maxlen. The sequence that is passed in
is yielded with its own type, and a new list is still used by default.
A custom sequence's append must only store the access; this is documented,
since recording is not a way to react to an access.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Add qcodes.parameters.add_parameter_observer, remove_parameter_observer and the observe_parameters context manager. An observer is called with a ParameterAccess after every get and set of every parameter, describing the parameter, value, raw value, start time, duration and any exception. A set that ramps in steps is reported once per step. Any number of observers can be registered, independently of on_set_callback and global_on_set_callback.
This makes it possible to record all instrument communication made by a piece of code without changing it or the drivers it uses.