def onScopeBegin(self): """The user requested jumping to the current scope begin""" if self.isPythonBuffer(): info = getBriefModuleInfoFromMemory(self.text) context = getContext(self, info, True) if context.getScope() != context.GlobalScope: GlobalData().mainWindow.jumpToLine(context.getLastScopeLine())
def highlightInOutline(self): """Triggered when highlight in outline browser is requested""" if self.isPythonBuffer(): info = getBriefModuleInfoFromMemory(self.text) context = getContext(self, info, True, False) line, _ = self.cursorPosition GlobalData().mainWindow.highlightInOutline(context, int(line) + 1) self.setFocus()
def __extractDocstring(self, content): """Extracts a docstring and sets it as a tooltip if needed""" if self.tooltip != "": return if isPythonFile(self.fileName): info = getBriefModuleInfoFromMemory("\n".join(content)) self.tooltip = "" if info.docstring is not None: self.tooltip = info.docstring.text
def getCurrentBufferInfo(self): """Provides the current buffer parsed info""" if self.__currentUUID is None: return None widget = self.__editorsManager.getWidgetByUUID(self.__currentUUID) if widget is None: return None editor = widget.getEditor() info = getBriefModuleInfoFromMemory(editor.text) return info
def __populateFromOpened(self): """Populates the name dialog from the opened files""" mainWindow = GlobalData().mainWindow editorsManager = mainWindow.editorsManagerWidget.editorsManager for record in editorsManager.getTextEditors(): # uuid = record[0] fname = record[1] widget = record[2] if isPythonFile(fname): content = widget.getEditor().text() info = getBriefModuleInfoFromMemory(content) self.__populateInfo(info, fname)
def __populateFromProject(self): """Populates find name dialog from the project files""" mainWindow = GlobalData().mainWindow for fname in GlobalData().project.filesList: if isPythonFile(fname): widget = mainWindow.getWidgetForFileName(fname) if widget is None: info = GlobalData().briefModinfoCache.get(fname) else: info = getBriefModuleInfoFromMemory( widget.getEditor().text) self.__populateInfo(info, fname)
def getImportsInLine(fileContent, lineNumber): """Provides a list of imports in in the given import line""" imports = [] importsWhat = [] info = getBriefModuleInfoFromMemory(str(fileContent)) for importObj in info.imports: if importObj.line == lineNumber: if importObj.name not in imports: imports.append(importObj.name) for whatObj in importObj.what: if whatObj.name not in importsWhat: importsWhat.append(whatObj.name) return imports, importsWhat
def test_lone_import(self): """Test for lone import keyword""" pythonFile = self.dir + "loneimport.py" info = cdmpyparser.getBriefModuleInfoFromFile(pythonFile) if info.isOK: self.fail("lone import test failure. Expected error. Option: " "directly from file: " + pythonFile) f = open(pythonFile) content = f.read() f.close() info = cdmpyparser.getBriefModuleInfoFromMemory(content) if info.isOK: self.fail("lone import test failure. Expected error. Option: " "from memory. File: " + pythonFile)
def __showParserError(self): """Shows the parser errors window""" if self.__currentUUID is None: return try: widget = self.__editorsManager.getWidgetByUUID(self.__currentUUID) if widget is None: return editor = widget.getEditor() info = getBriefModuleInfoFromMemory(editor.text) fName = self.__outlineBrowsers[self.__currentUUID].shortFileName dialog = ParserErrorsDialog(fName, info) dialog.exec_() except Exception as ex: logging.error(str(ex))
def __populateFromOpened(self): """Populates the name dialog from the opened files""" mainWindow = GlobalData().mainWindow editorsManager = mainWindow.editorsManagerWidget.editorsManager showTooltips = Settings()['findFileTooltips'] for record in editorsManager.getTextEditors(): # uuid = record[0] fname = record[1] widget = record[2] mime, icon, _ = getFileProperties(fname) tooltip = "" if showTooltips and isPythonMime(mime): content = widget.getEditor().text info = getBriefModuleInfoFromMemory(content) if info.docstring is not None: tooltip = info.docstring.text newItem = FileItem(self.rootItem, icon, fname, tooltip) self.rootItem.appendChild(newItem) self.count += 1
def __populateFromProject(self): """Populates find name dialog from the project files""" mainWindow = GlobalData().mainWindow showTooltips = Settings()['findFileTooltips'] for fname in GlobalData().project.filesList: if fname.endswith(os.path.sep): continue mime, icon, _ = getFileProperties(fname) tooltip = "" if showTooltips and isPythonMime(mime): widget = mainWindow.getWidgetForFileName(fname) if widget is None: info = GlobalData().briefModinfoCache.get(fname) else: content = widget.getEditor().text info = getBriefModuleInfoFromMemory(content) if info.docstring is not None: tooltip = info.docstring.text newItem = FileItem(self.rootItem, icon, fname, tooltip) self.rootItem.appendChild(newItem) self.count += 1
def encodingSanityCheck(fName, decodedText, expectedEncoding): """Checks if the expected encoding matches the encing in the file""" try: modInfo = getBriefModuleInfoFromMemory(decodedText) modEncoding = modInfo.encoding if modEncoding: if not isValidEncoding(modEncoding.name): logging.warning("Invalid encoding " + modEncoding.name + " found in the file " + fName) return False if not areEncodingsEqual(modEncoding.name, expectedEncoding): if expectedEncoding.startswith('bom-'): noBomEncoding = expectedEncoding[4:] if areEncodingsEqual(modEncoding.name, noBomEncoding): return True logging.warning("The explicitly set encoding " + expectedEncoding + " does not match encoding " + modEncoding.name + " found in the file " + fName) return False except: pass return True
def meat(self, pythonFile, errorMsg): """The test process meat""" info = cdmpyparser.getBriefModuleInfoFromFile(pythonFile) if not info.isOK: self.fail("Error parsing the file " + pythonFile + ". Option: directly from a file.") f = open(pythonFile) content = f.read() f.close() info = cdmpyparser.getBriefModuleInfoFromMemory(content) if not info.isOK: self.fail("Error parsing the file " + pythonFile + ". Option: from memory.") outFileName = pythonFile.replace(".py", ".out") outFile = open(outFileName, "w") outFile.write(info.niceStringify()) outFile.close() okFileName = pythonFile.replace(".py", ".ok") if not files_equal(outFileName, okFileName): self.fail(errorMsg)
def getImportsList(fileContent): """Parses a python file and provides a list imports in it""" info = getBriefModuleInfoFromMemory(fileContent) return info.imports
def updateBar(self): """Triggered when the timer is fired""" self.__updateTimer.stop() # just in case if not isPythonMime(self.__parentWidget.getMime()): return if not self.__connected: self.__connectEditorSignals() # Parse the buffer content self.__currentInfo = getBriefModuleInfoFromMemory(self.__editor.text) # Decide what icon to use if self.__currentInfo.isOK: self.__updateInfoIcon(self.STATE_OK_UTD) else: self.__updateInfoIcon(self.STATE_BROKEN_UTD) # Calc the cursor context context = getContext(self.__editor, self.__currentInfo, True, False) # Display the context self.__populateGlobalScope() if context.length == 0: self.__globalScopeCombo.setCurrentIndex(-1) else: index = self.__globalScopeCombo.findData( context.levels[0][0].line) self.__globalScopeCombo.setCurrentIndex(index) usedFromStore = 0 index = 1 while index < context.length: if len(self.__path) < index: newPathItem = PathElement(self) self.__path.append(newPathItem) self.__layout.addWidget(newPathItem.icon) self.__layout.addWidget(newPathItem.combo) combo = newPathItem.combo combo.pathIndex = len(self.__path) - 1 combo.jumpToLine.connect(self.__onJumpToLine) else: self.__path[index - 1].icon.setVisible(True) self.__path[index - 1].combo.setVisible(True) combo = self.__path[index - 1].combo combo.clear() # Populate the combo box self.__populateClassesAndFunctions(context.levels[index - 1][0], combo) combo.setCurrentIndex(combo.findData(context.levels[index][0].line)) index += 1 usedFromStore += 1 # it might need to have one more level with nothing selected if context.length > 0: if len(context.levels[context.length - 1][0].functions) > 0 or \ len(context.levels[context.length - 1][0].classes) > 0: # Need to add a combo if len(self.__path) <= usedFromStore: newPathItem = PathElement(self) self.__path.append(newPathItem) self.__layout.addWidget(newPathItem.icon) self.__layout.addWidget(newPathItem.combo) combo = newPathItem.combo combo.pathIndex = len(self.__path) - 1 combo.jumpToLine.connect(self.__onJumpToLine) else: self.__path[index - 1].icon.setVisible(True) self.__path[index - 1].combo.setVisible(True) combo = self.__path[index - 1].combo combo.clear() self.__populateClassesAndFunctions( context.levels[context.length - 1][0], combo) combo.setCurrentIndex(-1) usedFromStore += 1 # Hide extra components if so index = usedFromStore while index < len(self.__path): self.__path[index].icon.setVisible(False) self.__path[index].combo.setVisible(False) index += 1 # Make sure the spacer is the last item self.__layout.removeWidget(self.__spacer) self.__layout.addWidget(self.__spacer)
def __onTabChanged(self, index): """Triggered when another tab becomes active""" # If the timer is still active that means the tab was switched before # the handler had a chance to work. Therefore update the previous tab # first if so. if self.__updateTimer.isActive(): self.__updateTimer.stop() self.__updateView() # Now, switch the outline browser to the new tab if index == -1: widget = self.__editorsManager.currentWidget() else: widget = self.__editorsManager.getWidgetByIndex(index) if widget is None: self.__currentUUID = None self.__noneLabel.show() self.showParsingErrorsButton.setEnabled(False) return if widget.getType() not in [MainWindowTabWidgetBase.PlainTextEditor, MainWindowTabWidgetBase.VCSAnnotateViewer]: if self.__currentUUID is not None: self.__outlineBrowsers[self.__currentUUID].browser.hide() self.__currentUUID = None self.__noneLabel.show() self.showParsingErrorsButton.setEnabled(False) return # This is text editor, detect the file type if not isPythonMime(widget.getMime()): if self.__currentUUID is not None: self.__outlineBrowsers[self.__currentUUID].browser.hide() self.__currentUUID = None self.__noneLabel.show() self.showParsingErrorsButton.setEnabled(False) return # This is a python file, check if we already have the parsed info in # the cache uuid = widget.getUUID() if uuid in self.__outlineBrowsers: # We have it, hide the current and show the existed if self.__currentUUID is not None: self.__outlineBrowsers[self.__currentUUID].browser.hide() self.__currentUUID = None else: self.__noneLabel.hide() self.__currentUUID = uuid self.__outlineBrowsers[self.__currentUUID].browser.show() info = self.__outlineBrowsers[self.__currentUUID].info self.showParsingErrorsButton.setEnabled(info.isOK != True) return # It is first time we are here, create a new editor = widget.getEditor() editor.textChanged.connect(self.__onBufferChanged) editor.cursorPositionChanged.connect(self.__cursorPositionChanged) info = getBriefModuleInfoFromMemory(editor.text) self.showParsingErrorsButton.setEnabled(info.isOK != True) shortFileName = widget.getShortName() browser = OutlineBrowser(uuid, shortFileName, info, self) browser.setHeaderHighlight(info.isOK != True) self.__connectOutlineBrowser(browser) self.__layout.addWidget(browser) if self.__currentUUID is not None: self.__outlineBrowsers[self.__currentUUID].browser.hide() self.__currentUUID = None else: self.__noneLabel.hide() self.__currentUUID = uuid attributes = OutlineAttributes() attributes.browser = browser attributes.contextItem = None attributes.info = info attributes.shortFileName = shortFileName attributes.changed = False self.__outlineBrowsers[self.__currentUUID] = attributes self.__outlineBrowsers[self.__currentUUID].browser.show()
def __process(self): """Accumulation process""" # Intermediate working data self.__participantFiles = [] self.__projectImportDirs = [] self.__projectImportsCache = {} self.dataModel.clear() self.__inProgress = True try: self.infoLabel.setText('Building the list of files to analyze...') QApplication.processEvents() # Build the list of participating python files self.__buildParticipants() self.__projectImportDirs = \ GlobalData().project.getImportDirsAsAbsolutePaths() QApplication.processEvents() if self.__cancelRequest: QApplication.restoreOverrideCursor() self.close() return self.progressBar.setRange(0, len(self.__participantFiles)) index = 1 # Now, parse the files and build the diagram data model if self.__what == ImportsDiagramDialog.SingleBuffer: info = getBriefModuleInfoFromMemory(str(self.__buf)) self.__addSingleFileToDataModel(info, self.__path) else: infoSrc = GlobalData().briefModinfoCache for fName in self.__participantFiles: self.progressBar.setValue(index) self.infoLabel.setText('Analyzing ' + fName + "...") QApplication.processEvents() if self.__cancelRequest: QApplication.restoreOverrideCursor() self.dataModel.clear() self.close() return info = infoSrc.get(fName) self.__addSingleFileToDataModel(info, fName) index += 1 # The import caches and other working data are not needed anymore self.__participantFiles = None self.__projectImportDirs = None self.__projectImportsCache = None # Generating the graphviz layout self.infoLabel.setText('Generating layout using graphviz...') QApplication.processEvents() graph = getGraphFromDescriptionData(self.dataModel.toGraphviz()) graph.normalize(self.physicalDpiX(), self.physicalDpiY()) QApplication.processEvents() if self.__cancelRequest: QApplication.restoreOverrideCursor() self.dataModel.clear() self.close() return # Generate graphics scene self.infoLabel.setText('Generating graphics scene...') QApplication.processEvents() self.__buildGraphicsScene(graph) # Clear the data model self.dataModel = None except Exception as exc: QApplication.restoreOverrideCursor() logging.error(str(exc)) self.__inProgress = False self.__onClose() return QApplication.restoreOverrideCursor() self.infoLabel.setText('Done') QApplication.processEvents() self.__inProgress = False self.accept()
def getContext(editor, info=None, skipBlankLinesBack=False, skipDef=True): """Detects the context at the text cursor position. skipBlankLinesBack == False => current cursor position is used skipBlankLinesBack == True => skip blank lines back and use the first non-blank line as the cursor position. skipDef == True => treat a definition as belonging to an upper level context (not included into the context stack) skipDef == False => treat a definition as starting a context level (included into the context stack as the last one) """ # It is expected that this is a python editor. # If non-python editor is given, then a global context is provided context = TextCursorContext() if not editor.isPythonBuffer(): return context # It's not the first position, so the parsed module info is required if info is None: info = getBriefModuleInfoFromMemory(editor.text) line, pos = editor.cursorPosition if skipBlankLinesBack: while line >= 0: text = editor.lines[line] trimmedText = text.strip() if trimmedText != "": pos = len(text.rstrip()) break line -= 1 if line < 0: line = 0 pos = 0 _IdentifyScope(info, context, line + 1, pos, skipDef) if not skipDef: if _getDefinitionObject(info, line + 1, pos) is not None: return context if context.length == 0: return context continueLine = False currentLine = context.getLastScopeLine() + 1 for currentLine in range(context.getLastScopeLine(), len(editor.lines)): if currentLine == line: break text = editor.lines[currentLine] textLen = len(text) trimmedText = text.strip() if not continueLine: if trimmedText == "" or trimmedText.startswith("#"): continue # Here: there must be characters in the line nonSpacePos = _getFirstNonSpacePos(text) context.stripLevels(nonSpacePos) if context.length == 0: return context if trimmedText.endswith(",") or trimmedText.endswith('\\') or \ (textLen > 0 and editor.isStringLiteral(currentLine, textLen - 1)): continueLine = True else: continueLine = False if continueLine: context.stripLevels(nonSpacePos) else: nonSpacePos = _getFirstNonSpacePos(editor.lines[line]) if nonSpacePos == -1: context.stripLevels(pos) else: context.stripLevels(min(pos, nonSpacePos)) return context