--- a/ProjectPyramid/Project.py Sat Sep 28 13:34:57 2013 +0200 +++ b/ProjectPyramid/Project.py Sun Oct 27 22:43:17 2013 +0100 @@ -7,12 +7,21 @@ Module implementing the Pyramid project support. """ +from __future__ import unicode_literals # __IGNORE_WARNING__ + +try: + import configparser +except ImportError: + str = unicode # __IGNORE_WARNING__ + import ConfigParser as configparser # __IGNORE_WARNING__ + import os -import configparser import re +import sys -from PyQt4.QtCore import QObject, QFileInfo, QProcess, QTimer, QUrl +from PyQt4.QtCore import QObject, QFileInfo, QTimer, QUrl from PyQt4.QtGui import QMenu, QDialog, QInputDialog, QDesktopServices, QLineEdit +from PyQt4.QtCore import QProcess as QProcessPyQt from E5Gui.E5Application import e5App from E5Gui import E5MessageBox, E5FileDialog @@ -20,6 +29,7 @@ from .PyramidDialog import PyramidDialog +import Preferences import Utilities from Globals import isWindowsPlatform import UI.PixmapCache @@ -32,6 +42,41 @@ pass +class QProcess(QProcessPyQt): + """ + Class transforming the call arguments in case of gnome-terminal. + """ + def start(self, cmd, args=[], mode=QProcessPyQt.ReadWrite): + """ + Starts the given program (cmd) in a new process, if none is already + running, passing the command line arguments in argss. + + @param cmd start the given program cmd (string) + @keyparam args list of parameters (list of strings) + @keyparam mode access mode (QIODevice.OpenMode) + """ + if cmd.endswith('gnome-terminal') and args[0] == '-e': + args = ['-e', ' '.join(args[1:])] + + super(QProcess, self).start(cmd, args, mode) + + @staticmethod + def startDetached(cmd, args=[], path=''): + """ + Starts the given program (cmd) in a new process, if none is already + running, passing the command line arguments in argss. + + @param cmd start the given program cmd (string) + @keyparam args list of parameters (list of strings) + @keyparam path new working directory (string) + @return tuple of successful start and process id (boolean, integer) + """ + if cmd.endswith('gnome-terminal') and args[0] == '-e': + args = ['-e', ' '.join(args[1:])] + + return QProcessPyQt.startDetached(cmd, args, path) + + class Project(QObject): """ Class implementing the Pyramid project support. @@ -43,7 +88,7 @@ @param plugin reference to the plugin object @param parent parent (QObject) """ - super().__init__(parent) + super(Project, self).__init__(parent) self.__plugin = plugin self.__ui = parent @@ -519,24 +564,30 @@ if fullCmd != cmd: variants.append(variant) else: - try: - fullCmds = Utilities.getExecutablePaths(cmd) - except AttributeError: - fullCmds = self.__getExecutablePaths(cmd) - for fullCmd in fullCmds: + if isWindowsPlatform(): + debugEnv = self.__getDebugEnvironment(variant) + fullCmd = os.path.join(debugEnv, "Scripts", cmd) + if variant.lower() in fullCmd.lower(): + variants.append(variant) + else: 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 + 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 @@ -559,7 +610,28 @@ if virtEnv and not os.path.exists(virtEnv): virtEnv = "" return virtEnv - + + def __getDebugEnvironment(self, language=""): + """ + Private method to get the path of the debugger environment. + + @param language Python variant to get the debugger environment + for (string, one of '', 'Python2' or 'Python3') + @return path of the debugger environment (string) + """ + if not language: + language = self.__e5project.getProjectLanguage() + if language == "Python3": + debugEnv = Preferences.getDebugger("Python3Interpreter") + elif language == "Python2": + debugEnv = Preferences.getDebugger("PythonInterpreter") + else: + debugEnv = sys.executable + debugEnv = os.path.dirname(debugEnv) + if debugEnv and not os.path.exists(debugEnv): + debugEnv = sys.exec_prefix + return debugEnv + def getPyramidCommand(self, cmd, language=""): """ Public method to build a Pyramid command. @@ -570,17 +642,20 @@ @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 + if isWindowsPlatform() and not virtualEnv: + virtualEnv = self.__getDebugEnvironment(language) + if isWindowsPlatform(): + cmd = os.path.join(virtualEnv, "Scripts", cmd + '.exe') + else: + fullCmds = [ + os.path.join(virtualEnv, "bin", cmd), + os.path.join(virtualEnv, "local", "bin", cmd), + Utilities.getExecutablePath(cmd), + cmd # fall back to just cmd + ] + for cmd in fullCmds: + if os.path.exists(cmd): + break return cmd def getPythonCommand(self): @@ -589,25 +664,29 @@ @return python command (string) """ - python = "python" + language = self.__e5project.getProjectLanguage() + pythonExe = "python" if isWindowsPlatform(): - python += ".exe" + pythonExe += ".exe" else: - language = self.__e5project.getProjectLanguage() if language == "Python3": - python = "python3" + pythonExe = "python3" elif language == "Python2": - python = "python2" + pythonExe = "python2" virtualEnv = self.__getVirtualEnvironment() + if isWindowsPlatform() and not virtualEnv: + virtualEnv = self.__getDebugEnvironment(language) if virtualEnv: if isWindowsPlatform(): - python = os.path.join(virtualEnv, "Scripts", python) + python = os.path.join(virtualEnv, "Scripts", pythonExe) if not os.path.exists(python): - python = os.path.join(virtualEnv, python) + python = os.path.join(virtualEnv, pythonExe) else: - python = os.path.join(virtualEnv, "bin", python) + python = os.path.join(virtualEnv, "bin", pythonExe) if not os.path.exists(python): python = python[:-1] # omit the version character + else: + python = pythonExe return python @@ -643,6 +722,8 @@ """ if not self.__pyramidVersion: cmd = self.getPyramidCommand("pcreate") + if isWindowsPlatform(): + cmd = os.path.join(os.path.dirname(cmd), "pcreate-script.py") try: f = open(cmd, 'r', encoding="utf-8") lines = f.read().splitlines() @@ -800,10 +881,17 @@ if self.__currentProject is None: self.__e5project.pdata["TRANSLATIONPATTERN"] = [] else: + lowerProject = self.__project().lower() 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") + try: + outputDir = config.get("init_catalog", "output_dir") + except (configparser.NoOptionError, configparser.NoSectionError): + outputDir = '{0}/locale'.format(lowerProject) + try: + domain = config.get("init_catalog", "domain") + except (configparser.NoOptionError, configparser.NoSectionError): + domain = lowerProject self.__e5project.pdata["TRANSLATIONPATTERN"] = [ os.path.join(project, outputDir, "%language%", "LC_MESSAGES", "{0}.po".format(domain)) @@ -909,7 +997,10 @@ config = configparser.ConfigParser() config.read(os.path.join(projectPath, "development.ini")) - port = config.get("server:main", "port", fallback="6543") + try: + port = config.get("server:main", "port") + except (configparser.NoOptionError, configparser.NoSectionError): + port = "8080" url = QUrl("http://localhost:{0}".format(port)) res = QDesktopServices.openUrl(url) if not res: @@ -979,6 +1070,8 @@ res = dia.startProcess(cmd, args, wd) if res: dia.exec_() + initCmd = self.__getInitDbCommand() + self.initializeDbAct.setEnabled(os.path.exists(initCmd)) ################################################################## ## slots below implement distribution functions @@ -1164,8 +1257,13 @@ @return extracted locale (string) or None """ if self.__e5project.pdata["TRANSLATIONPATTERN"]: - pattern = self.__e5project.pdata["TRANSLATIONPATTERN"][0]\ - .replace("%language%", "(.*?)") + # On Windows, path typically contains backslashes. This leads + # to an invalid seach pattern '...\(' because the opening bracked + # will be escaped. + pattern = self.__e5project.pdata["TRANSLATIONPATTERN"][0] + pattern = os.path.normpath(pattern) + pattern = pattern.replace("%language%", "(.*?)") + pattern = pattern.replace('\\', '\\\\') match = re.search(pattern, filename) if match is not None: loc = match.group(1) @@ -1221,6 +1319,28 @@ self.trUtf8('No current Pyramid project selected or no Pyramid project' ' created yet. Aborting...')) return + + config = configparser.ConfigParser() + config.read(os.path.join(projectPath, "setup.cfg")) + try: + potFile = config.get("extract_messages", "output_file") + except configparser.NoSectionError: + E5MessageBox.warning(self.__ui, + title, + self.trUtf8('No setup.cfg found or no "extract_messages"' + ' section found in setup.cfg.')) + return + except configparser.NoOptionError: + E5MessageBox.warning(self.__ui, + title, + self.trUtf8('No "output_file" option found in setup.cfg.')) + return + + try: + path = os.path.join(projectPath, os.path.dirname(potFile)) + os.makedirs(path) + except OSError: + pass cmd = self.getPythonCommand() args = [] @@ -1232,25 +1352,8 @@ res = dia.startProcess(cmd, args, projectPath) if res: dia.exec_() + self.__e5project.appendFile(os.path.join(projectPath, potFile)) - 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. @@ -1280,7 +1383,7 @@ if res: dia.exec_() - langFile = self.__e4project.pdata["TRANSLATIONPATTERN"][0]\ + langFile = self.__e5project.pdata["TRANSLATIONPATTERN"][0]\ .replace("%language%", code) self.__e5project.appendFile(langFile)