Skip to content
This repository has been archived by the owner on Nov 13, 2024. It is now read-only.

Commit

Permalink
Improve analyzing director
Browse files Browse the repository at this point in the history
  • Loading branch information
BenediktBurger committed Mar 20, 2024
1 parent e449cfb commit eac4d14
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 41 deletions.
54 changes: 34 additions & 20 deletions pyleco_extras/directors/analyzing_director.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

from inspect import getmembers
from typing import Generic, Optional, TypeVar, Union

Expand All @@ -12,27 +11,35 @@
Device = TypeVar("Device")


def property_creator(name: str,
original_property: property,
) -> property:
"""Create a property which gets/sets the remote properties value."""
def property_creator(
name: str,
original_property: property,
) -> property:
"""Create a property which gets/sets the remote property's value."""
if original_property.fget is None:
fget = None
else:

def getter(self):
return self.director.get_parameters((name,)).get(name)

fget = getter
if original_property.fset is None:
fset = None
else:

def setter(self, value):
return self.director.set_parameters({name: value})

fset = setter
return property(fget, fset, doc=original_property.__doc__)


def create_device_copy(device_class: type[Device], director: Director, path: str = "",
) -> Device:
def create_device_copy(
device_class: type[Device],
director: Director,
path: str = "",
) -> Device:
"""Create an instance with the same methods and attributes as the given class.
This instance, however, calls methods of the `director` instead.
Expand All @@ -58,15 +65,20 @@ def call_action(self, action: str, *args, **kwargs):
# Channels of pymeasure
elif isinstance(member, Channel.ChannelCreator):
channel_class = member.pairs[0][0]
setattr(DeviceCopy, name, create_device_copy(device_class=channel_class,
director=director,
path=path + name))
setattr(
DeviceCopy,
name,
create_device_copy(device_class=channel_class, director=director, path=path + name),
)
elif isinstance(member, Channel.MultiChannelCreator):
prefix = member.kwargs.get("prefix")
for cls, id in member.pairs:
c_name = f"{prefix}{id}"
setattr(DeviceCopy, c_name, create_device_copy(cls, director=director,
path=path + c_name))
setattr(
DeviceCopy,
c_name,
create_device_copy(cls, director=director, path=path + c_name),
)
else:
# TODO plain attributes (int, str, float, list...) are not found: Combine with
# TransparentDirector?
Expand All @@ -79,14 +91,16 @@ class AnalyzingDirector(Director, Generic[Device]):
"""This Director analyzes an instrument class and behaves like that instrument, but directs
an Actor, which controls such an instrument.
"""

device: Device

def __init__(self,
device_class: type[Device],
actor: Optional[Union[bytes, str]] = None,
communicator: Optional[CommunicatorProtocol] = None,
name: str = "Director",
**kwargs):
def __init__(
self,
device_class: type[Device],
actor: Optional[Union[bytes, str]] = None,
communicator: Optional[CommunicatorProtocol] = None,
name: str = "Director",
**kwargs,
):
super().__init__(actor=actor, communicator=communicator, name=name, **kwargs)
self.device = create_device_copy(device_class=device_class,
director=self)
self.device = create_device_copy(device_class=device_class, director=self)
57 changes: 36 additions & 21 deletions tests/directors/test_analyzing_director.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

from unittest.mock import MagicMock

import pytest
Expand All @@ -7,8 +6,11 @@
from pyleco.core.message import Message, MessageTypes
from pyleco.test import FakeCommunicator

from pyleco_extras.directors.analyzing_director import (create_device_copy, AnalyzingDirector,
RemoteCall)
from pyleco_extras.directors.analyzing_director import (
create_device_copy,
AnalyzingDirector,
RemoteCall,
)


cid = b"conversation_id;"
Expand All @@ -31,7 +33,6 @@ def channel_method(self):


class ExampleInstrument:

channel = Instrument.ChannelCreator(cls=ExampleChannel, id=1)

mchannels = Instrument.MultiChannelCreator(cls=ExampleChannel, id=(2, 3))
Expand Down Expand Up @@ -66,8 +67,7 @@ def __init__(self):

@pytest.fixture
def instrument_cls():
cls = create_device_copy(ExampleInstrument,
director=None) # type: ignore
cls = create_device_copy(ExampleInstrument, director=None) # type: ignore
return cls.__class__


Expand Down Expand Up @@ -114,7 +114,7 @@ def test_setter(self, setter):

def test_set_parameters(self, fake_director: FakeDirector):
fake_director.device.set_property = 5
fake_director.set_parameters.assert_called_once_with({'set_property': 5})
fake_director.set_parameters.assert_called_once_with({"set_property": 5})


class TestController:
Expand Down Expand Up @@ -160,11 +160,11 @@ def test_get_property_of_multiCreator(self, fake_director: FakeDirector):

def test_set_property(self, fake_director: FakeDirector):
fake_director.device.channel.channel_property = 7 # type: ignore
fake_director.set_parameters.assert_called_once_with({'channel.channel_property': 7})
fake_director.set_parameters.assert_called_once_with({"channel.channel_property": 7})

def test_set_property_of_multiCreator(self, fake_director: FakeDirector):
fake_director.device.ch_2.channel_property = 9 # type: ignore
fake_director.set_parameters.assert_called_once_with({'ch_2.channel_property': 9})
fake_director.set_parameters.assert_called_once_with({"ch_2.channel_property": 9})

def test_method(self, fake_director: FakeDirector):
fake_director.device.channel.channel_method(5, kwarg=7) # type: ignore
Expand All @@ -176,22 +176,37 @@ class TestAnalyzingDirectorInit:
def director(self, monkeypatch):
def fake_generate_conversation_id():
return cid
monkeypatch.setattr("pyleco.core.serialization.generate_conversation_id",
fake_generate_conversation_id)
return AnalyzingDirector(device_class=ExampleInstrument,
actor="Actor",
communicator=FakeCommunicator("Director"))

monkeypatch.setattr(
"pyleco.core.serialization.generate_conversation_id", fake_generate_conversation_id
)
return AnalyzingDirector(
device_class=ExampleInstrument, actor="Actor", communicator=FakeCommunicator("Director")
)

def test_device_with_property(self, director: AnalyzingDirector):
director.communicator._r = [ # type: ignore
Message("Director", "Actor", message_type=MessageTypes.JSON, conversation_id=cid, data={
"jsonrpc": "2.0", "id": 1, "result": {'get_property': 5}
})]
Message(
"Director",
"Actor",
message_type=MessageTypes.JSON,
conversation_id=cid,
data={"jsonrpc": "2.0", "id": 1, "result": {"get_property": 5}},
)
]
result = director.device.get_property # type: ignore
assert director.communicator._s == [ # type: ignore
Message("Actor", "Director", message_type=MessageTypes.JSON, conversation_id=cid, data={
"jsonrpc": "2.0", "method": "get_parameters",
"params": {'parameters': ('get_property',)}, "id": 1,
})
Message(
"Actor",
"Director",
message_type=MessageTypes.JSON,
conversation_id=cid,
data={
"jsonrpc": "2.0",
"method": "get_parameters",
"params": {"parameters": ("get_property",)},
"id": 1,
},
)
]
assert result == 5

0 comments on commit eac4d14

Please sign in to comment.