import copy
import logging
import sys
from typing import Dict, Type, TypeVar
from ..._constants import MAX_ENUM_STRING_SIZE
from ..server import PVGroup, pvproperty
logger = logging.getLogger(__name__)
records = {}
T = TypeVar("T", bound="PVGroup")
[docs]def register_record(cls: Type[T]) -> Type[T]:
"""Register a record type to be used with pvproperty mock_record."""
if not issubclass(cls, PVGroup):
raise ValueError(f"Class {cls.__name__} must subclass from PVGroup.")
records[cls._record_type] = cls
logger.debug("Registered record type %r", cls._record_type)
return cls
def get_record_registry() -> Dict[str, Type[PVGroup]]:
"""Get a shallow-copy of the record registry dictionary."""
return dict(records)
def link_parent_attribute(
pvprop: pvproperty,
parent_attr_name: str,
*,
read_only: bool = False,
use_setattr: bool = False,
default=0,
):
"""Take a pvproperty and link its getter/putter to a parent attribute."""
@pvprop.getter
async def getter(self, instance):
return getattr(self.parent, parent_attr_name, default)
if not read_only:
if use_setattr:
@pvprop.putter
async def putter(self, instance, value):
if hasattr(self.parent, parent_attr_name):
setattr(self.parent, parent_attr_name, value)
else:
@pvprop.putter
async def putter(self, instance, value):
kw = {parent_attr_name: value}
await self.parent.write_metadata(**kw)
return pvprop
def link_enum_strings(pvprop: pvproperty, index: int):
"""Take a pvproperty and link its parent enum_strings[index]."""
@pvprop.getter
async def getter(self, instance):
try:
return self.parent.enum_strings[index]
except IndexError:
return ""
@pvprop.putter
async def putter(self, instance, value):
enum_strings = list(self.parent.enum_strings)
if index >= len(enum_strings):
missing_count = index - len(enum_strings) + 1
enum_strings = enum_strings + [""] * missing_count
old_enum = enum_strings[index]
enum_strings[index] = str(value)[: MAX_ENUM_STRING_SIZE - 1]
await self.parent.write_metadata(enum_strings=enum_strings)
if self.parent.value in (old_enum, index):
await self.parent.write(value=index)
return pvprop
[docs]def summarize(file=sys.stdout):
"""Summarize all supported records and their fields to a file."""
from .base import RecordFieldGroup
all_records = [("base", RecordFieldGroup)] + list(records.items())
def to_string(s):
if callable(s):
return "callable/" + s.__name__
return repr(s)
base_fields = {
pvprop.pvspec.name for attr, pvprop in RecordFieldGroup._pvs_.items()
}
for record, rclass in all_records:
info = [record, [cls.__name__ for cls in rclass.mro()]]
print("\t".join(to_string(s) for s in info), file=file)
for attr, pvprop in rclass._pvs_.items():
if record != "base" and pvprop.pvspec.name in base_fields:
continue
kwargs = (
f"{key}={value}".format(key, value)
for key, value in sorted(pvprop.pvspec.cls_kwargs.items())
)
info = [attr] + list(pvprop.pvspec) + list(kwargs)
print("\t".join(to_string(s) for s in info), file=file)
print(file=file)
def copy_pvproperties(locals_dict, *classes):
"""Copy pvproperties from the given classes into the locals() namespace."""
locals_dict.update({
attr: copy.copy(pvprop)
for cls in classes
for attr, pvprop in cls._pvs_.items()
})
__all__ = [
"copy_pvproperties",
"get_record_registry",
"link_enum_strings",
"link_parent_attribute",
"records",
"register_record",
"summarize",
]