class CodimensionProject( QObject ): " Provides codimension project singleton facility " # Constants for the projectChanged signal CompleteProject = 0 # It is a completely new project Properties = 1 # Project properties were updated projectChanged = pyqtSignal( int ) fsChanged = pyqtSignal( list ) def __init__( self ): QObject.__init__( self ) self.__dirWatcher = None self.__formatOK = True # Avoid pylint complains self.fileName = "" self.userProjectDir = "" # Directory in ~/.codimension/uuidNN/ self.filesList = set() self.scriptName = "" # Script to run the project self.creationDate = "" self.author = "" self.license = "" self.copyright = "" self.version = "" self.email = "" self.description = "" self.uuid = "" self.__ropeProject = None self.__ropeSourceDirs = [] # Coming from separate files from ~/.codimension/uuidN/ self.todos = [] self.bookmarks = [] self.runParamsCache = RunParametersCache() self.topLevelDirs = [] self.findHistory = [] self.findNameHistory = [] self.findFileHistory = [] self.replaceHistory = [] self.tabsStatus = [] self.findFilesWhat = [] self.findFilesDirs = [] self.findFilesMasks = [] self.findClassHistory = [] self.findFuncHistory = [] self.findGlobalHistory = [] self.recentFiles = [] self.importDirs = [] self.fileBrowserPaths = [] self.ignoredExcpt = [] self.breakpoints = [] self.watchpoints = [] # Precompile the exclude filters self.__excludeFilter = [] for flt in Settings().projectFilesFilters: self.__excludeFilter.append( re.compile( flt ) ) return def shouldExclude( self, name ): " Tests if a file must be excluded " for excl in self.__excludeFilter: if excl.match( name ): return True return False def __resetValues( self ): """ Initializes or resets all the project members """ # Empty file name means that the project has not been loaded or # created. This must be an absolute path. self.fileName = "" self.userProjectDir = "" # Generated having the project dir Full paths are stored. # The set holds all files and directories. The dirs end with os.path.sep self.filesList = set() self.scriptName = "" self.creationDate = "" self.author = "" self.license = "" self.copyright = "" self.version = "" self.email = "" self.description = "" self.uuid = "" # Coming from separate files from ~/.codimension/uuidN/ self.todos = [] self.bookmarks = [] self.runParamsCache = RunParametersCache() self.topLevelDirs = [] self.findHistory = [] self.findNameHistory = [] self.findFileHistory = [] self.replaceHistory = [] self.tabsStatus = [] self.findFilesWhat = [] self.findFilesDirs = [] self.findFilesMasks = [] self.findClassHistory = [] self.findFuncHistory = [] self.findGlobalHistory = [] self.recentFiles = [] self.importDirs = [] self.fileBrowserPaths = [] self.ignoredExcpt = [] self.breakpoints = [] self.watchpoints = [] # Reset the dir watchers if so if self.__dirWatcher is not None: del self.__dirWatcher self.__dirWatcher = None return def createNew( self, fileName, scriptName, importDirs, author, lic, copyRight, description, creationDate, version, email ): " Creates a new project " # Try to create the user project directory projectUuid = str( uuid.uuid1() ) userProjectDir = settingsDir + projectUuid + sep if not os.path.exists( userProjectDir ): try: os.mkdir( userProjectDir ) except: logging.error( "Cannot create user project directory: " + self.userProjectDir + ". Please check the " "available disk space and re-create the " "project." ) raise else: logging.warning( "The user project directory existed! " "The content will be overwritten." ) self.__removeProjectFiles( userProjectDir ) # Basic pre-requisites are met. We can reset the current project self.__resetValues() self.fileName = str( fileName ) self.importDirs = importDirs self.scriptName = scriptName self.creationDate = creationDate self.author = author self.license = lic self.copyright = copyRight self.version = version self.email = email self.description = description self.uuid = projectUuid self.userProjectDir = userProjectDir self.__createProjectFile() # ~/.codimension/uuidNN/project self.__generateFilesList() self.saveProject() # Update the watcher self.__dirWatcher = Watcher( Settings().projectFilesFilters, self.getProjectDir() ) self.__dirWatcher.fsChanged.connect( self.onFSChanged ) self.__createRopeProject() self.projectChanged.emit( self.CompleteProject ) return @staticmethod def __safeRemove( path ): " Safe file removal " try: os.remove( path ) except: return def __removeProjectFiles( self, userProjectDir ): " Removes user project files " self.__safeRemove( userProjectDir + "project" ) self.__safeRemove( userProjectDir + "bookmarks" ) self.__safeRemove( userProjectDir + "todos" ) self.__safeRemove( userProjectDir + "searchhistory" ) self.__safeRemove( userProjectDir + "topleveldirs" ) self.__safeRemove( userProjectDir + "tabsstatus" ) self.__safeRemove( userProjectDir + "findinfiles" ) self.__safeRemove( userProjectDir + "recentfiles" ) self.__safeRemove( userProjectDir + "filebrowser" ) self.__safeRemove( userProjectDir + "ignoredexcpt" ) self.__safeRemove( userProjectDir + "breakpoints" ) self.__safeRemove( userProjectDir + "watchpoints" ) return def __createProjectFile( self ): " Helper function to create the user project file " try: f = open( self.userProjectDir + "project", "w" ) f.write( self.fileName ) f.close() except: return def saveProject( self ): " Writes all the settings into the file " if not self.isLoaded(): return # Project properties part propertiesPart = "[properties]\n" \ "scriptname=" + self.scriptName + "\n" \ "creationdate=" + self.creationDate + "\n" \ "author=" + self.author + "\n" \ "license=" + self.license + "\n" \ "copyright=" + self.copyright + "\n" \ "description=" + \ self.description.replace( '\n', '<CR><LF>' ) + \ "\n" \ "version=" + self.version + "\n" \ "email=" + self.email + "\n" \ "uuid=" + self.uuid + "\n" # It could be another user project file without write permissions skipProjectFile = False if os.path.exists( self.fileName ): if not os.access( self.fileName, os.W_OK ): skipProjectFile = True else: if not os.access( os.path.dirname( self.fileName ), os.W_OK ): skipProjectFile = True if not skipProjectFile: f = open( self.fileName, "w" ) self.__writeHeader( f ) self.__writeList( f, "importdirs", "dir", self.importDirs ) f.write( propertiesPart + "\n\n\n" ) f.close() self.serializeRunParameters() self.__saveTopLevelDirs() self.__saveSearchHistory() self.__saveTabsStatus() self.__saveFindFiles() self.__saveFindObjects() self.__saveRecentFiles() self.__saveIgnoredExcpt() self.__saveBreakpoints() self.__saveWatchpoints() self.__formatOK = True return def serializeRunParameters( self ): " Saves the run parameters cache " self.runParamsCache.serialize( self.userProjectDir + "runparamscache" ) return def __saveTabsStatus( self ): " Helper to save tabs status " if self.isLoaded(): f = open( self.userProjectDir + "tabsstatus", "w" ) self.__writeHeader( f ) self.__writeList( f, "tabsstatus", "tab", self.tabsStatus ) f.close() return def __saveSearchHistory( self ): " Helper to save the project search history " if self.isLoaded(): f = open( self.userProjectDir + "searchhistory", "w" ) self.__writeHeader( f ) self.__writeList( f, "findhistory", "find", self.findHistory ) self.__writeList( f, "replacehistory", "replace", self.replaceHistory ) self.__writeList( f, "findnamehistory", "find", self.findNameHistory ) self.__writeList( f, "findfilehistory", "find", self.findFileHistory ) f.close() return def __saveTopLevelDirs( self ): " Helper to save the project top level dirs " if self.isLoaded(): f = open( self.userProjectDir + "topleveldirs", "w" ) self.__writeHeader( f ) self.__writeList( f, "topleveldirs", "dir", self.topLevelDirs ) f.close() return def __saveFindFiles( self ): " Helper to save the find in files history " if self.isLoaded(): f = open( self.userProjectDir + "findinfiles", "w" ) self.__writeHeader( f ) self.__writeList( f, "whathistory", "what", self.findFilesWhat ) self.__writeList( f, "dirhistory", "dir", self.findFilesDirs ) self.__writeList( f, "maskhistory", "mask", self.findFilesMasks ) f.close() return def __saveFindObjects( self ): " Helper to save find objects history " if self.isLoaded(): f = open( self.userProjectDir + "findobjects", "w" ) self.__writeHeader( f ) self.__writeList( f, "classhistory", "class", self.findClassHistory ) self.__writeList( f, "funchistory", "func", self.findFuncHistory ) self.__writeList( f, "globalhistory", "global", self.findGlobalHistory ) f.close() return def __saveSectionToFile( self, fileName, sectionName, itemName, values ): " Saves the given values into a file " if self.isLoaded(): f = open( self.userProjectDir + fileName, "w" ) self.__writeHeader( f ) self.__writeList( f, sectionName, itemName, values ) f.close() return def __saveRecentFiles( self ): " Helper to save recent files list " self.__saveSectionToFile( "recentfiles", "recentfiles", "file", self.recentFiles ) return def __saveIgnoredExcpt( self ): " Helper to save ignored exceptions list " self.__saveSectionToFile( "ignoredexcpt", "ignoredexcpt", "excpttype", self.ignoredExcpt ) return def __saveBreakpoints( self ): " Helper to save breakpoints " self.__saveSectionToFile( "breakpoints", "breakpoints", "bpoint", self.breakpoints ) return def __saveWatchpoints( self ): " helper to save watchpoints " self.__saveSectionToFile( "watchpoints", "watchpoints", "wpoint", self.watchpoints ) return @staticmethod def __writeHeader( fileObj ): " Helper to write a header with a warning " fileObj.write( "#\n" "# Generated automatically.\n" "# Don't edit it manually unless you " "know what you are doing.\n" "#\n\n" ) return @staticmethod def __writeList( fileObj, header, prefix, items ): " Helper to write a list " fileObj.write( "[" + header + "]\n" ) index = 0 for item in items: fileObj.write( prefix + str( index ) + "=" + item + "\n" ) index += 1 fileObj.write( "\n" ) return def __getStr( self, conf, sec, key, default ): " Helper to read a config value " try: return conf.get( sec, key ).strip() except: self.__formatOK = False return default def loadProject( self, projectFile ): """ Loads a project from the given file """ absPath = os.path.abspath( projectFile ) if not os.path.exists( absPath ): raise Exception( "Cannot open project file " + projectFile ) if not absPath.endswith( ".cdm" ): raise Exception( "Unexpected project file extension. " "Expected: .cdm" ) config = ConfigParser.ConfigParser() try: config.read( absPath ) except: # Bad error - cannot load project file at all config = None raise Exception( "Bad project file" ) self.__resetValues() self.fileName = str( absPath ) # Properties part self.scriptName = self.__getStr( config, 'properties', 'scriptname', '' ) self.creationDate = self.__getStr( config, 'properties', 'creationdate', '' ) self.author = self.__getStr( config, 'properties', 'author', '' ) self.license = self.__getStr( config, 'properties', 'license', '' ) self.copyright = self.__getStr( config, 'properties', 'copyright', '' ) self.description = self.__getStr( config, 'properties', 'description', '' ).replace( '<CR><LF>', '\n' ) self.version = self.__getStr( config, 'properties', 'version', '' ) self.email = self.__getStr( config, 'properties', 'email', '' ) self.uuid = self.__getStr( config, 'properties', 'uuid', '' ) if self.uuid == "": logging.warning( "Project file does not have UUID. " "Re-generate it..." ) self.uuid = str( uuid.uuid1() ) self.userProjectDir = settingsDir + self.uuid + sep if not os.path.exists( self.userProjectDir ): os.mkdir( self.userProjectDir ) # import dirs part index = 0 try: while True: dirName = config.get( 'importdirs', 'dir' + str( index ) ).strip() index += 1 if os.path.isabs( dirName ): absPath = dirName else: absPath = self.getProjectDir() + dirName if not os.path.exists( absPath ): logging.error( "Codimension project: cannot find " "import directory: " + dirName ) elif not isdir( absPath ): logging.error( "Codimension project: the import path: " + dirName + " is not a directory" ) self.importDirs.append( dirName ) except ConfigParser.NoSectionError: self.__formatOK = False except ConfigParser.NoOptionError: # just continue pass except: self.__formatOK = False config = None # Read the other config files self.__loadTopLevelDirs() self.__loadSearchHistory() self.__loadTabsStatus() self.__loadFindFiles() self.__loadFindObjects() self.__loadRecentFiles() self.__loadProjectBrowserExpandedDirs() self.__loadIgnoredExceptions() self.__loadBreakpoints() self.__loadWatchpoints() # The project might have been moved... self.__createProjectFile() # ~/.codimension/uuidNN/project self.__generateFilesList() if os.path.exists( self.userProjectDir + "runparamscache" ): self.runParamsCache.deserialize( self.userProjectDir + "runparamscache" ) if not self.__formatOK: logging.info( "Project files are broken or absent. " "Overwriting the project files." ) self.saveProject() # Update the recent list Settings().addRecentProject( self.fileName ) # Setup the new watcher self.__dirWatcher = Watcher( Settings().projectFilesFilters, self.getProjectDir() ) self.__dirWatcher.fsChanged.connect( self.onFSChanged ) self.__createRopeProject() self.projectChanged.emit( self.CompleteProject ) self.emit( SIGNAL( 'restoreProjectExpandedDirs' ) ) return def getImportDirsAsAbsolutePaths( self ): " Provides a list of import dirs as absolute paths " result = [] for path in self.importDirs: if os.path.isabs( path ): result.append( path ) else: result.append( self.getProjectDir() + path ) return result def __getImportDirsForRope( self ): " Provides the list of import dirs for the rope library " result = [] for path in self.importDirs: if not os.path.isabs( path ): # The only relative paths can be accepted by rope result.append( path ) result.sort() return result def getRopeProject( self ): " Provides the rope project " if self.__getImportDirsForRope() != self.__ropeSourceDirs: # The user changed the project import dirs, so let's # re-create the project self.__createRopeProject() return self.__ropeProject def validateRopeProject( self, fileName ): " Validates the rope project " # Currently rope can validate a directory only so the argument # is ignored self.__ropeProject.validate() return def __createRopeProject( self ): " Creates a rope library project " if self.__ropeProject is not None: self.__ropeProject.close() self.__ropeProject = None self.__ropeSourceDirs = [] # Deal with import dirs and preferences first self.__ropeSourceDirs = self.__getImportDirsForRope() prefs = copy.deepcopy( ropePreferences ) if len( self.__ropeSourceDirs ) != 0: prefs[ "source_folders" ] = self.__ropeSourceDirs # Rope folder is default here, so it will be created self.__ropeProject = RopeProject( self.getProjectDir(), **prefs ) self.__ropeProject.validate( self.__ropeProject.root ) return def onFSChanged( self, items ): " Triggered when the watcher detects changes " ## report = "REPORT: " ## projectItems = [] for item in items: item = str( item ) # if not islink( item ): # realPath = realpath( item[ 1: ] ) # isDir = item.endswith( sep ) # if isDir: # if self.isProjectDir( realPath + sep ): # item = item[ 0 ] + realPath + sep # else: # if self.isProjectFile( realPath + sep ): # item = item[ 0 ] + realPath # projectItems.append( item ) ## report += " " + item try: if item.startswith( '+' ): self.filesList.add( item[ 1: ] ) else: self.filesList.remove( item[ 1: ] ) ## projectItems.append( item ) except: # print "EXCEPTION for '" + item + "'" pass # print "'" + report + "'" self.fsChanged.emit( items ) # self.__dirWatcher.debug() return def __loadTabsStatus( self ): " Loads the last tabs status " configFile = self.userProjectDir + "tabsstatus" if not os.path.exists( configFile ): logging.info( "Cannot find tabsstatus project file. " "Expected here: " + configFile ) self.__formatOK = False return config = ConfigParser.ConfigParser() try: config.read( configFile ) except: # Bad error - cannot load project file at all config = None self.__formatOK = False logging.warning( "Cannot read tabsstatus project file " "from here: " + configFile ) return # tabs part self.tabsStatus = self.__loadListSection( config, 'tabsstatus', 'tab' ) config = None return def __loadProjectBrowserExpandedDirs( self ): " Loads the project browser expanded dirs " configFile = self.userProjectDir + "filebrowser" if not os.path.exists( configFile ): self.fileBrowserPaths = [] return config = ConfigParser.ConfigParser() try: config.read( configFile ) except: # Bad error - cannot load project file at all config = None self.fileBrowserPaths = [] return # dirs part self.fileBrowserPaths = self.__loadListSection( config, 'filebrowser', 'path' ) config = None return def __loadTopLevelDirs( self ): " Loads the top level dirs " configFile = self.userProjectDir + "topleveldirs" if not os.path.exists( configFile ): logging.info( "Cannot find topleveldirs project file. " "Expected here: " + configFile ) self.__formatOK = False return config = ConfigParser.ConfigParser() try: config.read( configFile ) except: # Bad error - cannot load project file at all config = None self.__formatOK = False logging.warning( "Cannot read topleveldirs project file " "from here: " + configFile ) return # dirs part self.topLevelDirs = self.__loadListSection( config, 'topleveldirs', 'dir' ) config = None return def __loadSearchHistory( self ): " Loads the search history file content " confFile = self.userProjectDir + "searchhistory" if not os.path.exists( confFile ): logging.info( "Cannot find searchhistory project file. " "Expected here: " + confFile ) self.__formatOK = False return config = ConfigParser.ConfigParser() try: config.read( confFile ) except: # Bad error - cannot load project file at all config = None self.__formatOK = False logging.warning( "Cannot read searchhistory project file " "from here: " + confFile ) return # find part self.findHistory = self.__loadListSection( config, 'findhistory', 'find' ) self.findNameHistory = self.__loadListSection( config, 'findnamehistory', 'find' ) self.findFileHistory = self.__loadListSection( config, 'findfilehistory', 'find' ) # replace part self.replaceHistory = self.__loadListSection( config, 'replacehistory', 'replace' ) config = None return def __loadFindObjects( self ): " Loads the find objects history " confFile = self.userProjectDir + "findobjects" if not os.path.exists( confFile ): logging.info( "Cannot find findobjects project file. " "Expected here: " + confFile ) self.__formatOK = False return config = ConfigParser.ConfigParser() try: config.read( confFile ) except: # Bad error - cannot load project file at all config = None self.__formatOK = False logging.warning( "Cannot read findobjects project file " "from here: " + confFile ) return self.findClassHistory = self.__loadListSection( config, 'classhistory', 'class' ) self.findFuncHistory = self.__loadListSection( config, 'funchistory', 'func' ) self.findGlobalHistory = self.__loadListSection( config, 'globalhistory', 'global' ) config = None return def __loadFindFiles( self ): " Loads the find in files history " confFile = self.userProjectDir + "findinfiles" if not os.path.exists( confFile ): logging.info( "Cannot find findinfiles project file. " "Expected here: " + confFile ) self.__formatOK = False return config = ConfigParser.ConfigParser() try: config.read( confFile ) except: # Bad error - cannot load project file at all config = None self.__formatOK = False logging.warning( "Cannot read findinfiles project file " "from here: " + confFile ) return self.findFilesWhat = self.__loadListSection( config, 'whathistory', 'what' ) self.findFilesDirs = self.__loadListSection( config, 'dirhistory', 'dir' ) self.findFilesMasks = self.__loadListSection( config, 'maskhistory', 'mask' ) config = None return def __loadRecentFiles( self ): " Loads the recent files list " confFile = self.userProjectDir + "recentfiles" if not os.path.exists( confFile ): logging.info( "Cannot find recentfiles project file. " "Expected here: " + confFile ) self.__formatOK = False return config = ConfigParser.ConfigParser() try: config.read( confFile ) except: # Bad error - cannot load project file at all config = None self.__formatOK = False logging.warning( "Cannot read recentfiles project file " "from here: " + confFile ) return self.recentFiles = self.__loadListSection( config, 'recentfiles', 'file' ) # Due to a bug there could be the same files twice in the list. # The difference is doubled path separator. Fix it here. temp = set() for path in self.recentFiles: temp.add( os.path.normpath( path ) ) self.recentFiles = list( temp ) config = None return def __loadSectionFromFile( self, fileName, sectionName, itemName, errMessageEntity ): " Loads the given section from the given file " confFile = self.userProjectDir + fileName if not os.path.exists( confFile ): logging.info( "Cannot find " + errMessageEntity + " file. " "Expected here: " + confFile ) self.__formatOK = False return [] config = ConfigParser.ConfigParser() try: config.read( confFile ) except: # Bad error - cannot load project file at all config = None self.__formatOK = False logging.warning( "Cannot read " + errMessageEntity + " file " "from here: " + confFile ) return [] values = self.__loadListSection( config, sectionName, itemName ) config = None return values def __loadIgnoredExceptions( self ): " Loads the ignored exceptions list " self.ignoredExcpt = self.__loadSectionFromFile( "ignoredexcpt", "ignoredexcpt", "excpttype", "ignored exceptions" ) return def __loadBreakpoints( self ): " Loads the project breakpoints " self.breakpoints = self.__loadSectionFromFile( "breakpoints", "breakpoints", "bpoint", "breakpoints" ) return def __loadWatchpoints( self ): " Loads the project watchpoints " self.watchpoints = self.__loadSectionFromFile( "watchpoints", "watchpoints", "wpoint", "watchpoints" ) return def __loadListSection( self, config, section, listPrefix ): " Loads a list off the given section from the given file " items = [] index = 0 try: while True: item = config.get( section, listPrefix + str(index) ).strip() index += 1 items.append( item ) except ConfigParser.NoSectionError: self.__formatOK = False except ConfigParser.NoOptionError: pass # Just continue except: self.__formatOK = False return items def unloadProject( self, emitSignal = True ): """ Unloads the current project if required """ self.emit( SIGNAL( 'projectAboutToUnload' ) ) if self.isLoaded(): self.__saveProjectBrowserExpandedDirs() self.__resetValues() if emitSignal: # No need to send a signal e.g. if IDE is closing self.projectChanged.emit( self.CompleteProject ) if self.__ropeProject is not None: try: # If the project directory is read only then closing the # rope project generates exception self.__ropeProject.close() except: pass self.__ropeProject = None self.__ropeSourceDirs = [] return def __saveProjectBrowserExpandedDirs( self ): " Saves the pathes expanded in the project browser " if not self.isLoaded(): return try: f = open( self.userProjectDir + "filebrowser", "w" ) self.__writeHeader( f ) self.__writeList( f, "filebrowser", "path", self.fileBrowserPaths ) f.close() except: pass return def setImportDirs( self, paths ): " Sets a new set of the project import dirs " if self.importDirs != paths: self.importDirs = paths self.saveProject() self.projectChanged.emit( self.Properties ) return def __generateFilesList( self ): """ Generates the files list having the list of dirs """ self.filesList = set() path = self.getProjectDir() self.filesList.add( path ) self.__scanDir( path ) return def __scanDir( self, path ): """ Recursive function to scan one dir """ # The path is with '/' at the end for item in os.listdir( path ): if self.shouldExclude( item ): continue # Exclude symlinks if they point to the other project # covered pieces candidate = path + item if islink( candidate ): realItem = realpath( candidate ) if isdir( realItem ): if self.isProjectDir( realItem ): continue else: if self.isProjectDir( os.path.dirname( realItem ) ): continue if isdir( candidate ): self.filesList.add( candidate + sep ) self.__scanDir( candidate + sep ) continue self.filesList.add( candidate ) return def isProjectDir( self, path ): " Returns True if the path belongs to the project " if not self.isLoaded(): return False path = realpath( str( path ) ) # it could be a symlink if not path.endswith( sep ): path += sep return path.startswith( self.getProjectDir() ) def isProjectFile( self, path ): " Returns True if the path belongs to the project " if not self.isLoaded(): return False return self.isProjectDir( os.path.dirname( path ) ) def isTopLevelDir( self, path ): " Checks if the path is a top level dir " if not path.endswith( sep ): path += sep return path in self.topLevelDirs def addTopLevelDir( self, path ): " Adds the path to the top level dirs list " if not path.endswith( sep ): path += sep if path in self.topLevelDirs: logging.warning( "Top level dir " + path + " is already in the list of dirs. " "Ignore adding..." ) return self.topLevelDirs.append( path ) self.__saveTopLevelDirs() return def removeTopLevelDir( self, path ): " Removes the path from the top level dirs list " if not path.endswith( sep ): path += sep if path not in self.topLevelDirs: logging.warning( "Top level dir " + path + " is not in the list of dirs. Ignore removing..." ) return self.topLevelDirs.remove( path ) self.__saveTopLevelDirs() return def setFindNameHistory( self, history ): " Sets the new find name history and saves it into a file " self.findNameHistory = history self.__saveSearchHistory() return def setFindFileHistory( self, history ): " Sets the new find file history and saves it into a file " self.findFileHistory = history self.__saveSearchHistory() return def setFindHistory( self, history ): " Sets the new find history and save it into a file " self.findHistory = history self.__saveSearchHistory() return def setReplaceHistory( self, whatHistory, toHistory ): " Sets the new replace history and save it into a file " self.findHistory = whatHistory self.replaceHistory = toHistory self.__saveSearchHistory() return def setTabsStatus( self, status ): " Sets the new tabs status and save it into a file " self.tabsStatus = status self.__saveTabsStatus() return def setFindInFilesHistory( self, what, dirs, masks ): " Sets the new lists and save them into a file " self.findFilesWhat = what self.findFilesDirs = dirs self.findFilesMasks = masks self.__saveFindFiles() return def setFindClassHistory( self, history ): " Sets the new history and saves it into a file " self.findClassHistory = history self.__saveFindObjects() return def setFindFuncHistory( self, history ): " Sets the new history and saves it into a file " self.findFuncHistory = history self.__saveFindObjects() return def setFindGlobalHistory( self, history ): " Sets the new history and saves it into a file " self.findGlobalHistory = history self.__saveFindObjects() return def updateProperties( self, scriptName, importDirs, creationDate, author, lic, copy_right, version, email, description ): " Updates the project properties " if self.scriptName == scriptName and \ self.creationDate == creationDate and \ self.author == author and \ self.license == lic and \ self.copyright == copy_right and \ self.version == version and \ self.email == email and \ self.description == description and \ self.importDirs == importDirs: # No real changes return self.importDirs = importDirs self.scriptName = scriptName self.creationDate = creationDate self.author = author self.license = lic self.copyright = copy_right self.version = version self.email = email self.description = description self.saveProject() self.projectChanged.emit( self.Properties ) return def onProjectFileUpdated( self ): " Called when a project file is updated via direct editing " scriptName, importDirs, \ creationDate, author, \ lic, copy_right, \ description, version, \ email, projectUuid = getProjectProperties( self.fileName ) self.importDirs = importDirs self.scriptName = scriptName self.creationDate = creationDate self.author = author self.license = lic self.copyright = copy_right self.version = version self.email = email self.description = description # no need to save, but signal just in case self.projectChanged.emit( self.Properties ) return def isLoaded( self ): " returns True if a project is loaded " return self.fileName != "" def getProjectDir( self ): " Provides an absolute path to the project dir " if not self.isLoaded(): return "" return os.path.dirname( realpath( self.fileName ) ) + sep def getProjectScript( self ): " Provides the project script file name " if not self.isLoaded(): return "" if self.scriptName == "": return "" if os.path.isabs( self.scriptName ): return self.scriptName return os.path.normpath( self.getProjectDir() + self.scriptName ) def addRecentFile( self, path ): " Adds a single recent file. True if a new file was inserted. " if path in self.recentFiles: self.recentFiles.remove( path ) self.recentFiles.insert( 0, path ) self.__saveRecentFiles() return False self.recentFiles.insert( 0, path ) self.__saveRecentFiles() if len( self.recentFiles ) > 32: self.recentFiles = self.recentFiles[ 0 : 32 ] self.emit( SIGNAL( 'recentFilesChanged' ) ) return True def removeRecentFile( self, path ): " Removes a single recent file " if path in self.recentFiles: self.recentFiles.remove( path ) self.__saveRecentFiles() return def addExceptionFilter( self, excptType ): " Adds a new ignored exception type " if excptType not in self.ignoredExcpt: self.ignoredExcpt.append( excptType ) self.__saveIgnoredExcpt() return def deleteExceptionFilter( self, excptType ): " Remove ignored exception type " if excptType in self.ignoredExcpt: self.ignoredExcpt.remove( excptType ) self.__saveIgnoredExcpt() return def setExceptionFilters( self, newFilters ): " Sets the new filters " self.ignoredExcpt = newFilters self.__saveIgnoredExcpt() return def addBreakpoint( self, bpointStr ): " Adds serialized breakpoint " if bpointStr not in self.breakpoints: self.breakpoints.append( bpointStr ) self.__saveBreakpoints() return def deleteBreakpoint( self, bpointStr ): " Deletes serialized breakpoint " if bpointStr in self.breakpoints: self.breakpoints.remove( bpointStr ) self.__saveBreakpoints() return def setBreakpoints( self, bpointStrList ): " Sets breakpoints list " self.breakpoints = bpointStrList self.__saveBreakpoints() return def addWatchpoint( self, wpointStr ): " Adds serialized watchpoint " if wpointStr not in self.watchpoints: self.watchpoints.append( wpointStr ) self.__saveWatchpoints() return def deleteWatchpoint( self, wpointStr ): " Deletes serialized watchpoint " if wpointStr in self.watchpoints: self.watchpoints.remove( wpointStr ) self.__saveWatchpoints() return def setWatchpoints( self, wpointStrList ): " Sets watchpoints list " self.watchpoints = wpointStrList self.__saveWatchpoints() return
class Singleton: """ Provides singleton facility """ def __init__( self ): self.application = None self.splash = None self.mainWindow = None self.skin = None self.screenWidth = 0 self.screenHeight = 0 self.version = "unknown" self.project = CodimensionProject() self.pluginManager = CDMPluginManager() self.briefModinfoCache = BriefModuleInfoCache() self.runParamsCache = RunParametersCache() if os.path.isfile( settingsDir + "runparamscache" ): self.runParamsCache.deserialize( settingsDir + "runparamscache" ) self.pylintAvailable = self.__checkPylint() self.graphvizAvailable = self.__checkGraphviz() self.pylintVersion = None if self.pylintAvailable: self.pylintVersion = self.__getPylintVersion() if self.pylintVersion is None: self.pylintAvailable = False return def getRunParameters( self, fileName ): " Provides the run parameters " if self.project.isLoaded(): if self.project.isProjectFile( fileName ): key = relpath( fileName, os.path.dirname( self.project.fileName ) ) else: key = fileName return self.project.runParamsCache.get( key ) # No project loaded return self.runParamsCache.get( fileName ) def addRunParams( self, fileName, params ): " Registers new latest run parameters " if self.project.isLoaded(): if self.project.isProjectFile( fileName ): key = relpath( fileName, os.path.dirname( self.project.fileName ) ) else: key = fileName self.project.runParamsCache.add( key, params ) self.project.serializeRunParameters() return # No project loaded self.runParamsCache.add( fileName, params ) self.runParamsCache.serialize( settingsDir + "runparamscache" ) return def getProfileOutputPath( self ): " Provides the path to the profile output file " if self.project.isLoaded(): return self.project.userProjectDir + "profile.out" # No project loaded return settingsDir + "profile.out" def getRopeProject( self, fileName = "" ): " Provides existed or creates a new rope project " if self.project.isLoaded(): return self.project.getRopeProject() # There is no current project so create a temporary one. # Two cases: the buffer has been saved # not saved buffer if os.path.isabs( fileName ): dirName = os.path.dirname( fileName ) else: # Unsaved buffer, make an assumption that # it is in home directory dirName = str( QDir.homePath() ) prefs = copy.deepcopy( ropePreferences ) # Exclude nested dirs as it could take too long # Get only dir names and do not get those dirs # where __init__.py[3] are present subdirsToExclude = getSubdirs( dirName, True, True ) if "ignored_resources" in prefs: prefs[ "ignored_resources" ] += subdirsToExclude else: prefs[ "ignored_resources" ] = subdirsToExclude project = RopeProject( dirName, None, None, **prefs ) project.validate( project.root ) return project def validateRopeProject( self, fileName = "" ): " Validates the existed rope project if so " if not self.project.isLoaded(): return # Currently rope supports validating of directories only # There is a hope that it will support validating a single file # one day. So the fileName argument is ignored by now and the whole # project is invalidated. if fileName != "": from fileutils import ( detectFileType, PythonFileType, Python3FileType ) fileType = detectFileType( fileName ) if fileType not in [ PythonFileType, Python3FileType ]: return self.project.validateRopeProject( fileName ) return def getProjectImportDirs( self ): """ Provides a list of the project import dirs if so. Note: the paths do not have '/' at the end due to os.path.normpath """ if not self.project.isLoaded(): return [] basePath = self.project.getProjectDir() result = list( self.project.importDirs ) index = len( result ) - 1 while index >= 0: path = result[ index ] if not os.path.isabs( path ): result[ index ] = os.path.normpath( basePath + path ) index -= 1 return result def isProjectScriptValid( self ): " True if the project script valid " scriptName = self.project.getProjectScript() if not os.path.exists( scriptName ): return False scriptName = os.path.realpath( scriptName ) if not os.path.isfile( scriptName ): return False from fileutils import ( detectFileType, PythonFileType, Python3FileType ) if detectFileType( scriptName ) in [ PythonFileType, Python3FileType ]: return True return False def getFileLineDocstring( self, fName, line ): " Provides a docstring if so for the given file and line " from fileutils import ( detectFileType, PythonFileType, Python3FileType ) if detectFileType( fName ) not in [ PythonFileType, Python3FileType ]: return "" infoCache = self.briefModinfoCache def checkFuncObject( obj, line ): " Checks docstring for a function or a class " if obj.line == line or obj.keywordLine == line: if obj.docstring is None: return True, "" return True, obj.docstring.text for item in obj.classes + obj.functions: found, docstring = checkFuncObject( item, line ) if found: return True, docstring return False, "" try: info = infoCache.get( fName ) for item in info.classes + info.functions: found, docstring = checkFuncObject( item, line ) if found: return docstring except: pass return "" def getModInfo( self, path ): " Provides a module info for the given file " from fileutils import ( detectFileType, PythonFileType, Python3FileType ) if detectFileType( path ) not in [ PythonFileType, Python3FileType ]: raise Exception( "Trying to parse non-python file: " + path ) return self.briefModinfoCache.get( path ) @staticmethod def __checkGraphviz(): " Checks if the graphviz available " if 'win' in sys.platform.lower(): return os.system( 'which dot > /NUL 2>&1' ) == 0 return os.system( 'which dot > /dev/null 2>&1' ) == 0 @staticmethod def __checkPylint(): " Checks if pylint is available " if 'win' in sys.platform.lower(): return os.system( 'which pylint > /NUL 2>&1' ) == 0 return os.system( 'which pylint > /dev/null 2>&1' ) == 0 @staticmethod def __getPylintVersion(): " Provides the pylint version " output = check_output( "pylint --version; exit 0", stderr = STDOUT, shell = True ) for line in output.splitlines(): line = line.strip() if line.startswith( "pylint " ): version = line.replace( "pylint", "" ).replace( ",", "" ) try: return StrictVersion( version.strip() ) except: return None return None
class CodimensionProject(QObject): " Provides codimension project singleton facility " # Constants for the projectChanged signal CompleteProject = 0 # It is a completely new project Properties = 1 # Project properties were updated projectChanged = pyqtSignal(int) fsChanged = pyqtSignal(list) def __init__(self): QObject.__init__(self) self.__dirWatcher = None self.__formatOK = True # Avoid pylint complains self.fileName = "" self.userProjectDir = "" # Directory in ~/.codimension/uuidNN/ self.filesList = set() self.scriptName = "" # Script to run the project self.creationDate = "" self.author = "" self.license = "" self.copyright = "" self.version = "" self.email = "" self.description = "" self.uuid = "" self.__ropeProject = None self.__ropeSourceDirs = [] # Coming from separate files from ~/.codimension/uuidN/ self.todos = [] self.bookmarks = [] self.runParamsCache = RunParametersCache() self.topLevelDirs = [] self.findHistory = [] self.findNameHistory = [] self.findFileHistory = [] self.replaceHistory = [] self.tabsStatus = [] self.findFilesWhat = [] self.findFilesDirs = [] self.findFilesMasks = [] self.findClassHistory = [] self.findFuncHistory = [] self.findGlobalHistory = [] self.recentFiles = [] self.importDirs = [] self.fileBrowserPaths = [] self.ignoredExcpt = [] self.breakpoints = [] self.watchpoints = [] # Precompile the exclude filters self.__excludeFilter = [] for flt in Settings().projectFilesFilters: self.__excludeFilter.append(re.compile(flt)) return def shouldExclude(self, name): " Tests if a file must be excluded " for excl in self.__excludeFilter: if excl.match(name): return True return False def __resetValues(self): """ Initializes or resets all the project members """ # Empty file name means that the project has not been loaded or # created. This must be an absolute path. self.fileName = "" self.userProjectDir = "" # Generated having the project dir Full paths are stored. # The set holds all files and directories. The dirs end with os.path.sep self.filesList = set() self.scriptName = "" self.creationDate = "" self.author = "" self.license = "" self.copyright = "" self.version = "" self.email = "" self.description = "" self.uuid = "" # Coming from separate files from ~/.codimension/uuidN/ self.todos = [] self.bookmarks = [] self.runParamsCache = RunParametersCache() self.topLevelDirs = [] self.findHistory = [] self.findNameHistory = [] self.findFileHistory = [] self.replaceHistory = [] self.tabsStatus = [] self.findFilesWhat = [] self.findFilesDirs = [] self.findFilesMasks = [] self.findClassHistory = [] self.findFuncHistory = [] self.findGlobalHistory = [] self.recentFiles = [] self.importDirs = [] self.fileBrowserPaths = [] self.ignoredExcpt = [] self.breakpoints = [] self.watchpoints = [] # Reset the dir watchers if so if self.__dirWatcher is not None: del self.__dirWatcher self.__dirWatcher = None return def createNew(self, fileName, scriptName, importDirs, author, lic, copyRight, description, creationDate, version, email): " Creates a new project " # Try to create the user project directory projectUuid = str(uuid.uuid1()) userProjectDir = settingsDir + projectUuid + sep if not os.path.exists(userProjectDir): try: os.mkdir(userProjectDir) except: logging.error("Cannot create user project directory: " + self.userProjectDir + ". Please check the " "available disk space and re-create the " "project.") raise else: logging.warning("The user project directory existed! " "The content will be overwritten.") self.__removeProjectFiles(userProjectDir) # Basic pre-requisites are met. We can reset the current project self.__resetValues() self.fileName = str(fileName) self.importDirs = importDirs self.scriptName = scriptName self.creationDate = creationDate self.author = author self.license = lic self.copyright = copyRight self.version = version self.email = email self.description = description self.uuid = projectUuid self.userProjectDir = userProjectDir self.__createProjectFile() # ~/.codimension/uuidNN/project self.__generateFilesList() self.saveProject() # Update the watcher self.__dirWatcher = Watcher(Settings().projectFilesFilters, self.getProjectDir()) self.__dirWatcher.fsChanged.connect(self.onFSChanged) self.__createRopeProject() self.projectChanged.emit(self.CompleteProject) return @staticmethod def __safeRemove(path): " Safe file removal " try: os.remove(path) except: return def __removeProjectFiles(self, userProjectDir): " Removes user project files " self.__safeRemove(userProjectDir + "project") self.__safeRemove(userProjectDir + "bookmarks") self.__safeRemove(userProjectDir + "todos") self.__safeRemove(userProjectDir + "searchhistory") self.__safeRemove(userProjectDir + "topleveldirs") self.__safeRemove(userProjectDir + "tabsstatus") self.__safeRemove(userProjectDir + "findinfiles") self.__safeRemove(userProjectDir + "recentfiles") self.__safeRemove(userProjectDir + "filebrowser") self.__safeRemove(userProjectDir + "ignoredexcpt") self.__safeRemove(userProjectDir + "breakpoints") self.__safeRemove(userProjectDir + "watchpoints") return def __createProjectFile(self): " Helper function to create the user project file " try: f = open(self.userProjectDir + "project", "w") f.write(self.fileName) f.close() except: return def saveProject(self): " Writes all the settings into the file " if not self.isLoaded(): return # Project properties part propertiesPart = "[properties]\n" \ "scriptname=" + self.scriptName + "\n" \ "creationdate=" + self.creationDate + "\n" \ "author=" + self.author + "\n" \ "license=" + self.license + "\n" \ "copyright=" + self.copyright + "\n" \ "description=" + \ self.description.replace( '\n', '<CR><LF>' ) + \ "\n" \ "version=" + self.version + "\n" \ "email=" + self.email + "\n" \ "uuid=" + self.uuid + "\n" # It could be another user project file without write permissions skipProjectFile = False if os.path.exists(self.fileName): if not os.access(self.fileName, os.W_OK): skipProjectFile = True else: if not os.access(os.path.dirname(self.fileName), os.W_OK): skipProjectFile = True if not skipProjectFile: f = open(self.fileName, "w") self.__writeHeader(f) self.__writeList(f, "importdirs", "dir", self.importDirs) f.write(propertiesPart + "\n\n\n") f.close() self.serializeRunParameters() self.__saveTopLevelDirs() self.__saveSearchHistory() self.__saveTabsStatus() self.__saveFindFiles() self.__saveFindObjects() self.__saveRecentFiles() self.__saveIgnoredExcpt() self.__saveBreakpoints() self.__saveWatchpoints() self.__formatOK = True return def serializeRunParameters(self): " Saves the run parameters cache " self.runParamsCache.serialize(self.userProjectDir + "runparamscache") return def __saveTabsStatus(self): " Helper to save tabs status " if self.isLoaded(): f = open(self.userProjectDir + "tabsstatus", "w") self.__writeHeader(f) self.__writeList(f, "tabsstatus", "tab", self.tabsStatus) f.close() return def __saveSearchHistory(self): " Helper to save the project search history " if self.isLoaded(): f = open(self.userProjectDir + "searchhistory", "w") self.__writeHeader(f) self.__writeList(f, "findhistory", "find", self.findHistory) self.__writeList(f, "replacehistory", "replace", self.replaceHistory) self.__writeList(f, "findnamehistory", "find", self.findNameHistory) self.__writeList(f, "findfilehistory", "find", self.findFileHistory) f.close() return def __saveTopLevelDirs(self): " Helper to save the project top level dirs " if self.isLoaded(): f = open(self.userProjectDir + "topleveldirs", "w") self.__writeHeader(f) self.__writeList(f, "topleveldirs", "dir", self.topLevelDirs) f.close() return def __saveFindFiles(self): " Helper to save the find in files history " if self.isLoaded(): f = open(self.userProjectDir + "findinfiles", "w") self.__writeHeader(f) self.__writeList(f, "whathistory", "what", self.findFilesWhat) self.__writeList(f, "dirhistory", "dir", self.findFilesDirs) self.__writeList(f, "maskhistory", "mask", self.findFilesMasks) f.close() return def __saveFindObjects(self): " Helper to save find objects history " if self.isLoaded(): f = open(self.userProjectDir + "findobjects", "w") self.__writeHeader(f) self.__writeList(f, "classhistory", "class", self.findClassHistory) self.__writeList(f, "funchistory", "func", self.findFuncHistory) self.__writeList(f, "globalhistory", "global", self.findGlobalHistory) f.close() return def __saveSectionToFile(self, fileName, sectionName, itemName, values): " Saves the given values into a file " if self.isLoaded(): f = open(self.userProjectDir + fileName, "w") self.__writeHeader(f) self.__writeList(f, sectionName, itemName, values) f.close() return def __saveRecentFiles(self): " Helper to save recent files list " self.__saveSectionToFile("recentfiles", "recentfiles", "file", self.recentFiles) return def __saveIgnoredExcpt(self): " Helper to save ignored exceptions list " self.__saveSectionToFile("ignoredexcpt", "ignoredexcpt", "excpttype", self.ignoredExcpt) return def __saveBreakpoints(self): " Helper to save breakpoints " self.__saveSectionToFile("breakpoints", "breakpoints", "bpoint", self.breakpoints) return def __saveWatchpoints(self): " helper to save watchpoints " self.__saveSectionToFile("watchpoints", "watchpoints", "wpoint", self.watchpoints) return @staticmethod def __writeHeader(fileObj): " Helper to write a header with a warning " fileObj.write("#\n" "# Generated automatically.\n" "# Don't edit it manually unless you " "know what you are doing.\n" "#\n\n") return @staticmethod def __writeList(fileObj, header, prefix, items): " Helper to write a list " fileObj.write("[" + header + "]\n") index = 0 for item in items: fileObj.write(prefix + str(index) + "=" + item + "\n") index += 1 fileObj.write("\n") return def __getStr(self, conf, sec, key, default): " Helper to read a config value " try: return conf.get(sec, key).strip() except: self.__formatOK = False return default def loadProject(self, projectFile): """ Loads a project from the given file """ absPath = os.path.abspath(projectFile) if not os.path.exists(absPath): raise Exception("Cannot open project file " + projectFile) if not absPath.endswith(".cdm"): raise Exception("Unexpected project file extension. " "Expected: .cdm") config = ConfigParser.ConfigParser() try: config.read(absPath) except: # Bad error - cannot load project file at all config = None raise Exception("Bad project file") self.__resetValues() self.fileName = str(absPath) # Properties part self.scriptName = self.__getStr(config, 'properties', 'scriptname', '') self.creationDate = self.__getStr(config, 'properties', 'creationdate', '') self.author = self.__getStr(config, 'properties', 'author', '') self.license = self.__getStr(config, 'properties', 'license', '') self.copyright = self.__getStr(config, 'properties', 'copyright', '') self.description = self.__getStr(config, 'properties', 'description', '').replace('<CR><LF>', '\n') self.version = self.__getStr(config, 'properties', 'version', '') self.email = self.__getStr(config, 'properties', 'email', '') self.uuid = self.__getStr(config, 'properties', 'uuid', '') if self.uuid == "": logging.warning("Project file does not have UUID. " "Re-generate it...") self.uuid = str(uuid.uuid1()) self.userProjectDir = settingsDir + self.uuid + sep if not os.path.exists(self.userProjectDir): os.mkdir(self.userProjectDir) # import dirs part index = 0 try: while True: dirName = config.get('importdirs', 'dir' + str(index)).strip() index += 1 if os.path.isabs(dirName): absPath = dirName else: absPath = self.getProjectDir() + dirName if not os.path.exists(absPath): logging.error("Codimension project: cannot find " "import directory: " + dirName) elif not isdir(absPath): logging.error("Codimension project: the import path: " + dirName + " is not a directory") self.importDirs.append(dirName) except ConfigParser.NoSectionError: self.__formatOK = False except ConfigParser.NoOptionError: # just continue pass except: self.__formatOK = False config = None # Read the other config files self.__loadTopLevelDirs() self.__loadSearchHistory() self.__loadTabsStatus() self.__loadFindFiles() self.__loadFindObjects() self.__loadRecentFiles() self.__loadProjectBrowserExpandedDirs() self.__loadIgnoredExceptions() self.__loadBreakpoints() self.__loadWatchpoints() # The project might have been moved... self.__createProjectFile() # ~/.codimension/uuidNN/project self.__generateFilesList() if os.path.exists(self.userProjectDir + "runparamscache"): self.runParamsCache.deserialize(self.userProjectDir + "runparamscache") if not self.__formatOK: logging.info("Project files are broken or absent. " "Overwriting the project files.") self.saveProject() # Update the recent list Settings().addRecentProject(self.fileName) # Setup the new watcher self.__dirWatcher = Watcher(Settings().projectFilesFilters, self.getProjectDir()) self.__dirWatcher.fsChanged.connect(self.onFSChanged) self.__createRopeProject() self.projectChanged.emit(self.CompleteProject) self.emit(SIGNAL('restoreProjectExpandedDirs')) return def getImportDirsAsAbsolutePaths(self): " Provides a list of import dirs as absolute paths " result = [] for path in self.importDirs: if os.path.isabs(path): result.append(path) else: result.append(self.getProjectDir() + path) return result def __getImportDirsForRope(self): " Provides the list of import dirs for the rope library " result = [] for path in self.importDirs: if not os.path.isabs(path): # The only relative paths can be accepted by rope result.append(path) result.sort() return result def getRopeProject(self): " Provides the rope project " if self.__getImportDirsForRope() != self.__ropeSourceDirs: # The user changed the project import dirs, so let's # re-create the project self.__createRopeProject() return self.__ropeProject def validateRopeProject(self, fileName): " Validates the rope project " # Currently rope can validate a directory only so the argument # is ignored self.__ropeProject.validate() return def __createRopeProject(self): " Creates a rope library project " if self.__ropeProject is not None: self.__ropeProject.close() self.__ropeProject = None self.__ropeSourceDirs = [] # Deal with import dirs and preferences first self.__ropeSourceDirs = self.__getImportDirsForRope() prefs = copy.deepcopy(ropePreferences) if len(self.__ropeSourceDirs) != 0: prefs["source_folders"] = self.__ropeSourceDirs # Rope folder is default here, so it will be created self.__ropeProject = RopeProject(self.getProjectDir(), **prefs) self.__ropeProject.validate(self.__ropeProject.root) return def onFSChanged(self, items): " Triggered when the watcher detects changes " ## report = "REPORT: " ## projectItems = [] for item in items: item = str(item) # if not islink( item ): # realPath = realpath( item[ 1: ] ) # isDir = item.endswith( sep ) # if isDir: # if self.isProjectDir( realPath + sep ): # item = item[ 0 ] + realPath + sep # else: # if self.isProjectFile( realPath + sep ): # item = item[ 0 ] + realPath # projectItems.append( item ) ## report += " " + item try: if item.startswith('+'): self.filesList.add(item[1:]) else: self.filesList.remove(item[1:]) ## projectItems.append( item ) except: # print "EXCEPTION for '" + item + "'" pass # print "'" + report + "'" self.fsChanged.emit(items) # self.__dirWatcher.debug() return def __loadTabsStatus(self): " Loads the last tabs status " configFile = self.userProjectDir + "tabsstatus" if not os.path.exists(configFile): logging.info("Cannot find tabsstatus project file. " "Expected here: " + configFile) self.__formatOK = False return config = ConfigParser.ConfigParser() try: config.read(configFile) except: # Bad error - cannot load project file at all config = None self.__formatOK = False logging.warning("Cannot read tabsstatus project file " "from here: " + configFile) return # tabs part self.tabsStatus = self.__loadListSection(config, 'tabsstatus', 'tab') config = None return def __loadProjectBrowserExpandedDirs(self): " Loads the project browser expanded dirs " configFile = self.userProjectDir + "filebrowser" if not os.path.exists(configFile): self.fileBrowserPaths = [] return config = ConfigParser.ConfigParser() try: config.read(configFile) except: # Bad error - cannot load project file at all config = None self.fileBrowserPaths = [] return # dirs part self.fileBrowserPaths = self.__loadListSection(config, 'filebrowser', 'path') config = None return def __loadTopLevelDirs(self): " Loads the top level dirs " configFile = self.userProjectDir + "topleveldirs" if not os.path.exists(configFile): logging.info("Cannot find topleveldirs project file. " "Expected here: " + configFile) self.__formatOK = False return config = ConfigParser.ConfigParser() try: config.read(configFile) except: # Bad error - cannot load project file at all config = None self.__formatOK = False logging.warning("Cannot read topleveldirs project file " "from here: " + configFile) return # dirs part self.topLevelDirs = self.__loadListSection(config, 'topleveldirs', 'dir') config = None return def __loadSearchHistory(self): " Loads the search history file content " confFile = self.userProjectDir + "searchhistory" if not os.path.exists(confFile): logging.info("Cannot find searchhistory project file. " "Expected here: " + confFile) self.__formatOK = False return config = ConfigParser.ConfigParser() try: config.read(confFile) except: # Bad error - cannot load project file at all config = None self.__formatOK = False logging.warning("Cannot read searchhistory project file " "from here: " + confFile) return # find part self.findHistory = self.__loadListSection(config, 'findhistory', 'find') self.findNameHistory = self.__loadListSection(config, 'findnamehistory', 'find') self.findFileHistory = self.__loadListSection(config, 'findfilehistory', 'find') # replace part self.replaceHistory = self.__loadListSection(config, 'replacehistory', 'replace') config = None return def __loadFindObjects(self): " Loads the find objects history " confFile = self.userProjectDir + "findobjects" if not os.path.exists(confFile): logging.info("Cannot find findobjects project file. " "Expected here: " + confFile) self.__formatOK = False return config = ConfigParser.ConfigParser() try: config.read(confFile) except: # Bad error - cannot load project file at all config = None self.__formatOK = False logging.warning("Cannot read findobjects project file " "from here: " + confFile) return self.findClassHistory = self.__loadListSection(config, 'classhistory', 'class') self.findFuncHistory = self.__loadListSection(config, 'funchistory', 'func') self.findGlobalHistory = self.__loadListSection( config, 'globalhistory', 'global') config = None return def __loadFindFiles(self): " Loads the find in files history " confFile = self.userProjectDir + "findinfiles" if not os.path.exists(confFile): logging.info("Cannot find findinfiles project file. " "Expected here: " + confFile) self.__formatOK = False return config = ConfigParser.ConfigParser() try: config.read(confFile) except: # Bad error - cannot load project file at all config = None self.__formatOK = False logging.warning("Cannot read findinfiles project file " "from here: " + confFile) return self.findFilesWhat = self.__loadListSection(config, 'whathistory', 'what') self.findFilesDirs = self.__loadListSection(config, 'dirhistory', 'dir') self.findFilesMasks = self.__loadListSection(config, 'maskhistory', 'mask') config = None return def __loadRecentFiles(self): " Loads the recent files list " confFile = self.userProjectDir + "recentfiles" if not os.path.exists(confFile): logging.info("Cannot find recentfiles project file. " "Expected here: " + confFile) self.__formatOK = False return config = ConfigParser.ConfigParser() try: config.read(confFile) except: # Bad error - cannot load project file at all config = None self.__formatOK = False logging.warning("Cannot read recentfiles project file " "from here: " + confFile) return self.recentFiles = self.__loadListSection(config, 'recentfiles', 'file') # Due to a bug there could be the same files twice in the list. # The difference is doubled path separator. Fix it here. temp = set() for path in self.recentFiles: temp.add(os.path.normpath(path)) self.recentFiles = list(temp) config = None return def __loadSectionFromFile(self, fileName, sectionName, itemName, errMessageEntity): " Loads the given section from the given file " confFile = self.userProjectDir + fileName if not os.path.exists(confFile): logging.info("Cannot find " + errMessageEntity + " file. " "Expected here: " + confFile) self.__formatOK = False return [] config = ConfigParser.ConfigParser() try: config.read(confFile) except: # Bad error - cannot load project file at all config = None self.__formatOK = False logging.warning("Cannot read " + errMessageEntity + " file " "from here: " + confFile) return [] values = self.__loadListSection(config, sectionName, itemName) config = None return values def __loadIgnoredExceptions(self): " Loads the ignored exceptions list " self.ignoredExcpt = self.__loadSectionFromFile("ignoredexcpt", "ignoredexcpt", "excpttype", "ignored exceptions") return def __loadBreakpoints(self): " Loads the project breakpoints " self.breakpoints = self.__loadSectionFromFile("breakpoints", "breakpoints", "bpoint", "breakpoints") return def __loadWatchpoints(self): " Loads the project watchpoints " self.watchpoints = self.__loadSectionFromFile("watchpoints", "watchpoints", "wpoint", "watchpoints") return def __loadListSection(self, config, section, listPrefix): " Loads a list off the given section from the given file " items = [] index = 0 try: while True: item = config.get(section, listPrefix + str(index)).strip() index += 1 items.append(item) except ConfigParser.NoSectionError: self.__formatOK = False except ConfigParser.NoOptionError: pass # Just continue except: self.__formatOK = False return items def unloadProject(self, emitSignal=True): """ Unloads the current project if required """ self.emit(SIGNAL('projectAboutToUnload')) if self.isLoaded(): self.__saveProjectBrowserExpandedDirs() self.__resetValues() if emitSignal: # No need to send a signal e.g. if IDE is closing self.projectChanged.emit(self.CompleteProject) if self.__ropeProject is not None: try: # If the project directory is read only then closing the # rope project generates exception self.__ropeProject.close() except: pass self.__ropeProject = None self.__ropeSourceDirs = [] return def __saveProjectBrowserExpandedDirs(self): " Saves the pathes expanded in the project browser " if not self.isLoaded(): return try: f = open(self.userProjectDir + "filebrowser", "w") self.__writeHeader(f) self.__writeList(f, "filebrowser", "path", self.fileBrowserPaths) f.close() except: pass return def setImportDirs(self, paths): " Sets a new set of the project import dirs " if self.importDirs != paths: self.importDirs = paths self.saveProject() self.projectChanged.emit(self.Properties) return def __generateFilesList(self): """ Generates the files list having the list of dirs """ self.filesList = set() path = self.getProjectDir() self.filesList.add(path) self.__scanDir(path) return def __scanDir(self, path): """ Recursive function to scan one dir """ # The path is with '/' at the end for item in os.listdir(path): if self.shouldExclude(item): continue # Exclude symlinks if they point to the other project # covered pieces candidate = path + item if islink(candidate): realItem = realpath(candidate) if isdir(realItem): if self.isProjectDir(realItem): continue else: if self.isProjectDir(os.path.dirname(realItem)): continue if isdir(candidate): self.filesList.add(candidate + sep) self.__scanDir(candidate + sep) continue self.filesList.add(candidate) return def isProjectDir(self, path): " Returns True if the path belongs to the project " if not self.isLoaded(): return False path = realpath(str(path)) # it could be a symlink if not path.endswith(sep): path += sep return path.startswith(self.getProjectDir()) def isProjectFile(self, path): " Returns True if the path belongs to the project " if not self.isLoaded(): return False return self.isProjectDir(os.path.dirname(path)) def isTopLevelDir(self, path): " Checks if the path is a top level dir " if not path.endswith(sep): path += sep return path in self.topLevelDirs def addTopLevelDir(self, path): " Adds the path to the top level dirs list " if not path.endswith(sep): path += sep if path in self.topLevelDirs: logging.warning("Top level dir " + path + " is already in the list of dirs. " "Ignore adding...") return self.topLevelDirs.append(path) self.__saveTopLevelDirs() return def removeTopLevelDir(self, path): " Removes the path from the top level dirs list " if not path.endswith(sep): path += sep if path not in self.topLevelDirs: logging.warning("Top level dir " + path + " is not in the list of dirs. Ignore removing...") return self.topLevelDirs.remove(path) self.__saveTopLevelDirs() return def setFindNameHistory(self, history): " Sets the new find name history and saves it into a file " self.findNameHistory = history self.__saveSearchHistory() return def setFindFileHistory(self, history): " Sets the new find file history and saves it into a file " self.findFileHistory = history self.__saveSearchHistory() return def setFindHistory(self, history): " Sets the new find history and save it into a file " self.findHistory = history self.__saveSearchHistory() return def setReplaceHistory(self, whatHistory, toHistory): " Sets the new replace history and save it into a file " self.findHistory = whatHistory self.replaceHistory = toHistory self.__saveSearchHistory() return def setTabsStatus(self, status): " Sets the new tabs status and save it into a file " self.tabsStatus = status self.__saveTabsStatus() return def setFindInFilesHistory(self, what, dirs, masks): " Sets the new lists and save them into a file " self.findFilesWhat = what self.findFilesDirs = dirs self.findFilesMasks = masks self.__saveFindFiles() return def setFindClassHistory(self, history): " Sets the new history and saves it into a file " self.findClassHistory = history self.__saveFindObjects() return def setFindFuncHistory(self, history): " Sets the new history and saves it into a file " self.findFuncHistory = history self.__saveFindObjects() return def setFindGlobalHistory(self, history): " Sets the new history and saves it into a file " self.findGlobalHistory = history self.__saveFindObjects() return def updateProperties(self, scriptName, importDirs, creationDate, author, lic, copy_right, version, email, description): " Updates the project properties " if self.scriptName == scriptName and \ self.creationDate == creationDate and \ self.author == author and \ self.license == lic and \ self.copyright == copy_right and \ self.version == version and \ self.email == email and \ self.description == description and \ self.importDirs == importDirs: # No real changes return self.importDirs = importDirs self.scriptName = scriptName self.creationDate = creationDate self.author = author self.license = lic self.copyright = copy_right self.version = version self.email = email self.description = description self.saveProject() self.projectChanged.emit(self.Properties) return def onProjectFileUpdated(self): " Called when a project file is updated via direct editing " scriptName, importDirs, \ creationDate, author, \ lic, copy_right, \ description, version, \ email, projectUuid = getProjectProperties( self.fileName ) self.importDirs = importDirs self.scriptName = scriptName self.creationDate = creationDate self.author = author self.license = lic self.copyright = copy_right self.version = version self.email = email self.description = description # no need to save, but signal just in case self.projectChanged.emit(self.Properties) return def isLoaded(self): " returns True if a project is loaded " return self.fileName != "" def getProjectDir(self): " Provides an absolute path to the project dir " if not self.isLoaded(): return "" return os.path.dirname(realpath(self.fileName)) + sep def getProjectScript(self): " Provides the project script file name " if not self.isLoaded(): return "" if self.scriptName == "": return "" if os.path.isabs(self.scriptName): return self.scriptName return os.path.normpath(self.getProjectDir() + self.scriptName) def addRecentFile(self, path): " Adds a single recent file. True if a new file was inserted. " if path in self.recentFiles: self.recentFiles.remove(path) self.recentFiles.insert(0, path) self.__saveRecentFiles() return False self.recentFiles.insert(0, path) self.__saveRecentFiles() if len(self.recentFiles) > 32: self.recentFiles = self.recentFiles[0:32] self.emit(SIGNAL('recentFilesChanged')) return True def removeRecentFile(self, path): " Removes a single recent file " if path in self.recentFiles: self.recentFiles.remove(path) self.__saveRecentFiles() return def addExceptionFilter(self, excptType): " Adds a new ignored exception type " if excptType not in self.ignoredExcpt: self.ignoredExcpt.append(excptType) self.__saveIgnoredExcpt() return def deleteExceptionFilter(self, excptType): " Remove ignored exception type " if excptType in self.ignoredExcpt: self.ignoredExcpt.remove(excptType) self.__saveIgnoredExcpt() return def setExceptionFilters(self, newFilters): " Sets the new filters " self.ignoredExcpt = newFilters self.__saveIgnoredExcpt() return def addBreakpoint(self, bpointStr): " Adds serialized breakpoint " if bpointStr not in self.breakpoints: self.breakpoints.append(bpointStr) self.__saveBreakpoints() return def deleteBreakpoint(self, bpointStr): " Deletes serialized breakpoint " if bpointStr in self.breakpoints: self.breakpoints.remove(bpointStr) self.__saveBreakpoints() return def setBreakpoints(self, bpointStrList): " Sets breakpoints list " self.breakpoints = bpointStrList self.__saveBreakpoints() return def addWatchpoint(self, wpointStr): " Adds serialized watchpoint " if wpointStr not in self.watchpoints: self.watchpoints.append(wpointStr) self.__saveWatchpoints() return def deleteWatchpoint(self, wpointStr): " Deletes serialized watchpoint " if wpointStr in self.watchpoints: self.watchpoints.remove(wpointStr) self.__saveWatchpoints() return def setWatchpoints(self, wpointStrList): " Sets watchpoints list " self.watchpoints = wpointStrList self.__saveWatchpoints() return
class Singleton: """ Provides singleton facility """ def __init__(self): self.application = None self.splash = None self.mainWindow = None self.skin = None self.screenWidth = 0 self.screenHeight = 0 self.version = "unknown" self.project = CodimensionProject() self.pluginManager = CDMPluginManager() self.briefModinfoCache = BriefModuleInfoCache() self.runParamsCache = RunParametersCache() if os.path.isfile(settingsDir + "runparamscache"): self.runParamsCache.deserialize(settingsDir + "runparamscache") self.pylintAvailable = self.__checkPylint() self.graphvizAvailable = self.__checkGraphviz() self.pylintVersion = None if self.pylintAvailable: self.pylintVersion = self.__getPylintVersion() if self.pylintVersion is None: self.pylintAvailable = False return def getRunParameters(self, fileName): " Provides the run parameters " if self.project.isLoaded(): if self.project.isProjectFile(fileName): key = relpath(fileName, os.path.dirname(self.project.fileName)) else: key = fileName return self.project.runParamsCache.get(key) # No project loaded return self.runParamsCache.get(fileName) def addRunParams(self, fileName, params): " Registers new latest run parameters " if self.project.isLoaded(): if self.project.isProjectFile(fileName): key = relpath(fileName, os.path.dirname(self.project.fileName)) else: key = fileName self.project.runParamsCache.add(key, params) self.project.serializeRunParameters() return # No project loaded self.runParamsCache.add(fileName, params) self.runParamsCache.serialize(settingsDir + "runparamscache") return def getProfileOutputPath(self): " Provides the path to the profile output file " if self.project.isLoaded(): return self.project.userProjectDir + "profile.out" # No project loaded return settingsDir + "profile.out" def getRopeProject(self, fileName=""): " Provides existed or creates a new rope project " if self.project.isLoaded(): return self.project.getRopeProject() # There is no current project so create a temporary one. # Two cases: the buffer has been saved # not saved buffer if os.path.isabs(fileName): dirName = os.path.dirname(fileName) else: # Unsaved buffer, make an assumption that # it is in home directory dirName = str(QDir.homePath()) prefs = copy.deepcopy(ropePreferences) # Exclude nested dirs as it could take too long # Get only dir names and do not get those dirs # where __init__.py[3] are present subdirsToExclude = getSubdirs(dirName, True, True) if "ignored_resources" in prefs: prefs["ignored_resources"] += subdirsToExclude else: prefs["ignored_resources"] = subdirsToExclude project = RopeProject(dirName, None, None, **prefs) project.validate(project.root) return project def validateRopeProject(self, fileName=""): " Validates the existed rope project if so " if not self.project.isLoaded(): return # Currently rope supports validating of directories only # There is a hope that it will support validating a single file # one day. So the fileName argument is ignored by now and the whole # project is invalidated. if fileName != "": from fileutils import (detectFileType, PythonFileType, Python3FileType) fileType = detectFileType(fileName) if fileType not in [PythonFileType, Python3FileType]: return self.project.validateRopeProject(fileName) return def getProjectImportDirs(self): """ Provides a list of the project import dirs if so. Note: the paths do not have '/' at the end due to os.path.normpath """ if not self.project.isLoaded(): return [] basePath = self.project.getProjectDir() result = list(self.project.importDirs) index = len(result) - 1 while index >= 0: path = result[index] if not os.path.isabs(path): result[index] = os.path.normpath(basePath + path) index -= 1 return result def isProjectScriptValid(self): " True if the project script valid " scriptName = self.project.getProjectScript() if not os.path.exists(scriptName): return False scriptName = os.path.realpath(scriptName) if not os.path.isfile(scriptName): return False from fileutils import (detectFileType, PythonFileType, Python3FileType) if detectFileType(scriptName) in [PythonFileType, Python3FileType]: return True return False def getFileLineDocstring(self, fName, line): " Provides a docstring if so for the given file and line " from fileutils import (detectFileType, PythonFileType, Python3FileType) if detectFileType(fName) not in [PythonFileType, Python3FileType]: return "" infoCache = self.briefModinfoCache def checkFuncObject(obj, line): " Checks docstring for a function or a class " if obj.line == line or obj.keywordLine == line: if obj.docstring is None: return True, "" return True, obj.docstring.text for item in obj.classes + obj.functions: found, docstring = checkFuncObject(item, line) if found: return True, docstring return False, "" try: info = infoCache.get(fName) for item in info.classes + info.functions: found, docstring = checkFuncObject(item, line) if found: return docstring except: pass return "" def getModInfo(self, path): " Provides a module info for the given file " from fileutils import (detectFileType, PythonFileType, Python3FileType) if detectFileType(path) not in [PythonFileType, Python3FileType]: raise Exception("Trying to parse non-python file: " + path) return self.briefModinfoCache.get(path) @staticmethod def __checkGraphviz(): " Checks if the graphviz available " if 'win' in sys.platform.lower(): return os.system('which dot > /NUL 2>&1') == 0 return os.system('which dot > /dev/null 2>&1') == 0 @staticmethod def __checkPylint(): " Checks if pylint is available " if 'win' in sys.platform.lower(): return os.system('which pylint > /NUL 2>&1') == 0 return os.system('which pylint > /dev/null 2>&1') == 0 @staticmethod def __getPylintVersion(): " Provides the pylint version " output = check_output("pylint --version; exit 0", stderr=STDOUT, shell=True) for line in output.splitlines(): line = line.strip() if line.startswith("pylint "): version = line.replace("pylint", "").replace(",", "") try: return StrictVersion(version.strip()) except: return None return None