ProjectFlask/Project.py

Thu, 19 Nov 2020 18:34:05 +0100

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Thu, 19 Nov 2020 18:34:05 +0100
changeset 14
d2da14b2a233
parent 13
ed33cdfca13d
child 15
3f5c05eb2d5f
permissions
-rw-r--r--

Continued implementing pybabel translations support.

# -*- 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, QProcessEnvironment, QTimer
)
from PyQt5.QtWidgets import QMenu, QDialog

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.__hooksInstalled = False
         
        self.__serverDialog = None
        self.__routesDialog = None
        self.__shellProcess = None
        
        self.__projectData = {
            "flask": {},
            "pybabel": {},
        }
        
        self.__flaskVersions = {
            "python": "",
            "flask": "",
            "werkzeug": "",
        }
    
    def initActions(self):
        """
        Public method to define the Flask actions.
        """
        self.actions = []
        
        ##############################
        ## run actions below        ##
        ##############################
        
        self.runServerAct = E5Action(
            self.tr('Run Server'),
            self.tr('Run &Server'),
            0, 0,
            self, 'flask_run_server')
        self.runServerAct.setStatusTip(self.tr(
            'Starts the Flask Web server'))
        self.runServerAct.setWhatsThis(self.tr(
            """<b>Run Server</b>"""
            """<p>Starts the Flask Web server.</p>"""
        ))
        self.runServerAct.triggered.connect(self.__runServer)
        self.actions.append(self.runServerAct)
        
        self.runDevServerAct = E5Action(
            self.tr('Run Development Server'),
            self.tr('Run &Development Server'),
            0, 0,
            self, 'flask_run_dev_server')
        self.runDevServerAct.setStatusTip(self.tr(
            'Starts the Flask Web server in development mode'))
        self.runDevServerAct.setWhatsThis(self.tr(
            """<b>Run Development Server</b>"""
            """<p>Starts the Flask Web server in development mode.</p>"""
        ))
        self.runDevServerAct.triggered.connect(self.__runDevelopmentServer)
        self.actions.append(self.runDevServerAct)
        
        self.askForServerOptionsAct = E5Action(
            self.tr('Ask for Server Start Options'),
            self.tr('Ask for Server Start Options'),
            0, 0,
            self, 'flask_ask_server_options')
        self.askForServerOptionsAct.setStatusTip(self.tr(
            'Ask for server start options'))
        self.askForServerOptionsAct.setWhatsThis(self.tr(
            """<b>Ask for Server Start Options</b>"""
            """<p>Asks for server start options before the Flask Web server"""
            """ is started. If this is unchecked, the server is started with"""
            """ default parameters.</p>"""
        ))
        self.askForServerOptionsAct.setCheckable(True)
        self.actions.append(self.askForServerOptionsAct)
        
        ###############################
        ## shell action below        ##
        ###############################
        
        self.runPythonShellAct = E5Action(
            self.tr('Start Flask Python Console'),
            self.tr('Start Flask &Python Console'),
            0, 0,
            self, 'flask_python_console')
        self.runPythonShellAct.setStatusTip(self.tr(
            'Starts an interactive Python interpreter'))
        self.runPythonShellAct.setWhatsThis(self.tr(
            """<b>Start Flask Python Console</b>"""
            """<p>Starts an interactive Python interpreter.</p>"""
        ))
        self.runPythonShellAct.triggered.connect(self.__runPythonShell)
        self.actions.append(self.runPythonShellAct)
        
        ################################
        ## routes action below        ##
        ################################
        
        self.showRoutesAct = E5Action(
            self.tr('Show Routes'),
            self.tr('Show &Routes'),
            0, 0,
            self, 'flask_show_routes')
        self.showRoutesAct.setStatusTip(self.tr(
            'Shows a dialog with the routes of the flask app'))
        self.showRoutesAct.setWhatsThis(self.tr(
            """<b>Show Routes</b>"""
            """<p>Shows a dialog with the routes of the flask app.</p>"""
        ))
        self.showRoutesAct.triggered.connect(self.__showRoutes)
        self.actions.append(self.showRoutesAct)
        
        ##################################
        ## database action below        ##
        ##################################
        
        self.initDatabaseAct = E5Action(
            self.tr('Initialize Database'),
            self.tr('&Initialize Database'),
            0, 0,
            self, 'flask_init_database')
        self.initDatabaseAct.setStatusTip(self.tr(
            'Shows a dialog with the result of the database creation'))
        self.initDatabaseAct.setWhatsThis(self.tr(
            """<b>Initialize Database</b>"""
            """<p>Shows a dialog with the result of the database"""
            """ creation.</p>"""
        ))
        self.initDatabaseAct.triggered.connect(self.__initDatabase)
        self.actions.append(self.initDatabaseAct)
        
        ##################################
        ## database action below        ##
        ##################################
        
        self.pybabelConfigAct = E5Action(
            self.tr('Configure PyBabel'),
            self.tr('Configure Py&Babel'),
            0, 0,
            self, 'flask_config_pybabel')
        self.pybabelConfigAct.setStatusTip(self.tr(
            'Shows a dialog to edit the configuration for pybabel'))
        self.pybabelConfigAct.setWhatsThis(self.tr(
            """<b>Configure PyBabel</b>"""
            """<p>Shows a dialog to edit the configuration for pybabel.</p>"""
        ))
        self.pybabelConfigAct.triggered.connect(self.__configurePybabel)
        self.actions.append(self.pybabelConfigAct)
        
        ##################################
        ## documentation action below   ##
        ##################################
        
        self.documentationAct = E5Action(
            self.tr('Documentation'),
            self.tr('D&ocumentation'),
            0, 0,
            self, 'flask_documentation')
        self.documentationAct.setStatusTip(self.tr(
            'Shows the help viewer with the Flask documentation'))
        self.documentationAct.setWhatsThis(self.tr(
            """<b>Documentation</b>"""
            """<p>Shows the help viewer with the Flask documentation.</p>"""
        ))
        self.documentationAct.triggered.connect(self.__showDocumentation)
        self.actions.append(self.documentationAct)
    
        ##############################
        ## 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.addSection("flask run")
        menu.addAction(self.runServerAct)
        menu.addAction(self.runDevServerAct)
        menu.addAction(self.askForServerOptionsAct)
        menu.addSection("flask shell")
        menu.addAction(self.runPythonShellAct)
        menu.addSection("flask routes")
        menu.addAction(self.showRoutesAct)
        menu.addSection("flask init-db")
        menu.addAction(self.initDatabaseAct)
        menu.addSection(self.tr("Translations"))
        menu.addAction(self.pybabelConfigAct)
        menu.addSection(self.tr("Various"))
        menu.addAction(self.documentationAct)
        menu.addSeparator()
        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())
    
    def registerOpenHook(self):
        """
        Public method to register the open hook to open a translations file
        in a translations editor.
        """
        if self.__hooksInstalled:
            editor = self.__plugin.getPreferences("TranslationsEditor")
            if editor:
                self.__translationsBrowser.addHookMethodAndMenuEntry(
                    "open", self.openPOEditor,
                    self.tr("Open with {0}").format(
                        os.path.basename(editor)))
            else:
                self.__translationsBrowser.removeHookMethod("open")
    
    def projectOpenedHooks(self):
        """
        Public method to add our hook methods.
        """
        if self.__e5project.getProjectType() == "Flask":
##            self.__formsBrowser = (
##                e5App().getObject("ProjectBrowser")
##                .getProjectBrowser("forms"))
##            self.__formsBrowser.addHookMethodAndMenuEntry(
##                "newForm", self.newForm, self.tr("New template..."))
##            
            if self.flaskBabelAvailable():
                self.__e5project.projectLanguageAddedByCode.connect(
                    self.__projectLanguageAdded)
                self.__translationsBrowser = (
                    e5App().getObject("ProjectBrowser")
                    .getProjectBrowser("translations"))
                self.__translationsBrowser.addHookMethodAndMenuEntry(
                    "extractMessages", self.extractMessages,
                    self.tr("Extract Messages"))
                self.__translationsBrowser.addHookMethodAndMenuEntry(
                    "releaseAll", self.compileCatalogs,
                    self.tr("Compile All Catalogs"))
                self.__translationsBrowser.addHookMethodAndMenuEntry(
                    "releaseSelected", self.compileSelectedCatalogs,
                    self.tr("Compile Selected Catalogs"))
                self.__translationsBrowser.addHookMethodAndMenuEntry(
                    "generateAll", self.updateCatalogs,
                    self.tr("Update All Catalogs"))
                self.__translationsBrowser.addHookMethodAndMenuEntry(
                    "generateSelected", self.updateSelectedCatalogs,
                    self.tr("Update Selected Catalogs"))
                
                self.__hooksInstalled = True
            
                self.registerOpenHook()
    
    def projectClosedHooks(self):
        """
        Public method to remove our hook methods.
        """
        if self.__hooksInstalled:
##            self.__formsBrowser.removeHookMethod("newForm")
##            self.__formsBrowser = None
##            
            self.__e5project.projectLanguageAddedByCode.disconnect(
                self.__projectLanguageAdded)
            self.__translationsBrowser.removeHookMethod("extractMessages")
            self.__translationsBrowser.removeHookMethod("releaseAll")
            self.__translationsBrowser.removeHookMethod("releaseSelected")
            self.__translationsBrowser.removeHookMethod("generateAll")
            self.__translationsBrowser.removeHookMethod("generateSelected")
            self.__translationsBrowser.removeHookMethod("open")
            self.__translationsBrowser = None
        
        self.__hooksInstalled = False
    
    ##################################################################
    ## slots below implement general functionality
    ##################################################################
    
    def projectClosed(self):
        """
        Public method to handle the closing of a project.
        """
        for dlg in (self.__serverDialog, self.__routesDialog):
            if dlg is not None:
                dlg.close()
    
    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
        """
        return self.__getFullCommand("flask")
    
    def getBabelCommand(self):
        """
        Public method to build the Babel command.
        
        @return full pybabel command
        @rtype str
        """
        return self.__getFullCommand("pybabel")
    
    def __getFullCommand(self, command):
        """
        Private method to get the full command for a given command name.
        
        @param command command name
        @type str
        @return full command
        @rtype str
        """
        virtualEnv = self.__getVirtualEnvironment()
        if isWindowsPlatform():
            fullCmds = [
                os.path.join(virtualEnv, "Scripts", command + '.exe'),
                os.path.join(virtualEnv, "bin", command + '.exe'),
                command     # fall back to just cmd
            ]
        else:
            fullCmds = [
                os.path.join(virtualEnv, "bin", command),
                os.path.join(virtualEnv, "local", "bin", command),
                Utilities.getExecutablePath(command),
                command     # fall back to just cmd
            ]
        for command in fullCmds:
            if os.path.exists(command):
                break
        return command
    
    @pyqtSlot()
    def __flaskInfo(self):
        """
        Private slot to show some info about Flask.
        """
        versions = self.getFlaskVersionStrings()
        url = "https://palletsprojects.com/p/flask/"
        
        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}\">"
                "The Pallets Projects - Flask</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
    
    def prepareRuntimeEnvironment(self, development=False):
        """
        Public method to prepare a QProcessEnvironment object and determine
        the appropriate working directory.
        
        @param development flag indicating development mode
        @type bool
        @return tuple containing the working directory and a prepared
            environment object to be used with QProcess
        @rtype tuple of (str, QProcessEnvironment)
        """
        workdir, app = self.getApplication()
        env = QProcessEnvironment.systemEnvironment()
        env.insert("FLASK_APP", app)
        if development:
            env.insert("FLASK_ENV", "development")
        
        return workdir, env
    
    def getApplication(self):
        """
        Public method to determine the application name and the respective
        working directory.
        
        @return tuple containing the working directory and the application name
        @rtype tuple of (str, str)
        """
        mainScript = self.__e5project.getMainScript(normalized=True)
        if not mainScript:
            E5MessageBox.critical(
                self.__ui,
                self.tr("Prepare Environment"),
                self.tr("""The project has no configured main script"""
                        """ (= Flask application). Aborting..."""))
            return "", None
        
        scriptPath, scriptName = os.path.split(mainScript)
        if scriptName == "__init__.py":
            workdir, app = os.path.split(scriptPath)
        else:
            workdir, app = scriptPath, scriptName
        return workdir, app
    
    def getData(self, category, key):
        """
        Public method to get data stored in the project store.
        
        @param category data category
        @type str
        @param key data key
        @type str
        @return referenced data
        @rtype any
        """
        if category not in self.__projectData:
            self.__projectData[category] = {}
        
        if not self.__projectData[category]:
            data = self.__e5project.getData(
                "PROJECTTYPESPECIFICDATA", category)
            if data is not None:
                self.__projectData[category] = data
        
        data = self.__projectData[category]
        if not key:
            # return complete category dictionary
            return data
        elif key in data:
            # return individual entry
            return data[key]
        else:
            # failure
            return None
    
    def setData(self, category, key, value):
        """
        Public method to store data in the project store.
        
        @param category data category
        @type str
        @param key data key
        @type str
        @param value data to be stored
        @type any (serializable type)
        """
        if category not in self.__projectData:
            self.__projectData[category] = {}
        
        if not self.__projectData[category]:
            data = self.__e5project.getData(
                "PROJECTTYPESPECIFICDATA", category)
            if data is not None:
                self.__projectData[category] = data
        
        if not key:
            # update the complete category
            self.__projectData[category] = value
        else:
            # update individual entry
            self.__projectData[category][key] = value
        
        self.__e5project.setData(
            "PROJECTTYPESPECIFICDATA", category, self.__projectData[category])
    
    ##################################################################
    ## slots below implement documentation functions
    ##################################################################
    
    def __showDocumentation(self):
        """
        Private slot to show the helpviewer with the Flask documentation.
        """
        page = self.__plugin.getPreferences("FlaskDocUrl")
        self.__ui.launchHelpViewer(page)
    
    ##################################################################
    ## slots below implement run functions for the server
    ##################################################################
    
    @pyqtSlot()
    def __runServer(self, development=False):
        """
        Private slot to start the Flask Web server.
        
        @param development flag indicating development mode
        @type bool
        """
        from .RunServerDialog import RunServerDialog
        
        if self.__serverDialog is not None:
            self.__serverDialog.close()
        
        askForOptions = self.askForServerOptionsAct.isChecked()
        dlg = RunServerDialog(self.__plugin, self)
        if dlg.startServer(development=development,
                           askForOptions=askForOptions):
            dlg.show()
            self.__serverDialog = dlg
    
    @pyqtSlot()
    def __runDevelopmentServer(self):
        """
        Private slot to start the Flask Web server in development mode.
        """
        self.__runServer(development=True)
    
    ##################################################################
    ## slots below implement functions for the flask console
    ##################################################################
    
    @pyqtSlot()
    def __runPythonShell(self):
        """
        Private slot to start a Python console in the app context.
        """
        workdir, env = self.prepareRuntimeEnvironment()
        if env is not None:
            command = self.getFlaskCommand()
            
            consoleCmd = self.__plugin.getPreferences("ConsoleCommand")
            if consoleCmd:
                self.__terminatePythonShell()
                
                args = Utilities.parseOptionString(consoleCmd)
                args[0] = Utilities.getExecutablePath(args[0])
                args += [command, "shell"]
                
                self.__shellProcess = QProcess()
                self.__shellProcess.setProcessEnvironment(env)
                self.__shellProcess.setWorkingDirectory(workdir)
                self.__shellProcess.finished.connect(
                    self.__shellProcessFinished)
                
                self.__shellProcess.start(args[0], args[1:])
                self.__shellProcess.waitForStarted(10000)
    
    @pyqtSlot()
    def __shellProcessFinished(self):
        """
        Private slot connected to the finished signal.
        """
        self.__shellProcess = None
    
    def __terminatePythonShell(self):
        """
        Private method to terminate the current Python console.
        """
        if (
            self.__shellProcess is not None and
            self.__shellProcess.state() != QProcess.NotRunning
        ):
            self.__shellProcess.terminate()
            QTimer.singleShot(2000, self.__shellProcess.kill)
            self.__shellProcess.waitForFinished(3000)
    
    ##################################################################
    ## slots below implement various debugging functions
    ##################################################################
    
    @pyqtSlot()
    def __showRoutes(self):
        """
        Private slot showing all URL dispatch routes.
        """
        from .RoutesDialog import RoutesDialog
        
        if self.__routesDialog is not None:
            self.__routesDialog.close()
        
        dlg = RoutesDialog(self)
        if dlg.showRoutes():
            dlg.show()
            self.__routesDialog = dlg
    
    @pyqtSlot()
    def __initDatabase(self):
        """
        Private slot showing the result of the database creation.
        """
        from .FlaskCommandDialog import FlaskCommandDialog
        
        dlg = FlaskCommandDialog(self)
        if dlg.startCommand("init-db"):
            dlg.exec()
    
    ##################################################################
    ## slots and methods below implement i18n and l10n support
    ##################################################################
    
    def flaskBabelAvailable(self):
        """
        Public method to check, if the 'flask-babel' package is available.
        
        @return flag indicating the availability of 'flask-babel'
        @rtype bool
        """
        venvName = self.__plugin.getPreferences("VirtualEnvironmentNamePy3")
        interpreter = self.__virtualEnvManager.getVirtualenvInterpreter(
            venvName)
        if interpreter and Utilities.isinpath(interpreter):
            detector = os.path.join(
                os.path.dirname(__file__), "FlaskBabelDetector.py")
            proc = QProcess()
            proc.setProcessChannelMode(QProcess.MergedChannels)
            proc.start(interpreter, [detector])
            finished = proc.waitForFinished(30000)
            if finished and proc.exitCode() == 0:
                return True
        
        return False
    
    @pyqtSlot()
    def __configurePybabel(self):
        """
        Private slot to show a dialog to edit the pybabel configuration.
        """
        # TODO: implement this
        from .PyBabelConfigDialog import PyBabelConfigDialog
        
        config = self.getData("pybabel", "")
        dlg = PyBabelConfigDialog(config)
        if dlg.exec() == QDialog.Accepted:
            config = dlg.getConfiguration()
            self.setData("pybabel", "", config)
            
            cfgFileName = self.__e5project.getAbsoluteUniversalPath(
                config["configFile"])
            if not os.path.exists(cfgFileName):
                self.__createBabelCfg(cfgFileName)
    
    def __ensurePybabelConfigured(self):
        """
        Private method to ensure, that PyBabel has been configured.
        
        @return flag indicating successful configuration
        @rtype bool
        """
        config = self.getData("pybabel", "")
        if not config:
            self.__configurePybabel()
            return True
        
        configFileName = self.getData("pybabel", "configFile")
        if configFileName:
            cfgFileName = self.__e5project.getAbsoluteUniversalPath(
                configFileName)
            if os.path.exists(cfgFileName):
                return True
            else:
                return self.__createBabelCfg(cfgFileName)
        
        return False
    
    def __createBabelCfg(self, configFile):
        """
        Private method to create a template pybabel configuration file.
        
        @return flag indicating successful configuration file creation
        @rtype bool
        """
        _, app = self.getApplication()
        if app.endswith(".py"):
            template = (
                "[python: {0}]\n"
                "[jinja2: templates/**.html]\n"
                "extensions=jinja2.ext.autoescape,jinja2.ext.with_\n"
            )
        else:
            template = (
                "[python: {0}/**.py]\n"
                "[jinja2: {0}/templates/**.html]\n"
                "extensions=jinja2.ext.autoescape,jinja2.ext.with_\n"
            )
        try:
            with open(configFile, "w") as f:
                f.write(template.format(app))
            self.__e5project.appendFile(configFile)
            E5MessageBox.information(
                None,
                self.tr("Generate PyBabel Configuration File"),
                self.tr("""The PyBabel configuration file was created."""
                        """ Please edit it to adjust the entries as"""
                        """ required.""")
            )
            return True
        except EnvironmentError as err:
            E5MessageBox.warning(
                None,
                self.tr("Generate PyBabel Configuration File"),
                self.tr("""<p>The PyBabel Configuration File could not be"""
                        """ generated.</p><p>Reason: {0}</p>""")
                .format(str(err))
            )
            return False
    
    def __projectLanguageAdded(self, code):
        # TODO: implement this with pybabel ...
        pass
    
    def openPOEditor(self):
        # TODO: implement this with pybabel ...
        pass
    
    def extractMessages(self):
        # TODO: implement this with pybabel ...
        pass
    
    def compileCatalogs(self):
        # TODO: implement this with pybabel ...
        pass
    
    def compileSelectedCatalogs(self):
        # TODO: implement this with pybabel ...
        pass
    
    def updateCatalogs(self):
        # TODO: implement this with pybabel ...
        pass
    
    def updateSelectedCatalogs(self):
        # TODO: implement this with pybabel ...
        pass

eric ide

mercurial