Fri, 25 Oct 2013 18:51:52 +0200
Fixed code style issues.
# -*- 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 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") try: if editor: self.__translationsBrowser.addHookMethodAndMenuEntry( "open", self.openPOEditor, self.trUtf8("Open with {0}").format( os.path.basename(editor))) else: self.__translationsBrowser.removeHookMethod("open") except KeyError: # ignore for older eric5 versions pass 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 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") try: self.__translationsBrowser.removeHookMethod("open") except KeyError: # ignore for older eric5 versions pass 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.__ui, 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. """ 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", "{0}.pot".format(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_() def openPOEditor(self, poFile): """ Public method to edit the given file in an external .po editor. @param poFile name of the .po file (string) """ editor = self.__plugin.getPreferences("TranslationsEditor") if poFile.endswith(".po") and editor: try: wd = self.__projectPath() except PyramidNoProjectSelectedException: wd = "" started, pid = QProcess.startDetached(editor, [poFile], wd) if not started: E5MessageBox.critical( None, self.trUtf8('Process Generation Error'), self.trUtf8('The translations editor process ({0}) could' ' not be started.').format( os.path.basename(editor)))