Plugins/CheckerPlugins/Pep8/Pep8Fixer.py

changeset 2892
b601ede6dbbf
parent 2891
6843b7d23279
child 2894
8e4264045fc9
equal deleted inserted replaced
2891:6843b7d23279 2892:b601ede6dbbf
27 "E231", "E241", "E242", "E251", "E261", "E262", 27 "E231", "E241", "E242", "E251", "E261", "E262",
28 "E271", "E272", "E273", "E274", "W291", "W292", 28 "E271", "E272", "E273", "E274", "W291", "W292",
29 "W293", "E301", "E302", "E303", "E304", "W391", 29 "W293", "E301", "E302", "E303", "E304", "W391",
30 "E401", "E501", "E502", "W603", "E701", "E702", 30 "E401", "E501", "E502", "W603", "E701", "E702",
31 "E703", "E711", "E712" 31 "E703", "E711", "E712"
32 ] 32 ]
33 33
34 34
35 class Pep8Fixer(QObject): 35 class Pep8Fixer(QObject):
36 """ 36 """
37 Class implementing a fixer for certain PEP 8 issues. 37 Class implementing a fixer for certain PEP 8 issues.
68 self.__eol = "" 68 self.__eol = ""
69 self.__indentWord = self.__getIndentWord() 69 self.__indentWord = self.__getIndentWord()
70 70
71 if not inPlace: 71 if not inPlace:
72 self.__origName = self.__filename 72 self.__origName = self.__filename
73 self.__filename = os.path.join(os.path.dirname(self.__filename), 73 self.__filename = os.path.join(
74 os.path.dirname(self.__filename),
74 "fixed_" + os.path.basename(self.__filename)) 75 "fixed_" + os.path.basename(self.__filename))
75 76
76 self.__fixes = { 77 self.__fixes = {
77 "E101": self.__fixE101, 78 "E101": self.__fixE101,
78 "E111": self.__fixE101, 79 "E111": self.__fixE101,
151 152
152 txt = "".join(self.__source) 153 txt = "".join(self.__source)
153 try: 154 try:
154 Utilities.writeEncodedFile(self.__filename, txt, encoding) 155 Utilities.writeEncodedFile(self.__filename, txt, encoding)
155 except (IOError, Utilities.CodingError, UnicodeError) as err: 156 except (IOError, Utilities.CodingError, UnicodeError) as err:
156 E5MessageBox.critical(self, 157 E5MessageBox.critical(
158 self,
157 self.trUtf8("Fix PEP 8 issues"), 159 self.trUtf8("Fix PEP 8 issues"),
158 self.trUtf8( 160 self.trUtf8(
159 """<p>Could not save the file <b>{0}</b>.""" 161 """<p>Could not save the file <b>{0}</b>."""
160 """ Skipping it.</p><p>Reason: {1}</p>""") 162 """ Skipping it.</p><p>Reason: {1}</p>""")
161 .format(self.__filename, str(err)) 163 .format(self.__filename, str(err))
162 ) 164 )
163 return False 165 return False
164 166
165 return True 167 return True
166 168
467 """ 469 """
468 if self.__reindenter is None: 470 if self.__reindenter is None:
469 self.__reindenter = Pep8Reindenter(self.__source) 471 self.__reindenter = Pep8Reindenter(self.__source)
470 self.__reindenter.run() 472 self.__reindenter.run()
471 fixedLine = self.__reindenter.fixedLine(line - 1) 473 fixedLine = self.__reindenter.fixedLine(line - 1)
472 if fixedLine is not None: 474 if fixedLine is not None and fixedLine != self.__source[line - 1]:
473 self.__source[line - 1] = fixedLine 475 self.__source[line - 1] = fixedLine
474 if code in ["E101", "W191"]: 476 if code in ["E101", "W191"]:
475 msg = self.trUtf8("Tab converted to 4 spaces.") 477 msg = self.trUtf8("Tab converted to 4 spaces.")
476 else: 478 else:
477 msg = self.trUtf8( 479 msg = self.trUtf8(
478 "Indentation adjusted to be a multiple of four.") 480 "Indentation adjusted to be a multiple of four.")
479 return (1, msg, 0) 481 return (1, msg, 0)
480 else: 482 else:
481 return (0, self.trUtf8("Fix for {0} failed.").format(code), 0) 483 return (0, "", 0)
482 484
483 def __fixE121(self, code, line, pos, apply=False): 485 def __fixE121(self, code, line, pos, apply=False):
484 """ 486 """
485 Private method to fix the indentation of continuation lines and 487 Private method to fix the indentation of continuation lines and
486 closing brackets (E121,E124). 488 closing brackets (E121,E124).
496 """ 498 """
497 if apply: 499 if apply:
498 logical = self.__getLogical(line, pos) 500 logical = self.__getLogical(line, pos)
499 if logical: 501 if logical:
500 # Fix by adjusting initial indent level. 502 # Fix by adjusting initial indent level.
501 self.__fixReindent(line, pos, logical) 503 changed = self.__fixReindent(line, pos, logical)
502 if code == "E121": 504 if changed:
503 msg = self.trUtf8("Indentation of continuation line corrected.") 505 if code == "E121":
504 elif code == "E124": 506 msg = self.trUtf8(
505 msg = self.trUtf8("Indentation of closing bracket corrected.") 507 "Indentation of continuation line corrected.")
506 return (1, msg, 0) 508 elif code == "E124":
509 msg = self.trUtf8(
510 "Indentation of closing bracket corrected.")
511 return (1, msg, 0)
512 return (0, "", 0)
507 else: 513 else:
508 id = self.__getID() 514 id = self.__getID()
509 self.__stackLogical.append((id, code, line, pos)) 515 self.__stackLogical.append((id, code, line, pos))
510 return (-1, "", id) 516 return (-1, "", id)
511 517
533 line = line - 1 539 line = line - 1
534 text = self.__source[line] 540 text = self.__source[line]
535 indentation = self.__getIndent(text) 541 indentation = self.__getIndent(text)
536 self.__source[line] = indentation + \ 542 self.__source[line] = indentation + \
537 self.__indentWord + text.lstrip() 543 self.__indentWord + text.lstrip()
538 return ( 544 return (
539 1, 545 1,
540 self.trUtf8( 546 self.trUtf8(
541 "Missing indentation of continuation line corrected."), 547 "Missing indentation of continuation line corrected."),
542 0) 548 0)
549 return (0, "", 0)
543 else: 550 else:
544 id = self.__getID() 551 id = self.__getID()
545 self.__stackLogical.append((id, code, line, pos)) 552 self.__stackLogical.append((id, code, line, pos))
546 return (-1, "", id) 553 return (-1, "", id)
547 554
567 row = line - 1 574 row = line - 1
568 text = self.__source[row] 575 text = self.__source[row]
569 newText = self.__getIndent(logicalLines[0]) + text.lstrip() 576 newText = self.__getIndent(logicalLines[0]) + text.lstrip()
570 if newText == text: 577 if newText == text:
571 # fall back to slower method 578 # fall back to slower method
572 self.__fixReindent(line, pos, logical) 579 changed = self.__fixReindent(line, pos, logical)
573 else: 580 else:
574 self.__source[row] = newText 581 self.__source[row] = newText
575 return ( 582 changed = True
576 1, 583 if changed:
577 self.trUtf8("Closing bracket aligned to opening bracket."), 584 return (1, self.trUtf8(
578 0) 585 "Closing bracket aligned to opening bracket."),
586 0)
587 return (0, "", 0)
579 else: 588 else:
580 id = self.__getID() 589 id = self.__getID()
581 self.__stackLogical.append((id, code, line, pos)) 590 self.__stackLogical.append((id, code, line, pos))
582 return (-1, "", id) 591 return (-1, "", id)
583 592
603 if not modified: 612 if not modified:
604 row = line - 1 613 row = line - 1
605 text = self.__source[row] 614 text = self.__source[row]
606 self.__source[row] = self.__getIndent(text) + \ 615 self.__source[row] = self.__getIndent(text) + \
607 self.__indentWord + text.lstrip() 616 self.__indentWord + text.lstrip()
608 return (1, self.trUtf8("Indentation level changed."), 0) 617 return (1, self.trUtf8("Indentation level changed."), 0)
618 return (0, "", 0)
609 else: 619 else:
610 id = self.__getID() 620 id = self.__getID()
611 self.__stackLogical.append((id, code, line, pos)) 621 self.__stackLogical.append((id, code, line, pos))
612 return (-1, "", id) 622 return (-1, "", id)
613 623
634 text = self.__source[row] 644 text = self.__source[row]
635 newText = self.__getIndent(logicalLines[0]) + \ 645 newText = self.__getIndent(logicalLines[0]) + \
636 self.__indentWord + text.lstrip() 646 self.__indentWord + text.lstrip()
637 if newText == text: 647 if newText == text:
638 # fall back to slower method 648 # fall back to slower method
639 self.__fixReindent(line, pos, logical) 649 changed = self.__fixReindent(line, pos, logical)
640 else: 650 else:
641 self.__source[row] = newText 651 self.__source[row] = newText
642 return ( 652 changed = True
643 1, 653 if changed:
644 self.trUtf8( 654 return (1, self.trUtf8(
645 "Indentation level of hanging indentation changed."), 655 "Indentation level of hanging indentation changed."),
646 0) 656 0)
657 return (0, "", 0)
647 else: 658 else:
648 id = self.__getID() 659 id = self.__getID()
649 self.__stackLogical.append((id, code, line, pos)) 660 self.__stackLogical.append((id, code, line, pos))
650 return (-1, "", id) 661 return (-1, "", id)
651 662
687 if startIndex is not None: 698 if startIndex is not None:
688 newText = startIndex * ' ' + text.lstrip() 699 newText = startIndex * ' ' + text.lstrip()
689 700
690 if newText == text: 701 if newText == text:
691 # fall back to slower method 702 # fall back to slower method
692 self.__fixReindent(line, pos, logical) 703 changed = self.__fixReindent(line, pos, logical)
693 else: 704 else:
694 self.__source[row] = newText 705 self.__source[row] = newText
695 return (1, self.trUtf8("Visual indentation corrected."), 0) 706 changed = True
707 if changed:
708 return (1, self.trUtf8("Visual indentation corrected."), 0)
709 return (0, "", 0)
696 else: 710 else:
697 id = self.__getID() 711 id = self.__getID()
698 self.__stackLogical.append((id, code, line, pos)) 712 self.__stackLogical.append((id, code, line, pos))
699 return (-1, "", id) 713 return (-1, "", id)
700 714
712 """ 726 """
713 line = line - 1 727 line = line - 1
714 text = self.__source[line] 728 text = self.__source[line]
715 729
716 if '"""' in text or "'''" in text or text.rstrip().endswith('\\'): 730 if '"""' in text or "'''" in text or text.rstrip().endswith('\\'):
717 return ( 731 return (0, "", 0)
718 0, self.trUtf8("Extraneous whitespace cannot be removed."), 0)
719 732
720 newText = self.__fixWhitespace(text, pos, '') 733 newText = self.__fixWhitespace(text, pos, '')
721 if newText == text: 734 if newText == text:
722 return (0, "", 0) 735 return (0, "", 0)
723 736
739 """ 752 """
740 line = line - 1 753 line = line - 1
741 text = self.__source[line] 754 text = self.__source[line]
742 755
743 if '"""' in text or "'''" in text or text.rstrip().endswith('\\'): 756 if '"""' in text or "'''" in text or text.rstrip().endswith('\\'):
744 return ( 757 return (0, "", 0)
745 0, self.trUtf8("Extraneous whitespace cannot be removed."), 0)
746 758
747 newText = self.__fixWhitespace(text, pos, ' ') 759 newText = self.__fixWhitespace(text, pos, ' ')
748 if newText == text: 760 if newText == text:
749 return (0, "", 0) 761 return (0, "", 0)
750 762
766 fix (integer) 778 fix (integer)
767 """ 779 """
768 line = line - 1 780 line = line - 1
769 pos = pos + 1 781 pos = pos + 1
770 self.__source[line] = self.__source[line][:pos] + \ 782 self.__source[line] = self.__source[line][:pos] + \
771 " " + \ 783 " " + self.__source[line][pos:]
772 self.__source[line][pos:]
773 return (1, self.trUtf8("Missing whitespace added."), 0) 784 return (1, self.trUtf8("Missing whitespace added."), 0)
774 785
775 def __fixE251(self, code, line, pos): 786 def __fixE251(self, code, line, pos):
776 """ 787 """
777 Private method to fix extraneous whitespace around keyword and 788 Private method to fix extraneous whitespace around keyword and
877 if delta < 0: 888 if delta < 0:
878 # insert blank lines (one or two) 889 # insert blank lines (one or two)
879 while delta < 0: 890 while delta < 0:
880 self.__source.insert(line, self.__getEol()) 891 self.__source.insert(line, self.__getEol())
881 delta += 1 892 delta += 1
893 changed = True
882 elif delta > 0: 894 elif delta > 0:
883 # delete superfluous blank lines 895 # delete superfluous blank lines
884 while delta > 0: 896 while delta > 0:
885 del self.__source[line - 1] 897 del self.__source[line - 1]
886 line -= 1 898 line -= 1
887 delta -= 1 899 delta -= 1
900 changed = True
901 else:
902 changed = False
888 903
889 if delta < 0: 904 if changed:
890 msg = self.trUtf8("%n blank line(s) inserted.", "", -delta) 905 if delta < 0:
891 elif delta > 0: 906 msg = self.trUtf8(
892 msg = self.trUtf8("%n superfluous lines removed", "", delta) 907 "%n blank line(s) inserted.", "", -delta)
893 else: 908 elif delta > 0:
894 msg = "" 909 msg = self.trUtf8(
895 return (1, msg, 0) 910 "%n superfluous lines removed", "", delta)
911 else:
912 msg = ""
913 return (1, msg, 0)
914 return (0, "", 0)
896 else: 915 else:
897 id = self.__getID() 916 id = self.__getID()
898 self.__stack.append((id, code, line, pos)) 917 self.__stack.append((id, code, line, pos))
899 return (-1, "", id) 918 return (-1, "", id)
900 919
945 if self.__source[index].strip() == "": 964 if self.__source[index].strip() == "":
946 del self.__source[index] 965 del self.__source[index]
947 index -= 1 966 index -= 1
948 else: 967 else:
949 break 968 break
950 return ( 969 return (1, self.trUtf8(
951 1, 970 "Superfluous blank lines after function decorator removed."),
952 self.trUtf8("Superfluous blank lines after function"
953 " decorator removed."),
954 0) 971 0)
955 else: 972 else:
956 id = self.__getID() 973 id = self.__getID()
957 self.__stack.append((id, code, line, pos)) 974 self.__stack.append((id, code, line, pos))
958 return (-1, "", id) 975 return (-1, "", id)
1015 prevText = "" 1032 prevText = ""
1016 if line < len(self.__source) - 1: 1033 if line < len(self.__source) - 1:
1017 nextText = self.__source[line + 1] 1034 nextText = self.__source[line + 1]
1018 else: 1035 else:
1019 nextText = "" 1036 nextText = ""
1020 shortener = Pep8LineShortener(text, prevText, nextText, 1037 shortener = Pep8LineShortener(
1021 maxLength=self.__maxLineLength, eol=self.__getEol(), 1038 text, prevText, nextText,
1022 indentWord=self.__indentWord, isDocString=isDocString) 1039 maxLength=self.__maxLineLength, eol=self.__getEol(),
1040 indentWord=self.__indentWord, isDocString=isDocString)
1023 changed, newText, newNextText = shortener.shorten() 1041 changed, newText, newNextText = shortener.shorten()
1024 if changed: 1042 if changed:
1025 if newText != text: 1043 if newText != text:
1026 self.__source[line] = newText 1044 self.__source[line] = newText
1027 if newNextText and newNextText != nextText: 1045 if newNextText and newNextText != nextText:
1049 self.__source[line - 1].rstrip("\n\r \t\\") + self.__getEol() 1067 self.__source[line - 1].rstrip("\n\r \t\\") + self.__getEol()
1050 return (1, self.trUtf8("Redundant backslash in brackets removed."), 0) 1068 return (1, self.trUtf8("Redundant backslash in brackets removed."), 0)
1051 1069
1052 def __fixE701(self, code, line, pos, apply=False): 1070 def __fixE701(self, code, line, pos, apply=False):
1053 """ 1071 """
1054 Private method to fix colon-separated compund statements (E701). 1072 Private method to fix colon-separated compound statements (E701).
1055 1073
1056 @param code code of the issue (string) 1074 @param code code of the issue (string)
1057 @param line line number of the issue (integer) 1075 @param line line number of the issue (integer)
1058 @param pos position inside line (integer) 1076 @param pos position inside line (integer)
1059 @keyparam apply flag indicating, that the fix should be applied 1077 @keyparam apply flag indicating, that the fix should be applied
1782 first = source[:maxLen] 1800 first = source[:maxLen]
1783 second = source[maxLen:] 1801 second = source[maxLen:]
1784 else: 1802 else:
1785 first = source[:blank] 1803 first = source[:blank]
1786 second = source[blank + 1:] 1804 second = source[blank + 1:]
1787 return (True, 1805 return (
1806 True,
1788 first + quote + " \\" + self.__eol + 1807 first + quote + " \\" + self.__eol +
1789 indent + self.__indentWord + quote + second, 1808 indent + self.__indentWord + quote + second,
1790 "") 1809 "")
1791 else: 1810 else:
1792 # Cannot break 1811 # Cannot break
1811 indentation = self.__getIndent(newText) + '# ' 1830 indentation = self.__getIndent(newText) + '# '
1812 maxLength = min(self.__maxLength, 1831 maxLength = min(self.__maxLength,
1813 len(indentation) + 72) 1832 len(indentation) + 72)
1814 1833
1815 MIN_CHARACTER_REPEAT = 5 1834 MIN_CHARACTER_REPEAT = 5
1816 if (len(newText) - len(newText.rstrip(newText[-1])) >= \ 1835 if len(newText) - len(newText.rstrip(newText[-1])) >= \
1817 MIN_CHARACTER_REPEAT and 1836 MIN_CHARACTER_REPEAT and \
1818 not newText[-1].isalnum()): 1837 not newText[-1].isalnum():
1819 # Trim comments that end with things like --------- 1838 # Trim comments that end with things like ---------
1820 return newText[:maxLength] + self.__eol 1839 return newText[:maxLength] + self.__eol
1821 elif isLast and re.match(r"\s*#+\s*\w+", newText): 1840 elif isLast and re.match(r"\s*#+\s*\w+", newText):
1822 import textwrap 1841 import textwrap
1823 splitLines = textwrap.wrap(newText.lstrip(" \t#"), 1842 splitLines = textwrap.wrap(newText.lstrip(" \t#"),
1924 ): 1943 ):
1925 # Move inline comments to previous line. 1944 # Move inline comments to previous line.
1926 offset = tkn[2][1] 1945 offset = tkn[2][1]
1927 first = source[:offset] 1946 first = source[:offset]
1928 second = source[offset:] 1947 second = source[offset:]
1929 candidates.append(indent + second.strip() + self.__eol + 1948 candidates.append(
1930 indent + first.strip() + self.__eol) 1949 indent + second.strip() + self.__eol +
1950 indent + first.strip() + self.__eol)
1931 elif tokenType == tokenize.OP and tokenString != '=': 1951 elif tokenType == tokenize.OP and tokenString != '=':
1932 # Don't break on '=' after keyword as this violates PEP 8. 1952 # Don't break on '=' after keyword as this violates PEP 8.
1933 1953
1934 assert tokenType != tokenize.INDENT 1954 assert tokenType != tokenize.INDENT
1935 1955
2033 if currentLine.startswith(badStart): 2053 if currentLine.startswith(badStart):
2034 rank += 100 2054 rank += 100
2035 2055
2036 for ending in '([{': 2056 for ending in '([{':
2037 # Avoid lonely opening. They result in longer lines. 2057 # Avoid lonely opening. They result in longer lines.
2038 if (currentLine.endswith(ending) and 2058 if currentLine.endswith(ending) and \
2039 len(currentLine.strip()) <= \ 2059 len(currentLine.strip()) <= len(self.__indentWord):
2040 len(self.__indentWord)):
2041 rank += 100 2060 rank += 100
2042 2061
2043 if currentLine.endswith('%'): 2062 if currentLine.endswith('%'):
2044 rank -= 20 2063 rank -= 20
2045 2064

eric ide

mercurial