ProjectFlask/Project.py

changeset 2
6cc80e4db8a7
child 3
265c3c2914e2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ProjectFlask/Project.py	Sun Nov 08 17:54:22 2020 +0100
@@ -0,0 +1,319 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2020 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the Flask project support.
+"""
+
+import os
+
+from PyQt5.QtCore import pyqtSlot, QObject, QProcess, QTimer
+from PyQt5.QtWidgets import QMenu
+
+from E5Gui import E5MessageBox
+from E5Gui.E5Action import E5Action
+from E5Gui.E5Application import e5App
+
+from Globals import isWindowsPlatform
+
+import UI.PixmapCache
+import Utilities
+
+
+class Project(QObject):
+    """
+    Class implementing the Flask project support.
+    """
+    def __init__(self, plugin, iconSuffix, parent=None):
+        """
+        Constructor
+        
+        @param plugin reference to the plugin object
+        @type ProjectFlaskPlugin
+        @param iconSuffix suffix for the icons
+        @type str
+        @param parent parent
+        @type QObject
+        """
+        super(Project, self).__init__(parent)
+        
+        self.__plugin = plugin
+        self.__iconSuffix = iconSuffix
+        self.__ui = parent
+
+        self.__e5project = e5App().getObject("Project")
+        self.__virtualEnvManager = e5App().getObject("VirtualEnvManager")
+        
+        self.__menus = {}   # dictionary with references to menus
+         
+        self.__serverProc = None
+       
+        self.__flaskVersions = {
+            "python": "",
+            "flask": "",
+            "werkzeug": "",
+        }
+    
+    def initActions(self):
+        """
+        Public method to define the Flask actions.
+        """
+        self.actions = []
+    
+        ##############################
+        ## about action below       ##
+        ##############################
+        
+        self.aboutFlaskAct = E5Action(
+            self.tr('About Flask'),
+            self.tr('About &Flask'),
+            0, 0,
+            self, 'flask_about')
+        self.aboutFlaskAct.setStatusTip(self.tr(
+            'Shows some information about Flask'))
+        self.aboutFlaskAct.setWhatsThis(self.tr(
+            """<b>About Flask</b>"""
+            """<p>Shows some information about Flask.</p>"""
+        ))
+        self.aboutFlaskAct.triggered.connect(self.__flaskInfo)
+        self.actions.append(self.aboutFlaskAct)
+    
+    def initMenu(self):
+        """
+        Public method to initialize the Flask menu.
+        
+        @return the menu generated
+        @rtype QMenu
+        """
+        self.__menus = {}   # clear menus references
+        
+        menu = QMenu(self.tr('&Flask'), self.__ui)
+        menu.setTearOffEnabled(True)
+        
+        menu.addAction(self.aboutFlaskAct)
+        
+        self.__menus["main"] = menu
+        
+        return menu
+    
+    def getMenu(self, name):
+        """
+        Public method to get a reference to the requested menu.
+        
+        @param name name of the menu
+        @type str
+        @return reference to the menu or None, if no menu with the given
+            name exists
+        @rtype QMenu or None
+        """
+        if name in self.__menus:
+            return self.__menus[name]
+        else:
+            return None
+    
+    def getMenuNames(self):
+        """
+        Public method to get the names of all menus.
+        
+        @return menu names
+        @rtype list of str
+        """
+        return list(self.__menus.keys())
+    
+    ##################################################################
+    ## slots below implement general functionality
+    ##################################################################
+    
+    def projectClosed(self):
+        """
+        Public method to handle the closing of a project.
+        """
+        if self.__serverProc is not None:
+            self.__serverProcFinished()
+    
+    # TODO: implement this correctly
+    def supportedPythonVariants(self):
+        """
+        Public method to get the supported Python variants.
+        
+        @return list of supported Python variants
+        @rtype list of str
+        """
+        variants = []
+        
+        virtEnv = self.__getVirtualEnvironment()
+        if virtEnv:
+            fullCmd = self.getFlaskCommand()
+            if fullCmd:
+                variants.append("Python3")
+        else:
+            fullCmd = self.getFlaskCommand()
+            if isWindowsPlatform():
+                if fullCmd:
+                    variants.append("Python3")
+            else:
+                fullCmds = Utilities.getExecutablePaths("flask")
+                for fullCmd in fullCmds:
+                    try:
+                        with open(fullCmd, 'r', encoding='utf-8') as f:
+                            l0 = f.readline()
+                    except (IOError, OSError):
+                        l0 = ""
+                    if self.__isSuitableForVariant("Python3", l0):
+                        variants.append("Python3")
+                        break
+        
+        return variants
+    
+    def __isSuitableForVariant(self, variant, line0):
+        """
+        Private method to test, if a detected command file is suitable for the
+        given Python variant.
+        
+        @param variant Python variant to test for
+        @type str
+        @param line0 first line of the executable
+        @type str
+        @return flag indicating a suitable file was found
+        @rtype bool
+        """
+        l0 = line0.lower()
+        ok = (variant.lower() in l0 or
+              "{0}.".format(variant[-1]) in l0)
+        ok |= "pypy3" in l0
+        
+        return ok
+    
+    def __getVirtualEnvironment(self):
+        """
+        Private method to get the path of the virtual environment.
+        
+        @return path of the virtual environment
+        @rtype str
+        """
+        language = self.__e5project.getProjectLanguage()
+        if language == "Python3":
+            venvName = self.__plugin.getPreferences(
+                "VirtualEnvironmentNamePy3")
+        else:
+            venvName = ""
+        if venvName:
+            virtEnv = self.__virtualEnvManager.getVirtualenvDirectory(
+                venvName)
+        else:
+            virtEnv = ""
+        
+        if virtEnv and not os.path.exists(virtEnv):
+            virtEnv = ""
+        
+        return virtEnv      # __IGNORE_WARNING_M834__
+    
+    def getFlaskCommand(self):
+        """
+        Public method to build the Flask command.
+        
+        @return full flask command
+        @rtype str
+        """
+        cmd = "flask"
+        
+        virtualEnv = self.__getVirtualEnvironment()
+        if isWindowsPlatform():
+            fullCmds = [
+                os.path.join(virtualEnv, "Scripts", cmd + '.exe'),
+                os.path.join(virtualEnv, "bin", cmd + '.exe'),
+                cmd     # fall back to just cmd
+            ]
+            for cmd in fullCmds:
+                if os.path.exists(cmd):
+                    break
+        else:
+            fullCmds = [
+                os.path.join(virtualEnv, "bin", cmd),
+                os.path.join(virtualEnv, "local", "bin", cmd),
+                Utilities.getExecutablePath(cmd),
+                cmd     # fall back to just cmd
+            ]
+            for cmd in fullCmds:
+                if os.path.exists(cmd):
+                    break
+        return cmd
+    
+    @pyqtSlot()
+    def __flaskInfo(self):
+        """
+        Private slot to show some info about Flask.
+        """
+        versions = self.getFlaskVersionStrings()
+        url = "https://flask.palletsprojects.com"
+        
+        msgBox = E5MessageBox.E5MessageBox(
+            E5MessageBox.Question,
+            self.tr("About Flask"),
+            self.tr(
+                "<p>Flask is a lightweight WSGI web application framework."
+                " It is designed to make getting started quick and easy,"
+                " with the ability to scale up to complex applications.</p>"
+                "<p><table>"
+                "<tr><td>Flask Version:</td><td>{0}</td></tr>"
+                "<tr><td>Werkzeug Version:</td><td>{1}</td></tr>"
+                "<tr><td>Python Version:</td><td>{2}</td></tr>"
+                "<tr><td>Flask URL:</td><td><a href=\"{3}\">"
+                "{3}</a></td></tr>"
+                "</table></p>"
+            ).format(versions["flask"], versions["werkzeug"],
+                     versions["python"], url),
+            modal=True,
+            buttons=E5MessageBox.Ok)
+        msgBox.setIconPixmap(UI.PixmapCache.getPixmap(
+            os.path.join("ProjectFlask", "icons",
+                         "flask64-{0}".format(self.__iconSuffix))))
+        msgBox.exec()
+    
+    def getFlaskVersionStrings(self):
+        """
+        Public method to get the Flask, Werkzeug and Python versions as a
+        string.
+        
+        @return dictionary containing the Flask, Werkzeug and Python versions
+        @rtype dict
+        """
+        if not self.__flaskVersions["flask"]:
+            cmd = self.getFlaskCommand()
+            proc = QProcess()
+            proc.start(cmd, ["--version"])
+            if proc.waitForFinished(10000):
+                output = str(proc.readAllStandardOutput(), "utf-8")
+                for line in output.lower().splitlines():
+                    key, version = line.strip().split(None, 1)
+                    self.__flaskVersions[key] = version
+        
+        return self.__flaskVersions
+    
+    ##################################################################
+    ## slots below implement run functions
+    ##################################################################
+    
+    def __runServer(self, logging=False):
+        """
+        Private slot to start the Pyramid Web server.
+        
+        @param logging flag indicating to enable logging
+        @type bool
+        """
+        # TODO: implement this
+    
+    def __serverProcFinished(self):
+        """
+        Private slot connected to the finished signal.
+        """
+        if (
+            self.__serverProc is not None and
+            self.__serverProc.state() != QProcess.NotRunning
+        ):
+            self.__serverProc.terminate()
+            QTimer.singleShot(2000, self.__serverProc.kill)
+            self.__serverProc.waitForFinished(3000)
+        self.__serverProc = None

eric ide

mercurial