Skip to content

Commit 9ec4587

Browse files
author
James Souter
committed
Reorder generic args of AttributeIO and simplify defaulted TypeVars
1 parent a673575 commit 9ec4587

File tree

8 files changed

+36
-36
lines changed

8 files changed

+36
-36
lines changed

src/fastcs/attribute_io.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55
from fastcs.datatypes import T
66

77

8-
class AttributeIO(Generic[AttributeIORefT, T]):
8+
class AttributeIO(Generic[T, AttributeIORefT]):
99
def __init_subclass__(cls) -> None:
1010
# sets ref_type from subclass generic args
1111
# from python 3.12 we can use types.get_original_bases
1212
args = get_args(cast(Any, cls).__orig_bases__[0])
13-
cls.ref_type = args[0]
13+
cls.ref_type = args[1]
1414

1515
async def update(self, attr: AttrR[T, AttributeIORefT]) -> None:
1616
raise NotImplementedError()
@@ -19,4 +19,4 @@ async def send(self, attr: AttrRW[T, AttributeIORefT], value: T) -> None:
1919
raise NotImplementedError()
2020

2121

22-
AnyAttributeIO = AttributeIO[AttributeIORef, T]
22+
AnyAttributeIO = AttributeIO[T, AttributeIORef]

src/fastcs/attribute_io_ref.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
from dataclasses import dataclass
2-
from typing import TypeVar
2+
3+
from typing_extensions import TypeVar
34

45

56
@dataclass(kw_only=True)
67
class AttributeIORef:
78
update_period: float | None = None
89

910

10-
AttributeIORefT = TypeVar("AttributeIORefT", bound=AttributeIORef)
11+
AttributeIORefT = TypeVar(
12+
"AttributeIORefT", bound=AttributeIORef, default=AttributeIORef
13+
)

src/fastcs/attributes.py

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,16 @@
44
from collections.abc import Callable
55
from typing import Generic
66

7-
from typing_extensions import TypeVar
8-
97
import fastcs
10-
from fastcs.attribute_io_ref import AttributeIORef
118

9+
from .attribute_io_ref import AttributeIORefT
1210
from .datatypes import ATTRIBUTE_TYPES, AttrSetCallback, AttrUpdateCallback, DataType, T
1311

14-
# TODO rename this: typevar with default
15-
AttributeIORefTD = TypeVar(
16-
"AttributeIORefTD", bound=AttributeIORef, default=AttributeIORef, covariant=True
17-
)
18-
1912
ONCE = float("inf")
2013
"""Special value to indicate that an attribute should be updated once on start up."""
2114

2215

23-
class Attribute(Generic[T, AttributeIORefTD]):
16+
class Attribute(Generic[T, AttributeIORefT]):
2417
"""Base FastCS attribute.
2518
2619
Instances of this class added to a ``Controller`` will be used by the backend.
@@ -29,7 +22,7 @@ class Attribute(Generic[T, AttributeIORefTD]):
2922
def __init__(
3023
self,
3124
datatype: DataType[T],
32-
io_ref: AttributeIORefTD | None = None,
25+
io_ref: AttributeIORefT | None = None,
3326
group: str | None = None,
3427
description: str | None = None,
3528
) -> None:
@@ -48,7 +41,7 @@ def __init__(
4841
self._update_datatype_callbacks: list[Callable[[DataType[T]], None]] = []
4942

5043
@property
51-
def io_ref(self) -> AttributeIORefTD:
44+
def io_ref(self) -> AttributeIORefT:
5245
if self._io_ref is None:
5346
raise RuntimeError(f"{self} has no AttributeIORef")
5447
return self._io_ref
@@ -86,13 +79,13 @@ def update_datatype(self, datatype: DataType[T]) -> None:
8679
callback(datatype)
8780

8881

89-
class AttrR(Attribute[T, AttributeIORefTD]):
82+
class AttrR(Attribute[T, AttributeIORefT]):
9083
"""A read-only ``Attribute``."""
9184

9285
def __init__(
9386
self,
9487
datatype: DataType[T],
95-
io_ref: AttributeIORefTD | None = None,
88+
io_ref: AttributeIORefT | None = None,
9689
group: str | None = None,
9790
initial_value: T | None = None,
9891
description: str | None = None,
@@ -133,13 +126,13 @@ async def update(self):
133126
await asyncio.gather(*[cb() for cb in self._on_update_callbacks])
134127

135128

136-
class AttrW(Attribute[T, AttributeIORefTD]):
129+
class AttrW(Attribute[T, AttributeIORefT]):
137130
"""A write-only ``Attribute``."""
138131

139132
def __init__(
140133
self,
141134
datatype: DataType[T],
142-
io_ref: AttributeIORefTD | None = None,
135+
io_ref: AttributeIORefT | None = None,
143136
group: str | None = None,
144137
description: str | None = None,
145138
) -> None:
@@ -180,7 +173,7 @@ def add_write_display_callback(self, callback: AttrSetCallback[T]) -> None:
180173
self._write_display_callbacks.append(callback)
181174

182175

183-
class AttrRW(AttrR[T, AttributeIORefTD], AttrW[T, AttributeIORefTD]):
176+
class AttrRW(AttrR[T, AttributeIORefT], AttrW[T, AttributeIORefT]):
184177
"""A read-write ``Attribute``."""
185178

186179
async def process(self, value: T) -> None:

src/fastcs/controller.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
from typing import get_type_hints
77

88
from fastcs.attribute_io import AttributeIO
9+
from fastcs.attribute_io_ref import AttributeIORefT
910
from fastcs.attributes import Attribute, AttrR, AttrRW, AttrW
11+
from fastcs.datatypes import T
1012

1113

1214
class BaseController:
@@ -21,7 +23,7 @@ def __init__(
2123
self,
2224
path: list[str] | None = None,
2325
description: str | None = None,
24-
ios: Sequence[AttributeIO] | None = None,
26+
ios: Sequence[AttributeIO[T, AttributeIORefT]] | None = None,
2527
) -> None:
2628
if (
2729
description is not None
@@ -192,7 +194,9 @@ class Controller(BaseController):
192194
"""
193195

194196
def __init__(
195-
self, description: str | None = None, ios: Sequence[AttributeIO] | None = None
197+
self,
198+
description: str | None = None,
199+
ios: Sequence[AttributeIO[T, AttributeIORefT]] | None = None,
196200
) -> None:
197201
super().__init__(description=description, ios=ios)
198202

@@ -210,6 +214,8 @@ class SubController(BaseController):
210214
root_attribute: Attribute | None = None
211215

212216
def __init__(
213-
self, description: str | None = None, ios: Sequence[AttributeIO] | None = None
217+
self,
218+
description: str | None = None,
219+
ios: Sequence[AttributeIO[T, AttributeIORefT]] | None = None,
214220
) -> None:
215221
super().__init__(description=description, ios=ios)

src/fastcs/demo/controllers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class TemperatureControllerAttributeIORef(AttributeIORef):
3535

3636

3737
class TemperatureControllerAttributeIO(
38-
AttributeIO[TemperatureControllerAttributeIORef, NumberT]
38+
AttributeIO[NumberT, TemperatureControllerAttributeIORef]
3939
):
4040
def __init__(self, connection: IPConnection, suffix: str):
4141
self._connection = connection

tests/assertable_controller.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ class MyTestAttributeIORef(AttributeIORef):
2323
update_period = 1
2424

2525

26-
class MyTestAttributeIO(AttributeIO[MyTestAttributeIORef, T]):
27-
async def update(self, attr: AttrR[T]):
26+
class MyTestAttributeIO(AttributeIO[T, MyTestAttributeIORef]):
27+
async def update(self, attr: AttrR[T, MyTestAttributeIORef]):
2828
print(f"update {attr}")
2929

30-
async def send(self, attr: AttrW[T], value: T):
30+
async def send(self, attr: AttrW[T, MyTestAttributeIORef], value: T):
3131
print(f"sending {attr} = {value}")
3232

3333

tests/test_attribute.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ async def test_attribute_io():
6969
class MyAttributeIORef(AttributeIORef):
7070
cool: int
7171

72-
class MyAttributeIO(AttributeIO[MyAttributeIORef, int]):
72+
class MyAttributeIO(AttributeIO[int, MyAttributeIORef]):
7373
async def update(self, attr: AttrR[T, MyAttributeIORef]):
7474
print("I am updating", self.ref_type, attr.io_ref.cool)
7575

@@ -123,10 +123,10 @@ class DemoParameterAttributeIORef(AttributeIORef, Generic[NumberT]):
123123
max: NumberT | None = None
124124
read_only: bool = False
125125

126-
class DemoParameterAttributeIO(AttributeIO[DemoParameterAttributeIORef, NumberT]):
126+
class DemoParameterAttributeIO(AttributeIO[NumberT, DemoParameterAttributeIORef]):
127127
async def update(
128128
self,
129-
attr: AttrR[NumberT],
129+
attr: AttrR[NumberT, DemoParameterAttributeIORef],
130130
):
131131
# OK, so this doesn't really work when we have min and maxes...
132132
await attr.set(attr.get() + 1)
@@ -145,14 +145,12 @@ async def send(
145145

146146
if (io_min := attr.io_ref.min) is not None and value < io_min:
147147
raise RuntimeError(
148-
f"Could not set {attr.io_ref.name} to {value}, "
149-
"min is {attr.io_ref.min}"
148+
f"Could not set {attr.io_ref.name} to {value}, min is {io_min}"
150149
)
151150

152151
if (io_max := attr.io_ref.max) is not None and value > io_max:
153152
raise RuntimeError(
154-
f"Could not set {attr.io_ref.name} to {value}, "
155-
f"max is {attr.io_ref.max}"
153+
f"Could not set {attr.io_ref.name} to {value}, max is {io_max}"
156154
)
157155
# TODO: we should always end send with a update_display_without_process...
158156

tests/test_backend.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ class AttributeIORefTimesCalled(AttributeIORef):
101101
update_period: float | None = None
102102
_times_called = 0
103103

104-
class AttributeIOTimesCalled(AttributeIO[AttributeIORefTimesCalled, int]):
104+
class AttributeIOTimesCalled(AttributeIO[int, AttributeIORefTimesCalled]):
105105
async def update(self, attr: AttrR[int, AttributeIORefTimesCalled]):
106106
attr.io_ref._times_called += 1
107107
await attr.set(attr.io_ref._times_called)

0 commit comments

Comments
 (0)