Skip to content

Commit

Permalink
Linting devices.py.
Browse files Browse the repository at this point in the history
  • Loading branch information
mickp committed Jan 13, 2020
1 parent 78366c8 commit 62d7655
Showing 1 changed file with 66 additions and 59 deletions.
125 changes: 66 additions & 59 deletions microscope/devices.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-

## Copyright (C) 2017 David Pinto <[email protected]>
## Copyright (C) 2016 Mick Phillips <[email protected]>
##
## Microscope is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## Microscope is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Microscope. If not, see <http://www.gnu.org/licenses/>.
# Copyright (C) 2017-2020 David Pinto <[email protected]>
# Copyright (C) 2016-2020 Mick Phillips <[email protected]>
#
# Microscope is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Microscope is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Microscope. If not, see <http://www.gnu.org/licenses/>.

"""Classes for control of microscope components.
Expand Down Expand Up @@ -55,9 +55,9 @@
(TRIGGER_AFTER, TRIGGER_BEFORE, TRIGGER_DURATION, TRIGGER_SOFT) = range(4)

# Mapping of setting data types to descriptors allowed-value description types.
# For python 2 and 3 compatibility, we convert the type into a descriptor string.
# This avoids problems with, say a python 2 client recognising a python 3
# <class 'int'> as a python 2 <type 'int'>.
# For python 2 and 3 compatibility, we convert the type into a descriptor
# string. This avoids problems with, say a python 2 client recognising a
# python 3 <class 'int'> as a python 2 <type 'int'>.
DTYPES = {'int': ('int', tuple),
'float': ('float', tuple),
'bool': ('bool', type(None)),
Expand All @@ -70,16 +70,19 @@
str: ('str', int),
tuple: ('tuple', type(None))}

# A utility function to call callables or return value of non-callables.
# noinspection PyPep8
_call_if_callable = lambda f: f() if callable(f) else f

def call_if_callable(f):
"""Call callables, or return value of non-callables."""
return f() if callable(f) else f


class _Setting():
# TODO: refactor into subclasses to avoid if isinstance .. elif .. else.
# Settings classes should be private: devices should use a factory method
# rather than instantiate settings directly; most already use add_setting for this.
def __init__(self, name, dtype, get_func, set_func=None, values=None, readonly=False):
# rather than instantiate settings directly; most already use add_setting
# for this.
def __init__(self, name, dtype, get_func, set_func=None, values=None,
readonly=False):
"""Create a setting.
:param name: the setting's name
Expand All @@ -102,7 +105,8 @@ def __init__(self, name, dtype, get_func, set_func=None, values=None, readonly=F
if dtype not in DTYPES:
raise Exception('Unsupported dtype.')
elif not (isinstance(values, DTYPES[dtype][1:]) or callable(values)):
raise Exception("Invalid values type for %s '%s': expected function or %s" %
raise Exception("Invalid values type for %s '%s':"
"expected function or %s" %
(dtype, name, DTYPES[dtype][1:]))
self.dtype = DTYPES[dtype][0]
self._get = get_func
Expand Down Expand Up @@ -211,11 +215,9 @@ def __init__(self, index=None):
def __del__(self):
self.shutdown()


def get_is_enabled(self):
return self.enabled


def _on_disable(self):
"""Do any device-specific work on disable.
Expand Down Expand Up @@ -268,7 +270,8 @@ def make_safe(self):
"""Put the device into a safe state."""
pass

def add_setting(self, name, dtype, get_func, set_func, values, readonly=False):
def add_setting(self, name, dtype, get_func, set_func, values,
readonly=False):
"""Add a setting definition.
:param name: the setting's name
Expand All @@ -291,7 +294,8 @@ class with getter, setter, etc., and adding Setting instances as
if dtype not in DTYPES:
raise Exception('Unsupported dtype.')
elif not (isinstance(values, DTYPES[dtype][1:]) or callable(values)):
raise Exception("Invalid values type for %s '%s': expected function or %s" %
raise Exception("Invalid values type for %s '%s':"
"expected function or %s" %
(dtype, name, DTYPES[dtype][1:]))
else:
self._settings[name] = _Setting(name, dtype, get_func, set_func,
Expand Down Expand Up @@ -465,7 +469,6 @@ def enable(self):
_logger.debug("... enabled.")
return self.enabled


def disable(self):
"""Disable the data capture device.
Expand Down Expand Up @@ -501,13 +504,14 @@ def _send_data(self, client, data, timestamp):
# this function name as an argument to set_client, but
# not sure how to subsequently resolve this over Pyro.
client.receiveData(data, timestamp)
except (Pyro4.errors.ConnectionClosedError, Pyro4.errors.CommunicationError):
except (Pyro4.errors.ConnectionClosedError,
Pyro4.errors.CommunicationError):
# Client not listening
_logger.info("Removing %s from client stack: disconnected.",
client._pyroUri)
self._clientStack = list(filter(client.__ne__, self._clientStack))
self._liveClients = self._liveClients.difference([client])
except:
except Exception:
raise

def _dispatch_loop(self):
Expand All @@ -525,12 +529,13 @@ def _dispatch_loop(self):
err = e
else:
try:
self._send_data(client, self._process_data(data), timestamp)
self._send_data(client, self._process_data(data),
timestamp)
except Exception as e:
err = e
if err:
# Raising an exception will kill the dispatch loop. We need another
# way to notify the client that there was a problem.
# Raising an exception will kill the dispatch loop. We need
# another way to notify the client that there was a problem.
_logger.error("in _dispatch_loop:", exc_info=err)
self._dispatch_buffer.task_done()

Expand All @@ -543,13 +548,13 @@ def _fetch_loop(self):
data = self._fetch_data()
except Exception as e:
_logger.error("in _fetch_loop:", exc_info=e)
# Raising an exception will kill the fetch loop. We need another
# way to notify the client that there was a problem.
# Raising an exception will kill the fetch loop. We need
# another way to notify the client that there was a problem.
timestamp = time.time()
self._put(e, timestamp)
data = None
if data is not None:
# ***TODO*** Add support for timestamp from hardware.
# TODO Add support for timestamp from hardware.
timestamp = time.time()
self._put(data, timestamp)
else:
Expand Down Expand Up @@ -602,7 +607,6 @@ def set_client(self, new_client):
else:
_logger.info("Current client is %s.", str(self._client))


@keep_acquiring
def update_settings(self, settings, init=False):
"""Update settings, toggling acquisition if necessary."""
Expand Down Expand Up @@ -679,6 +683,7 @@ def __init__(self, **kwargs):
self.get_roi,
self.set_roi,
None)

def _process_data(self, data):
"""Apply self._transform to data."""
flips = (self._transform[0], self._transform[1])
Expand Down Expand Up @@ -708,7 +713,8 @@ def set_transform(self, transform):
if isinstance(transform, str):
transform = literal_eval(transform)
self._client_transform = transform
lr, ud, rot = (self._readout_transform[i] ^ transform[i] for i in range(3))
lr, ud, rot = (self._readout_transform[i] ^ transform[i]
for i in range(3))
if self._readout_transform[2] and self._client_transform[2]:
lr = not lr
ud = not ud
Expand Down Expand Up @@ -758,7 +764,7 @@ def _get_binning(self):
pass

def get_binning(self):
"""Return a tuple of (horizontal, vertical), corrected for transform."""
"""Return a tuple of (horizontal, vertical) corrected for transform."""
binning = self._get_binning()
if self._transform[2]:
# 90 degree rotation
Expand Down Expand Up @@ -808,9 +814,9 @@ def set_roi(self, roi):
maxw, maxh = self.get_sensor_shape()
binning = self.get_binning()
left, top, width, height = roi
if not width: # 0 or None
if not width: # 0 or None
width = maxw // binning.h
if not height: # 0 o rNone
if not height: # 0 o rNone
height = maxh // binning.v
if self._transform[2]:
roi = ROI(left, top, height, width)
Expand Down Expand Up @@ -843,6 +849,7 @@ class TriggerType(Enum):
FALLING_EDGE = 2
PULSE = 3


class TriggerMode(Enum):
ONCE = 1
BULB = 2
Expand All @@ -866,6 +873,7 @@ class TriggerTargetMixIn(metaclass=abc.ABCMeta):
@property
def trigger_mode(self) -> TriggerMode:
return self._trigger_mode

@property
def trigger_type(self) -> TriggerType:
return self._trigger_type
Expand All @@ -889,11 +897,11 @@ class SerialDeviceMixIn(metaclass=abc.ABCMeta):
"""
def __init__(self, **kwargs):
super().__init__(**kwargs)
## TODO: We should probably construct the connection here but
## the Serial constructor takes a lot of arguments, and
## it becomes tricky to separate those from arguments to
## the constructor of other parent classes.
self.connection = None # serial.Serial (to be constructed by child)
# TODO: We should probably construct the connection here but
# the Serial constructor takes a lot of arguments, and
# it becomes tricky to separate those from arguments to
# the constructor of other parent classes.
self.connection = None # serial.Serial (to be constructed by child)
self._comms_lock = threading.RLock()

def _readline(self):
Expand Down Expand Up @@ -962,8 +970,8 @@ def __init__(self, **kwargs) -> None:
"""
super().__init__(**kwargs)

self._patterns = None # type: typing.Optional[numpy.ndarray]
self._pattern_idx = -1 # type: int
self._patterns = None # type: typing.Optional[numpy.ndarray]
self._pattern_idx = -1 # type: int

@property
@abc.abstractmethod
Expand Down Expand Up @@ -1008,7 +1016,7 @@ def queue_patterns(self, patterns: numpy.ndarray) -> None:
"""
self._validate_patterns(patterns)
self._patterns = patterns
self._pattern_idx = -1 # none is applied yet
self._pattern_idx = -1 # none is applied yet

def next_pattern(self) -> None:
"""Apply the next pattern in the queue.
Expand All @@ -1018,7 +1026,7 @@ def next_pattern(self) -> None:
if self._patterns is None:
raise Exception("no pattern queued to apply")
self._pattern_idx += 1
self.apply_pattern(self._patterns[self._pattern_idx,:])
self.apply_pattern(self._patterns[self._pattern_idx, :])

def initialize(self) -> None:
pass
Expand Down Expand Up @@ -1087,29 +1095,28 @@ def set_power_mw(self, mw):


class FilterWheelBase(Device, metaclass=abc.ABCMeta):
def __init__(self, filters: typing.Union[typing.Mapping[int, str], typing.Iterable] = [],
positions: int = 0, **kwargs) -> None:
def __init__(self, filters: typing.Union[typing.Mapping[int, str],
typing.Iterable] = [], positions: int = 0, **kwargs) -> None:
super().__init__(**kwargs)
if isinstance(filters, dict):
self._filters = filters
else:
self._filters = {i:f for (i, f) in enumerate(filters)}
self._filters = {i: f for (i, f) in enumerate(filters)}
self._inv_filters = {val: key for key, val in self._filters.items()}
if not hasattr(self, '_positions'):
self._positions = positions # type: int
self._positions = positions # type: int
# The position as an integer.
# Deprecated: clients should call get_position and set_position;
# still exposed as a setting until cockpit uses set_position.
self.add_setting('position',
'int',
self.get_position,
self.set_position,
lambda: (0, self.get_num_positions()) )

lambda: (0, self.get_num_positions()))

def get_num_positions(self) -> int:
"""Returns the number of wheel positions."""
return(max( self._positions, len(self._filters)))
return(max(self._positions, len(self._filters)))

@abc.abstractmethod
def get_position(self) -> int:
Expand All @@ -1122,7 +1129,7 @@ def set_position(self, position: int) -> None:
pass

def get_filters(self) -> typing.List[typing.Tuple[int, str]]:
return [(k,v) for k,v in self._filters.items()]
return [(k, v) for k, v in self._filters.items()]


class ControllerDevice(Device, metaclass=abc.ABCMeta):
Expand Down

0 comments on commit 62d7655

Please sign in to comment.