diff --git a/src/controls/device.py b/src/controls/device.py index d126692..fe85384 100644 --- a/src/controls/device.py +++ b/src/controls/device.py @@ -1,12 +1,16 @@ from dataclasses import dataclass from typing import Optional, Collection, Any -from abc import abstractmethod - -class DeviceLifecycleState: - pass # TODO(Homework #3) +from abc import abstractmethod, ABC +import enum -class DevaceError(Exception): +class DeviceLifecycleState(enum.Enum): + INIT = "INIT" + OPEN = "OPEN" + CLOSE = "CLOSE" + + +class DeviceError(Exception): pass @@ -18,6 +22,12 @@ class NonWritableTrait(Exception): pass +class NoSuchTrait(Exception): + pass + +class NoSuchAction(Exception): + pass + @dataclass class TraitDescriptor: name: str @@ -33,8 +43,7 @@ class ActionDescriptor: info: Optional[str] = None -class Device: - # TODO(Homework #3) +class Device(ABC): _state = DeviceLifecycleState.INIT @property @@ -44,9 +53,14 @@ class Device: def close(self): self._state = DeviceLifecycleState.CLOSE + + @property + @abstractmethod def trait_descriptors(self) -> Collection[TraitDescriptor]: pass + @property + @abstractmethod def action_descriptors(self) -> Collection[ActionDescriptor]: pass diff --git a/src/equipment/turtle_device.py b/src/equipment/turtle_device.py index 3ba251f..f433dd8 100644 --- a/src/equipment/turtle_device.py +++ b/src/equipment/turtle_device.py @@ -1,8 +1,85 @@ from turtle import Turtle -from controls.device import SynchronyDevice +from controls.device import ( + SynchronyDevice, + TraitDescriptor, + ActionDescriptor, + NoSuchTrait, + NoSuchAction, +) +from typing import Collection, Optional, Any +import inspect + class TurtleDevice(SynchronyDevice): - pass # TODO(Homework #3) + def __init__(self) -> None: + super().__init__() + self.open() + def open(self): + self.turtle = Turtle() + def close(self): + del self.turtle + def execute(self, action_name: str, *args, **kwargs): + pass + + def read(self, trait_name: str) -> Any: + traits = self.trait_descriptors + for trait in traits: + if trait.name == trait_name: + return self.turtle.__getattribute__(trait_name) + raise NoSuchTrait + + def write(self, trait_name: str, value: Any) -> bool: + traits = self.trait_descriptors + for trait in traits: + if trait.name == trait_name: + attribute_type = type(self.turtle.__getattribute__(trait_name)) + assert ( + type(value) == attribute_type + ), f"Wrong value type. Type should be {attribute_type}, but you pass {type(value)}" + self.turtle.__setattr__(trait_name, value) + return True + raise NoSuchTrait + + def invalidate(self, trait_name: str): + pass + + def __getitem__(self, trait_name: str) -> Optional[Any]: + pass + + @property + def trait_descriptors(self) -> Collection[TraitDescriptor]: + return [ + TraitDescriptor(name=attr, info=getattr(self.turtle, attr).__doc__) + for attr in dir(self.turtle) + if attr[0] != "_" + and attr[-1] != "_" + and not callable(getattr(self.turtle, attr)) + ] + + @property + def action_descriptors(self) -> Collection[ActionDescriptor]: + methods = [ + attr + for attr in dir(self.turtle) + if attr[0] != "_" + and attr[-1] != "_" + and callable(getattr(self.turtle, attr)) + ] + + result = [] + for method_name in methods: + method = getattr(self.turtle, method_name) + args = inspect.signature(method).parameters + arg_types = {} + for arg_name, arg in args.items(): + arg_types[arg_name] = arg.annotation + result.append( + ActionDescriptor( + name=method_name, info=method.__doc__, arguments=arg_types + ) + ) + + return result diff --git a/src/tests/controls/test_device.py b/src/tests/controls/test_device.py index 51249ad..7ae2435 100644 --- a/src/tests/controls/test_device.py +++ b/src/tests/controls/test_device.py @@ -9,4 +9,6 @@ class DeviceLifecycleStateTest(TestCase): pass def test_enum(self): - self.assertEqual(DeviceLifecycleStateTest["INIT"], DeviceLifecycleStateTest.INIT) + # NOTE: это имеется в виду? + # self.assertEqual(DeviceLifecycleStateTest["INIT"], DeviceLifecycleStateTest.INIT) + self.assertEqual(DeviceLifecycleState["INIT"], DeviceLifecycleState.INIT)