# HG changeset patch # User Edouard Tisserant # Date 1552262612 -3600 # Node ID 7dd551ac2fa03c63068f5ae320ea58053370ba59 # Parent 3620395194549d76bc83b1f77f1cbcc0384c1a52 check_sources.sh makes me become even less productive diff -r 362039519454 -r 7dd551ac2fa0 Beremiz_service.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( diff -r 362039519454 -r 7dd551ac2fa0 PSKManagement.py --- 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 - - diff -r 362039519454 -r 7dd551ac2fa0 ProjectController.py --- 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() diff -r 362039519454 -r 7dd551ac2fa0 connectors/ConnectorBase.py --- 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) - diff -r 362039519454 -r 7dd551ac2fa0 connectors/PYRO/PSK_Adapter.py --- 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'(?PPYROLOCPSK)://(?P[^\s:]+):?(?P\d+)?/(?P\S*)',URI) + x = re.match(r'(?PPYROLOCPSK)://(?P[^\s:]+):?(?P\d+)?/(?P\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 diff -r 362039519454 -r 7dd551ac2fa0 connectors/PYRO/__init__.py --- 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), diff -r 362039519454 -r 7dd551ac2fa0 connectors/PYRO_dialog.py --- 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 '' - diff -r 362039519454 -r 7dd551ac2fa0 connectors/SchemeEditor.py --- 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} diff -r 362039519454 -r 7dd551ac2fa0 connectors/WAMP/__init__.py --- 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) diff -r 362039519454 -r 7dd551ac2fa0 connectors/WAMP_dialog.py --- 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) - diff -r 362039519454 -r 7dd551ac2fa0 connectors/__init__.py --- 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() diff -r 362039519454 -r 7dd551ac2fa0 controls/DiscoveryPanel.py --- 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" \ diff -r 362039519454 -r 7dd551ac2fa0 controls/IDBrowser.py --- 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) - diff -r 362039519454 -r 7dd551ac2fa0 controls/__init__.py --- 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 - diff -r 362039519454 -r 7dd551ac2fa0 dialogs/IDManager.py --- 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() - - diff -r 362039519454 -r 7dd551ac2fa0 dialogs/IDMergeDialog.py --- 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) diff -r 362039519454 -r 7dd551ac2fa0 dialogs/UriEditor.py --- 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() - diff -r 362039519454 -r 7dd551ac2fa0 dialogs/__init__.py --- 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 - diff -r 362039519454 -r 7dd551ac2fa0 runtime/PLCObject.py --- 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, [] diff -r 362039519454 -r 7dd551ac2fa0 runtime/PyroServer.py --- 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() diff -r 362039519454 -r 7dd551ac2fa0 runtime/ServicePublisher.py --- 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) diff -r 362039519454 -r 7dd551ac2fa0 runtime/Stunnel.py --- 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 - diff -r 362039519454 -r 7dd551ac2fa0 runtime/Worker.py --- 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): diff -r 362039519454 -r 7dd551ac2fa0 runtime/spawn_subprocess.py --- 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"]) diff -r 362039519454 -r 7dd551ac2fa0 wxglade_hmi/wxglade_hmi.py --- 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])