Skip to content

Commit 95614b8

Browse files
committed
Wip experiment with model for metadata
1 parent 8d7698b commit 95614b8

File tree

4 files changed

+99
-11
lines changed

4 files changed

+99
-11
lines changed

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ dependencies = [
5656
"tornado>=6.3.3",
5757
"ipython>=8.10.0",
5858
"pillow>=9.0.0",
59+
"pydantic>=2.0.0",
5960
]
6061

6162
dynamic = ["version"]

src/qcodes/metadatable/metadatable_base.py

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
from abc import abstractmethod
2-
from typing import TYPE_CHECKING, Any, Optional, final
2+
from typing import TYPE_CHECKING, Any, Generic, Optional, final
3+
4+
from pydantic import BaseModel
5+
from typing_extensions import TypeVar
36

47
from qcodes.utils import deep_update
58

@@ -18,9 +21,29 @@
1821
Snapshot = dict[str, Any]
1922

2023

21-
class Metadatable:
22-
def __init__(self, metadata: Optional["Mapping[str, Any]"] = None):
24+
class EmptyMetaDataModel(BaseModel):
25+
pass
26+
27+
MetaDataSnapShotType = TypeVar("MetaDataSnapShotType", bound=EmptyMetaDataModel)
28+
29+
30+
class EmptyTypedSnapShot(BaseModel):
31+
pass
32+
33+
34+
SnapShotType = TypeVar("SnapShotType", bound=EmptyTypedSnapShot)
35+
36+
37+
class Metadatable(Generic[SnapShotType, MetaDataSnapShotType]):
38+
def __init__(
39+
self,
40+
metadata: Optional["Mapping[str, Any]"] = None,
41+
model: type[SnapShotType] = EmptyTypedSnapShot,
42+
metadata_model: type[MetaDataSnapShotType] = EmptyMetaDataModel,
43+
):
2344
self.metadata: dict[str, Any] = {}
45+
self._model = model or EmptyTypedSnapShot
46+
self._metadata_model = metadata_model or EmptyMetaDataModel
2447
self.load_metadata(metadata or {})
2548

2649
def load_metadata(self, metadata: "Mapping[str, Any]") -> None:
@@ -53,6 +76,16 @@ def snapshot(self, update: Optional[bool] = False) -> Snapshot:
5376

5477
return snap
5578

79+
@final
80+
def typed_snapshot(self) -> SnapShotType:
81+
snapshot_dict = self.snapshot() # probably want to filter metadata here
82+
snapshot = self._model(**snapshot_dict)
83+
return snapshot
84+
85+
@final
86+
def typed_metadata(self) -> MetaDataSnapShotType:
87+
return self._metadata_model(**self.metadata)
88+
5689
def snapshot_base(
5790
self,
5891
update: Optional[bool] = False,
@@ -63,8 +96,20 @@ def snapshot_base(
6396
"""
6497
return {}
6598

99+
# @property
100+
# def metadata_model(self) -> type[BaseModel] | None:
101+
# return self._metadata_model
102+
103+
104+
# @metadata_model.setter
105+
# def metadata_model(self, model: type[BaseModel] | None) -> None:
106+
# self._metadata_model = model
107+
66108

67-
class MetadatableWithName(Metadatable):
109+
class MetadatableWithName(
110+
Metadatable[SnapShotType, MetaDataSnapShotType],
111+
Generic[SnapShotType, MetaDataSnapShotType],
112+
):
68113
"""Add short_name and full_name properties to Metadatable.
69114
This is used as a base class for all components in QCoDeS that
70115
are members of a station to ensure that they have a name and

src/qcodes/parameters/parameter.py

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,14 @@
66
import logging
77
import os
88
from types import MethodType
9-
from typing import TYPE_CHECKING, Any, Callable, Literal
9+
from typing import TYPE_CHECKING, Any, Callable, Generic, Literal
10+
11+
from qcodes.metadatable.metadatable_base import (
12+
EmptyMetaDataModel,
13+
EmptyTypedSnapShot,
14+
MetaDataSnapShotType,
15+
SnapShotType,
16+
)
1017

1118
from .command import Command
1219
from .parameter_base import ParamDataType, ParameterBase, ParamRawDataType
@@ -21,7 +28,10 @@
2128
log = logging.getLogger(__name__)
2229

2330

24-
class Parameter(ParameterBase):
31+
class Parameter(
32+
ParameterBase[SnapShotType, MetaDataSnapShotType],
33+
Generic[SnapShotType, MetaDataSnapShotType],
34+
):
2535
"""
2636
A parameter represents a single degree of freedom. Most often,
2737
this is the standard parameter for Instruments, though it can also be
@@ -179,6 +189,8 @@ def __init__(
179189
docstring: str | None = None,
180190
initial_cache_value: float | str | None = None,
181191
bind_to_instrument: bool = True,
192+
model: type[SnapShotType] = EmptyTypedSnapShot,
193+
metadata_model: type[MetaDataSnapShotType] = EmptyMetaDataModel,
182194
**kwargs: Any,
183195
) -> None:
184196
def _get_manual_parameter(self: Parameter) -> ParamRawDataType:
@@ -240,6 +252,8 @@ def _set_manual_parameter(
240252
vals=vals,
241253
max_val_age=max_val_age,
242254
bind_to_instrument=bind_to_instrument,
255+
model=model,
256+
metadata_model=metadata_model,
243257
**kwargs,
244258
)
245259

@@ -441,6 +455,19 @@ def sweep(
441455
return SweepFixedValues(self, start=start, stop=stop, step=step, num=num)
442456

443457

458+
class ParameterSnapshot(EmptyTypedSnapShot):
459+
# need to handle replacing __class__ with a different name that is compatible
460+
value: Any # use paramdatatype
461+
raw_value: Any # useparamdatatype
462+
ts: str | None
463+
inter_delay: float
464+
name: str
465+
post_delay: float
466+
validators: list[str]
467+
label: str
468+
unit: str
469+
470+
444471
class ManualParameter(Parameter):
445472
def __init__(
446473
self,

src/qcodes/parameters/parameter_base.py

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,18 @@
77
from contextlib import contextmanager
88
from datetime import datetime
99
from functools import cached_property, wraps
10-
from typing import TYPE_CHECKING, Any, ClassVar, overload
11-
12-
from qcodes.metadatable import Metadatable, MetadatableWithName
10+
from typing import TYPE_CHECKING, Any, ClassVar, Generic, overload
11+
12+
from qcodes.metadatable import (
13+
Metadatable,
14+
MetadatableWithName,
15+
)
16+
from qcodes.metadatable.metadatable_base import (
17+
EmptyMetaDataModel,
18+
EmptyTypedSnapShot,
19+
MetaDataSnapShotType,
20+
SnapShotType,
21+
)
1322
from qcodes.utils import DelegateAttributes, full_class, qcodes_abstractmethod
1423
from qcodes.validators import Enum, Ints, Validator
1524

@@ -85,7 +94,10 @@ def invert_val_mapping(val_mapping: Mapping[Any, Any]) -> dict[Any, Any]:
8594
return {v: k for k, v in val_mapping.items()}
8695

8796

88-
class ParameterBase(MetadatableWithName):
97+
class ParameterBase(
98+
MetadatableWithName[SnapShotType, MetaDataSnapShotType],
99+
Generic[SnapShotType, MetaDataSnapShotType],
100+
):
89101
"""
90102
Shared behavior for all parameters. Not intended to be used
91103
directly, normally you should use ``Parameter``, ``ArrayParameter``,
@@ -201,8 +213,11 @@ def __init__(
201213
abstract: bool | None = False,
202214
bind_to_instrument: bool = True,
203215
register_name: str | None = None,
216+
model: type[SnapShotType] = EmptyTypedSnapShot,
217+
metadata_model: type[MetaDataSnapShotType] = EmptyMetaDataModel,
218+
**kwargs: Any,
204219
) -> None:
205-
super().__init__(metadata)
220+
super().__init__(metadata, model=model, metadata_model=metadata_model, **kwargs)
206221
if not str(name).isidentifier():
207222
raise ValueError(
208223
f"Parameter name must be a valid identifier "

0 commit comments

Comments
 (0)