Project/ProjectProtocolsBrowser.py

changeset 5970
411f7ba577d5
parent 5969
584c21b6587a
child 5974
dfb291b5b620
--- a/Project/ProjectProtocolsBrowser.py	Mon Nov 13 20:20:06 2017 +0100
+++ b/Project/ProjectProtocolsBrowser.py	Tue Nov 14 19:13:28 2017 +0100
@@ -16,6 +16,7 @@
 
 import os
 import glob
+import sys
 
 from PyQt5.QtCore import QThread, pyqtSignal, QProcess
 from PyQt5.QtWidgets import QDialog, QApplication, QMenu
@@ -61,13 +62,6 @@
         @param parent parent widget of this browser
         @type QWidget
         """
-        self.__protoc = Preferences.getProtobuf("protoc")
-        if self.__protoc == "":
-            self.__protoc = Utilities.isWindowsPlatform() and \
-                "protoc.exe" or "protoc"
-        if not Utilities.isinpath(self.__protoc):
-            self.__protoc = None
-        
         ProjectBaseBrowser.__init__(self, project,
                                     ProjectBrowserProtocolsType, parent)
         
@@ -96,12 +90,19 @@
         self.dirMultiMenuActions = []
         
         self.sourceMenu = QMenu(self)
-        if self.__protoc is not None:
-            self.sourceMenu.addAction(
-                self.tr('Compile protocol'), self.__compileProtocol)
-            self.sourceMenu.addAction(
-                self.tr('Compile all protocols'),
-                self.__compileAllProtocols)
+        self.sourceMenu.addAction(
+            self.tr('Compile protocol'), self.__compileProtocol)
+        self.sourceMenu.addAction(
+            self.tr('Compile all protocols'),
+            self.__compileAllProtocols)
+        self.sourceMenu.addSeparator()
+        self.sourceMenu.addAction(
+            self.tr('Compile protocol as grpc'),
+            lambda: self.__compileProtocol(grpc=True))
+        self.sourceMenu.addAction(
+            self.tr('Compile all protocols as grpc'),
+            lambda: self.__compileAllProtocols(grpc=True))
+        self.sourceMenu.addSeparator()
         self.sourceMenu.addAction(self.tr('Open'), self._openItem)
         self.sourceMenu.addSeparator()
         act = self.sourceMenu.addAction(
@@ -133,12 +134,19 @@
             self.tr('Configure Protobuf...'), self.__configureProtobuf)
 
         self.menu = QMenu(self)
-        if self.__protoc is not None:
-            self.menu.addAction(
-                self.tr('Compile protocol'), self.__compileProtocol)
-            self.menu.addAction(
-                self.tr('Compile all protocols'),
-                self.__compileAllProtocols)
+        self.menu.addAction(
+            self.tr('Compile protocol'), self.__compileProtocol)
+        self.menu.addAction(
+            self.tr('Compile all protocols'),
+            self.__compileAllProtocols)
+        self.menu.addSeparator()
+        self.menu.addAction(
+            self.tr('Compile protocol as grpc'),
+            lambda: self.__compileProtocol(grpc=True))
+        self.menu.addAction(
+            self.tr('Compile all protocols as grpc'),
+            lambda: self.__compileAllProtocols(grpc=True))
+        self.menu.addSeparator()
         self.menu.addAction(self.tr('Open'), self._openItem)
         self.menu.addSeparator()
         self.menu.addAction(
@@ -157,11 +165,14 @@
             self.tr('Configure Protobuf...'), self.__configureProtobuf)
 
         self.backMenu = QMenu(self)
-        if self.__protoc is not None:
-            self.backMenu.addAction(
-                self.tr('Compile all protocols'),
-                self.__compileAllProtocols)
-            self.backMenu.addSeparator()
+        self.backMenu.addAction(
+            self.tr('Compile all protocols'),
+            self.__compileAllProtocols)
+        self.backMenu.addSeparator()
+        self.backMenu.addAction(
+            self.tr('Compile all protocols as grpc'),
+            lambda: self.__compileAllProtocols(grpc=True))
+        self.backMenu.addSeparator()
         self.backMenu.addAction(
             self.tr('Add protocols...'), self.project.addProtoFiles)
         self.backMenu.addAction(
@@ -179,10 +190,14 @@
 
         # create the menu for multiple selected files
         self.multiMenu = QMenu(self)
-        if self.__protoc is not None:
-            self.multiMenu.addAction(
-                self.tr('Compile protocols'),
-                self.__compileSelectedProtocols)
+        self.multiMenu.addAction(
+            self.tr('Compile protocols'),
+            self.__compileSelectedProtocols)
+        self.multiMenu.addSeparator()
+        self.multiMenu.addAction(
+            self.tr('Compile protocols as grpc'),
+            lambda: self.__compileSelectedProtocols(grpc=True))
+        self.multiMenu.addSeparator()
         self.multiMenu.addAction(self.tr('Open'), self._openItem)
         self.multiMenu.addSeparator()
         act = self.multiMenu.addAction(
@@ -202,11 +217,13 @@
             self.tr('Configure Protobuf...'), self.__configureProtobuf)
 
         self.dirMenu = QMenu(self)
-        if self.__protoc is not None:
-            self.dirMenu.addAction(
-                self.tr('Compile all protocols'),
-                self.__compileAllProtocols)
-            self.dirMenu.addSeparator()
+        self.dirMenu.addAction(
+            self.tr('Compile all protocols'),
+            self.__compileAllProtocols)
+        self.dirMenu.addSeparator()
+        self.dirMenu.addAction(
+            self.tr('Compile all protocols as grpc'),
+            lambda: self.__compileAllProtocols(grpc=True))
         act = self.dirMenu.addAction(
             self.tr('Remove from project'), self._removeFile)
         self.dirMenuActions.append(act)
@@ -233,11 +250,13 @@
             self.tr('Configure Protobuf...'), self.__configureProtobuf)
         
         self.dirMultiMenu = QMenu(self)
-        if self.__protoc is not None:
-            self.dirMultiMenu.addAction(
-                self.tr('Compile all protocols'),
-                self.__compileAllProtocols)
-            self.dirMultiMenu.addSeparator()
+        self.dirMultiMenu.addAction(
+            self.tr('Compile all protocols'),
+            self.__compileAllProtocols)
+        self.dirMultiMenu.addSeparator()
+        self.dirMultiMenu.addAction(
+            self.tr('Compile all protocols as grpc'),
+            lambda: self.__compileAllProtocols(grpc=True))
         self.dirMultiMenu.addAction(
             self.tr('Add protocols...'), self.project.addProtoFiles)
         self.dirMultiMenu.addAction(
@@ -437,6 +456,33 @@
     ##  Methods to handle the various compile commands
     ###########################################################################
     
+    def __getCompilerCommand(self, grpc):
+        """
+        Private method to get the compiler command.
+        
+        @param grpc flag indicating to get a grpc command
+        @type bool
+        @return tuple giving the executable and its parameter list
+        @rtype tuple of (str, list of str)
+        """
+        exe = None
+        exeArgs = []
+        
+        if grpc:
+            exe = Preferences.getProtobuf("grpcPython")
+            if exe == "":
+                exe = sys.executable
+            exeArgs = ['-m', 'grpc_tools.protoc']
+        else:
+            exe = Preferences.getProtobuf("protoc")
+            if exe == "":
+                exe = Utilities.isWindowsPlatform() and \
+                    "protoc.exe" or "protoc"
+            if not Utilities.isinpath(exe):
+                exe = None
+        
+        return exe, exeArgs
+    
     def __readStdout(self):
         """
         Private slot to handle the readyReadStandardOutput signal of the
@@ -471,18 +517,24 @@
             s += error
             self.appendStderr.emit(s)
         
-    def __compileProtocolDone(self, exitCode, exitStatus):
+    def __compileProtoDone(self, exitCode, exitStatus, grpc):
         """
         Private slot to handle the finished signal of the protoc process.
         
-        @param exitCode exit code of the process (integer)
-        @param exitStatus exit status of the process (QProcess.ExitStatus)
+        @param exitCode exit code of the process
+        @type int
+        @param exitStatus exit status of the process
+        @type QProcess.ExitStatus
+        @param grpc flag indicating to compile as grpc files
+        @type bool
         """
         self.__compileRunning = False
         ui = e5App().getObject("UserInterface")
         if exitStatus == QProcess.NormalExit and exitCode == 0:
             path = os.path.dirname(self.__protoFile)
             fileList = glob.glob(os.path.join(path, "*_pb2.py"))
+            if grpc:
+                fileList += glob.glob(os.path.join(path, "*_pb2_grpc.py"))
             for file in fileList:
                 self.project.appendFile(file)
             if not self.noDialog and not ui.notificationsEnabled():
@@ -514,64 +566,87 @@
                         "The compilation of the protocol file failed."))
         self.compileProc = None
         
-    def __compileProto(self, fn, noDialog=False, progress=None):
+    def __compileProto(self, fn, noDialog=False, progress=None, grpc=False):
         """
         Private method to compile a .proto file to Python.
 
-        @param fn filename of the .proto file to be compiled (string)
-        @param noDialog flag indicating silent operations (boolean)
-        @param progress reference to the progress dialog (E5ProgressDialog)
-        @return reference to the compile process (QProcess)
+        @param fn filename of the .proto file to be compiled
+        @type str
+        @param noDialog flag indicating silent operations
+        @type bool
+        @param progress reference to the progress dialog
+        @type E5ProgressDialog
+        @param grpc flag indicating to compile as grpc files
+        @type bool
+        @return reference to the compile process
+        @rtype QProcess
         """
-        self.compileProc = QProcess()
-        args = []
-        
-        fn = os.path.join(self.project.ppath, fn)
-        self.__protoFile = fn
-        
-        srcPath = os.path.dirname(fn)
-        args.append("--proto_path={0}".format(srcPath))
-        args.append("--python_out={0}".format(srcPath))
-        args.append(fn)
-        
-        self.compileProc.finished.connect(self.__compileProtocolDone)
-        self.compileProc.readyReadStandardOutput.connect(self.__readStdout)
-        self.compileProc.readyReadStandardError.connect(self.__readStderr)
-        
-        self.noDialog = noDialog
-        self.compileProc.start(self.__protoc, args)
-        procStarted = self.compileProc.waitForStarted(5000)
-        if procStarted:
-            self.__compileRunning = True
-            return self.compileProc
+        exe, exeArgs = self.__getCompilerCommand(grpc)
+        if exe:
+            self.compileProc = QProcess()
+            args = []
+            
+            fn = os.path.join(self.project.ppath, fn)
+            self.__protoFile = fn
+            
+            srcPath = os.path.dirname(fn)
+            args.append("--proto_path={0}".format(srcPath))
+            args.append("--python_out={0}".format(srcPath))
+            if grpc:
+                args.append("--grpc_python_out={0}".format(srcPath))
+            args.append(fn)
+            
+            self.compileProc.finished.connect(
+                lambda c, s: self.__compileProtoDone(c, s, grpc))
+            self.compileProc.readyReadStandardOutput.connect(self.__readStdout)
+            self.compileProc.readyReadStandardError.connect(self.__readStderr)
+            
+            self.noDialog = noDialog
+            self.compileProc.start(exe, exeArgs + args)
+            procStarted = self.compileProc.waitForStarted(5000)
+            if procStarted:
+                self.__compileRunning = True
+                return self.compileProc
+            else:
+                self.__compileRunning = False
+                if progress is not None:
+                    progress.cancel()
+                E5MessageBox.critical(
+                    self,
+                    self.tr('Process Generation Error'),
+                    self.tr(
+                        '<p>Could not start {0}.<br>'
+                        'Ensure that it is in the search path.</p>'
+                    ).format(exe))
+                return None
         else:
-            self.__compileRunning = False
-            if progress is not None:
-                progress.cancel()
             E5MessageBox.critical(
                 self,
-                self.tr('Process Generation Error'),
-                self.tr(
-                    '<p>Could not start {0}.<br>'
-                    'Ensure that it is in the search path.</p>'
-                ).format(self.__protoc))
+                self.tr('Compiler Invalid'),
+                self.tr('The configured compiler is invalid.'))
             return None
         
-    def __compileProtocol(self):
+    def __compileProtocol(self, grpc=False):
         """
         Private method to compile a protocol to Python.
+        
+        @param grpc flag indicating to compile as grpc files
+        @type bool
         """
-        if self.__protoc is not None:
+        if self.__getCompilerCommand(grpc)[0] is not None:
             itm = self.model().item(self.currentIndex())
             fn2 = itm.fileName()
             fn = self.project.getRelativePath(fn2)
-            self.__compileProto(fn)
+            self.__compileProto(fn, grpc=grpc)
         
-    def __compileAllProtocols(self):
+    def __compileAllProtocols(self, grpc=False):
         """
         Private method to compile all protocols to Python.
+        
+        @param grpc flag indicating to compile as grpc files
+        @type bool
         """
-        if self.__protoc is not None:
+        if self.__getCompilerCommand(grpc)[0] is not None:
             numProtos = len(self.project.pdata["PROTOCOLS"])
             progress = E5ProgressDialog(
                 self.tr("Compiling Protocols..."),
@@ -586,7 +661,7 @@
                 progress.setValue(i)
                 if progress.wasCanceled():
                     break
-                proc = self.__compileProto(fn, True, progress)
+                proc = self.__compileProto(fn, True, progress, grpc=grpc)
                 if proc is not None:
                     while proc.state() == QProcess.Running:
                         QApplication.processEvents()
@@ -598,11 +673,14 @@
             
             progress.setValue(numProtos)
         
-    def __compileSelectedProtocols(self):
+    def __compileSelectedProtocols(self, grpc=False):
         """
         Private method to compile selected protocols to Python.
+        
+        @param grpc flag indicating to compile as grpc files
+        @type bool
         """
-        if self.__protoc is not None:
+        if self.__getCompilerCommand(grpc)[0] is not None:
             items = self.getSelectedItems()
             
             files = [self.project.getRelativePath(itm.fileName())
@@ -621,7 +699,7 @@
                 progress.setValue(i)
                 if progress.wasCanceled():
                     break
-                proc = self.__compileProto(fn, True, progress)
+                proc = self.__compileProto(fn, True, progress, grpc=grpc)
                 if proc is not None:
                     while proc.state() == QProcess.Running:
                         QApplication.processEvents()

eric ide

mercurial