diff --git a/secop/errors.py b/secop/errors.py index 368ad0f..c50cbac 100644 --- a/secop/errors.py +++ b/secop/errors.py @@ -25,7 +25,7 @@ class SECoPError(RuntimeError): def __init__(self, *args, **kwds): - RuntimeError.__init__(self) + super().__init__() self.args = args for k, v in list(kwds.items()): setattr(self, k, v) @@ -151,7 +151,8 @@ EXCEPTIONS = dict( IsError=IsErrorError, Disabled=DisabledError, SyntaxError=ProtocolError, - NotImplementedError=NotImplementedError, + NotImplemented=NotImplementedError, + ProtocolError=ProtocolError, InternalError=InternalError, # internal short versions (candidates for spec) Protocol=ProtocolError, diff --git a/secop/gui/cfg_editor/mainwindow.py b/secop/gui/cfg_editor/mainwindow.py index 099686c..937b4f5 100644 --- a/secop/gui/cfg_editor/mainwindow.py +++ b/secop/gui/cfg_editor/mainwindow.py @@ -39,7 +39,7 @@ COMMENT = 'comment' class MainWindow(QMainWindow): def __init__(self, file_path=None, parent=None): - QMainWindow.__init__(self, parent) + super().__init__(parent) loadUi(self, 'mainwindow.ui') self.tabWidget.currentChanged.connect(self.tab_relevant_btns_disable) if file_path is None: diff --git a/secop/gui/cfg_editor/node_display.py b/secop/gui/cfg_editor/node_display.py index 300d55c..6ecc95d 100644 --- a/secop/gui/cfg_editor/node_display.py +++ b/secop/gui/cfg_editor/node_display.py @@ -26,7 +26,7 @@ from secop.gui.qt import QHBoxLayout, QSizePolicy, QSpacerItem, Qt, QWidget class NodeDisplay(QWidget): def __init__(self, file_path=None, parent=None): - QWidget.__init__(self, parent) + super().__init__(parent) loadUi(self, 'node_display.ui') self.saved = bool(file_path) self.created = self.tree_widget.set_file(file_path) diff --git a/secop/gui/cfg_editor/tree_widget_item.py b/secop/gui/cfg_editor/tree_widget_item.py index 948d891..20b41b2 100644 --- a/secop/gui/cfg_editor/tree_widget_item.py +++ b/secop/gui/cfg_editor/tree_widget_item.py @@ -44,7 +44,7 @@ class TreeWidgetItem(QTreeWidgetItem): the datatype passed onto ValueWidget should be on of secop.datatypes""" # TODO: like stated in docstring the datatype for parameters and # properties must be found out through their object - QTreeWidgetItem.__init__(self, parent) + super().__init__(parent) self.kind = kind self.name = name self.class_object = class_object @@ -129,7 +129,7 @@ class ValueWidget(QWidget): def __init__(self, name='', value='', datatype=None, kind='', parent=None): # TODO: implement: change module/interface class - QWidget.__init__(self, parent) + super().__init__(parent) self.datatype = datatype self.layout = QVBoxLayout() self.name_label = QLabel(name) @@ -205,7 +205,7 @@ class ValueWidget(QWidget): class ChangeNameDialog(QDialog): def __init__(self, current_name='', invalid_names=None, parent=None): - QWidget.__init__(self, parent) + super().__init__(parent) loadUi(self, 'change_name_dialog.ui') self.invalid_names = invalid_names self.name.setText(current_name) diff --git a/secop/gui/cfg_editor/widgets.py b/secop/gui/cfg_editor/widgets.py index 577ac29..6c866d4 100644 --- a/secop/gui/cfg_editor/widgets.py +++ b/secop/gui/cfg_editor/widgets.py @@ -31,7 +31,7 @@ from secop.gui.cfg_editor.utils import get_all_items, \ get_props, loadUi, set_name_edit_style, setActionIcon from secop.gui.qt import QComboBox, QDialog, QDialogButtonBox, QLabel, \ QLineEdit, QMenu, QPoint, QSize, QStandardItem, QStandardItemModel, \ - Qt, QTabBar, QTextEdit, QTreeView, QTreeWidget, QWidget, pyqtSignal + Qt, QTabBar, QTextEdit, QTreeView, QTreeWidget, pyqtSignal NODE = 'node' MODULE = 'module' @@ -47,7 +47,7 @@ class TreeWidget(QTreeWidget): add_canceled = pyqtSignal() def __init__(self, parent=None): - QTreeWidget.__init__(self, parent) + super().__init__(parent) self.file_path = None self.setIconSize(QSize(24, 24)) self.setSelectionMode(QTreeWidget.SingleSelection) @@ -335,7 +335,7 @@ class AddDialog(QDialog): """Notes: self.get_value: is mapped to the specific method for getting the value from self.value""" - QWidget.__init__(self, parent) + super().__init__(parent) loadUi(self, 'add_dialog.ui') self.setWindowTitle('add %s' % kind) self.kind = kind @@ -402,7 +402,7 @@ class AddDialog(QDialog): class TabBar(QTabBar): def __init__(self, parent=None): - QTabBar.__init__(self, parent) + super().__init__(parent) self.setContextMenuPolicy(Qt.CustomContextMenu) self.context_pos = QPoint(0, 0) self.menu = QMenu() @@ -436,7 +436,7 @@ class TabBar(QTabBar): class TreeComboBox(QComboBox): def __init__(self, value_dict, parent=None): - QComboBox.__init__(self, parent) + super().__init__(parent) self.tree_view = QTreeView() self.tree_view.setHeaderHidden(True) self.tree_view.expanded.connect(self.resize_length) diff --git a/secop/gui/mainwindow.py b/secop/gui/mainwindow.py index 3399eba..11d417e 100644 --- a/secop/gui/mainwindow.py +++ b/secop/gui/mainwindow.py @@ -44,7 +44,7 @@ class QSECNode(QObject): logEntry = pyqtSignal(str) def __init__(self, uri, parent=None): - QObject.__init__(self, parent) + super().__init__(parent) self.conn = conn = secop.client.SecopClient(uri) conn.validate_data = True self.log = conn.log @@ -83,10 +83,7 @@ class QSECNode(QObject): return self.conn.getParameter(module, parameter, True) def execCommand(self, module, command, argument): - try: - return self.conn.execCommand(module, command, argument) - except Exception as e: - return 'ERROR: %r' % e, {} + return self.conn.execCommand(module, command, argument) def queryCache(self, module): return {k: Value(*self.conn.cache[(module, k)]) @@ -115,7 +112,7 @@ class QSECNode(QObject): class MainWindow(QMainWindow): def __init__(self, hosts, parent=None): - super(MainWindow, self).__init__(parent) + super().__init__(parent) loadUi(self, 'mainwindow.ui') diff --git a/secop/gui/miniplot.py b/secop/gui/miniplot.py index 0e43318..15e56ac 100644 --- a/secop/gui/miniplot.py +++ b/secop/gui/miniplot.py @@ -160,7 +160,7 @@ class MiniPlotFitCurve(MiniPlotCurve): return float('-inf') def __init__(self, formula, params): - super(MiniPlotFitCurve, self).__init__() + super().__init__() self.formula = formula self.params = params @@ -193,7 +193,7 @@ class MiniPlot(QWidget): autoticky = True def __init__(self, parent=None): - QWidget.__init__(self, parent) + super().__init__(parent) self.xmin = self.xmax = None self.ymin = self.ymax = None self.curves = [] diff --git a/secop/gui/modulectrl.py b/secop/gui/modulectrl.py index 0fe5210..16553fe 100644 --- a/secop/gui/modulectrl.py +++ b/secop/gui/modulectrl.py @@ -32,7 +32,7 @@ from secop.gui.valuewidgets import get_widget class CommandDialog(QDialog): def __init__(self, cmdname, argument, parent=None): - super(CommandDialog, self).__init__(parent) + super().__init__(parent) loadUi(self, 'cmddialog.ui') self.setWindowTitle('Arguments for %s' % cmdname) @@ -58,7 +58,7 @@ class CommandDialog(QDialog): return True, self.widgets[0].get_value() def exec_(self): - if super(CommandDialog, self).exec_(): + if super().exec_(): return self.get_value() return None @@ -71,16 +71,17 @@ def showCommandResultDialog(command, args, result, extras=''): m.exec_() -def showErrorDialog(error): +def showErrorDialog(command, args, error): m = QMessageBox() - m.setText('Error %r' % error) + args = '' if args is None else repr(args) + m.setText('calling: %s(%s)\nraised %r' % (command, args, error)) m.exec_() class ParameterGroup(QWidget): def __init__(self, groupname, parent=None): - super(ParameterGroup, self).__init__(parent) + super().__init__(parent) loadUi(self, 'paramgroup.ui') self._groupname = groupname @@ -112,7 +113,7 @@ class ParameterGroup(QWidget): class CommandButton(QPushButton): def __init__(self, cmdname, cmdinfo, cb, parent=None): - super(CommandButton, self).__init__(parent) + super().__init__(parent) self._cmdname = cmdname self._argintype = cmdinfo['datatype'].argument # single datatype @@ -140,7 +141,7 @@ class CommandButton(QPushButton): class ModuleCtrl(QWidget): def __init__(self, node, module, parent=None): - super(ModuleCtrl, self).__init__(parent) + super().__init__(parent) loadUi(self, 'modulectrl.ui') self._node = node self._module = module @@ -161,10 +162,9 @@ class ModuleCtrl(QWidget): try: result, qualifiers = self._node.execCommand( self._module, command, args) - except TypeError: - result = None - qualifiers = {} - # XXX: flag missing data report as error + except Exception as e: + showErrorDialog(command, args, e) + return if result is not None: showCommandResultDialog(command, args, result, qualifiers) diff --git a/secop/gui/params/__init__.py b/secop/gui/params/__init__.py index 60da285..f2162fe 100644 --- a/secop/gui/params/__init__.py +++ b/secop/gui/params/__init__.py @@ -39,7 +39,7 @@ class ParameterWidget(QWidget): initvalue=None, readonly=True, parent=None): - super(ParameterWidget, self).__init__(parent) + super().__init__(parent) self._module = module self._paramcmd = paramcmd self._datatype = datatype @@ -82,7 +82,6 @@ class GenericParameterWidget(ParameterWidget): else: value = fmtstr % (value.value,) self.currentLineEdit.setText(value) - # self.currentLineEdit.setText(str(value)) class EnumParameterWidget(GenericParameterWidget): diff --git a/secop/properties.py b/secop/properties.py index 97a6c45..047c147 100644 --- a/secop/properties.py +++ b/secop/properties.py @@ -24,24 +24,12 @@ import inspect -import sys from secop.errors import BadValueError, ConfigError, ProgrammingError +from secop.lib.py35compat import Object -class HasDescriptorMeta(type): - def __new__(cls, name, bases, attrs): - newtype = type.__new__(cls, name, bases, attrs) - if sys.version_info < (3, 6): - # support older python versions - for key, attr in attrs.items(): - if hasattr(attr, '__set_name__'): - attr.__set_name__(newtype, key) - newtype.__init_subclass__() - return newtype - - -class HasDescriptors(metaclass=HasDescriptorMeta): +class HasDescriptors(Object): @classmethod def __init_subclass__(cls): # when migrating old style declarations, sometimes the trailing comma is not removed @@ -142,12 +130,6 @@ class HasProperties(HasDescriptors): @classmethod def __init_subclass__(cls): super().__init_subclass__() - # raise an error when an attribute is a tuple with one single descriptor as element - # when migrating old style declarations, sometimes the trailing comma is not removed - bad = [k for k, v in cls.__dict__.items() - if isinstance(v, tuple) and len(v) == 1 and hasattr(v[0], '__set_name__')] - if bad: - raise ProgrammingError('misplaced trailing comma after %s.%s' % (cls.__name__, '/'.join(bad))) properties = {} # using cls.__bases__ and base.propertyDict for this would fail on some multiple inheritance cases for base in reversed(cls.__mro__): diff --git a/secop/protocol/dispatcher.py b/secop/protocol/dispatcher.py index 0273865..7d9f7ff 100644 --- a/secop/protocol/dispatcher.py +++ b/secop/protocol/dispatcher.py @@ -60,12 +60,11 @@ def make_update(modulename, pobj): class Dispatcher: - - OMIT_UNCHANGED_WITHIN = 1 # do not send unchanged updates within 1 sec - def __init__(self, name, logger, options, srv): # to avoid errors, we want to eat all options here self.equipment_id = options.pop('id', name) + # time interval for omitting updates of unchanged values + self.omit_unchanged_within = options.pop('omit_unchanged_within', 0.1) self.nodeprops = {} for k in list(options): self.nodeprops[k] = options.pop(k) @@ -84,7 +83,6 @@ class Dispatcher: self._subscriptions = {} self._lock = threading.RLock() self.restart = srv.restart - self.shutdown = srv.shutdown def broadcast_event(self, msg, reallyall=False): """broadcasts a msg to all active connections diff --git a/secop/protocol/interface/tcp.py b/secop/protocol/interface/tcp.py index 417db97..a41f4b4 100644 --- a/secop/protocol/interface/tcp.py +++ b/secop/protocol/interface/tcp.py @@ -202,3 +202,11 @@ class TCPServer(socketserver.ThreadingTCPServer): if ntry: self.log.warning('tried again %d times after "Address already in use"' % ntry) self.log.info("TCPServer initiated") + + # py35 compatibility + if not hasattr(socketserver.ThreadingTCPServer, '__exit__'): + def __enter__(self): + return self + + def __exit__(self, *args): + self.server_close() diff --git a/secop/proxy.py b/secop/proxy.py index d4d3a2a..5d01401 100644 --- a/secop/proxy.py +++ b/secop/proxy.py @@ -182,7 +182,7 @@ def proxy_class(remote_class, name=None): for aname, aobj in rcls.accessibles.items(): if isinstance(aobj, Parameter): - pobj = aobj.override(poll=False, handler=None, needscfg=False) + pobj = aobj.merge(dict(poll=False, handler=None, needscfg=False)) attrs[aname] = pobj def rfunc(self, pname=aname):