WIP python3 support for runtime
authorEdouard Tisserant <edouard.tisserant@gmail.com>
Fri, 12 May 2023 18:09:55 +0200
changeset 3800 a5a6ee271e65
parent 3799 2b995a4963a4
child 3801 6839dbb9a1df
WIP python3 support for runtime
Beremiz_service.py
runtime/NevowServer.py
runtime/PyroServer.py
--- a/Beremiz_service.py	Wed May 10 17:02:05 2023 +0200
+++ b/Beremiz_service.py	Fri May 12 18:09:55 2023 +0200
@@ -31,7 +31,7 @@
 import shlex
 import traceback
 import threading
-from threading import Thread, Semaphore, Lock, currentThread
+from threading import Thread, Semaphore, Lock, current_thread
 import builtins
 from functools import partial
 
@@ -208,19 +208,7 @@
     # Define locale domain
     loc.AddCatalog(domain)
 
-    global default_locale
-    default_locale = locale.getdefaultlocale()[1]
-
-    # sys.stdout.encoding = default_locale
-    # if Beremiz_service is started from Beremiz IDE
-    # sys.stdout.encoding is None (that means 'ascii' encoding').
-    # And unicode string returned by wx.GetTranslation() are
-    # automatically converted to 'ascii' string.
-    def unicode_translation(message):
-        return wx.GetTranslation(message).encode(default_locale)
-
-    builtins.__dict__['_'] = unicode_translation
-    # builtins.__dict__['_'] = wx.GetTranslation
+    builtins.__dict__['_'] = wx.GetTranslation
 
 
 # Life is hard... have a candy.
@@ -371,7 +359,7 @@
                 _servicename = self.pyroserver.servicename
                 _servicename = '' if _servicename is None else _servicename
                 dlg = ParamsEntryDialog(None, _("Enter a name "), defaultValue=_servicename)
-                dlg.SetTests([(lambda name: len(name) is not 0, _("Name must not be null!"))])
+                dlg.SetTests([(lambda name: len(name) != 0, _("Name must not be null!"))])
                 if dlg.ShowModal() == wx.ID_OK:
                     self.pyroserver.servicename = dlg.GetValue()
                     self.pyroserver.Restart()
@@ -448,10 +436,10 @@
         obj.res = default_evaluator(tocall, *args, **kwargs)
         wx_eval_lock.release()
 
-    main_thread_id = currentThread().ident
+    main_thread_id = current_thread().ident
     def evaluator(tocall, *args, **kwargs):
         # To prevent deadlocks, check if current thread is not one already main
-        current_id = currentThread().ident
+        current_id = current_thread().ident
 
         if main_thread_id != current_id:
             o = type('', (object,), dict(call=(tocall, args, kwargs), res=None))
--- a/runtime/NevowServer.py	Wed May 10 17:02:05 2023 +0200
+++ b/runtime/NevowServer.py	Fri May 12 18:09:55 2023 +0200
@@ -29,7 +29,7 @@
 import collections
 import shutil
 import platform as platform_module
-from zope.interface import implements
+from zope.interface import implementer
 from nevow import appserver, inevow, tags, loaders, athena, url, rend
 from nevow.page import renderer
 from nevow.static import File
@@ -44,7 +44,7 @@
 
 PAGE_TITLE = 'Beremiz Runtime Web Interface'
 
-xhtml_header = '''<?xml version="1.0" encoding="utf-8"?>
+xhtml_header = b'''<?xml version="1.0" encoding="utf-8"?>
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
 "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
 '''
@@ -248,6 +248,7 @@
 
 extensions_settings_od = collections.OrderedDict()
 
+@implementer(ISettings)
 class SettingsPage(rend.Page):
     # We deserve a slash
     addSlash = True
@@ -255,8 +256,6 @@
     # This makes webform_css url answer some default CSS
     child_webform_css = webform.defaultCSS
     child_webinterface_css = File(paths.AbsNeighbourFile(__file__, 'webinterface.css'), 'text/css')
-
-    implements(ISettings)
    
     def __getattr__(self, name):
         global extensions_settings_od
--- a/runtime/PyroServer.py	Wed May 10 17:02:05 2023 +0200
+++ b/runtime/PyroServer.py	Fri May 12 18:09:55 2023 +0200
@@ -14,11 +14,42 @@
 import sys
 import os
 
-import Pyro
-import Pyro.core as pyro
+import Pyro5
+import Pyro5.server
+
 import runtime
 from runtime.ServicePublisher import ServicePublisher
 
+def make_pyro_exposed_stub(method_name):
+    stub = lambda self, *args, **kwargs: \
+        getattr(self.plc_object_instance, method_name)(self, *args, **kwargs)
+    stub.__name__ = method_name
+    Pyro5.server.expose(stub)
+    return stub
+    
+
+class PLCObjectPyroAdapter(type("PLCObjectPyroStubs", (), {
+    name: make_pyro_exposed_stub(name) for name in [
+        "AppendChunkToBlob",
+        "GetLogMessage",
+        "GetPLCID",
+        "GetPLCstatus",
+        "GetTraceVariables",
+        "MatchMD5", 
+        "NewPLC",
+        "PurgeBlobs",
+        "RemoteExec",
+        "RepairPLC",
+        "ResetLogCount",
+        "SeedBlob",
+        "SetTraceVariablesList",
+        "StartPLC",
+        "StopPLC"
+    ]
+})):
+    def __init__(self, plc_object_instance):
+        self.plc_object_instance = plc_object_instance
+    
 
 class PyroServer(object):
     def __init__(self, servicename, ip_addr, port):
@@ -47,33 +78,14 @@
             self.Publish()
 
         while self.continueloop:
-            Pyro.config.PYRO_MULTITHREADED = 0
-            pyro.initServer()
-            self.daemon = pyro.Daemon(host=self.ip_addr, port=self.port)
+            self.daemon = Pyro5.server.Daemon(host=self.ip_addr, port=self.port)
 
-            # pyro never frees memory after connection close if no timeout set
-            # taking too small timeout value may cause
-            # unwanted diconnection when IDE is kept busy for long periods
-            self.daemon.setTimeout(60)
-
-            pyro_obj = Pyro.core.ObjBase()
-            pyro_obj.delegateTo(runtime.GetPLCObjectSingleton())
-
-            self.daemon.connect(pyro_obj, "PLCObject")
+            self.daemon.register(PLCObjectPyroAdapter(runtime.GetPLCObjectSingleton()), "PLCObject")
 
             when_ready()
 
-            # "pipe to self" trick to to accelerate runtime shutdown 
-            # instead of waiting for arbitrary pyro timeout.
-            others = []
-            if not sys.platform.startswith('win'):
-                self.piper, self.pipew = os.pipe()
-                others.append(self.piper)
+            self.daemon.requestLoop()
 
-            self.daemon.requestLoop(others=others, callback=lambda x: None)
-            self.piper, self.pipew = None, None
-            if hasattr(self, 'sock'):
-                self.daemon.sock.close()
         self.Unpublish()
 
     def Restart(self):
@@ -81,8 +93,7 @@
 
     def Quit(self):
         self.continueloop = False
-        self.daemon.shutdown(True)
-        self.daemon.closedown()
+        self.daemon.shutdown()
         if not sys.platform.startswith('win'):
             if self.pipew is not None:
                 os.write(self.pipew, "goodbye")