check_sources.sh makes me become even less productive
authorEdouard Tisserant <edouard.tisserant@gmail.com>
Mon, 11 Mar 2019 01:03:32 +0100
changeset 2492 7dd551ac2fa0
parent 2491 362039519454
child 2493 0ad5e616d37f
check_sources.sh makes me become even less productive
Beremiz_service.py
PSKManagement.py
ProjectController.py
connectors/ConnectorBase.py
connectors/PYRO/PSK_Adapter.py
connectors/PYRO/__init__.py
connectors/PYRO_dialog.py
connectors/SchemeEditor.py
connectors/WAMP/__init__.py
connectors/WAMP_dialog.py
connectors/__init__.py
controls/DiscoveryPanel.py
controls/IDBrowser.py
controls/__init__.py
dialogs/IDManager.py
dialogs/IDMergeDialog.py
dialogs/UriEditor.py
dialogs/__init__.py
runtime/PLCObject.py
runtime/PyroServer.py
runtime/ServicePublisher.py
runtime/Stunnel.py
runtime/Worker.py
runtime/spawn_subprocess.py
wxglade_hmi/wxglade_hmi.py
--- a/Beremiz_service.py	Thu Mar 07 21:57:18 2019 +0100
+++ b/Beremiz_service.py	Mon Mar 11 01:03:32 2019 +0100
@@ -41,6 +41,7 @@
 from runtime.xenomai import TryPreloadXenomai
 from runtime import LogMessageAndException
 from runtime import PlcStatus
+from runtime.Stunnel import ensurePSK
 import util.paths as paths
 
 
@@ -415,7 +416,7 @@
         reactor.registerWxApp(app)
 
 twisted_reactor_thread_id = None
-ui_thread = None 
+ui_thread = None
 
 if havewx:
     wx_eval_lock = Semaphore(0)
@@ -439,8 +440,8 @@
         if ui_thread is not None \
             and ui_thread.ident != current_id \
             and (not havetwisted or (
-                twisted_reactor_thread_id is not None 
-                and twisted_reactor_thread_id != current_id)):
+                    twisted_reactor_thread_id is not None
+                    and twisted_reactor_thread_id != current_id)):
 
             o = type('', (object,), dict(call=(tocall, args, kwargs), res=None))
             wx.CallAfter(wx_evaluator, o)
@@ -508,7 +509,6 @@
 # Some extension may set 'servicename' to a computed ID or Serial Number
 # instead of using commandline '-n'
 if servicename is not None and PSKpath is not None:
-    from runtime.Stunnel import ensurePSK
     ensurePSK(servicename, PSKpath)
 
 runtime.CreatePLCObjectSingleton(
--- a/PSKManagement.py	Thu Mar 07 21:57:18 2019 +0100
+++ b/PSKManagement.py	Mon Mar 11 01:03:32 2019 +0100
@@ -14,26 +14,32 @@
 COL_ID, COL_URI, COL_DESC, COL_LAST = range(4)
 REPLACE, REPLACE_ALL, KEEP, KEEP_ALL, CANCEL = range(5)
 
+
 def _pskpath(project_path):
     return os.path.join(project_path, 'psk')
 
+
 def _mgtpath(project_path):
     return os.path.join(_pskpath(project_path), 'management.json')
 
+
 def _ensurePSKdir(project_path):
     pskpath = _pskpath(project_path)
     if not os.path.exists(pskpath):
         os.mkdir(pskpath)
     return pskpath
 
+
 def _default(ID):
     return [ID,
-            '', # default description
-            None, # last known URI
+            '',  # default description
+            None,  # last known URI
             None]  # last connection date
 
+
 def _dataByID(data):
-    return {row[COL_ID]:row for row in data}
+    return {row[COL_ID]: row for row in data}
+
 
 def _LoadData(project_path):
     """ load known keys metadata """
@@ -43,6 +49,7 @@
             return json.loads(open(_path).read())
     return []
 
+
 def _filterData(psk_files, data_input):
     input_by_ID = _dataByID(data_input)
     output = []
@@ -51,11 +58,12 @@
     # this implicitly filters IDs out of metadata who's
     # secret is missing
     for filename in psk_files:
-       if filename.endswith('.secret'):
-           ID = filename[:-7]  # strip filename extension
-           output.append(input_by_ID.get(ID,_default(ID)))
+        if filename.endswith('.secret'):
+            ID = filename[:-7]  # strip filename extension
+            output.append(input_by_ID.get(ID, _default(ID)))
     return output
 
+
 def GetData(project_path):
     loaded_data = _LoadData(project_path)
     if loaded_data:
@@ -63,15 +71,18 @@
         return _filterData(psk_files, loaded_data)
     return []
 
+
 def DeleteID(project_path, ID):
     secret_path = os.path.join(_pskpath(project_path), ID+'.secret')
     os.remove(secret_path)
 
+
 def SaveData(project_path, data):
     _ensurePSKdir(project_path)
     with open(_mgtpath(project_path), 'w') as f:
         f.write(json.dumps(data))
 
+
 def UpdateID(project_path, ID, secret, URI):
     pskpath = _ensurePSKdir(project_path)
     if not os.path.exists(pskpath):
@@ -88,10 +99,10 @@
 
     _is_new_ID = dataForID is None
     if _is_new_ID:
-       dataForID = _default(ID)
+        dataForID = _default(ID)
 
     dataForID[COL_URI] = URI
-    # FIXME : could store time instead os a string and use DVC model's cmp 
+    # FIXME : could store time instead os a string and use DVC model's cmp
     # then date display could be smarter, etc - sortable sting hack for now
     dataForID[COL_LAST] = time.strftime('%y/%M/%d-%H:%M:%S')
 
@@ -100,6 +111,7 @@
 
     SaveData(project_path, data)
 
+
 def ExportIDs(project_path, export_zip):
     with ZipFile(export_zip, 'w') as zf:
         path = _pskpath(project_path)
@@ -107,6 +119,7 @@
             if nm.endswith('.secret') or nm == 'management.json':
                 zf.write(os.path.join(path, nm), nm)
 
+
 def ImportIDs(project_path, import_zip, should_I_replace_callback):
     zf = ZipFile(import_zip, 'r')
     data = GetData(project_path)
@@ -132,18 +145,16 @@
 
             if result == CANCEL:
                 return
-            
+
             if result in [REPLACE_ALL, REPLACE]:
                 # replace with imported
                 existing_row[:] = imported_row
                 # copy the key of selected
                 keys_to_import.append(ID)
-    
+
     for ID in keys_to_import:
         zf.extract(ID+".secret", _pskpath(project_path))
 
     SaveData(project_path, data)
 
     return data
-
-
--- a/ProjectController.py	Thu Mar 07 21:57:18 2019 +0100
+++ b/ProjectController.py	Mon Mar 11 01:03:32 2019 +0100
@@ -1789,8 +1789,8 @@
             self._SetConnector(connectors.ConnectorFactory(uri, self))
         except Exception as e:
             self.logger.write_error(
-                _("Exception while connecting to '%s': %s\n") % (uri, str(e)))
-            #self.logger.write_error(traceback.format_exc())
+                _("Exception while connecting to '{uri}': {ex}\n").format(
+                    uri=uri, ex=e))
 
         # Did connection success ?
         if self._connector is None:
@@ -1813,7 +1813,7 @@
         if self._connector is None:
             return
         builder = self.GetBuilder()
-        if builder is None :
+        if builder is None:
             return
         MD5 = builder.GetBinaryMD5()
         if MD5 is None:
@@ -1846,7 +1846,7 @@
             self.logger.write_error(_("Fatal : cannot get builder.\n"))
             return False
 
-        # recover md5 from last build 
+        # recover md5 from last build
         MD5 = builder.GetBinaryMD5()
 
         # Check if md5 file is empty : ask user to build PLC
@@ -1871,11 +1871,11 @@
 
             for name in os.listdir(extrafilespath):
                 extrafiles.append((
-                    name, 
+                    name,
                     self._connector.BlobFromFile(
                         # use file name as a seed to avoid collisions
                         # with files having same content
-                        os.path.join(extrafilespath, name),name)))
+                        os.path.join(extrafilespath, name), name)))
 
         # Send PLC on target
         object_path = builder.GetBinaryPath()
--- a/connectors/ConnectorBase.py	Thu Mar 07 21:57:18 2019 +0100
+++ b/connectors/ConnectorBase.py	Mon Mar 11 01:03:32 2019 +0100
@@ -3,20 +3,22 @@
 
 # See COPYING file for copyrights details.
 
+from __future__ import absolute_import
 import md5
 
+
 class ConnectorBase(object):
 
-    #chuncksize = 16384
     chuncksize = 1024*1024
+
     def BlobFromFile(self, filepath, seed):
         s = md5.new()
         s.update(seed)
         blobID = self.SeedBlob(seed)
         with open(filepath, "rb") as f:
             while blobID == s.digest():
-                chunk = f.read(self.chuncksize) 
-                if len(chunk) == 0: return blobID
+                chunk = f.read(self.chuncksize)
+                if len(chunk) == 0:
+                    return blobID
                 blobID = self.AppendChunkToBlob(chunk, blobID)
                 s.update(chunk)
-
--- a/connectors/PYRO/PSK_Adapter.py	Thu Mar 07 21:57:18 2019 +0100
+++ b/connectors/PYRO/PSK_Adapter.py	Mon Mar 11 01:03:32 2019 +0100
@@ -7,11 +7,11 @@
 import ssl
 import Pyro
 from Pyro.core import PyroURI
-from Pyro.protocol import _connect_socket,TCPConnection,PYROAdapter
+from Pyro.protocol import _connect_socket, TCPConnection, PYROAdapter
 from Pyro.errors import ConnectionDeniedError, ProtocolError
 from Pyro.util import Log
 
-#
+
 # The TLS-PSK adapter that handles SSL connections instead of regular sockets,
 # but using Pre Shared Keys instead of Certificates
 #
@@ -19,67 +19,76 @@
     # This is essentialy the same as in Pyro/protocol.py
     # only raw_sock wrapping into sock through sslpsk.wrap_socket was added
     # Pyro unfortunately doesn't allow cleaner customization
-    def bindToURI(self,URI):
+    def bindToURI(self, URI):
         with self.lock:   # only 1 thread at a time can bind the URI
             try:
-                self.URI=URI
+                self.URI = URI
 
                 # This are the statements that differ from Pyro/protocol.py
                 raw_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                 _connect_socket(raw_sock, URI.address, URI.port, self.timeout)
                 sock = sslpsk.wrap_socket(
                     raw_sock, psk=Pyro.config.PYROPSK, server_side=False,
-                    ciphers="PSK-AES256-CBC-SHA", # available in openssl 1.0.2 
+                    ciphers="PSK-AES256-CBC-SHA",  # available in openssl 1.0.2
                     ssl_version=ssl.PROTOCOL_TLSv1)
-                # all the rest is the same as in Pyro/protocol.py 
+                # all the rest is the same as in Pyro/protocol.py
 
-                conn=TCPConnection(sock, sock.getpeername())
+                conn = TCPConnection(sock, sock.getpeername())
                 # receive the authentication challenge string, and use that to build the actual identification string.
                 try:
-                    authChallenge=self.recvAuthChallenge(conn)
-                except ProtocolError,x:
+                    authChallenge = self.recvAuthChallenge(conn)
+                except ProtocolError, x:
                     # check if we were denied
-                    if hasattr(x,"partialMsg") and x.partialMsg[:len(self.denyMSG)]==self.denyMSG:
+                    if hasattr(x, "partialMsg") and x.partialMsg[:len(self.denyMSG)] == self.denyMSG:
                         raise ConnectionDeniedError(Pyro.constants.deniedReasons[int(x.partialMsg[-1])])
                     else:
                         raise
                 # reply with our ident token, generated from the ident passphrase and the challenge
-                msg = self._sendConnect(sock,self.newConnValidator.createAuthToken(self.ident, authChallenge, conn.addr, self.URI, None) )
-                if msg==self.acceptMSG:
-                    self.conn=conn
-                    self.conn.connected=1
-                    Log.msg('PYROAdapter','connected to',str(URI))
-                    if URI.protocol=='PYROLOCPSK':
-                        self.resolvePYROLOC_URI("PYROPSK") # updates self.URI
-                elif msg[:len(self.denyMSG)]==self.denyMSG:
+                msg = self._sendConnect(sock, self.newConnValidator.createAuthToken(self.ident, authChallenge, conn.addr, self.URI, None))
+                if msg == self.acceptMSG:
+                    self.conn = conn
+                    self.conn.connected = 1
+                    Log.msg('PYROAdapter', 'connected to', str(URI))
+                    if URI.protocol == 'PYROLOCPSK':
+                        self.resolvePYROLOC_URI("PYROPSK")  # updates self.URI
+                elif msg[:len(self.denyMSG)] == self.denyMSG:
                     try:
                         raise ConnectionDeniedError(Pyro.constants.deniedReasons[int(msg[-1])])
-                    except (KeyError,ValueError):
+                    except (KeyError, ValueError):
                         raise ConnectionDeniedError('invalid response')
             except socket.error:
-                Log.msg('PYROAdapter','connection failed to URI',str(URI))
+                Log.msg('PYROAdapter', 'connection failed to URI', str(URI))
                 raise ProtocolError('connection failed')
 
+
 _getProtocolAdapter = Pyro.protocol.getProtocolAdapter
+
+
 def getProtocolAdapter(protocol):
     if protocol in ('PYROPSK', 'PYROLOCPSK'):
         return PYROPSKAdapter()
     return _getProtocolAdapter(protocol)
 
+
 Pyro.protocol.getProtocolAdapter = getProtocolAdapter
 
+
 _processStringURI = Pyro.core.processStringURI
+
+
 def processStringURI(URI):
-    x=re.match(r'(?P<protocol>PYROLOCPSK)://(?P<hostname>[^\s:]+):?(?P<port>\d+)?/(?P<name>\S*)',URI)
+    x = re.match(r'(?P<protocol>PYROLOCPSK)://(?P<hostname>[^\s:]+):?(?P<port>\d+)?/(?P<name>\S*)', URI)
     if x:
-        protocol=x.group('protocol')
-        hostname=x.group('hostname')
-        port=x.group('port')
+        protocol = x.group('protocol')
+        hostname = x.group('hostname')
+        port = x.group('port')
         if port:
-            port=int(port)
+            port = int(port)
         else:
-            port=0
-        name=x.group('name')
-        return PyroURI(hostname,name,port,protocol)
+            port = 0
+        name = x.group('name')
+        return PyroURI(hostname, name, port, protocol)
     return _processStringURI(URI)
+
+
 Pyro.core.processStringURI = processStringURI
--- a/connectors/PYRO/__init__.py	Thu Mar 07 21:57:18 2019 +0100
+++ b/connectors/PYRO/__init__.py	Mon Mar 11 01:03:32 2019 +0100
@@ -55,8 +55,9 @@
     scheme, location = uri.split("://")
     if scheme == "PYROS":
         import connectors.PYRO.PSK_Adapter
+        _unused_module_imported_for_monkey_patching_pylint_sucks = connectors.PYRO.PSK_Adapter
         schemename = "PYROLOCPSK"
-        url, ID = location.split('#') #TODO fix exception when # not found
+        url, ID = location.split('#')  # TODO fix exception when # not found
         # load PSK from project
         secpath = os.path.join(str(confnodesroot.ProjectPath), 'psk', ID+'.secret')
         if not os.path.exists(secpath):
@@ -73,9 +74,10 @@
     # Try to get the proxy object
     try:
         RemotePLCObjectProxy = Pyro.core.getAttrProxyForURI(schemename + "://" + location + "/PLCObject")
-    except Exception:
-        confnodesroot.logger.write_error(_("Connection to '%s' failed with exception '%s'\n") % (location, str(e)))
-        #confnodesroot.logger.write_error(traceback.format_exc())
+    except Exception, e:
+        confnodesroot.logger.write_error(
+            _("Connection to {loc} failed with exception {ex}\n").format(
+                loc=location, exo=str(e)))
         return None
 
     RemotePLCObjectProxy.adapter.setTimeout(60)
@@ -108,10 +110,9 @@
     if IDPSK is None:
         confnodesroot.logger.write_warning(_("PLC did not provide identity and security infomation.\n"))
     else:
-        ID,secret = IDPSK
+        ID, secret = IDPSK
         PSK.UpdateID(confnodesroot.ProjectPath, ID, secret, uri)
 
-
     _special_return_funcs = {
         "StartPLC": False,
         "GetTraceVariables": (PlcStatus.Broken, None),
--- a/connectors/PYRO_dialog.py	Thu Mar 07 21:57:18 2019 +0100
+++ b/connectors/PYRO_dialog.py	Mon Mar 11 01:03:32 2019 +0100
@@ -11,27 +11,28 @@
 from connectors.SchemeEditor import SchemeEditor
 
 
-model = [('host',_("Host:")),
-         ('port',_("Port:"))]
+model = [('host', _("Host:")),
+         ('port', _("Port:"))]
 
 # (scheme, model, secure)
 models = [("LOCAL", [], False), ("PYRO", model, False), ("PYROS", model, True)]
 
 Schemes = list(zip(*models)[0])
 
-_PerSchemeConf = {sch : (mod,sec) for sch,mod,sec in models}
+_PerSchemeConf = {sch: (mod, sec) for sch, mod, sec in models}
+
 
 class PYRO_dialog(SchemeEditor):
     def __init__(self, scheme, *args, **kwargs):
-       
         # ID selector is enabled only on PYROS (secure)
         self.model, self.EnableIDSelector = _PerSchemeConf[scheme]
 
         SchemeEditor.__init__(self, scheme, *args, **kwargs)
 
+    # pylint: disable=unused-variable
     def SetLoc(self, loc):
-        hostport, ID = list(islice(chain(loc.split("#"), repeat("")),2))
-        host, port = list(islice(chain(hostport.split(":"), repeat("")),2))
+        hostport, ID = list(islice(chain(loc.split("#"), repeat("")), 2))
+        host, port = list(islice(chain(hostport.split(":"), repeat("")), 2))
         self.SetFields(locals())
 
     def GetLoc(self):
@@ -39,10 +40,9 @@
             fields = self.GetFields()
             template = "{host}"
             if fields['port']:
-                template += ":{port}" 
+                template += ":{port}"
             if fields['ID']:
-                template += "#{ID}" 
+                template += "#{ID}"
 
             return template.format(**fields)
         return ''
-
--- a/connectors/SchemeEditor.py	Thu Mar 07 21:57:18 2019 +0100
+++ b/connectors/SchemeEditor.py	Mon Mar 11 01:03:32 2019 +0100
@@ -5,15 +5,15 @@
 
 from __future__ import absolute_import
 
-from itertools import repeat, izip_longest
 from functools import partial
 import wx
 
 from controls.IDBrowser import IDBrowser
 
+
 class SchemeEditor(wx.Panel):
     def __init__(self, scheme, parent, *args, **kwargs):
-        self.txtctrls = {} 
+        self.txtctrls = {}
         wx.Panel.__init__(self, parent, *args, **kwargs)
 
         self.fieldsizer = wx.FlexGridSizer(cols=2, hgap=10, vgap=10)
@@ -25,8 +25,9 @@
             txtctrl = wx.TextCtrl(parent=self, size=wx.Size(200, -1))
             self.txtctrls[tag] = txtctrl
             for win, flag in [
-                (wx.StaticText(self, label=label), wx.ALIGN_CENTER_VERTICAL),
-                (txtctrl, wx.GROW)]:
+                    (wx.StaticText(self, label=label),
+                    wx.ALIGN_CENTER_VERTICAL),
+                    (txtctrl, wx.GROW)]:
                 self.fieldsizer.AddWindow(win, flag=flag)
 
         self.fieldsizer.AddSpacer(20)
@@ -45,9 +46,8 @@
             self.SetSizer(self.fieldsizer)
 
     def SetFields(self, fields):
-        for tag, label in self.model:
+        for tag, _label in self.model:
             self.txtctrls[tag].SetValue(fields[tag])
 
     def GetFields(self):
-        return {tag: self.txtctrls[tag].GetValue() for tag,label in self.model}
-
+        return {tag: self.txtctrls[tag].GetValue() for tag, _label in self.model}
--- a/connectors/WAMP/__init__.py	Thu Mar 07 21:57:18 2019 +0100
+++ b/connectors/WAMP/__init__.py	Mon Mar 11 01:03:32 2019 +0100
@@ -159,4 +159,5 @@
 
     return WampPLCObjectProxy
 
+
 WAMP_connector_factory = partial(_WAMP_connector_factory, WampSession)
--- a/connectors/WAMP_dialog.py	Thu Mar 07 21:57:18 2019 +0100
+++ b/connectors/WAMP_dialog.py	Mon Mar 11 01:03:32 2019 +0100
@@ -6,15 +6,15 @@
 from __future__ import absolute_import
 
 from itertools import repeat, islice, chain
-import wx
 
 from connectors.SchemeEditor import SchemeEditor
 
 Schemes = ["WAMP", "WAMPS"]
 
-model = [('host',_("Host:")),
-         ('port',_("Port:")),
-         ('realm',_("Realm:"))]
+model = [('host', _("Host:")),
+         ('port', _("Port:")),
+         ('realm', _("Realm:"))]
+
 
 class WAMP_dialog(SchemeEditor):
     def __init__(self, *args, **kwargs):
@@ -22,19 +22,19 @@
         self.EnableIDSelector = True
         SchemeEditor.__init__(self, *args, **kwargs)
 
+    # pylint: disable=unused-variable
     def SetLoc(self, loc):
-        hostport, realm, ID = list(islice(chain(loc.split("#"), repeat("")),3))
-        host, port = list(islice(chain(hostport.split(":"), repeat("")),2))
+        hostport, realm, ID = list(islice(chain(loc.split("#"), repeat("")), 3))
+        host, port = list(islice(chain(hostport.split(":"), repeat("")), 2))
         self.SetFields(locals())
 
     def GetLoc(self):
         fields = self.GetFields()
 
-        #TODO : input validation test
+        # TODO : input validation test
 
         template = "{host}" + \
                    (":{port}" if fields['port'] else '') +\
                    "#{realm}#{ID}"
 
         return template.format(**fields)
-
--- a/connectors/__init__.py	Thu Mar 07 21:57:18 2019 +0100
+++ b/connectors/__init__.py	Mon Mar 11 01:03:32 2019 +0100
@@ -32,7 +32,7 @@
 from connectors.ConnectorBase import ConnectorBase
 from types import ClassType
 
-connectors_packages = ["PYRO","WAMP"]
+connectors_packages = ["PYRO", "WAMP"]
 
 
 def _GetLocalConnectorClassFactory(name):
@@ -44,17 +44,18 @@
 
 _dialogs_imported = False
 per_URI_connectors = None
-schemes = None 
+schemes = None
+
 
 # lazy import of connectors dialogs, only if used
 def _Import_Dialogs():
     global per_URI_connectors, schemes, _dialogs_imported
-    if not _dialogs_imported: 
+    if not _dialogs_imported:
         _dialogs_imported = True
         per_URI_connectors = {}
         schemes = []
         for con_name in connectors_packages:
-            module =  __import__(con_name + '_dialog', globals(), locals())
+            module = __import__(con_name + '_dialog', globals(), locals())
 
             for scheme in module.Schemes:
                 per_URI_connectors[scheme] = getattr(module, con_name + '_dialog')
@@ -110,20 +111,22 @@
         return None
 
     # import module according to uri type and get connector specific baseclass
-    # first call to import the module, 
+    # first call to import the module,
     # then call with parameters to create the class
     connector_specific_class = connectors[scheme]()(uri, confnodesroot)
-    
+
     if connector_specific_class is None:
         return None
 
     # new class inheriting from generic and specific connector base classes
-    return ClassType(_scheme + "_connector", 
+    return ClassType(_scheme + "_connector",
                      (ConnectorBase, connector_specific_class), {})()
 
+
 def EditorClassFromScheme(scheme):
     _Import_Dialogs()
-    return per_URI_connectors.get(scheme, None) 
+    return per_URI_connectors.get(scheme, None)
+
 
 def ConnectorSchemes():
     _Import_Dialogs()
--- a/controls/DiscoveryPanel.py	Thu Mar 07 21:57:18 2019 +0100
+++ b/controls/DiscoveryPanel.py	Mon Mar 11 01:03:32 2019 +0100
@@ -30,7 +30,6 @@
 import wx
 import wx.lib.mixins.listctrl as listmix
 from zeroconf import ServiceBrowser, Zeroconf, get_all_addresses
-import netifaces
 
 service_type = '_Beremiz._tcp.local.'
 
@@ -41,6 +40,7 @@
         wx.ListCtrl.__init__(self, parent, id, pos, size, style, name=name)
         listmix.ListCtrlAutoWidthMixin.__init__(self)
 
+
 class DiscoveryPanel(wx.Panel, listmix.ColumnSorterMixin):
 
     def _init_coll_MainSizer_Items(self, parent):
@@ -129,7 +129,7 @@
         self.IfacesMonitorState = None
         self.IfacesMonitorTimer = wx.Timer(self)
         self.IfacesMonitorTimer.Start(2000)
-        self.Bind(wx.EVT_TIMER, self.IfacesMonitor , self.IfacesMonitorTimer)
+        self.Bind(wx.EVT_TIMER, self.IfacesMonitor, self.IfacesMonitorTimer)
 
     def __del__(self):
         self.IfacesMonitorTimer.Stop()
@@ -139,7 +139,7 @@
     def IfacesMonitor(self, event):
         NewState = get_all_addresses(socket.AF_INET)
 
-        if self.IfacesMonitorState != NewState: 
+        if self.IfacesMonitorState != NewState:
             if self.IfacesMonitorState is not None:
                 # refresh only if a new address appeared
                 for addr in NewState:
@@ -192,7 +192,7 @@
         if self.LatestSelection is not None:
             # if self.ByIPCheck.IsChecked():
             svcname, scheme, host, port = \
-                map(lambda col:self.getColumnText(self.LatestSelection, col),
+                map(lambda col: self.getColumnText(self.LatestSelection, col),
                     range(4))
             return ("%s://%s:%s#%s" % (scheme, host, port, svcname)) \
                 if scheme[-1] == "S" \
--- a/controls/IDBrowser.py	Thu Mar 07 21:57:18 2019 +0100
+++ b/controls/IDBrowser.py	Mon Mar 11 01:03:32 2019 +0100
@@ -4,13 +4,13 @@
 # See COPYING file for copyrights details.
 
 from __future__ import absolute_import
-import os
 import wx
 import wx.dataview as dv
 import PSKManagement as PSK
 from PSKManagement import *
 from dialogs.IDMergeDialog import IDMergeDialog
 
+
 class IDBrowserModel(dv.PyDataViewIndexListModel):
     def __init__(self, project_path, columncount):
         self.project_path = project_path
@@ -36,9 +36,9 @@
 
     def GetCount(self):
         return len(self.data)
-    
+
     def Compare(self, item1, item2, col, ascending):
-        if not ascending: # swap sort order?
+        if not ascending:  # swap sort order?
             item2, item1 = item1, item2
         row1 = self.GetRow(item1)
         row2 = self.GetRow(item2)
@@ -50,13 +50,13 @@
     def DeleteRows(self, rows):
         rows = list(rows)
         rows.sort(reverse=True)
-        
+
         for row in rows:
             PSK.DeleteID(self.project_path, self.data[row][COL_ID])
             del self.data[row]
             self.RowDeleted(row)
         self._saveData()
-            
+
     def AddRow(self, value):
         self.data.append(value)
         self.RowAppended()
@@ -66,16 +66,18 @@
         data = PSK.ImportIDs(self.project_path, filepath, sircb)
         if data is not None:
             self.data = data
-            self.Reset(len(self.data)) 
+            self.Reset(len(self.data))
 
     def Export(self, filepath):
         PSK.ExportIDs(self.project_path, filepath)
 
-colflags = dv.DATAVIEW_COL_RESIZABLE|dv.DATAVIEW_COL_SORTABLE
+
+colflags = dv.DATAVIEW_COL_RESIZABLE | dv.DATAVIEW_COL_SORTABLE
+
 
 class IDBrowser(wx.Panel):
     def __init__(self, parent, ctr, SelectURICallBack=None, SelectIDCallBack=None, **kwargs):
-        big = self.isManager = SelectURICallBack is None and SelectIDCallBack is None 
+        big = self.isManager = SelectURICallBack is None and SelectIDCallBack is None
         wx.Panel.__init__(self, parent, -1, size=(800 if big else 450,
                                                   600 if big else 200))
 
@@ -83,66 +85,66 @@
         self.SelectIDCallBack = SelectIDCallBack
 
         dvStyle = wx.BORDER_THEME | dv.DV_ROW_LINES
-        if self.isManager :
+        if self.isManager:
             # no multiple selection in selector mode
             dvStyle |= dv.DV_MULTIPLE
-        self.dvc = dv.DataViewCtrl(self, style = dvStyle)
-                    
-        args = lambda *a,**k:(a,k)
+        self.dvc = dv.DataViewCtrl(self, style=dvStyle)
+
+        def args(*a, **k):
+            return (a, k)
 
         ColumnsDesc = [
-            args(_("ID"), COL_ID, width = 70),
-            args(_("Last URI"), COL_URI, width = 300 if big else 80),
-            args(_("Description"), COL_DESC, width = 300 if big else 200, 
-                mode = dv.DATAVIEW_CELL_EDITABLE 
-                       if self.isManager 
-                       else dv.DATAVIEW_CELL_INERT),
-            args(_("Last connection"),  COL_LAST, width = 120),
+            args(_("ID"), COL_ID, width=70),
+            args(_("Last URI"), COL_URI, width=300 if big else 80),
+            args(_("Description"), COL_DESC, width=300 if big else 200,
+                 mode=dv.DATAVIEW_CELL_EDITABLE
+                      if self.isManager
+                      else dv.DATAVIEW_CELL_INERT),
+            args(_("Last connection"), COL_LAST, width=120),
         ]
 
         self.model = IDBrowserModel(ctr.ProjectPath, len(ColumnsDesc))
         self.dvc.AssociateModel(self.model)
 
         col_list = []
-        for a,k in ColumnsDesc:
+        for a, k in ColumnsDesc:
             col_list.append(
-                self.dvc.AppendTextColumn(*a,**dict(k, flags = colflags)))
+                self.dvc.AppendTextColumn(*a, **dict(k, flags=colflags)))
         col_list[COL_LAST].SetSortOrder(False)
 
         # TODO : sort by last bvisit by default
 
-        self.Sizer = wx.BoxSizer(wx.VERTICAL) 
+        self.Sizer = wx.BoxSizer(wx.VERTICAL)
         self.Sizer.Add(self.dvc, 1, wx.EXPAND)
 
         btnbox = wx.BoxSizer(wx.HORIZONTAL)
-        if self.isManager :
+        if self.isManager:
 
             # deletion of secret and metadata
             deleteButton = wx.Button(self, label=_("Delete ID"))
             self.Bind(wx.EVT_BUTTON, self.OnDeleteButton, deleteButton)
-            btnbox.Add(deleteButton, 0, wx.LEFT|wx.RIGHT, 5)
+            btnbox.Add(deleteButton, 0, wx.LEFT | wx.RIGHT, 5)
 
             # export all
             exportButton = wx.Button(self, label=_("Export all"))
             self.Bind(wx.EVT_BUTTON, self.OnExportButton, exportButton)
-            btnbox.Add(exportButton, 0, wx.LEFT|wx.RIGHT, 5)
+            btnbox.Add(exportButton, 0, wx.LEFT | wx.RIGHT, 5)
 
             # import with a merge -> duplicates are asked for
             importButton = wx.Button(self, label=_("Import"))
             self.Bind(wx.EVT_BUTTON, self.OnImportButton, importButton)
-            btnbox.Add(importButton, 0, wx.LEFT|wx.RIGHT, 5)
-
-        else :
+            btnbox.Add(importButton, 0, wx.LEFT | wx.RIGHT, 5)
+
+        else:
             # selector mode
             self.useURIButton = wx.Button(self, label=_("Use last URI"))
             self.Bind(wx.EVT_BUTTON, self.OnUseURIButton, self.useURIButton)
             self.useURIButton.Disable()
-            btnbox.Add(self.useURIButton, 0, wx.LEFT|wx.RIGHT, 5)
-
-        self.Sizer.Add(btnbox, 0, wx.TOP|wx.BOTTOM, 5)
+            btnbox.Add(self.useURIButton, 0, wx.LEFT | wx.RIGHT, 5)
+
+        self.Sizer.Add(btnbox, 0, wx.TOP | wx.BOTTOM, 5)
         self.Bind(dv.EVT_DATAVIEW_SELECTION_CHANGED, self.OnSelectionChanged, self.dvc)
 
-
     def OnDeleteButton(self, evt):
         items = self.dvc.GetSelections()
         rows = [self.model.GetRow(item) for item in items]
@@ -150,13 +152,13 @@
         # Ask if user really wants to delete
         if wx.MessageBox(_('Are you sure to delete selected IDs?'),
                          _('Delete IDs'),
-                             wx.YES_NO | wx.CENTRE | wx.NO_DEFAULT) != wx.YES:
+                         wx.YES_NO | wx.CENTRE | wx.NO_DEFAULT) != wx.YES:
             return
 
         self.model.DeleteRows(rows)
 
     def OnSelectionChanged(self, evt):
-        if not self.isManager :
+        if not self.isManager:
             items = self.dvc.GetSelections()
             somethingSelected = len(items) > 0
             self.useURIButton.Enable(somethingSelected)
@@ -165,7 +167,6 @@
                 ID = self.model.GetValueByRow(row, COL_ID)
                 self.SelectIDCallBack(ID)
 
-
     def OnUseURIButton(self, evt):
         row = self.model.GetRow(self.dvc.GetSelections()[0])
         URI = self.model.GetValueByRow(row, COL_URI)
@@ -174,16 +175,18 @@
 
     def OnExportButton(self, evt):
         dialog = wx.FileDialog(self, _("Choose a file"),
-                               wildcard = _("PSK ZIP files (*.zip)|*.zip"), 
-                               style = wx.SAVE | wx.OVERWRITE_PROMPT)
+                               wildcard=_("PSK ZIP files (*.zip)|*.zip"),
+                               style=wx.SAVE | wx.OVERWRITE_PROMPT)
         if dialog.ShowModal() == wx.ID_OK:
             self.model.Export(dialog.GetPath())
 
-    def ShouldIReplaceCallback(self,existing,replacement):
-        ID,URI,DESC,LAST = existing
-        _ID,_URI,_DESC,_LAST = replacement
-        dlg = IDMergeDialog(self, 
-            _("Import IDs"), 
+    # pylint: disable=unused-variable
+    def ShouldIReplaceCallback(self, existing, replacement):
+        ID, URI, DESC, LAST = existing
+        _ID, _URI, _DESC, _LAST = replacement
+        dlg = IDMergeDialog(
+            self,
+            _("Import IDs"),
             (_("Replace information for ID {ID} ?") + "\n\n" +
              _("Existing:") + "\n    " +
              _("Description:") + " {DESC}\n    " +
@@ -194,9 +197,9 @@
              _("Last known URI:") + " {_URI}\n    " +
              _("Last connection:") + " {_LAST}\n").format(**locals()),
             _("Do the same for following IDs"),
-            [_("Replace"), _("Keep"),_("Cancel")])
-
-        answer = dlg.ShowModal() # return value ignored as we have "Ok" only anyhow
+            [_("Replace"), _("Keep"), _("Cancel")])
+
+        answer = dlg.ShowModal()  # return value ignored as we have "Ok" only anyhow
         if answer == wx.ID_CANCEL:
             return CANCEL
 
@@ -211,9 +214,8 @@
 
     def OnImportButton(self, evt):
         dialog = wx.FileDialog(self, _("Choose a file"),
-                               wildcard = _("PSK ZIP files (*.zip)|*.zip"), 
-                               style = wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
+                               wildcard=_("PSK ZIP files (*.zip)|*.zip"),
+                               style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
         if dialog.ShowModal() == wx.ID_OK:
             self.model.Import(dialog.GetPath(),
                               self.ShouldIReplaceCallback)
-
--- a/controls/__init__.py	Thu Mar 07 21:57:18 2019 +0100
+++ b/controls/__init__.py	Mon Mar 11 01:03:32 2019 +0100
@@ -44,4 +44,3 @@
 from controls.LogViewer import LogViewer
 from controls.CustomStyledTextCtrl import CustomStyledTextCtrl
 from controls.CustomToolTip import CustomToolTip
-
--- a/dialogs/IDManager.py	Thu Mar 07 21:57:18 2019 +0100
+++ b/dialogs/IDManager.py	Mon Mar 11 01:03:32 2019 +0100
@@ -1,10 +1,9 @@
 from __future__ import absolute_import
 
 import wx
-from connectors import ConnectorSchemes, EditorClassFromScheme
-from controls.DiscoveryPanel import DiscoveryPanel
 from controls.IDBrowser import IDBrowser
 
+
 class IDManager(wx.Dialog):
     def __init__(self, parent, ctr):
         self.ctr = ctr
@@ -12,7 +11,7 @@
                            name='IDManager', parent=parent,
                            title=_('URI Editor'),
                            style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
-                           size=(800,600))
+                           size=(800, 600))
         # start IDBrowser in manager mode
         self.browser = IDBrowser(self, ctr)
         self.Bind(wx.EVT_CHAR_HOOK, self.OnEscapeKey)
@@ -23,5 +22,3 @@
             self.EndModal(wx.ID_CANCEL)
         else:
             event.Skip()
-
-
--- a/dialogs/IDMergeDialog.py	Thu Mar 07 21:57:18 2019 +0100
+++ b/dialogs/IDMergeDialog.py	Mon Mar 11 01:03:32 2019 +0100
@@ -6,7 +6,8 @@
 from __future__ import absolute_import
 import wx
 
-# class RichMessageDialog is still not available in wxPython 3.0.2 
+
+# class RichMessageDialog is still not available in wxPython 3.0.2
 class IDMergeDialog(wx.Dialog):
     def __init__(self, parent, title, question, optiontext, button_texts):
         wx.Dialog.__init__(self, parent, title=title)
@@ -15,17 +16,19 @@
 
         message = wx.StaticText(self, label=question)
         main_sizer.AddWindow(message, border=20,
-                             flag = wx.ALIGN_CENTER_HORIZONTAL | wx.TOP | wx.LEFT | wx.RIGHT)
+                             flag=wx.ALIGN_CENTER_HORIZONTAL | wx.TOP | wx.LEFT | wx.RIGHT)
 
         self.check = wx.CheckBox(self, label=optiontext)
         main_sizer.AddWindow(self.check, border=20,
                              flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.ALIGN_CENTER_HORIZONTAL)
 
         buttons_sizer = wx.BoxSizer(wx.HORIZONTAL)
-        for label,wxID in zip(button_texts, [wx.ID_YES, wx.ID_NO, wx.ID_CANCEL]):
+        for label, wxID in zip(button_texts, [wx.ID_YES, wx.ID_NO, wx.ID_CANCEL]):
             Button = wx.Button(self, label=label)
+
             def OnButtonFactory(_wxID):
                 return lambda event: self.EndModal(_wxID)
+
             self.Bind(wx.EVT_BUTTON, OnButtonFactory(wxID), Button)
             buttons_sizer.AddWindow(Button)
 
--- a/dialogs/UriEditor.py	Thu Mar 07 21:57:18 2019 +0100
+++ b/dialogs/UriEditor.py	Mon Mar 11 01:03:32 2019 +0100
@@ -4,6 +4,7 @@
 from connectors import ConnectorSchemes, EditorClassFromScheme
 from controls.DiscoveryPanel import DiscoveryPanel
 
+
 class UriEditor(wx.Dialog):
     def _init_ctrls(self, parent):
         self.UriTypeChoice = wx.Choice(parent=self, choices=self.choices)
@@ -16,7 +17,7 @@
         self.mainSizer = wx.BoxSizer(wx.VERTICAL)
         typeSizer = wx.BoxSizer(wx.HORIZONTAL)
         typeSizer.Add(wx.StaticText(self, wx.ID_ANY, _("Scheme :")), border=5,
-                                    flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL)
+                      flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL)
         typeSizer.Add(self.UriTypeChoice, border=5, flag=wx.ALL)
         self.mainSizer.Add(typeSizer)
 
@@ -46,9 +47,9 @@
 
     def SetURI(self, uri):
         try:
-            scheme, loc = uri.strip().split("://",1)
+            scheme, loc = uri.strip().split("://", 1)
             scheme = scheme.upper()
-        except:
+        except Exception:
             scheme = None
 
         if scheme in ConnectorSchemes():
@@ -62,7 +63,6 @@
         if scheme is not None:
             self.scheme_editor.SetLoc(loc)
 
-
     def GetURI(self):
         if self.scheme is None:
             return self.scheme_editor.GetURI()
@@ -71,23 +71,22 @@
 
     def _replaceSchemeEditor(self, scheme):
         self.scheme = scheme
-       
+
         if self.scheme_editor is not None:
             self.editor_sizer.Detach(self.scheme_editor)
             self.scheme_editor.Destroy()
             self.scheme_editor = None
 
-        if scheme is not None :
+        if scheme is not None:
             EditorClass = EditorClassFromScheme(scheme)
-            self.scheme_editor = EditorClass(scheme,self)
-        else :
+            self.scheme_editor = EditorClass(scheme, self)
+        else:
             # None is for searching local network
-            self.scheme_editor = DiscoveryPanel(self) 
+            self.scheme_editor = DiscoveryPanel(self)
 
         self.editor_sizer.Add(self.scheme_editor)
         self.scheme_editor.Refresh()
-            
+
         self.editor_sizer.Layout()
         self.mainSizer.Layout()
         self.Fit()
-
--- a/dialogs/__init__.py	Thu Mar 07 21:57:18 2019 +0100
+++ b/dialogs/__init__.py	Mon Mar 11 01:03:32 2019 +0100
@@ -51,4 +51,3 @@
 from dialogs.BrowseValuesLibraryDialog import BrowseValuesLibraryDialog
 from dialogs.UriEditor import UriEditor
 from dialogs.IDManager import IDManager
-
--- a/runtime/PLCObject.py	Thu Mar 07 21:57:18 2019 +0100
+++ b/runtime/PLCObject.py	Mon Mar 11 01:03:32 2019 +0100
@@ -30,14 +30,12 @@
 import traceback
 from time import time
 import _ctypes  # pylint: disable=wrong-import-order
+from six.moves import xrange
 from past.builtins import execfile
-import Pyro.core as pyro
-import six
-from six.moves import _thread, xrange
 import md5
 from tempfile import mkstemp
 import shutil
-from functools import wraps
+from functools import wraps, partial
 
 from runtime.typemapping import TypeTranslator
 from runtime.loglevels import LogLevelsDefault, LogLevelsCount
@@ -79,7 +77,7 @@
 
 class PLCObject(object):
     def __init__(self, WorkingDir, argv, statuschange, evaluator, pyruntimevars):
-        self.workingdir = WorkingDir # must exits already
+        self.workingdir = WorkingDir  # must exits already
         self.tmpdir = os.path.join(WorkingDir, 'tmp')
         if os.path.exists(self.tmpdir):
             shutil.rmtree(self.tmpdir)
@@ -457,18 +455,18 @@
 
     @RunInMain
     def GetPLCID(self):
-        return getPSKID()
+        return getPSKID(partial(self.LogMessage, 0))
 
     def _init_blobs(self):
         self.blobs = {}
         if os.path.exists(self.tmpdir):
             shutil.rmtree(self.tmpdir)
         os.mkdir(self.tmpdir)
-    
+
     @RunInMain
     def SeedBlob(self, seed):
         blob = (mkstemp(dir=self.tmpdir) + (md5.new(),))
-        fobj, path, md5sum = blob
+        _fobj, _path, md5sum = blob
         md5sum.update(seed)
         newBlobID = md5sum.digest()
         self.blobs[newBlobID] = blob
@@ -481,17 +479,17 @@
         if blob is None:
             return None
 
-        fobj, path, md5sum = blob
+        fobj, _path, md5sum = blob
         md5sum.update(data)
         newBlobID = md5sum.digest()
-        os.write(fobj,data)
+        os.write(fobj, data)
         self.blobs[newBlobID] = blob
         return newBlobID
 
     @RunInMain
     def PurgeBlobs(self):
-        for fobj, path, md5sum in self.blobs:
-            os.close(fobj) 
+        for fobj, _path, _md5sum in self.blobs:
+            os.close(fobj)
         self._init_blobs()
 
     def _BlobAsFile(self, blobID, newpath):
@@ -500,10 +498,10 @@
         if blob is None:
             raise Exception(_("Missing data to create file: {}").format(newpath))
 
-        fobj, path, md5sum = blob
+        fobj, path, _md5sum = blob
         os.close(fobj)
         shutil.move(path, newpath)
-            
+
     @RunInMain
     def NewPLC(self, md5sum, plc_object, extrafiles):
         if self.PLCStatus in [PlcStatus.Stopped, PlcStatus.Empty, PlcStatus.Broken]:
@@ -608,8 +606,7 @@
 
     @RunInMain
     def GetTraceVariables(self, DebugToken):
-        if (DebugToken is not None and
-            DebugToken == self.DebugToken):
+        if (DebugToken is not None and DebugToken == self.DebugToken):
             return self.PLCStatus, self._TracesSwap()
         return PlcStatus.Broken, []
 
--- a/runtime/PyroServer.py	Thu Mar 07 21:57:18 2019 +0100
+++ b/runtime/PyroServer.py	Mon Mar 11 01:03:32 2019 +0100
@@ -41,7 +41,9 @@
         sys.stdout.flush()
 
     def PyroLoop(self, when_ready):
-        if self._to_be_published(): self.Publish()
+        if self._to_be_published():
+            self.Publish()
+
         while self.continueloop:
             Pyro.config.PYRO_MULTITHREADED = 0
             pyro.initServer()
--- a/runtime/ServicePublisher.py	Thu Mar 07 21:57:18 2019 +0100
+++ b/runtime/ServicePublisher.py	Mon Mar 11 01:03:32 2019 +0100
@@ -31,6 +31,7 @@
 
 service_type = '_Beremiz._tcp.local.'
 
+
 class ServicePublisher(object):
     def __init__(self, protocol):
         # type: fully qualified service type name
@@ -61,13 +62,13 @@
 
         if ip == "0.0.0.0":
             print("MDNS brodcasted on all interfaces")
-            interfaces=zeroconf.InterfaceChoice.All
+            interfaces = zeroconf.InterfaceChoice.All
             ip = self.gethostaddr()
         else:
-            interfaces=[ip]
+            interfaces = [ip]
 
         self.server = zeroconf.Zeroconf(interfaces=interfaces)
-        
+
         print("MDNS brodcasted service address :" + ip)
         self.ip_32b = socket.inet_aton(ip)
 
--- a/runtime/Stunnel.py	Thu Mar 07 21:57:18 2019 +0100
+++ b/runtime/Stunnel.py	Mon Mar 11 01:03:32 2019 +0100
@@ -1,3 +1,4 @@
+from __future__ import absolute_import
 import os
 from binascii import b2a_hqx
 try:
@@ -5,14 +6,15 @@
 except ImportError:
     from subprocess import call
 
-restart_stunnel_cmdline = ["/etc/init.d/S50stunnel","restart"]
+restart_stunnel_cmdline = ["/etc/init.d/S50stunnel", "restart"]
 
 _PSKpath = None
 
+
 def PSKgen(ID, PSKpath):
 
     # b2a_hqx output len is 4/3 input len
-    secret = os.urandom(192) # int(256/1.3333)
+    secret = os.urandom(192)  # int(256/1.3333)
     secretstring = b2a_hqx(secret)
 
     PSKstring = ID+":"+secretstring
@@ -20,6 +22,7 @@
         f.write(PSKstring)
     call(restart_stunnel_cmdline)
 
+
 def ensurePSK(ID, PSKpath):
     global _PSKpath
     _PSKpath = PSKpath
@@ -28,14 +31,14 @@
         # create if needed
         PSKgen(ID, PSKpath)
 
-def getPSKID():
-    if _PSKpath is not None :
+
+def getPSKID(errorlog):
+    if _PSKpath is not None:
         if not os.path.exists(_PSKpath):
-            confnodesroot.logger.write_error(
+            errorlog(
                 'Error: Pre-Shared-Key Secret in %s is missing!\n' % _PSKpath)
             return None
-        ID,_sep,PSK = open(_PSKpath).read().partition(':')
+        ID, _sep, PSK = open(_PSKpath).read().partition(':')
         PSK = PSK.rstrip('\n\r')
-        return (ID,PSK)
+        return (ID, PSK)
     return None
-    
--- a/runtime/Worker.py	Thu Mar 07 21:57:18 2019 +0100
+++ b/runtime/Worker.py	Mon Mar 11 01:03:32 2019 +0100
@@ -9,9 +9,9 @@
 
 from __future__ import absolute_import
 import sys
-import six
 import thread
 from threading import Lock, Condition
+import six
 
 
 class job(object):
--- a/runtime/spawn_subprocess.py	Thu Mar 07 21:57:18 2019 +0100
+++ b/runtime/spawn_subprocess.py	Mon Mar 11 01:03:32 2019 +0100
@@ -3,14 +3,16 @@
 
 # subset of subprocess built-in module using posix_spawn rather than fork.
 
-import posix_spawn
+from __future__ import absolute_import
 import os
 import signal
+import posix_spawn
 
 PIPE = "42"
 
+
 class Popen(object):
-    def __init__(self, args,stdin=None, stdout=None):
+    def __init__(self, args, stdin=None, stdout=None):
         self.returncode = None
         self.stdout = None
         self.stdin = None
@@ -40,7 +42,7 @@
 
     def _wait(self):
         if self.returncode is None:
-            self.returncode = os.waitpid(self.pid,0)[1]
+            self.returncode = os.waitpid(self.pid, 0)[1]
 
     def communicate(self):
         if self.stdin is not None:
@@ -50,7 +52,7 @@
             stdoutdata = self.stdout.read()
         else:
             stdoutdata = ""
-        
+
         # TODO
         stderrdata = ""
 
@@ -58,7 +60,7 @@
         if self.stdout is not None:
             self.stdout.close()
             self.stdout = None
-        
+
         return (stdoutdata, stderrdata)
 
     def wait(self):
@@ -74,7 +76,7 @@
     def poll(self):
         if self.returncode is None:
             pid, ret = os.waitpid(self.pid, os.WNOHANG)
-            if (pid,ret) != (0,0):
+            if (pid, ret) != (0, 0):
                 self.returncode = ret
 
                 if self.stdin is not None:
@@ -85,7 +87,7 @@
                     self.stdout = None
 
         return self.returncode
-        
+
     def kill(self):
         os.kill(self.pid, signal.SIGKILL)
 
@@ -96,23 +98,23 @@
             self.stdout.close()
             self.stdout = None
 
-        
 
 def call(*args):
     cmd = []
     if isinstance(args[0], str):
-        if len(args)==1:
+        if len(args) == 1:
             # oversimplified splitting of arguments,
             # TODO: care about use of simple and double quotes
             cmd = args[0].split()
         else:
             cmd = args
-    elif isinstance(args[0], list) and len(args)==1:
+    elif isinstance(args[0], list) and len(args) == 1:
         cmd = args[0]
     else:
         raise Exception("Wrong arguments passed to subprocess.call")
     pid = posix_spawn.posix_spawnp(cmd[0], cmd)
-    return os.waitpid(pid,0)
+    return os.waitpid(pid, 0)
+
 
 if __name__ == '__main__':
     # unit test
@@ -120,12 +122,12 @@
     p = Popen(["tr", "abc", "def"], stdin=PIPE, stdout=PIPE)
     p.stdin.write("blah")
     p.stdin.close()
-    print p.stdout.read()
+    print(p.stdout.read())
     p.wait()
 
     p = Popen(["tr", "abc", "def"], stdin=PIPE, stdout=PIPE)
     p.stdin.write("blah")
-    print p.communicate()
+    print(p.communicate())
 
     call("echo blah0")
     call(["echo", "blah1"])
--- a/wxglade_hmi/wxglade_hmi.py	Thu Mar 07 21:57:18 2019 +0100
+++ b/wxglade_hmi/wxglade_hmi.py	Mon Mar 11 01:03:32 2019 +0100
@@ -136,24 +136,24 @@
             [x["name"] for x in main_frames]) if len(main_frames) > 0 else "")
 
         declare_hmi = \
-"\n".join(["%(name)s = None\n" % x for x in main_frames]) + \
-"\n".join(["\n".join(["%(class)s.%(h)s = %(h)s" %
-                          dict(x, h=h) for h in x['handlers']])
-                      for x in hmi_objects]) + """\
+            "\n".join(["%(name)s = None\n" % x for x in main_frames]) + \
+            "\n".join(["\n".join(["%(class)s.%(h)s = %(h)s" % dict(x, h=h)
+                                  for h in x['handlers']])
+                       for x in hmi_objects]) + """\
 
 def OnCloseFrame(evt):
     wx.MessageBox(_("Please stop PLC to close"))
 
 def InitHMI():
-    """+ global_hmi + "\n" + "\n".join(["""\
+    """ + global_hmi + "\n" + "\n".join(["""\
     %(name)s = %(class)s(None)
     %(name)s.Bind(wx.EVT_CLOSE, OnCloseFrame)
     %(name)s.Show()
 
 """ % x for x in main_frames]) + """\
 def CleanupHMI():
-    """+ global_hmi + "\n" + "\n".join(["""\
-    if %(name)s is not None: 
+    """ + global_hmi + "\n" + "\n".join(["""\
+    if %(name)s is not None:
         %(name)s.Destroy()
 """ % x for x in main_frames])