Mon, 01 Apr 2013 19:40:41 +0200
Extended the Python variant detection to only offer the Pyramid project type, if it is found for the respective Python.
# -*- coding: utf-8 -*- # Copyright (c) 2012 - 2013 Detlev Offenbach <detlev@die-offenbachs.de> # """ Module implementing the Pyramid project support. """ import os import configparser import re from PyQt4.QtCore import QObject, QFileInfo, QProcess, QTimer, QUrl from PyQt4.QtGui import QMenu, QDialog, QInputDialog, QDesktopServices, QLineEdit from E5Gui.E5Application import e5App from E5Gui import E5MessageBox, E5FileDialog from E5Gui.E5Action import E5Action from .PyramidDialog import PyramidDialog import Utilities from Globals import isWindowsPlatform import UI.PixmapCache class PyramidNoProjectSelectedException(Exception): """ Exception thrown to signal, that there is no current Pyramid project. """ pass class Project(QObject): """ Class implementing the Pyramid project support. """ def __init__(self, plugin, parent=None): """ Constructor @param plugin reference to the plugin object @param parent parent (QObject) """ super().__init__(parent) self.__plugin = plugin self.__ui = parent self.__e5project = e5App().getObject("Project") self.__hooksInstalled = False self.__mainMenu = None self.__serverProc = None self.__pyramidVersion = "" def initActions(self): """ Public method to define the Pyramid actions. """ self.actions = [] self.selectProjectAct = E5Action(self.trUtf8('Current Pyramid Project'), "", 0, 0, self, 'pyramid_current_project') self.selectProjectAct.setStatusTip(self.trUtf8( 'Selects the current Pyramid project')) self.selectProjectAct.setWhatsThis(self.trUtf8( """<b>Current Pyramid Project</b>""" """<p>Selects the Pyramid project. Used for multi-project """ """Pyramid projects to switch between the projects.</p>""" )) self.selectProjectAct.triggered[()].connect(self.__selectProject) self.actions.append(self.selectProjectAct) ############################### ## create actions below ## ############################### self.createProjectAct = E5Action(self.trUtf8('Create Pyramid Project'), self.trUtf8('Create Pyramid &Project'), 0, 0, self, 'pyramid_create_project') self.createProjectAct.setStatusTip(self.trUtf8( 'Creates a new Pyramid project')) self.createProjectAct.setWhatsThis(self.trUtf8( """<b>Create Pyramid Project</b>""" """<p>Creates a new Pyramid project using "pcreate".</p>""" )) self.createProjectAct.triggered[()].connect(self.__createProject) self.actions.append(self.createProjectAct) ############################## ## run actions below ## ############################## self.runServerAct = E5Action(self.trUtf8('Run Server'), self.trUtf8('Run &Server'), 0, 0, self, 'pyramid_run_server') self.runServerAct.setStatusTip(self.trUtf8( 'Starts the Pyramid Web server')) self.runServerAct.setWhatsThis(self.trUtf8( """<b>Run Server</b>""" """<p>Starts the Pyramid Web server using""" """ "pserve --reload development.ini".</p>""" )) self.runServerAct.triggered[()].connect(self.__runServer) self.actions.append(self.runServerAct) self.runLoggingServerAct = E5Action(self.trUtf8('Run Server with Logging'), self.trUtf8('Run Server with &Logging'), 0, 0, self, 'pyramid_run_logging_server') self.runLoggingServerAct.setStatusTip(self.trUtf8( 'Starts the Pyramid Web server with logging')) self.runLoggingServerAct.setWhatsThis(self.trUtf8( """<b>Run Server with Logging</b>""" """<p>Starts the Pyramid Web server with logging using""" """ "pserve --log-file=server.log --reload development.ini".</p>""" )) self.runLoggingServerAct.triggered[()].connect(self.__runLoggingServer) self.actions.append(self.runLoggingServerAct) self.runBrowserAct = E5Action(self.trUtf8('Run Web-Browser'), self.trUtf8('Run &Web-Browser'), 0, 0, self, 'pyramid_run_browser') self.runBrowserAct.setStatusTip(self.trUtf8( 'Starts the default Web-Browser with the URL of the Pyramid Web server')) self.runBrowserAct.setWhatsThis(self.trUtf8( """<b>Run Web-Browser</b>""" """<p>Starts the default Web-Browser with the URL of the """ """Pyramid Web server.</p>""" )) self.runBrowserAct.triggered[()].connect(self.__runBrowser) self.actions.append(self.runBrowserAct) self.runPythonShellAct = E5Action(self.trUtf8('Start Pyramid Python Console'), self.trUtf8('Start Pyramid &Python Console'), 0, 0, self, 'pyramid_python_console') self.runPythonShellAct.setStatusTip(self.trUtf8( 'Starts an interactive Python interpreter')) self.runPythonShellAct.setWhatsThis(self.trUtf8( """<b>Start Pyramid Python Console</b>""" """<p>Starts an interactive Python interpreter.</p>""" )) self.runPythonShellAct.triggered[()].connect(self.__runPythonShell) self.actions.append(self.runPythonShellAct) ############################## ## setup actions below ## ############################## self.setupDevelopAct = E5Action(self.trUtf8('Setup Development Environment'), self.trUtf8('Setup &Development Environment'), 0, 0, self, 'pyramid_setup_development') self.setupDevelopAct.setStatusTip(self.trUtf8( 'Setup the Pyramid project in development mode')) self.setupDevelopAct.setWhatsThis(self.trUtf8( """<b>Setup Development Environment</b>""" """<p>Setup the Pyramid project in development mode using""" """ "python setup.py develop".</p>""" )) self.setupDevelopAct.triggered[()].connect(self.__setupDevelop) self.actions.append(self.setupDevelopAct) ############################### ## database actions below ## ############################### self.initializeDbAct = E5Action(self.trUtf8('Initialize Database'), self.trUtf8('Initialize &Database'), 0, 0, self, 'pyramid_initialize_database') self.initializeDbAct.setStatusTip(self.trUtf8( 'Initializes (or re-initializes) the database of the current' ' Pyramid project')) self.initializeDbAct.setWhatsThis(self.trUtf8( """<b>Initialize Database</b>""" """<p>Initializes (or re-initializes) the database of the current""" """ Pyramid project.</p>""" )) self.initializeDbAct.triggered[()].connect(self.__initializeDatabase) self.actions.append(self.initializeDbAct) ############################### ## show actions below ## ############################### self.showViewsAct = E5Action(self.trUtf8('Show Matching Views'), self.trUtf8('Show Matching &Views'), 0, 0, self, 'pyramid_show_views') self.showViewsAct.setStatusTip(self.trUtf8( 'Show views matching a given URL')) self.showViewsAct.setWhatsThis(self.trUtf8( """<b>Show Matching Views</b>""" """<p>Show views matching a given URL.</p>""" )) self.showViewsAct.triggered[()].connect(self.__showMatchingViews) self.actions.append(self.showViewsAct) self.showRoutesAct = E5Action(self.trUtf8('Show Routes'), self.trUtf8('Show &Routes'), 0, 0, self, 'pyramid_show_routes') self.showRoutesAct.setStatusTip(self.trUtf8( 'Show all URL dispatch routes used by a Pyramid application')) self.showRoutesAct.setWhatsThis(self.trUtf8( """<b>Show Routes</b>""" """<p>Show all URL dispatch routes used by a Pyramid application""" """ in the order in which they are evaluated.</p>""" )) self.showRoutesAct.triggered[()].connect(self.__showRoutes) self.actions.append(self.showRoutesAct) self.showTweensAct = E5Action(self.trUtf8('Show Tween Objects'), self.trUtf8('Show &Tween Objects'), 0, 0, self, 'pyramid_show_routes') self.showTweensAct.setStatusTip(self.trUtf8( 'Show all implicit and explicit tween objects used by a Pyramid application')) self.showTweensAct.setWhatsThis(self.trUtf8( """<b>Show Tween Objects</b>""" """<p>Show all implicit and explicit tween objects used by a""" """ Pyramid application.</p>""" )) self.showTweensAct.triggered[()].connect(self.__showTweens) self.actions.append(self.showTweensAct) ################################## ## distribution actions below ## ################################## self.buildDistroAct = E5Action(self.trUtf8('Build Distribution'), self.trUtf8('Build &Distribution'), 0, 0, self, 'pyramid_build_distribution') self.buildDistroAct.setStatusTip(self.trUtf8( 'Builds a distribution file for the Pyramid project')) self.buildDistroAct.setWhatsThis(self.trUtf8( """<b>Build Distribution</b>""" """<p>Builds a distribution file for the Pyramid project using""" """ "python setup.py sdist".</p>""" )) self.buildDistroAct.triggered[()].connect(self.__buildDistribution) self.actions.append(self.buildDistroAct) ################################## ## documentation action below ## ################################## self.documentationAct = E5Action(self.trUtf8('Documentation'), self.trUtf8('D&ocumentation'), 0, 0, self, 'pyramid_documentation') self.documentationAct.setStatusTip(self.trUtf8( 'Shows the help viewer with the Pyramid documentation')) self.documentationAct.setWhatsThis(self.trUtf8( """<b>Documentation</b>""" """<p>Shows the help viewer with the Pyramid documentation.</p>""" )) self.documentationAct.triggered[()].connect(self.__showDocumentation) self.actions.append(self.documentationAct) ############################## ## about action below ## ############################## self.aboutPyramidAct = E5Action(self.trUtf8('About Pyramid'), self.trUtf8('About P&yramid'), 0, 0, self, 'pyramid_about') self.aboutPyramidAct.setStatusTip(self.trUtf8( 'Shows some information about Pyramid')) self.aboutPyramidAct.setWhatsThis(self.trUtf8( """<b>About Pyramid</b>""" """<p>Shows some information about Pyramid.</p>""" )) self.aboutPyramidAct.triggered[()].connect(self.__pyramidInfo) self.actions.append(self.aboutPyramidAct) self.__setCurrentProject(None) def initMenu(self): """ Public slot to initialize the Pyramid menu. @return the menu generated (QMenu) """ menu = QMenu(self.trUtf8('P&yramid'), self.__ui) menu.setTearOffEnabled(True) menu.addAction(self.selectProjectAct) menu.addSeparator() menu.addAction(self.runServerAct) menu.addAction(self.runLoggingServerAct) menu.addAction(self.runBrowserAct) menu.addSeparator() menu.addAction(self.createProjectAct) menu.addSeparator() menu.addAction(self.setupDevelopAct) menu.addSeparator() menu.addAction(self.initializeDbAct) menu.addSeparator() menu.addAction(self.showViewsAct) menu.addAction(self.showRoutesAct) menu.addAction(self.showTweensAct) menu.addSeparator() menu.addAction(self.runPythonShellAct) menu.addSeparator() menu.addAction(self.buildDistroAct) menu.addSeparator() menu.addAction(self.documentationAct) menu.addSeparator() menu.addAction(self.aboutPyramidAct) self.__mainMenu = menu return menu def projectOpenedHooks(self): """ Public method to add our hook methods. """ if self.__e5project.getProjectType() == "Pyramid": self.__formsBrowser = \ e5App().getObject("ProjectBrowser").getProjectBrowser("forms") self.__formsBrowser.addHookMethodAndMenuEntry("newForm", self.newForm, self.trUtf8("New template...")) if self.__e5project.getProjectLanguage() == "Python2": # Babel and lingua are not yet available for Python 3 self.__e5project.projectLanguageAddedByCode.connect( self.__projectLanguageAdded) self.__translationsBrowser = \ e5App().getObject("ProjectBrowser").getProjectBrowser("translations") self.__translationsBrowser.addHookMethodAndMenuEntry("extractMessages", self.extractMessages, self.trUtf8("Extract Messages")) self.__translationsBrowser.addHookMethodAndMenuEntry("releaseAll", self.compileCatalogs, self.trUtf8("Compile All Catalogs")) self.__translationsBrowser.addHookMethodAndMenuEntry("releaseSelected", self.compileSelectedCatalogs, self.trUtf8("Compile Selected Catalogs")) self.__translationsBrowser.addHookMethodAndMenuEntry("generateAll", self.updateCatalogs, self.trUtf8("Update All Catalogs")) self.__translationsBrowser.addHookMethodAndMenuEntry("generateSelected", self.updateSelectedCatalogs, self.trUtf8("Update Selected Catalogs")) self.__hooksInstalled = True 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 = None self.__hooksInstalled = False def newForm(self, path): """ Public method to create a new form. @param path full directory path for the new form file (string) """ from .FormSelectionDialog import FormSelectionDialog dlg = FormSelectionDialog() if dlg.exec_() == QDialog.Accepted: template = dlg.getTemplateText() filter = self.trUtf8( "Chameleon Templates (*.pt);;" "Chameleon Text Templates (*.txt);;" "Mako Templates (*.mako);;" "Mako Templates (*.mak);;" "HTML Files (*.html);;" "HTML Files (*.htm);;" "All Files (*)") fname, selectedFilter = E5FileDialog.getSaveFileNameAndFilter( self.__ui, self.trUtf8("New Form"), path, filter, None, E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite)) if fname: ext = QFileInfo(fname).suffix() if not ext: ex = selectedFilter.split("(*")[1].split(")")[0] if ex: fname += ex if os.path.exists(fname): res = E5MessageBox.yesNo(self.__ui, self.trUtf8("New Form"), self.trUtf8("""The file already exists! Overwrite it?"""), icon=E5MessageBox.Warning) if not res: # user selected to not overwrite return try: f = open(fname, "w", encoding="utf-8") f.write(template) f.close() except IOError as e: E5MessageBox.critical(self, self.trUtf8("New Form"), self.trUtf8("<p>The new form file <b>{0}</b> could not be" " created.<br/>" "Problem: {1}</p>").format(fname, e)) return self.__e5project.appendFile(fname) self.__formsBrowser.sourceFile.emit(fname) ################################################################## ## methods below implement general functionality ################################################################## def projectClosed(self): """ Public method to handle the closing of a project. """ if self.__serverProc is not None: self.__serverProcFinished() self.__setCurrentProject(None) def __getExecutablePaths(self, file): """ Private method to build all full path of an executable file from the environment. @param file filename of the executable (string) @return list of full executable names, if the executable file is accessible via the searchpath defined by the PATH environment variable, or an empty list otherwise. """ paths = [] if os.path.isabs(file): if os.access(file, os.X_OK): return [file] else: return [] cur_path = os.path.join(os.curdir, file) if os.path.exists(cur_path): if os.access(cur_path, os.X_OK): paths.append(cur_path) path = os.getenv('PATH') # environment variable not defined if path is not None: dirs = path.split(os.pathsep) for dir in dirs: exe = os.path.join(dir, file) if os.access(exe, os.X_OK) and exe not in paths: paths.append(exe) return paths def supportedPythonVariants(self): """ Public method to get the supported Python variants. @return list of supported Python variants (list of strings) """ variants = [] cmd = "pcreate" for variant in 'Python2', 'Python3': virtEnv = self.__getVirtualEnvironment(variant) if virtEnv: fullCmd = self.getPyramidCommand(cmd, variant) if fullCmd != cmd: variants.append(variant) else: try: fullCmds = Utilities.getExecutablePaths(cmd) except AttributeError: fullCmds = self.__getExecutablePaths(cmd) for fullCmd in fullCmds: try: f = open(fullCmd, 'r', encoding='utf-8') l0 = f.readline() f.close() except (IOError, OSError): l0 = "" if variant.lower() in l0.lower() or \ "{0}.".format(variant[-1]) in l0 or \ (variant == "Python2" and \ "python3" not in l0.lower() and \ "python" in l0.lower()): variants.append(variant) break return variants def __getVirtualEnvironment(self, language=""): """ Private method to get the path of the virtual environment. @param language Python variant to get the virtual environment for (string, one of '', 'Python2' or 'Python3') @return path of the virtual environment (string) """ if not language: language = self.__e5project.getProjectLanguage() if language == "Python3": virtEnv = self.__plugin.getPreferences("VirtualEnvironmentPy3") elif language == "Python2": virtEnv = self.__plugin.getPreferences("VirtualEnvironmentPy2") else: virtEnv = "" if virtEnv and not os.path.exists(virtEnv): virtEnv = "" return virtEnv def getPyramidCommand(self, cmd, language=""): """ Public method to build a Pyramid command. @param cmd command (string) @param language Python variant to get the virtual environment for (string, one of '', 'Python2' or 'Python3') @return full pyramid command (string) """ virtualEnv = self.__getVirtualEnvironment(language) if virtualEnv: if isWindowsPlatform(): cmd = os.path.join(virtualEnv, "Scripts", cmd) else: fullCmd = os.path.join(virtualEnv, "bin", cmd) if not os.path.exists(fullCmd): fullCmd = os.path.join(virtualEnv, "local", "bin", cmd) if not os.path.exists(fullCmd): # fall back to just cmd fullCmd = cmd cmd = fullCmd return cmd def getPythonCommand(self): """ Public method to build the Python command. @return python command (string) """ python = "python" if isWindowsPlatform(): python += ".exe" else: language = self.__e5project.getProjectLanguage() if language == "Python3": python = "python3" elif language == "Python2": python = "python2" virtualEnv = self.__getVirtualEnvironment() if virtualEnv: if isWindowsPlatform(): python = os.path.join(virtualEnv, "Scripts", python) if not os.path.exists(python): python = os.path.join(virtualEnv, python) else: python = os.path.join(virtualEnv, "bin", python) if not os.path.exists(python): python = python[:-1] # omit the version character return python def __pyramidInfo(self): """ Private slot to show some info about Pyramid. """ version = self.getPyramidVersion() url = "http://www.pylonsproject.org/projects/pyramid/about" msgBox = E5MessageBox.E5MessageBox(E5MessageBox.Question, self.trUtf8("About Pyramid"), self.trUtf8( "<p>Pyramid is a high-level Python Web framework that encourages rapid " "development and clean, pragmatic design.</p>" "<p><table>" "<tr><td>Version:</td><td>{0}</td></tr>" "<tr><td>URL:</td><td><a href=\"{1}\">" "{1}</a></td></tr>" "</table></p>" ).format(version, url), modal=True, buttons=E5MessageBox.Ok) msgBox.setIconPixmap(UI.PixmapCache.getPixmap( os.path.join("ProjectPyramid", "icons", "pyramid64.png"))) msgBox.exec_() def getPyramidVersion(self): """ Public method to get the Pyramid version. @return Pyramid version (string) """ if not self.__pyramidVersion: cmd = self.getPyramidCommand("pcreate") try: f = open(cmd, 'r', encoding="utf-8") lines = f.read().splitlines() f.close() for line in lines: if line.startswith("__requires__"): # sample: __requires__ = 'pyramid==1.4' vers = line.strip().split()[-1][1:-1].split("==")[1] self.__pyramidVersion = vers except (IOError, OSError): self.__pyramidVersion = "" return self.__pyramidVersion def isSpawningConsole(self, consoleCmd): """ Public method to check, if the given console is a spawning console. @param consoleCmd console command (string) @return tuple of two entries giving an indication, if the console is spawning (boolean) and the (possibly) cleaned console command (string) """ if consoleCmd and consoleCmd[0] == '@': return (True, consoleCmd[1:]) else: return (False, consoleCmd) ################################################################## ## slots below implement creation functions ################################################################## def __createProject(self): """ Private slot to create a new Pyramid project. """ from .CreateParametersDialog import CreateParametersDialog dlg = CreateParametersDialog(self) if dlg.exec_() == QDialog.Accepted: scaffold, project, overwrite, simulate = dlg.getData() cmd = self.getPyramidCommand("pcreate") args = [] if overwrite: args.append("--overwrite") else: args.append("--interactive") if simulate: args.append("--simulate") args.append("--scaffold={0}".format(scaffold)) args.append(project) dlg = PyramidDialog(self.trUtf8("Create Pyramid Project"), linewrap=False, parent=self.__ui) if dlg.startProcess(cmd, args, self.__e5project.getProjectPath()): dlg.exec_() if dlg.normalExit() and not simulate: # search for files created by pcreate and add them to the project projectPath = os.path.join(self.__e5project.getProjectPath(), project) for entry in os.walk(projectPath): for fileName in entry[2]: fullName = os.path.join(entry[0], fileName) self.__e5project.appendFile(fullName) # create the base directory for translations i18nPath = os.path.join(projectPath, project.lower(), "i18n") if not os.path.exists(i18nPath): os.makedirs(i18nPath) self.__e5project.setDirty(True) self.__setCurrentProject(project) ################################################################## ## methods below implement site related functions ################################################################## def __findProjects(self): """ Private method to determine the relative path of all Pyramid projects (= top level dirs). @return list of projects (list of string) """ projects = [] ppath = self.__e5project.getProjectPath() for entry in os.listdir(ppath): if entry[0] not in "._" and \ os.path.isdir(os.path.join(ppath, entry)): projects.append(entry) return projects def __selectProject(self): """ Private method to select a Pyramid project to work with. @return selected project (string) """ projects = self.__findProjects() if len(projects) == 0: project = None elif len(projects) == 1: project = projects[0] else: if self.__currentProject is not None: try: cur = projects.index(self.__currentProject) except ValueError: cur = 0 else: cur = 0 project, ok = QInputDialog.getItem( self.__ui, self.trUtf8("Select Pyramid Project"), self.trUtf8("Select the Pyramid project to work with."), projects, cur, False) if not ok: projects = None self.__setCurrentProject(project) def __projectPath(self): """ Private method to calculate the full path of the Pyramid project. @exception PyramidNoProjectSelectedException raised, if no project is selected @return path of the project (string) """ if self.__currentProject is None: self.__selectProject() if self.__currentProject is None: raise PyramidNoProjectSelectedException else: return os.path.join(self.__e5project.getProjectPath(), self.__currentProject) def __setCurrentProject(self, project): """ Private slot to set the current project. @param project name of the project (string) """ if project is not None and len(project) == 0: self.__currentProject = None else: self.__currentProject = project if self.__currentProject is None: curProject = self.trUtf8("None") else: curProject = self.__currentProject self.selectProjectAct.setText( self.trUtf8('&Current Pyramid Project ({0})').format(curProject)) if self.__currentProject is None: self.__e5project.pdata["TRANSLATIONPATTERN"] = [] else: config = configparser.ConfigParser() config.read(os.path.join(self.__projectPath(), "setup.cfg")) outputDir = config.get("init_catalog", "output_dir") domain = config.get("init_catalog", "domain") self.__e5project.pdata["TRANSLATIONPATTERN"] = [ os.path.join(project, outputDir, "%language%", "LC_MESSAGES", "{0}.po".format(domain)) ] if self.__currentProject is None: self.initializeDbAct.setEnabled(False) else: initCmd = self.__getInitDbCommand() self.initializeDbAct.setEnabled(os.path.exists(initCmd)) def __project(self): """ Private method to get the name of the current Pyramid project. @exception PyramidNoProjectSelectedException raised, if no project is selected @return name of the project (string) """ if self.__currentProject is None: self.__selectProject() if self.__currentProject is None: raise PyramidNoProjectSelectedException else: return self.__currentProject ################################################################## ## 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 (boolean) """ consoleCmd = self.isSpawningConsole( self.__plugin.getPreferences("ConsoleCommand"))[1] if consoleCmd: try: projectPath = self.__projectPath() except PyramidNoProjectSelectedException: E5MessageBox.warning(self.__ui, self.trUtf8('Run Server'), self.trUtf8('No current Pyramid project selected or no Pyramid ' 'project created yet. Aborting...')) return args = Utilities.parseOptionString(consoleCmd) args[0] = Utilities.getExecutablePath(args[0]) args.append(self.getPyramidCommand("pserve")) if logging: args.append("--log-file=server.log") args.append("--reload") args.append(os.path.join(projectPath, "development.ini")) if isWindowsPlatform(): serverProcStarted, pid = \ QProcess.startDetached(args[0], args[1:], projectPath) else: if self.__serverProc is not None: self.__serverProcFinished() self.__serverProc = QProcess() self.__serverProc.finished.connect(self.__serverProcFinished) self.__serverProc.setWorkingDirectory(projectPath) self.__serverProc.start(args[0], args[1:]) serverProcStarted = self.__serverProc.waitForStarted() if not serverProcStarted: E5MessageBox.critical(self.__ui, self.trUtf8('Process Generation Error'), self.trUtf8('The Pyramid server could not be started.')) def __runLoggingServer(self): """ Private slot to start the Pyramid Web server with logging. """ self.__runServer(True) 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 def __runBrowser(self): """ Private slot to start the default web browser with the server URL. """ try: projectPath = self.__projectPath() except PyramidNoProjectSelectedException: E5MessageBox.warning(self.__ui, self.trUtf8('Run Web-Browser'), self.trUtf8('No current Pyramid project selected or no Pyramid project' ' created yet. Aborting...')) return config = configparser.ConfigParser() config.read(os.path.join(projectPath, "development.ini")) port = config.get("server:main", "port", fallback="6543") url = QUrl("http://localhost:{0}".format(port)) res = QDesktopServices.openUrl(url) if not res: E5MessageBox.critical(self.__ui, self.trUtf8('Run Web-Browser'), self.trUtf8('Could not start the web-browser for the URL "{0}".')\ .format(url.toString())) def __runPythonShell(self): """ Private slot to start a Python console for a Pyramid project. """ consoleCmd = self.isSpawningConsole( self.__plugin.getPreferences("ConsoleCommand"))[1] if consoleCmd: try: projectPath = self.__projectPath() except PyramidNoProjectSelectedException: E5MessageBox.warning(self.__ui, self.trUtf8('Start Pyramid Python Console'), self.trUtf8('No current Pyramid project selected or no Pyramid ' 'project created yet. Aborting...')) return args = Utilities.parseOptionString(consoleCmd) args[0] = Utilities.getExecutablePath(args[0]) args.append(self.getPyramidCommand("pshell")) language = self.__e5project.getProjectLanguage() if language == "Python2": consoleType = self.__plugin.getPreferences("Python2ConsoleType") else: consoleType = self.__plugin.getPreferences("Python3ConsoleType") args.append("--python-shell={0}".format(consoleType)) args.append(os.path.join(projectPath, "development.ini")) started, pid = QProcess.startDetached(args[0], args[1:], projectPath) if not started: E5MessageBox.critical(self.__ui, self.trUtf8('Process Generation Error'), self.trUtf8('The Pyramid Shell process could not be started.')) ################################################################## ## slots below implement setup functions ################################################################## def __setupDevelop(self): """ Private slot to set up the development environment for the current project. """ title = self.trUtf8("Setup Development Environment") try: wd = self.__projectPath() except PyramidNoProjectSelectedException: E5MessageBox.warning(self.__ui, title, self.trUtf8('No current Pyramid project selected or no Pyramid project' ' created yet. Aborting...')) return cmd = self.getPythonCommand() args = [] args.append("setup.py") args.append("develop") dia = PyramidDialog(title, msgSuccess=self.trUtf8("Pyramid development environment setup successfully.")) res = dia.startProcess(cmd, args, wd) if res: dia.exec_() ################################################################## ## slots below implement distribution functions ################################################################## def __buildDistribution(self): """ Private slot to build a distribution file for the current Pyramid project. """ title = self.trUtf8("Build Distribution File") try: projectPath = self.__projectPath() except PyramidNoProjectSelectedException: E5MessageBox.warning(self.__ui, title, self.trUtf8('No current Pyramid project selected or no Pyramid project' ' created yet. Aborting...')) return from .DistributionTypeSelectionDialog import DistributionTypeSelectionDialog dlg = DistributionTypeSelectionDialog(self, projectPath, self.__ui) if dlg.exec_() == QDialog.Accepted: formats = dlg.getFormats() cmd = self.getPythonCommand() args = [] args.append("setup.py") args.append("sdist") if formats: args.append("--formats={0}".format(','.join(formats))) dia = PyramidDialog(title, msgSuccess=self.trUtf8("Python distribution file built successfully.")) res = dia.startProcess(cmd, args, projectPath) if res: dia.exec_() ################################################################## ## slots below implement database functions ################################################################## def __getInitDbCommand(self): """ Private method to create the path to the initialization script. @return path to the initialization script (string) """ try: cmd = "initialize_{0}_db".format(self.__project()) return self.getPyramidCommand(cmd) except PyramidNoProjectSelectedException: E5MessageBox.warning(self.__ui, self.trUtf8("Initialize Database"), self.trUtf8('No current Pyramid project selected or no Pyramid project' ' created yet. Aborting...')) return "" def __initializeDatabase(self): """ Private slot to initialize the database of the Pyramid project. """ title = self.trUtf8("Initialize Database") try: projectPath = self.__projectPath() except PyramidNoProjectSelectedException: E5MessageBox.warning(self.__ui, title, self.trUtf8('No current Pyramid project selected or no Pyramid project' ' created yet. Aborting...')) return cmd = self.__getInitDbCommand() args = [] args.append("development.ini") dia = PyramidDialog(title, msgSuccess=self.trUtf8("Database initialized successfully.")) res = dia.startProcess(cmd, args, projectPath) if res: dia.exec_() ################################################################## ## slots below implement various debugging functions ################################################################## def __showMatchingViews(self): """ Private slot showing all views that would match a given URL. """ title = self.trUtf8("Show Matching Views") try: projectPath = self.__projectPath() except PyramidNoProjectSelectedException: E5MessageBox.warning(self.__ui, title, self.trUtf8('No current Pyramid project selected or no Pyramid project' ' created yet. Aborting...')) return url, ok = QInputDialog.getText( self.__ui, self.trUtf8("Show Matching Views"), self.trUtf8("Enter the URL to be matched:"), QLineEdit.Normal, "/") if not ok or url == "": return cmd = self.getPyramidCommand("pviews") args = [] args.append("development.ini") args.append(url) dia = PyramidDialog(title, fixed=True, linewrap=False) res = dia.startProcess(cmd, args, projectPath) if res: dia.exec_() def __showRoutes(self): """ Private slot showing all URL dispatch routes. """ title = self.trUtf8("Show Routes") try: projectPath = self.__projectPath() except PyramidNoProjectSelectedException: E5MessageBox.warning(self.__ui, title, self.trUtf8('No current Pyramid project selected or no Pyramid project' ' created yet. Aborting...')) return from .PyramidRoutesDialog import PyramidRoutesDialog dia = PyramidRoutesDialog(self) res = dia.start(projectPath) if res: dia.exec_() def __showTweens(self): """ Private slot showing all implicit and explicit tween objects. """ title = self.trUtf8("Show Tween Objects") try: projectPath = self.__projectPath() except PyramidNoProjectSelectedException: E5MessageBox.warning(self.__ui, title, self.trUtf8('No current Pyramid project selected or no Pyramid project' ' created yet. Aborting...')) return cmd = self.getPyramidCommand("ptweens") args = [] args.append("development.ini") dia = PyramidDialog(title, fixed=True, linewrap=False) res = dia.startProcess(cmd, args, projectPath) if res: dia.exec_() ################################################################## ## slots below implement documentation functions ################################################################## def __showDocumentation(self): """ Private slot to show the helpviewer with the Pyramid documentation. """ page = self.__plugin.getPreferences("PyramidDocUrl") self.__ui.launchHelpViewer(page) ################################################################## ## slots below implement translation functions ################################################################## def __getLocale(self, filename): """ Private method to extract the locale out of a file name. @param filename name of the file used for extraction (string) @return extracted locale (string) or None """ if self.__e5project.pdata["TRANSLATIONPATTERN"]: pattern = self.__e5project.pdata["TRANSLATIONPATTERN"][0]\ .replace("%language%", "(.*?)") match = re.search(pattern, filename) if match is not None: loc = match.group(1) return loc else: loc = None else: loc = None return loc def __normalizeList(self, filenames): """ Private method to normalize a list of file names. @param filenames list of file names to normalize (list of string) @return normalized file names (list of string) """ nfilenames = [] for filename in filenames: if filename.endswith(".mo"): filename = filename.replace(".mo", ".po") if filename not in nfilenames: nfilenames.append(filename) return nfilenames def __projectFilteredList(self, filenames): """ Private method to filter a list of file names by Pyramid project. @param filenames list of file names to be filtered (list of string) @return file names belonging to the current site (list of string) """ project = self.__project() nfilenames = [] for filename in filenames: if filename.startswith(project + os.sep): nfilenames.append(filename) return nfilenames def extractMessages(self): """ Public method to extract the messages catalog template file. """ title = self.trUtf8("Extract messages") try: projectPath = self.__projectPath() except PyramidNoProjectSelectedException: E5MessageBox.warning(self.__ui, title, self.trUtf8('No current Pyramid project selected or no Pyramid project' ' created yet. Aborting...')) return cmd = self.getPythonCommand() args = [] args.append("setup.py") args.append("extract_messages") dia = PyramidDialog(title, msgSuccess=self.trUtf8("\nMessages extracted successfully.")) res = dia.startProcess(cmd, args, projectPath) if res: dia.exec_() config = configparser.ConfigParser() config.read(os.path.join(projectPath, "setup.cfg")) potFile = config.get("extract_messages", "output_file") if potFile: self.__e5project.appendFile(os.path.join(projectPath, potFile)) else: try: lowerProject = self.__project().lower() except PyramidNoProjectSelectedException: E5MessageBox.warning(self.__ui, title, self.trUtf8('No current Pyramid project selected or no Pyramid' ' project created yet. Aborting...')) return self.__e5project.appendFile(os.path.join( projectPath, lowerProject, "locale", "%s.pot" % lowerProject)) def __projectLanguageAdded(self, code): """ Private slot handling the addition of a new language. @param code language code of the new language (string) """ title = self.trUtf8("Initializing message catalog for '{0}'").format(code) try: projectPath = self.__projectPath() except PyramidNoProjectSelectedException: E5MessageBox.warning(self.__ui, title, self.trUtf8('No current Pyramid project selected or no Pyramid project' ' created yet. Aborting...')) return cmd = self.getPythonCommand() args = [] args.append("setup.py") args.append("init_catalog") args.append("-l") args.append(code) dia = PyramidDialog(title, msgSuccess=self.trUtf8("\nMessage catalog initialized successfully.")) res = dia.startProcess(cmd, args, projectPath) if res: dia.exec_() langFile = self.__e4project.pdata["TRANSLATIONPATTERN"][0]\ .replace("%language%", code) self.__e5project.appendFile(langFile) def compileCatalogs(self, filenames): """ Public method to compile the message catalogs. @param filenames list of filenames (not used) """ title = self.trUtf8("Compiling message catalogs") try: projectPath = self.__projectPath() except PyramidNoProjectSelectedException: E5MessageBox.warning(self.__ui, title, self.trUtf8('No current Pyramid project selected or no Pyramid project' ' created yet. Aborting...')) return cmd = self.getPythonCommand() args = [] args.append("setup.py") args.append("compile_catalog") dia = PyramidDialog(title, msgSuccess=self.trUtf8("\nMessage catalogs compiled successfully.")) res = dia.startProcess(cmd, args, projectPath) if res: dia.exec_() for entry in os.walk(projectPath): for fileName in entry[2]: fullName = os.path.join(entry[0], fileName) if fullName.endswith('.mo'): self.__e5project.appendFile(fullName) def compileSelectedCatalogs(self, filenames): """ Public method to update the message catalogs. @param filenames list of file names (list of string) """ title = self.trUtf8("Compiling message catalogs") try: projectPath = self.__projectPath() except PyramidNoProjectSelectedException: E5MessageBox.warning(self.__ui, title, self.trUtf8('No current Pyramid project selected or no Pyramid project' ' created yet. Aborting...')) return argsLists = [] for filename in self.__normalizeList(self.__projectFilteredList(filenames)): locale = self.__getLocale(filename) if locale: args = [] args.append(self.getPythonCommand()) args.append("setup.py") args.append("compile_catalog") args.append("-l") args.append(locale) argsLists.append(args) if len(argsLists) == 0: E5MessageBox.warning(self.__ui, title, self.trUtf8('No locales detected. Aborting...')) return dia = PyramidDialog(title, msgSuccess=self.trUtf8("\nMessage catalogs compiled successfully.")) res = dia.startBatchProcesses(argsLists, projectPath) if res: dia.exec_() for entry in os.walk(self.__sitePath()): for fileName in entry[2]: fullName = os.path.join(entry[0], fileName) if fullName.endswith('.mo'): self.__e5project.appendFile(fullName) def updateCatalogs(self, filenames): """ Public method to update the message catalogs. @param filenames list of filenames (not used) """ title = self.trUtf8("Updating message catalogs") try: projectPath = self.__projectPath() except PyramidNoProjectSelectedException: E5MessageBox.warning(self.__ui, title, self.trUtf8('No current Pyramid project selected or no Pyramid project' ' created yet. Aborting...')) return cmd = self.getPythonCommand() args = [] args.append("setup.py") args.append("update_catalog") dia = PyramidDialog(title, msgSuccess=self.trUtf8("\nMessage catalogs updated successfully.")) res = dia.startProcess(cmd, args, projectPath) if res: dia.exec_() def updateSelectedCatalogs(self, filenames): """ Public method to update the message catalogs. @param filenames list of filenames """ title = self.trUtf8("Updating message catalogs") try: projectPath = self.__projectPath() except PyramidNoProjectSelectedException: E5MessageBox.warning(self.__ui, title, self.trUtf8('No current Pyramid project selected or no Pyramid project' ' created yet. Aborting...')) return argsLists = [] for filename in self.__normalizeList(self.__projectFilteredList(filenames)): locale = self.__getLocale(filename) if locale: args = [] args.append(self.getPythonCommand()) args.append("setup.py") args.append("update_catalog") args.append("-l") args.append(locale) argsLists.append(args) if len(argsLists) == 0: E5MessageBox.warning(self.__ui, title, self.trUtf8('No locales detected. Aborting...')) return dia = PyramidDialog(title, msgSuccess=self.trUtf8("\nMessage catalogs updated successfully.")) res = dia.startBatchProcesses(argsLists, projectPath) if res: dia.exec_()