Plugins/CheckerPlugins/Pep8/Pep8Fixer.py

changeset 849
996367a89673
child 851
321d29b93238
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/CheckerPlugins/Pep8/Pep8Fixer.py	Sun Jan 16 16:09:21 2011 +0100
@@ -0,0 +1,279 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2011 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a class to fix certain PEP 8 issues.
+"""
+
+import os
+import re
+
+from PyQt4.QtCore import QObject
+
+from E5Gui import E5MessageBox
+
+import Utilities
+
+Pep8FixableIssues = ["W191", "W291", "W292", "W293", "E301", "E303", "E304", "W391", "W603"]
+
+class Pep8Fixer(QObject):
+    """
+    Class implementing a fixer for certain PEP 8 issues.
+    """
+    def __init__(self, project, filename, sourceLines, fixCodes, inPlace):
+        """
+        Constructor
+        
+        @param project  reference to the project object (Project)
+        @param filename name of the file to be fixed (string)
+        @param sourceLines list of source lines including eol marker
+            (list of string)
+        @param fixCodes list of codes to be fixed as a comma separated
+            string (string)
+        @param inPlace flag indicating to modify the file in place (boolean)
+        """
+        QObject.__init__(self)
+        
+        self.__project = project
+        self.__filename = filename
+        self.__origName = ""
+        self.__source = sourceLines[:]  # save a copy
+        self.__fixCodes = [c.strip() for c in fixCodes.split(",") if c.strip()]
+        
+        if not inPlace:
+            self.__origName = self.__filename
+            self.__filename = os.path.join(os.path.dirname(self.__filename), 
+                "fixed_" + os.path.basename(self.__filename))
+        
+        self.__fixes = {
+            "W191" : self.__fixTabs, 
+            "W291" : self.__fixWhitespace, 
+            "W292" : self.__fixNewline, 
+            "W293" : self.__fixWhitespace,
+            "E301" : self.__fixOneBlankLine, 
+            "E303" : self.__fixTooManyBlankLines, 
+            "E304" : self.__fixBlankLinesAfterDecorator, 
+            "W391" : self.__fixTrailingBlankLines, 
+            "W603" : self.__fixNotEqual, 
+        }
+        self.__modified = False
+        self.__stack = []   # these need to be fixed before the file is saved
+                            # but after all inline fixes
+    
+    def saveFile(self, encoding):
+        """
+        Public method to save the modified file.
+        
+        @param encoding encoding of the source file (string)
+        @return flag indicating success (boolean)
+        """
+        if not self.__modified:
+            # no need to write
+            return True
+        
+        # apply deferred fixes
+        self.__finalize()
+        
+        txt = "".join(self.__source)
+        try:
+            Utilities.writeEncodedFile(self.__filename, txt, encoding)
+        except (IOError, Utilities.CodingError, UnicodeError) as err:
+            E5MessageBox.critical(self,
+                self.trUtf8("Fix PEP 8 issues"),
+                self.trUtf8(
+                    """<p>Could not save the file <b>{0}</b>."""
+                    """ Skipping it.</p><p>Reason: {1}</p>""")\
+                    .format(self.__filename, str(err))
+            )
+            return False
+        
+        return True
+    
+    def fixIssue(self, line, pos, message):
+        """
+        Public method to fix the fixable issues.
+        
+        @param line line number of issue (integer or string)
+        @param pos character position of issue (integer or string)
+        @param message message text (string)
+        @return flag indicating an applied fix (boolean) and a message for
+            the fix (string)
+        """
+        code = message.split(None, 1)[0].strip()
+        
+        if (code in self.__fixCodes or len(self.__fixCodes) == 0) and \
+           code in self.__fixes:
+            res = self.__fixes[code](code, line, pos)
+            if res[0]:
+                self.__modified = True
+        else:
+            res = (False, "")
+        
+        return res
+    
+    def __finalize(self):
+        """
+        Private method to apply all deferred fixes.
+        """
+        for code, line, pos in reversed(self.__stack):
+            self.__fixes[code](code, line, pos, apply=True)
+    
+    def __getEol(self):
+        """
+        Private method to get the applicable eol string.
+        
+        @return eol string (string)
+        """
+        if self.__origName:
+            fn = self.__origName
+        else:
+            fn = self.__filename
+        
+        if self.__project.isOpen() and self.__project.isProjectFile(fn):
+            eol = self.__project.getLineSeparator()
+        else:
+            eol = Utilities.linesep()
+        return eol
+    
+    def __fixTabs(self, code, line, pos):
+        """
+        Private method to fix obsolete tab usage.
+        
+        @param code code of the issue (string)
+        @param line line number of the issue (integer)
+        @param pos position inside line (integer)
+        @return flag indicating an applied fix (boolean) and a message for
+            the fix (string)
+        """
+        self.__source[line - 1] = self.__source[line - 1].replace("\t", "    ")
+        return (True, self.trUtf8("Tab converted to 4 spaces."))
+    
+    def __fixWhitespace(self, code, line, pos):
+        """
+        Private method to fix trailing whitespace.
+        
+        @param code code of the issue (string)
+        @param line line number of the issue (integer)
+        @param pos position inside line (integer)
+        @return flag indicating an applied fix (boolean) and a message for
+            the fix (string)
+        """
+        self.__source[line - 1] = re.sub(r'[\t ]*$', "", 
+                                         self.__source[line - 1])
+        return (True, self.trUtf8("Whitespace stripped from end of line."))
+    
+    def __fixNewline(self, code, line, pos):
+        """
+        Private method to fix a missing newline at the end of file.
+        
+        @param code code of the issue (string)
+        @param line line number of the issue (integer)
+        @param pos position inside line (integer)
+        @return flag indicating an applied fix (boolean) and a message for
+            the fix (string)
+        """
+        self.__source[line - 1] += self.__getEol()
+        return (True, self.trUtf8("newline added to end of file."))
+    
+    def __fixTrailingBlankLines(self, code, line, pos):
+        """
+        Private method to fix trailing blank lines.
+        
+        @param code code of the issue (string)
+        @param line line number of the issue (integer)
+        @param pos position inside line (integer)
+        @return flag indicating an applied fix (boolean) and a message for
+            the fix (string)
+        """
+        index = line - 1
+        while index:
+            if self.__source[index].strip() == "":
+                del self.__source[index]
+                index -= 1
+            else:
+                break
+        return (True, self.trUtf8(
+            "Superfluous trailing blank lines removed from end of file."))
+    
+    def __fixNotEqual(self, code, line, pos):
+        """
+        Private method to fix the not equal notation.
+        
+        @param code code of the issue (string)
+        @param line line number of the issue (integer)
+        @param pos position inside line (integer)
+        @return flag indicating an applied fix (boolean) and a message for
+            the fix (string)
+        """
+        self.__source[line - 1] = self.__source[line - 1].replace("<>", "!=")
+        return (True, self.trUtf8("'<>' replaced by '!='."))
+    
+    def __fixBlankLinesAfterDecorator(self, code, line, pos, apply=False):
+        """
+        Private method to fix superfluous blank lines after a function
+        decorator.
+        
+        @param code code of the issue (string)
+        @param line line number of the issue (integer)
+        @param pos position inside line (integer)
+        @keyparam apply flag indicating, that the fix should be applied
+            (boolean)
+        @return flag indicating an applied fix (boolean) and a message for
+            the fix (string)
+        """
+        if apply:
+            index = line - 2
+            while index:
+                if self.__source[index].strip() == "":
+                    del self.__source[index]
+                    index -= 1
+                else:
+                    break
+        else:
+            self.__stack.append((code, line, pos))
+        return (True, self.trUtf8(
+            "Superfluous blank lines after function decorator removed."))
+    
+    def __fixTooManyBlankLines(self, code, line, pos, apply=False):
+        """
+        Private method to fix superfluous blank lines.
+        
+        @param code code of the issue (string)
+        @param line line number of the issue (integer)
+        @param pos position inside line (integer)
+        @keyparam apply flag indicating, that the fix should be applied
+            (boolean)
+        @return flag indicating an applied fix (boolean) and a message for
+            the fix (string)
+        """
+        if apply:
+            index = line - 3
+            while index:
+                if self.__source[index].strip() == "":
+                    del self.__source[index]
+                    index -= 1
+                else:
+                    break
+        else:
+            self.__stack.append((code, line, pos))
+        return (True, self.trUtf8("Superfluous blank lines removed."))
+    
+    def __fixOneBlankLine(self, code, line, pos, apply=False):
+        """
+        Private method to fix the need for one blank line.
+        
+        @param code code of the issue (string)
+        @param line line number of the issue (integer)
+        @param pos position inside line (integer)
+        @keyparam apply flag indicating, that the fix should be applied
+            (boolean)
+        @return flag indicating an applied fix (boolean) and a message for
+            the fix (string)
+        """
+        if apply:
+            self.__source.insert(line - 1, self.__getEol())
+        else:
+            self.__stack.append((code, line, pos))
+        return (True, self.trUtf8("One blank line inserted."))

eric ide

mercurial