Skip to content

Commit 2a100f9

Browse files
committed
device: pass device constructor arguments as a dict (issue #91)
Pass arguments to construct the device as a dict instead of assuming they are the leftovers kwargs of microscope.device(). This change prevents issues if a device argument name clashes with the device server arguments (cls, host, port, and uid). Also make the uid argument appear after the new conf argument because most devices will need `conf` while uid is specific to floating devices.
1 parent bf2087d commit 2a100f9

File tree

5 files changed

+52
-18
lines changed

5 files changed

+52
-18
lines changed

NEWS

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ python-microscope releases.
44
Version 0.3.0 (yyyy/mm/dd)
55
--------------------------
66

7+
* Backwards incompatible changes:
8+
9+
* `microscope.device()`, function used create a device definition,
10+
changed signature. The arguments to the device constructor must
11+
now be passed as a dict.
12+
713
* Python 3.5 or later is now required. Python 2 is no longer
814
supported. This drops the dependency on the six and enum34.
915

doc/examples.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@ Configurations
6161
from microscope.testsuite.devices import TestFilterWheel
6262
6363
DEVICES = [
64-
device(TestCamera, '127.0.0.1', 8005, otherargs=1,),
64+
device(TestCamera, '127.0.0.1', 8005, {'otherargs' : 1}),
6565
device(TestLaser, '127.0.0.1', 8006),
6666
device(TestFilterWheel, '127.0.0.1', 8007,
67-
filters=[(0, 'GFP', 525), (1, 'RFP'), (2, 'Cy5')]),
67+
{'filters' : [(0, 'GFP', 525), (1, 'RFP'), (2, 'Cy5')]}),
6868
]

microscope/devices.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -156,16 +156,21 @@ def values(self):
156156
return values
157157

158158

159-
def device(cls, host, port, uid=None, **kwargs):
159+
def device(cls, host, port, conf={}, uid=None):
160160
"""Define a device and where to serve it.
161161
162-
A device definition for use in config files.
162+
A device definition for use in deviceserver config files.
163163
164-
Defines a device of type cls, served on host:port.
165-
UID is used to identify 'floating' devices (see below).
166-
kwargs can be used to pass any other parameters to cls.__init__.
164+
Args:
165+
cls (type): type/class of device to serve.
166+
host (str): hostname or ip address serving the device.
167+
port (int): port number used to serve the device.
168+
conf (dict): keyword arguments to construct the device. The
169+
device is effectively constructed with `cls(**conf)`.
170+
uid (str): used to identify "floating" devices (see
171+
documentation for :class:`FloatingDeviceMixin`)
167172
"""
168-
return dict(cls=cls, host=host, port=int(port), uid=uid, **kwargs)
173+
return dict(cls=cls, host=host, port=int(port), uid=uid, conf=conf)
169174

170175

171176
class FloatingDeviceMixin(object):

microscope/deviceserver.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -137,13 +137,7 @@ def run(self):
137137
logger.addFilter(Filter())
138138
logger.debug("Debugging messages on.")
139139

140-
## The device definition includes stuff that were never
141-
## meant for the device. Remove those.
142-
init_kwargs = self._device_def.copy()
143-
for def_key in ['cls', 'host', 'port', 'uid']:
144-
init_kwargs.pop(def_key)
145-
146-
self._device = self._device_def['cls'](**init_kwargs)
140+
self._device = self._device_def['cls'](**self._device_def['conf'])
147141
while not self.exit_event.is_set():
148142
try:
149143
self._device.initialize()
@@ -258,7 +252,7 @@ def term_func(sig, frame):
258252
uid_to_port = None
259253

260254
for dev in devs:
261-
dev['index'] = count
255+
dev['conf']['index'] = count
262256
servers.append(DeviceServer(dev, uid_to_host, uid_to_port,
263257
exit_event=exit_event))
264258
servers[-1].start()

microscope/testsuite/deviceserver_test.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
import unittest
2525
import unittest.mock
2626

27+
import microscope.clients
28+
import microscope.devices
2729
import microscope.deviceserver
2830

2931
from microscope.devices import device
@@ -78,9 +80,9 @@ def tearDown(self):
7880

7981
class TestStarting(BaseTestServeDevices):
8082
DEVICES = [
81-
device(TestCamera, '127.0.0.1', 8001, otherargs=1,),
83+
device(TestCamera, '127.0.0.1', 8001, {'otherargs' : 1}),
8284
device(TestFilterWheel, '127.0.0.1', 8003,
83-
filters=[(0, 'GFP', 525), (1, 'RFP'), (2, 'Cy5')]),
85+
{'filters' : [(0, 'GFP', 525), (1, 'RFP'), (2, 'Cy5')]}),
8486
]
8587

8688
def test_standard(self):
@@ -101,5 +103,32 @@ def test_empty_devices(self):
101103
"not dying for empty list of devices")
102104

103105

106+
class DeviceWithPort(microscope.devices.Device):
107+
def __init__(self, port, *args, **kwargs):
108+
super().__init__(*args, **kwargs)
109+
self._port = port
110+
111+
@property
112+
def port(self):
113+
return self._port
114+
115+
def _on_shutdown(self):
116+
pass
117+
118+
def initialize(self):
119+
pass
120+
121+
122+
class TestClashingArguments(BaseTestServeDevices):
123+
"""Device server and device constructor arguments do not clash"""
124+
DEVICES = [
125+
device(DeviceWithPort, '127.0.0.1', 8000, {'port' : 7000}),
126+
]
127+
def test_port_conflict(self):
128+
time.sleep(2)
129+
client = microscope.clients.Client('PYRO:DeviceWithPort@127.0.0.1:8000')
130+
self.assertEqual(client.port, 7000)
131+
132+
104133
if __name__ == '__main__':
105134
unittest.main()

0 commit comments

Comments
 (0)