Source code for qcodes.metadatable.metadatable_base
from abc import abstractmethod
from typing import TYPE_CHECKING, Any, Literal, final, overload
from typing_extensions import deprecated
from qcodes.utils import deep_update
if TYPE_CHECKING:
from collections.abc import Mapping, Sequence
SnapshotUpdate = Literal["All", "Only_invalid", "Never"]
"""
Canonical string values for the ``update`` argument of ``snapshot`` and
``snapshot_base``:
* ``"All"``: force an update of every value (equivalent to legacy ``True``).
* ``"Only_invalid"``: only update values whose cache is invalid, using the
latest cached value otherwise (equivalent to legacy ``None``).
* ``"Never"``: never update, always use the latest values in memory
(equivalent to legacy ``False``).
Internally, the ``update`` argument is always normalized to one of these
values via :func:`normalize_snapshot_update`. The legacy ``bool``/``None``
values are still accepted at the public interface for backwards compatibility.
"""
[docs]
def normalize_snapshot_update(
update: "bool | SnapshotUpdate | None",
) -> SnapshotUpdate:
"""
Normalize the ``update`` argument of ``snapshot``/``snapshot_base`` into
one of the canonical :data:`SnapshotUpdate` string values.
The legacy values ``True``, ``None`` and ``False`` are mapped to
``"All"``, ``"Only_invalid"`` and ``"Never"`` respectively, and the
canonical string values are returned unchanged. This is the single place
where the ``update`` argument is interpreted; all internal code should
work with the returned :data:`SnapshotUpdate` value rather than with the
legacy ``bool``/``None`` representation.
Args:
update: The ``update`` argument as passed to ``snapshot``/
``snapshot_base``.
Returns:
The equivalent canonical :data:`SnapshotUpdate` value.
Raises:
ValueError: If ``update`` is a string that is not a valid
:data:`SnapshotUpdate` value.
"""
if update is True:
return "All"
if update is False:
return "Never"
if update is None:
return "Only_invalid"
if update in ("All", "Only_invalid", "Never"):
return update
raise ValueError(
f"Invalid value for snapshot ``update``: {update!r}. Expected one of "
f"'All', 'Only_invalid', 'Never', or a bool, or None."
)
# NB: At the moment, the Snapshot type is a bit weak, as the Any
# for the value type doesn't tell us anything about the schema
# followed by snapshots.
# This is needed, however, since snapshots are Dict instances with
# homogeneous keys and heterogeneous values, something that
# recent Python versions largely replace with features like
# typing.NamedTuple and @dataclass.
# As those become more widely available, the weakness of this
# type constraint will become less of an issue.
Snapshot = dict[str, Any]
[docs]
class Metadatable:
def __init__(self, metadata: "Mapping[str, Any] | None" = None):
self.metadata: dict[str, Any] = {}
self.load_metadata(metadata or {})
[docs]
def load_metadata(self, metadata: "Mapping[str, Any]") -> None:
"""
Load metadata into this classes metadata dictionary.
Args:
metadata: Metadata to load.
"""
deep_update(self.metadata, metadata)
@overload
def snapshot(self, update: "SnapshotUpdate" = ...) -> Snapshot: ...
@overload
@deprecated(
"Passing a bool or None as the snapshot ``update`` argument is "
"deprecated; use one of the string values 'All', 'Only_invalid' or "
"'Never' instead."
)
def snapshot(self, update: "bool | None" = ...) -> Snapshot: ...
[docs]
@final
def snapshot(
self, update: "bool | SnapshotUpdate | None" = "Only_invalid"
) -> Snapshot:
"""
Decorate a snapshot dictionary with metadata.
DO NOT override this method if you want metadata in the snapshot
instead, override :meth:`snapshot_base`.
Args:
update: What to do about the values stored in the snapshot; passed
to :meth:`snapshot_base` after being normalized to a
:data:`SnapshotUpdate` value.
* ``"All"``: force an update of every value.
* ``"Only_invalid"`` (the default): only update values whose
cache is invalid, using the latest cached value otherwise.
* ``"Never"``: never update, always use the latest values in
memory.
The legacy ``True`` / ``None`` / ``False`` values are deprecated
aliases for ``"All"`` / ``"Only_invalid"`` / ``"Never"`` and are
still accepted for backwards compatibility (no warning is
raised).
Returns:
Base snapshot.
"""
snap = self.snapshot_base(update=normalize_snapshot_update(update))
if len(self.metadata):
snap["metadata"] = self.metadata
return snap
[docs]
def snapshot_base(
self,
update: "bool | SnapshotUpdate | None" = "Only_invalid",
params_to_skip_update: "Sequence[str] | None" = None,
) -> Snapshot:
"""
Override this with the primary information for a subclass.
"""
return {}
[docs]
class MetadatableWithName(Metadatable):
"""Add short_name and full_name properties to Metadatable.
This is used as a base class for all components in QCoDeS that
are members of a station to ensure that they have a name and
consistent interface."""
@property
@abstractmethod
def short_name(self) -> str:
"""
Name excluding name of any parent that this object is bound to.
"""
@property
@abstractmethod
def full_name(self) -> str:
"""
Name including name of any parent that this object is bound to separated by '_'.
"""