Skip to content

Commit

Permalink
added cast and initial_value methods to datatypes
Browse files Browse the repository at this point in the history
  • Loading branch information
evalott100 committed Nov 20, 2024
1 parent b8add05 commit 8a54ad4
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 18 deletions.
14 changes: 8 additions & 6 deletions src/fastcs/attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from enum import Enum
from typing import Any, Generic, Protocol, runtime_checkable

from .datatypes import ATTRIBUTE_TYPES, AttrCallback, DataType, T, validate_value
from .datatypes import ATTRIBUTE_TYPES, AttrCallback, DataType, T


class AttrMode(Enum):
Expand Down Expand Up @@ -126,15 +126,17 @@ def __init__(
allowed_values=allowed_values, # type: ignore
description=description,
)
self._value: T = datatype.dtype() if initial_value is None else initial_value
self._value: T = (
datatype.initial_value if initial_value is None else initial_value
)
self._update_callback: AttrCallback[T] | None = None
self._updater = handler

def get(self) -> T:
return self._value

async def set(self, value: T) -> None:
self._value = self._datatype.dtype(validate_value(self._datatype, value))
self._value = self._datatype.cast(value)

if self._update_callback is not None:
await self._update_callback(self._value)
Expand Down Expand Up @@ -177,11 +179,11 @@ async def process(self, value: T) -> None:

async def process_without_display_update(self, value: T) -> None:
if self._process_callback is not None:
await self._process_callback(self._datatype.dtype(value))
await self._process_callback(self._datatype.cast(value))

async def update_display_without_process(self, value: T) -> None:
if self._write_display_callback is not None:
await self._write_display_callback(self._datatype.dtype(value))
await self._write_display_callback(self._datatype.cast(value))

def set_process_callback(self, callback: AttrCallback[T] | None) -> None:
self._process_callback = callback
Expand Down Expand Up @@ -221,6 +223,6 @@ def __init__(
)

async def process(self, value: T) -> None:
await self.set(validate_value(self._datatype, value))
await self.set(value)

await super().process(value) # type: ignore
37 changes: 25 additions & 12 deletions src/fastcs/datatypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from abc import abstractmethod
from collections.abc import Awaitable, Callable
from dataclasses import dataclass
from typing import Generic, TypeVar
from typing import Any, Generic, TypeVar

T = TypeVar("T", int, float, bool, str)
ATTRIBUTE_TYPES: tuple[type] = T.__constraints__ # type: ignore
Expand All @@ -21,6 +21,18 @@ class DataType(Generic[T]):
def dtype(self) -> type[T]: # Using property due to lack of Generic ClassVars
pass

@abstractmethod
def cast(self, value: T) -> Any:
"""Cast a value to a more primative datatype for `Attribute` push.
Also validate it against fields in the datatype.
"""
pass

Check warning on line 30 in src/fastcs/datatypes.py

View check run for this annotation

Codecov / codecov/patch

src/fastcs/datatypes.py#L30

Added line #L30 was not covered by tests

@property
def initial_value(self) -> T:
return self.dtype()


T_Numerical = TypeVar("T_Numerical", int, float)

Expand All @@ -33,6 +45,13 @@ class _Numerical(DataType[T_Numerical]):
min_alarm: int | None = None
max_alarm: int | None = None

def cast(self, value: T_Numerical) -> T_Numerical:
if self.min is not None and value < self.min:
raise ValueError(f"Value {value} is less than minimum {self.min}")

Check warning on line 50 in src/fastcs/datatypes.py

View check run for this annotation

Codecov / codecov/patch

src/fastcs/datatypes.py#L50

Added line #L50 was not covered by tests
if self.max is not None and value > self.max:
raise ValueError(f"Value {value} is greater than maximum {self.max}")

Check warning on line 52 in src/fastcs/datatypes.py

View check run for this annotation

Codecov / codecov/patch

src/fastcs/datatypes.py#L52

Added line #L52 was not covered by tests
return value


@dataclass(frozen=True)
class Int(_Numerical[int]):
Expand Down Expand Up @@ -65,6 +84,9 @@ class Bool(DataType[bool]):
def dtype(self) -> type[bool]:
return bool

def cast(self, value: bool) -> bool:
return value


@dataclass(frozen=True)
class String(DataType[str]):
Expand All @@ -74,14 +96,5 @@ class String(DataType[str]):
def dtype(self) -> type[str]:
return str


def validate_value(datatype: DataType[T], value: T) -> T:
"""Validate a value against a datatype."""

if isinstance(datatype, (Int | Float)):
assert isinstance(value, (int | float)), f"Value {value} is not a number"
if datatype.min is not None and value < datatype.min:
raise ValueError(f"Value {value} is less than minimum {datatype.min}")
if datatype.max is not None and value > datatype.max:
raise ValueError(f"Value {value} is greater than maximum {datatype.max}")
return value
def cast(self, value: str) -> str:
return value

0 comments on commit 8a54ad4

Please sign in to comment.