--- a/src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/CodeStyleFixer.py Wed Jul 13 11:16:20 2022 +0200 +++ b/src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/CodeStyleFixer.py Wed Jul 13 14:55:47 2022 +0200 @@ -19,21 +19,82 @@ import pycodestyle FixableCodeStyleIssues = [ - "D111", "D112", "D121", "D131", "D141", "D142", - "D143", "D144", "D145", - "D221", "D222", "D231", "D242", "D243", "D244", - "D245", "D246", "D247", - "E101", "E111", "E121", "E122", "E123", "E124", - "E125", "E126", "E127", "E128", "E133", "E201", - "E202", "E203", "E211", "E221", "E222", "E223", - "E224", "E225", "E226", "E227", "E228", "E231", - "E241", "E242", "E251", "E261", "E262", "E271", - "E272", "E273", "E274", "E301", "E302", "E303", - "E304", "E305", "E306", "E307", "E308", "E401", - "E501", "E502", "E701", "E702", "E703", "E711", + "D111", + "D112", + "D121", + "D131", + "D141", + "D142", + "D143", + "D144", + "D145", + "D221", + "D222", + "D231", + "D242", + "D243", + "D244", + "D245", + "D246", + "D247", + "E101", + "E111", + "E121", + "E122", + "E123", + "E124", + "E125", + "E126", + "E127", + "E128", + "E133", + "E201", + "E202", + "E203", + "E211", + "E221", + "E222", + "E223", + "E224", + "E225", + "E226", + "E227", + "E228", + "E231", + "E241", + "E242", + "E251", + "E261", + "E262", + "E271", + "E272", + "E273", + "E274", + "E301", + "E302", + "E303", + "E304", + "E305", + "E306", + "E307", + "E308", + "E401", + "E501", + "E502", + "E701", + "E702", + "E703", + "E711", "E712", - "N804", "N805", "N806", - "W191", "W291", "W292", "W293", "W391", "W603", + "N804", + "N805", + "N806", + "W191", + "W291", + "W292", + "W293", + "W391", + "W603", ] @@ -41,11 +102,22 @@ """ Class implementing a fixer for certain code style issues. """ - def __init__(self, filename, sourceLines, fixCodes, noFixCodes, - maxLineLength, blankLines, inPlace, eol, backup=False): + + def __init__( + self, + filename, + sourceLines, + fixCodes, + noFixCodes, + maxLineLength, + blankLines, + inPlace, + eol, + backup=False, + ): """ Constructor - + @param filename name of the file to be fixed @type str @param sourceLines list of source lines including eol marker @@ -71,30 +143,30 @@ @type bool """ super().__init__() - + self.__filename = filename self.__origName = "" self.__source = sourceLines[:] # save a copy self.__fixCodes = [c.strip() for c in fixCodes.split(",") if c.strip()] - self.__noFixCodes = [ - c.strip() for c in noFixCodes.split(",") if c.strip()] + self.__noFixCodes = [c.strip() for c in noFixCodes.split(",") if c.strip()] self.__maxLineLength = maxLineLength self.__blankLines = { "toplevel": blankLines[0], "method": blankLines[1], } self.fixed = 0 - + self.__reindenter = None self.__indentWord = self.__getIndentWord() - + if inPlace: self.__createBackup = backup else: self.__origName = self.__filename self.__filename = os.path.join( os.path.dirname(self.__filename), - "fixed_" + os.path.basename(self.__filename)) + "fixed_" + os.path.basename(self.__filename), + ) self.__createBackup = False self.__eol = eol @@ -183,85 +255,87 @@ self.__stack = [] # These need to be fixed before the file is saved but after all # inline fixes. - + self.__multiLineNumbers = None self.__docLineNumbers = None - + self.__lastID = 0 - + def saveFile(self, encoding): """ Public method to save the modified file. - + @param encoding encoding of the source file (string) @return error message on failure (tuple of str) """ import codecs - + if not self.__modified: # no need to write return None - + if self.__createBackup: # create a backup file before writing any changes if os.path.islink(self.__filename): - bfn = '{0}~'.format(os.path.realpath(self.__filename)) + bfn = "{0}~".format(os.path.realpath(self.__filename)) else: - bfn = '{0}~'.format(self.__filename) + bfn = "{0}~".format(self.__filename) with contextlib.suppress(OSError): os.remove(bfn) with contextlib.suppress(OSError): os.rename(self.__filename, bfn) - + txt = "".join(self.__source) try: - enc = 'utf-8' if encoding == 'utf-8-bom' else encoding + enc = "utf-8" if encoding == "utf-8-bom" else encoding txt = txt.encode(enc) - if encoding == 'utf-8-bom': + if encoding == "utf-8-bom": txt = codecs.BOM_UTF8 + txt - + with open(self.__filename, "wb") as fp: fp.write(txt) except (OSError, UnicodeError) as err: # Could not save the file! Skipping it. Reason: {0} return ("FIXWRITE_ERROR", [str(err)]) - + return None - + def __codeMatch(self, code): """ Private method to check, if the code should be fixed. - + @param code to check (string) @return flag indicating it should be fixed (boolean) """ + def mutualStartswith(a, b): """ Local helper method to compare the beginnings of two strings against each other. - + @return flag indicating that one string starts with the other (boolean) """ return b.startswith(a) or a.startswith(b) - - if ( - self.__noFixCodes and - any(mutualStartswith(code.lower(), noFixCode.lower()) - for noFixCode in [c.strip() for c in self.__noFixCodes]) + + if self.__noFixCodes and any( + mutualStartswith(code.lower(), noFixCode.lower()) + for noFixCode in [c.strip() for c in self.__noFixCodes] ): return False if self.__fixCodes: - return any(mutualStartswith(code.lower(), fixCode.lower()) - for fixCode in [c.strip() for c in self.__fixCodes]) + return any( + mutualStartswith(code.lower(), fixCode.lower()) + for fixCode in [c.strip() for c in self.__fixCodes] + ) return True - + def fixIssue(self, line, pos, code): """ Public method to fix the fixable issues. - + @param line line number of the issue @type int @param pos position inside line @@ -274,9 +348,9 @@ @rtype tuple of (int, str, list, int) """ if ( - line <= len(self.__source) and - self.__codeMatch(code) and - code in self.__fixes + line <= len(self.__source) + and self.__codeMatch(code) + and code in self.__fixes ): res = self.__fixes[code](code, line, pos) if res[0] == 1: @@ -284,17 +358,17 @@ self.fixed += 1 else: res = (0, "", [], 0) - + return res - + def finalize(self): """ Public method to apply all deferred fixes. - + @return dictionary containing the fix results """ results = {} - + # step 1: do fixes operating on logical lines first for id_, code, line, pos in self.__stackLogical: res, msg, args, _ = self.__fixes[code](code, line, pos, apply=True) @@ -302,7 +376,7 @@ self.__modified = True self.fixed += 1 results[id_] = (res, msg, args) - + # step 2: do fixes that change the number of lines for id_, code, line, pos in reversed(self.__stack): res, msg, args, _ = self.__fixes[code](code, line, pos, apply=True) @@ -310,23 +384,23 @@ self.__modified = True self.fixed += 1 results[id_] = (res, msg, args) - + return results - + def __getID(self): """ Private method to get the ID for a deferred fix. - + @return ID for a deferred fix (integer) """ self.__lastID += 1 return self.__lastID - + def __findLogical(self): """ Private method to extract the index of all the starts and ends of lines. - + @return tuple containing two lists of integer with start and end tuples of lines """ @@ -336,9 +410,13 @@ sio = StringIO("".join(self.__source)) parens = 0 for t in tokenize.generate_tokens(sio.readline): - if t[0] in [tokenize.COMMENT, tokenize.DEDENT, - tokenize.INDENT, tokenize.NL, - tokenize.ENDMARKER]: + if t[0] in [ + tokenize.COMMENT, + tokenize.DEDENT, + tokenize.INDENT, + tokenize.NL, + tokenize.ENDMARKER, + ]: continue if not parens and t[0] in [tokenize.NEWLINE, tokenize.SEMI]: last_newline = True @@ -348,17 +426,17 @@ logical_start.append((t[2][0] - 1, t[2][1])) last_newline = False if t[0] == tokenize.OP: - if t[1] in '([{': + if t[1] in "([{": parens += 1 - elif t[1] in '}])': + elif t[1] in "}])": parens -= 1 return logical_start, logical_end - + def __getLogical(self, line, pos): """ Private method to get the logical line corresponding to the given position. - + @param line line number of the issue (integer) @param pos position inside line (integer) @return tuple of a tuple of two integers giving the start of the @@ -382,39 +460,39 @@ break if ls is None: return None - - original = self.__source[ls[0]:le[0] + 1] + + original = self.__source[ls[0] : le[0] + 1] return ls, le, original - + def __getIndentWord(self): """ Private method to determine the indentation type. - + @return string to be used for an indentation (string) """ sio = StringIO("".join(self.__source)) - indentWord = " " # default in case of failure + indentWord = " " # default in case of failure with contextlib.suppress(SyntaxError, tokenize.TokenError): for token in tokenize.generate_tokens(sio.readline): if token[0] == tokenize.INDENT: indentWord = token[1] break return indentWord - + def __getIndent(self, line): """ Private method to get the indentation string. - + @param line line to determine the indentation string from (string) @return indentation string (string) """ return line.replace(line.lstrip(), "") - + def __multilineStringLines(self): """ Private method to determine the line numbers that are within multi line strings and these which are part of a documentation string. - + @return tuple of a set of line numbers belonging to a multi line string and a set of line numbers belonging to a multi line documentation string (tuple of two set of integer) @@ -424,31 +502,29 @@ sio = StringIO(source) self.__multiLineNumbers = set() self.__docLineNumbers = set() - previousTokenType = '' + previousTokenType = "" with contextlib.suppress(SyntaxError, tokenize.TokenError): for t in tokenize.generate_tokens(sio.readline): tokenType = t[0] startRow = t[2][0] endRow = t[3][0] - if (tokenType == tokenize.STRING and startRow != endRow): + if tokenType == tokenize.STRING and startRow != endRow: if previousTokenType != tokenize.INDENT: - self.__multiLineNumbers |= set( - range(startRow, 1 + endRow)) + self.__multiLineNumbers |= set(range(startRow, 1 + endRow)) else: - self.__docLineNumbers |= set( - range(startRow, 1 + endRow)) + self.__docLineNumbers |= set(range(startRow, 1 + endRow)) previousTokenType = tokenType - + return self.__multiLineNumbers, self.__docLineNumbers - + def __fixReindent(self, line, pos, logical): """ Private method to fix a badly indented line. This is done by adding or removing from its initial indent only. - + @param line line number of the issue (integer) @param pos position inside line (integer) @param logical logical line structure @@ -457,14 +533,14 @@ """ if not logical: raise ValueError("Bad value for 'logical' parameter.") - + ls, _, original = logical rewrapper = IndentationWrapper(original) valid_indents = rewrapper.pep8Expected() if not rewrapper.rel_indent: return False - + if line > ls[0]: # got a valid continuation line number row = line - ls[0] - 1 @@ -473,14 +549,14 @@ got = rewrapper.rel_indent[row] else: return False - + line1 = ls[0] + row # always pick the expected indent, for now. indent_to = valid[0] if got != indent_to: orig_line = self.__source[line1] - new_line = ' ' * (indent_to) + orig_line.lstrip() + new_line = " " * (indent_to) + orig_line.lstrip() if new_line == orig_line: return False else: @@ -488,11 +564,11 @@ return True else: return False - + def __fixWhitespace(self, line, offset, replacement): """ Private method to correct whitespace at the given offset. - + @param line line to be corrected (string) @param offset offset within line (integer) @param replacement replacement string (string) @@ -504,13 +580,13 @@ return line else: return left + replacement + right - + def __fixD111(self, code, line, pos): """ Private method to fix docstring enclosed in wrong quotes. - + Codes: D111 - + @param code code of the issue @type str @param line line number of the issue @@ -523,8 +599,7 @@ @rtype tuple of (int, str, list or int, int) """ line -= 1 - quotes = re.match(r"""\s*[ru]?('''|'|\")""", - self.__source[line]).group(1) + quotes = re.match(r"""\s*[ru]?('''|'|\")""", self.__source[line]).group(1) left, right = self.__source[line].split(quotes, 1) self.__source[line] = left + '"""' + right while line < len(self.__source): @@ -533,16 +608,16 @@ self.__source[line] = left + '"""' + right break line += 1 - + # Triple single quotes converted to triple double quotes. return (1, "FIXD111", [], 0) - + def __fixD112(self, code, line, pos): """ Private method to fix docstring 'r' in leading quotes. - + Codes: D112 - + @param code code of the issue @type str @param line line number of the issue @@ -559,22 +634,22 @@ insertChar = "r" else: return (0, "", 0) - + newText = ( - self.__getIndent(self.__source[line]) + - insertChar + - self.__source[line].lstrip() + self.__getIndent(self.__source[line]) + + insertChar + + self.__source[line].lstrip() ) self.__source[line] = newText # Introductory quotes corrected to be {0}""" - return (1, 'FIXD112', [insertChar], 0) - + return (1, "FIXD112", [insertChar], 0) + def __fixD121(self, code, line, pos, apply=False): """ Private method to fix a single line docstring on multiple lines. - + Codes: D121 - + @param code code of the issue @type str @param line line number of the issue @@ -590,21 +665,17 @@ """ if apply: line -= 1 - if not self.__source[line].lstrip().startswith( - ('"""', 'r"""', 'u"""')): + if not self.__source[line].lstrip().startswith(('"""', 'r"""', 'u"""')): # only correctly formatted docstrings will be fixed return (0, "", [], 0) - - docstring = ( - self.__source[line].rstrip() + - self.__source[line + 1].strip() - ) + + docstring = self.__source[line].rstrip() + self.__source[line + 1].strip() if docstring.endswith('"""'): docstring += self.__eol else: docstring += self.__source[line + 2].lstrip() self.__source[line + 2] = "" - + self.__source[line] = docstring self.__source[line + 1] = "" # Single line docstring put on one line. @@ -613,14 +684,14 @@ fixId = self.__getID() self.__stack.append((fixId, code, line, pos)) return (-1, "", [], fixId) - + def __fixD131(self, code, line, pos): """ Private method to fix a docstring summary not ending with a period. - + Codes: D131 - + @param code code of the issue @type str @param line line number of the issue @@ -634,41 +705,41 @@ """ line -= 1 newText = "" - if ( - self.__source[line].rstrip().endswith(('"""', "'''")) and - self.__source[line].lstrip().startswith(('"""', 'r"""', 'u"""')) - ): + if self.__source[line].rstrip().endswith(('"""', "'''")) and self.__source[ + line + ].lstrip().startswith(('"""', 'r"""', 'u"""')): # it is a one-liner newText = ( - self.__source[line].rstrip()[:-3].rstrip() + - "." + - self.__source[line].rstrip()[-3:] + - self.__eol + self.__source[line].rstrip()[:-3].rstrip() + + "." + + self.__source[line].rstrip()[-3:] + + self.__eol ) else: - if ( - line < len(self.__source) - 1 and - (not self.__source[line + 1].strip() or - self.__source[line + 1].lstrip().startswith("@") or - (self.__source[line + 1].strip() in ('"""', "'''") and - not self.__source[line].lstrip().startswith("@"))) + if line < len(self.__source) - 1 and ( + not self.__source[line + 1].strip() + or self.__source[line + 1].lstrip().startswith("@") + or ( + self.__source[line + 1].strip() in ('"""', "'''") + and not self.__source[line].lstrip().startswith("@") + ) ): newText = self.__source[line].rstrip() + "." + self.__eol - + if newText: self.__source[line] = newText # Period added to summary line. return (1, "FIXD131", [], 0) else: return (0, "", [], 0) - + def __fixD141(self, code, line, pos, apply=False): """ Private method to fix a function/method docstring preceded by a blank line. - + Codes: D141 - + @param code code of the issue @type str @param line line number of the issue @@ -691,14 +762,14 @@ fixId = self.__getID() self.__stack.append((fixId, code, line, pos)) return (-1, "", [], fixId) - + def __fixD142(self, code, line, pos, apply=False): """ Private method to fix a class docstring not preceded by a blank line. - + Codes: D142 - + @param code code of the issue @type str @param line line number of the issue @@ -721,14 +792,14 @@ fixId = self.__getID() self.__stack.append((fixId, code, line, pos)) return (-1, "", [], fixId) - + def __fixD143(self, code, line, pos, apply=False): """ Private method to fix a class docstring not followed by a blank line. - + Codes: D143 - + @param code code of the issue @type str @param line line number of the issue @@ -751,14 +822,14 @@ fixId = self.__getID() self.__stack.append((fixId, code, line, pos)) return (-1, "", [], fixId) - + def __fixD144(self, code, line, pos, apply=False): """ Private method to fix a docstring summary not followed by a blank line. - + Codes: D144 - + @param code code of the issue @type str @param line line number of the issue @@ -777,7 +848,7 @@ if not self.__source[line].rstrip().endswith("."): # only correct summary lines can be fixed here return (0, "", 0) - + self.__source[line] += self.__eol # Blank line inserted after docstring summary. return (1, "FIXD144", [], 0) @@ -785,14 +856,14 @@ fixId = self.__getID() self.__stack.append((fixId, code, line, pos)) return (-1, "", [], fixId) - + def __fixD145(self, code, line, pos, apply=False): """ Private method to fix the last paragraph of a multi-line docstring not followed by a blank line. - + Codes: D143 - + @param code code of the issue @type str @param line line number of the issue @@ -815,14 +886,14 @@ fixId = self.__getID() self.__stack.append((fixId, code, line, pos)) return (-1, "", [], fixId) - + def __fixD221(self, code, line, pos, apply=False): """ Private method to fix leading and trailing quotes of docstring not on separate lines. - + Codes: D221, D222 - + @param code code of the issue @type str @param line line number of the issue @@ -849,14 +920,7 @@ else: # trailing first, second = source[:-3].strip(), source[-3:] - newText = ( - indent + - first + - self.__eol + - indent + - second + - self.__eol - ) + newText = indent + first + self.__eol + indent + second + self.__eol self.__source[line] = newText if code == "D221": # Leading quotes put on separate line. @@ -869,14 +933,14 @@ fixId = self.__getID() self.__stack.append((fixId, code, line, pos)) return (-1, "", [], fixId) - + def __fixD242(self, code, line, pos, apply=False): """ Private method to fix a class or function/method docstring preceded by a blank line. - + Codes: D242, D244 - + @param code code of the issue @type str @param line line number of the issue @@ -904,14 +968,14 @@ fixId = self.__getID() self.__stack.append((fixId, code, line, pos)) return (-1, "", [], fixId) - + def __fixD243(self, code, line, pos, apply=False): """ Private method to fix a class or function/method docstring followed by a blank line. - + Codes: D243, D245 - + @param code code of the issue @type str @param line line number of the issue @@ -939,14 +1003,14 @@ fixId = self.__getID() self.__stack.append((fixId, code, line, pos)) return (-1, "", [], fixId) - + def __fixD247(self, code, line, pos, apply=False): """ Private method to fix a last paragraph of a docstring followed by a blank line. - + Codes: D247 - + @param code code of the issue @type str @param line line number of the issue @@ -969,13 +1033,13 @@ fixId = self.__getID() self.__stack.append((fixId, code, line, pos)) return (-1, "", [], fixId) - + def __fixE101(self, code, line, pos): """ Private method to fix obsolete tab usage and indentation errors. - + Codes: E101, E111, W191 - + @param code code of the issue @type str @param line line number of the issue @@ -1002,14 +1066,14 @@ return (1, msg, [], 0) else: return (0, "", [], 0) - + def __fixE121(self, code, line, pos, apply=False): """ Private method to fix the indentation of continuation lines and closing brackets. - + Codes: E121, E124 - + @param code code of the issue @type str @param line line number of the issue @@ -1041,13 +1105,13 @@ fixId = self.__getID() self.__stackLogical.append((fixId, code, line, pos)) return (-1, "", [], fixId) - + def __fixE122(self, code, line, pos, apply=False): """ Private method to fix a missing indentation of continuation lines. - + Codes: E122 - + @param code code of the issue @type str @param line line number of the issue @@ -1072,8 +1136,7 @@ text = self.__source[line] indentation = self.__getIndent(text) self.__source[line] = ( - indentation + - self.__indentWord + text.lstrip() + indentation + self.__indentWord + text.lstrip() ) # Missing indentation of continuation line corrected. return (1, "FIXE122", [], 0) @@ -1082,13 +1145,13 @@ fixId = self.__getID() self.__stackLogical.append((fixId, code, line, pos)) return (-1, "", [], fixId) - + def __fixE123(self, code, line, pos, apply=False): """ Private method to fix the indentation of a closing bracket lines. - + Codes: E123 - + @param code code of the issue @type str @param line line number of the issue @@ -1124,14 +1187,14 @@ fixId = self.__getID() self.__stackLogical.append((fixId, code, line, pos)) return (-1, "", [], fixId) - + def __fixE125(self, code, line, pos, apply=False): """ Private method to fix the indentation of continuation lines not distinguishable from next logical line. - + Codes: E125 - + @param code code of the issue @type str @param line line number of the issue @@ -1154,8 +1217,7 @@ row = line - 1 text = self.__source[row] self.__source[row] = ( - self.__getIndent(text) + - self.__indentWord + text.lstrip() + self.__getIndent(text) + self.__indentWord + text.lstrip() ) # Indentation level changed. return (1, "FIXE125", [], 0) @@ -1164,14 +1226,14 @@ fixId = self.__getID() self.__stackLogical.append((fixId, code, line, pos)) return (-1, "", [], fixId) - + def __fixE126(self, code, line, pos, apply=False): """ Private method to fix over-indented/under-indented hanging indentation. - + Codes: E126, E133 - + @param code code of the issue @type str @param line line number of the issue @@ -1193,8 +1255,9 @@ row = line - 1 text = self.__source[row] newText = ( - self.__getIndent(logicalLines[0]) + - self.__indentWord + text.lstrip() + self.__getIndent(logicalLines[0]) + + self.__indentWord + + text.lstrip() ) if newText == text: # fall back to slower method @@ -1210,13 +1273,13 @@ fixId = self.__getID() self.__stackLogical.append((fixId, code, line, pos)) return (-1, "", [], fixId) - + def __fixE127(self, code, line, pos, apply=False): """ Private method to fix over/under indented lines. - + Codes: E127, E128 - + @param code code of the issue @type str @param line line number of the issue @@ -1238,16 +1301,16 @@ row = line - 1 text = self.__source[row] newText = text - - if logicalLines[0].rstrip().endswith('\\'): + + if logicalLines[0].rstrip().endswith("\\"): newText = ( - self.__getIndent(logicalLines[0]) + - self.__indentWord + - text.lstrip() + self.__getIndent(logicalLines[0]) + + self.__indentWord + + text.lstrip() ) else: startIndex = None - for symbol in '([{': + for symbol in "([{": if symbol in logicalLines[0]: foundIndex = logicalLines[0].find(symbol) + 1 if startIndex is None: @@ -1256,8 +1319,8 @@ startIndex = min(startIndex, foundIndex) if startIndex is not None: - newText = startIndex * ' ' + text.lstrip() - + newText = startIndex * " " + text.lstrip() + if newText == text: # fall back to slower method changed = self.__fixReindent(line, pos, logical) @@ -1272,13 +1335,13 @@ fixId = self.__getID() self.__stackLogical.append((fixId, code, line, pos)) return (-1, "", [], fixId) - + def __fixE201(self, code, line, pos): """ Private method to fix extraneous whitespace. - + Codes: E201, E202, E203, E211 - + @param code code of the issue @type str @param line line number of the issue @@ -1292,25 +1355,25 @@ """ line -= 1 text = self.__source[line] - - if '"""' in text or "'''" in text or text.rstrip().endswith('\\'): + + if '"""' in text or "'''" in text or text.rstrip().endswith("\\"): return (0, "", [], 0) - - newText = self.__fixWhitespace(text, pos, '') + + newText = self.__fixWhitespace(text, pos, "") if newText == text: return (0, "", [], 0) - + self.__source[line] = newText # Extraneous whitespace removed. return (1, "FIXE201", [], 0) - + def __fixE221(self, code, line, pos): """ Private method to fix extraneous whitespace around operator or keyword. - + Codes: E221, E222, E223, E224, E241, E242, E271, E272, E273, E274 - + @param code code of the issue @type str @param line line number of the issue @@ -1324,23 +1387,23 @@ """ line -= 1 text = self.__source[line] - - if '"""' in text or "'''" in text or text.rstrip().endswith('\\'): + + if '"""' in text or "'''" in text or text.rstrip().endswith("\\"): return (0, "", [], 0) - - newText = self.__fixWhitespace(text, pos, ' ') + + newText = self.__fixWhitespace(text, pos, " ") if newText == text: return (0, "", [], 0) - + self.__source[line] = newText return (1, "FIXE221", [], 0) - + def __fixE225(self, code, line, pos): """ Private method to fix extraneous whitespaces around operator. - + Codes: E225, E226, E227, E228 - + @param code code of the issue @type str @param line line number of the issue @@ -1354,13 +1417,13 @@ """ line -= 1 text = self.__source[line] - - if '"""' in text or "'''" in text or text.rstrip().endswith('\\'): + + if '"""' in text or "'''" in text or text.rstrip().endswith("\\"): return (0, "", [], 0) - + newText = text # determine length of operator - tokens = '<>*/=^&|%!+-' + tokens = "<>*/=^&|%!+-" pos2 = pos token_delimiter = len(tokens) for _ in range(3): @@ -1370,22 +1433,22 @@ token_delimiter = 5 else: break - if pos2 < len(text) and text[pos2] not in ' \t': - newText = self.__fixWhitespace(newText, pos2, ' ') - newText = self.__fixWhitespace(newText, pos, ' ') + if pos2 < len(text) and text[pos2] not in " \t": + newText = self.__fixWhitespace(newText, pos2, " ") + newText = self.__fixWhitespace(newText, pos, " ") if newText == text: return (0, "", [], 0) - + self.__source[line] = newText # Missing whitespaces added. return (1, "FIXE225", [], 0) - + def __fixE231(self, code, line, pos): """ Private method to fix missing whitespace after ',;:'. - + Codes: E231 - + @param code code of the issue @type str @param line line number of the issue @@ -1400,20 +1463,18 @@ line -= 1 pos += 1 self.__source[line] = ( - self.__source[line][:pos] + - " " + - self.__source[line][pos:] + self.__source[line][:pos] + " " + self.__source[line][pos:] ) # Missing whitespace added. return (1, "FIXE231", [], 0) - + def __fixE251(self, code, line, pos): """ Private method to fix extraneous whitespace around keyword and default parameter equals. - + Codes: E251 - + @param code code of the issue @type str @param line line number of the issue @@ -1427,32 +1488,30 @@ """ line -= 1 text = self.__source[line] - + # This is necessary since pycodestyle sometimes reports columns that # goes past the end of the physical line. This happens in cases like, # foo(bar\n=None) col = min(pos, len(text) - 1) newText = ( - text - if text[col].strip() else - text[:col].rstrip() + text[col:].lstrip() + text if text[col].strip() else text[:col].rstrip() + text[col:].lstrip() ) - + # There could be an escaped newline - if newText.endswith(('=\\\n', '=\\\r\n', '=\\\r')): + if newText.endswith(("=\\\n", "=\\\r\n", "=\\\r")): self.__source[line] = newText.rstrip("\n\r \t\\") self.__source[line + 1] = self.__source[line + 1].lstrip() else: self.__source[line] = newText # Extraneous whitespace removed. return (1, "FIXE251", [], 0) - + def __fixE261(self, code, line, pos): """ Private method to fix whitespace before or after inline comment. - + Codes: E261, E262 - + @param code code of the issue @type str @param line line number of the issue @@ -1466,20 +1525,20 @@ """ line -= 1 text = self.__source[line] - left = text[:pos].rstrip(' \t#') - right = text[pos:].lstrip(' \t#') + left = text[:pos].rstrip(" \t#") + right = text[pos:].lstrip(" \t#") newText = left + (" # " + right if right.strip() else right) self.__source[line] = newText # Whitespace around comment sign corrected. return (1, "FIXE261", [], 0) - + def __fixBlankLinesBefore(self, code, line, pos, apply=False): """ Private method to fix the need for blank lines before class, function and method definitions. - + Codes: E301, E302, E303, E305, E306, E307, E308 - + @param code code of the issue @type str @param line line number of the issue @@ -1500,7 +1559,7 @@ blankLinesBefore = 1 else: blankLinesBefore = self.__blankLines["toplevel"] - + # count blank lines index = line - 1 blanks = 0 @@ -1511,7 +1570,7 @@ else: break delta = blanks - blankLinesBefore - + line -= 1 if delta < 0: # insert blank lines (one or two) @@ -1534,14 +1593,14 @@ fixId = self.__getID() self.__stack.append((fixId, code, line, pos)) return (-1, "", [], fixId) - + def __fixE304(self, code, line, pos, apply=False): """ Private method to fix superfluous blank lines after a function decorator. - + Codes: E304 - + @param code code of the issue @type str @param line line number of the issue @@ -1569,13 +1628,13 @@ fixId = self.__getID() self.__stack.append((fixId, code, line, pos)) return (-1, "", [], fixId) - + def __fixE401(self, code, line, pos, apply=False): """ Private method to fix multiple imports on one line. - + Codes: E401 - + @param code code of the issue @type str @param line line number of the issue @@ -1594,19 +1653,19 @@ text = self.__source[line] if not text.lstrip().startswith("import"): return (0, "", [], 0) - + # pycodestyle (1.3.1) reports false positive if there is an import # statement followed by a semicolon and some unrelated # statement with commas in it. - if ';' in text: + if ";" in text: return (0, "", [], 0) - + newText = ( - text[:pos].rstrip("\t ,") + - self.__eol + - self.__getIndent(text) + - "import " + - text[pos:].lstrip("\t ,") + text[:pos].rstrip("\t ,") + + self.__eol + + self.__getIndent(text) + + "import " + + text[pos:].lstrip("\t ,") ) self.__source[line] = newText # Imports were put on separate lines. @@ -1615,13 +1674,13 @@ fixId = self.__getID() self.__stack.append((fixId, code, line, pos)) return (-1, "", [], fixId) - + def __fixE501(self, code, line, pos, apply=False): """ Private method to fix the long lines by breaking them. - + Codes: E501 - + @param code code of the issue @type str @param line line number of the issue @@ -1636,9 +1695,7 @@ @rtype tuple of (int, str, list or int, int) """ if apply: - multilineStringLines, docStringLines = ( - self.__multilineStringLines() - ) + multilineStringLines, docStringLines = self.__multilineStringLines() isDocString = line in docStringLines line -= 1 text = self.__source[line] @@ -1651,9 +1708,14 @@ else: nextText = "" shortener = LineShortener( - text, prevText, nextText, - maxLength=self.__maxLineLength, eol=self.__eol, - indentWord=self.__indentWord, isDocString=isDocString) + text, + prevText, + nextText, + maxLength=self.__maxLineLength, + eol=self.__eol, + indentWord=self.__indentWord, + isDocString=isDocString, + ) changed, newText, newNextText = shortener.shorten() if changed: if newText != text: @@ -1670,13 +1732,13 @@ fixId = self.__getID() self.__stack.append((fixId, code, line, pos)) return (-1, "", [], fixId) - + def __fixE502(self, code, line, pos): """ Private method to fix redundant backslash within brackets. - + Codes: E502 - + @param code code of the issue @type str @param line line number of the issue @@ -1689,18 +1751,17 @@ @rtype tuple of (int, str, list or int, int) """ self.__source[line - 1] = ( - self.__source[line - 1].rstrip("\n\r \t\\") + - self.__eol + self.__source[line - 1].rstrip("\n\r \t\\") + self.__eol ) # Redundant backslash in brackets removed. return (1, "FIXE502", [], 0) - + def __fixE701(self, code, line, pos, apply=False): """ Private method to fix colon-separated compound statements. - + Codes: E701 - + @param code code of the issue @type str @param line line number of the issue @@ -1718,14 +1779,14 @@ line -= 1 text = self.__source[line] pos += 1 - + newText = ( - text[:pos] + - self.__eol + - self.__getIndent(text) + - self.__indentWord + - text[pos:].lstrip("\n\r \t\\") + - self.__eol + text[:pos] + + self.__eol + + self.__getIndent(text) + + self.__indentWord + + text[pos:].lstrip("\n\r \t\\") + + self.__eol ) self.__source[line] = newText # Compound statement corrected. @@ -1734,13 +1795,13 @@ fixId = self.__getID() self.__stack.append((fixId, code, line, pos)) return (-1, "", [], fixId) - + def __fixE702(self, code, line, pos, apply=False): """ Private method to fix semicolon-separated compound statements. - + Codes: E702, E703 - + @param code code of the issue @type str @param line line number of the issue @@ -1757,7 +1818,7 @@ if apply: line -= 1 text = self.__source[line] - + if text.rstrip().endswith("\\"): # normalize '1; \\\n2' into '1; 2' self.__source[line] = text.rstrip("\n\r \t\\") @@ -1774,13 +1835,13 @@ fixId = self.__getID() self.__stack.append((fixId, code, line, pos)) return (-1, "", [], fixId) - + def __fixE711(self, code, line, pos): """ Private method to fix comparison with None. - + Codes: E711, E712 - + @param code code of the issue @type str @param line line number of the issue @@ -1794,36 +1855,36 @@ """ line -= 1 text = self.__source[line] - + rightPos = pos + 2 if rightPos >= len(text): return (0, "", 0) - + left = text[:pos].rstrip() center = text[pos:rightPos] right = text[rightPos:].lstrip() - + if not right.startswith(("None", "True", "False")): return (0, "", [], 0) - + if center.strip() == "==": center = "is" elif center.strip() == "!=": center = "is not" else: return (0, "", [], 0) - + self.__source[line] = " ".join([left, center, right]) # Comparison to None/True/False corrected. return (1, "FIXE711", [], 0) - + def __fixN804(self, code, line, pos, apply=False): """ Private method to fix a wrong first argument of normal and class methods. - + Codes: N804, N805 - + @param code code of the issue @type str @param line line number of the issue @@ -1844,15 +1905,15 @@ arg = "cls" else: arg = "self" - + if text.rstrip().endswith("("): newText = ( - text + - self.__getIndent(text) + - self.__indentWord + - arg + - "," + - self.__eol + text + + self.__getIndent(text) + + self.__indentWord + + arg + + "," + + self.__eol ) else: index = text.find("(") + 1 @@ -1870,13 +1931,13 @@ fixId = self.__getID() self.__stack.append((fixId, code, line, pos)) return (-1, "", [], fixId) - + def __fixN806(self, code, line, pos, apply=False): """ Private method to fix a wrong first argument of static methods. - + Codes: N806 - + @param code code of the issue @type str @param line line number of the issue @@ -1896,7 +1957,7 @@ index = text.find("(") + 1 left = text[:index] right = text[index:] - + if right.startswith(("cls", "self")): # cls or self are on the definition line if right.startswith("cls"): @@ -1923,26 +1984,24 @@ right = right.lstrip(", ") if right.startswith("):"): # merge with previous line - self.__source[line - 1] = ( - self.__source[line - 1].rstrip() + right - ) + self.__source[line - 1] = self.__source[line - 1].rstrip() + right self.__source[line] = "" else: self.__source[line] = indent + right - + # '{0}' argument removed. return (1, "FIXN806", [arg], 0) else: fixId = self.__getID() self.__stack.append((fixId, code, line, pos)) return (-1, "", [], fixId) - + def __fixW291(self, code, line, pos): """ Private method to fix trailing whitespace. - + Codes: W291, W293 - + @param code code of the issue @type str @param line line number of the issue @@ -1954,17 +2013,18 @@ message and an ID for a deferred fix @rtype tuple of (int, str, list or int, int) """ - self.__source[line - 1] = re.sub(r'[\t ]+(\r?)$', r"\1", - self.__source[line - 1]) + self.__source[line - 1] = re.sub( + r"[\t ]+(\r?)$", r"\1", self.__source[line - 1] + ) # Whitespace stripped from end of line. return (1, "FIXW291", [], 0) - + def __fixW292(self, code, line, pos): """ Private method to fix a missing newline at the end of file. - + Codes: W292 - + @param code code of the issue @type str @param line line number of the issue @@ -1979,13 +2039,13 @@ self.__source[line - 1] += self.__eol # newline added to end of file. return (1, "FIXW292", [], 0) - + def __fixW391(self, code, line, pos): """ Private method to fix trailing blank lines. - + Codes: W391 - + @param code code of the issue @type str @param line line number of the issue @@ -2006,13 +2066,13 @@ break # Superfluous trailing blank lines removed from end of file. return (1, "FIXW391", [], 0) - + def __fixW603(self, code, line, pos): """ Private method to fix the not equal notation. - + Codes: W603 - + @param code code of the issue @type str @param line line number of the issue @@ -2036,10 +2096,11 @@ Released to the public domain, by Tim Peters, 03 October 2000. """ + def __init__(self, sourceLines): """ Constructor - + @param sourceLines list of source lines including eol marker (list of string) """ @@ -2050,8 +2111,7 @@ # File lines, rstripped & tab-expanded. Dummy at start is so # that we can use tokenize's 1-based line numbering easily. # Note that a line is all-blank iff it's "\n". - self.lines = [line.rstrip().expandtabs() + "\n" - for line in self.raw] + self.lines = [line.rstrip().expandtabs() + "\n" for line in self.raw] self.lines.insert(0, None) self.index = 1 # index into self.lines of next line @@ -2060,18 +2120,18 @@ # signal that tokenize doesn't know what to do about them; # indeed, they're our headache! self.stats = [] - + def run(self): """ Public method to run the re-indenter. - + @return flag indicating that a change was done (boolean) """ try: stats = self.__genStats(tokenize.generate_tokens(self.getline)) except (SyntaxError, tokenize.TokenError): return False - + # Remove trailing empty lines. lines = self.lines while lines and lines[-1] == "\n": @@ -2113,9 +2173,9 @@ jline, jlevel = stats[j] if jlevel >= 0: want = ( - have + - self.__getlspace(after[jline - 1]) - - self.__getlspace(lines[jline]) + have + + self.__getlspace(after[jline - 1]) + - self.__getlspace(lines[jline]) ) break if want < 0: @@ -2138,23 +2198,23 @@ remove = min(self.__getlspace(line), -diff) after.append(line[remove:]) return self.raw != self.after - + def fixedLine(self, line): """ Public method to get a fixed line. - + @param line number of the line to retrieve (integer) @return fixed line (string) """ if line < len(self.after): return self.after[line] - + return "" - + def getline(self): """ Public method to get a line of text for tokenize. - + @return line of text (string) """ if self.index >= len(self.lines): @@ -2167,7 +2227,7 @@ def __genStats(self, tokens): """ Private method to generate the re-indent statistics. - + @param tokens tokens generator (tokenize._tokenize) @return reference to the generated statistics """ @@ -2208,15 +2268,15 @@ # must be the first token of the next program statement, or an # ENDMARKER. find_stmt = False - if line: # not endmarker + if line: # not endmarker stats.append((sline, level)) - + return stats - + def __getlspace(self, line): """ Private method to count number of leading blanks. - + @param line line to check (string) @return number of leading blanks (integer) """ @@ -2233,22 +2293,29 @@ Each instance operates on a single logical line. """ - SKIP_TOKENS = frozenset([ - tokenize.COMMENT, tokenize.NL, tokenize.INDENT, - tokenize.DEDENT, tokenize.NEWLINE, tokenize.ENDMARKER - ]) + + SKIP_TOKENS = frozenset( + [ + tokenize.COMMENT, + tokenize.NL, + tokenize.INDENT, + tokenize.DEDENT, + tokenize.NEWLINE, + tokenize.ENDMARKER, + ] + ) def __init__(self, physical_lines): """ Constructor - + @param physical_lines list of physical lines to operate on (list of strings) """ self.lines = physical_lines self.tokens = [] self.rel_indent = None - sio = StringIO(''.join(physical_lines)) + sio = StringIO("".join(physical_lines)) for t in tokenize.generate_tokens(sio.readline): if not len(self.tokens) and t[0] in self.SKIP_TOKENS: continue @@ -2260,7 +2327,7 @@ def __buildTokensLogical(self, tokens): """ Private method to build a logical line from a list of tokens. - + @param tokens list of tokens as generated by tokenize.generate_tokens @return logical line (string) """ @@ -2276,15 +2343,16 @@ start_line, start = t[2] if end_line != start_line: # different row prev_text = self.lines[end_line - 1][end - 1] - if prev_text == ',' or (prev_text not in '{[(' and - text not in '}])'): - logical.append(' ') + if prev_text == "," or ( + prev_text not in "{[(" and text not in "}])" + ): + logical.append(" ") elif end != start: # different column fill = self.lines[end_line - 1][end:start] logical.append(fill) logical.append(text) previous = t - logical_line = ''.join(logical) + logical_line = "".join(logical) return logical_line def pep8Expected(self): @@ -2314,7 +2382,7 @@ # bug, really. return valid_indents - indent_next = self.logical_line.endswith(':') + indent_next = self.logical_line.endswith(":") row = depth = 0 parens = [0] * nrows @@ -2328,8 +2396,10 @@ newline = row < start[0] - first_row if newline: row = start[0] - first_row - newline = (not last_token_multiline and - token_type not in (tokenize.NL, tokenize.NEWLINE)) + newline = not last_token_multiline and token_type not in ( + tokenize.NL, + tokenize.NEWLINE, + ) if newline: # This is where the differences start. Instead of looking at @@ -2352,7 +2422,7 @@ # list vi = [] add_second_chances = False - if token_type == tokenize.OP and text in ']})': + if token_type == tokenize.OP and text in "]})": # this line starts with a closing bracket, so it needs to # be closed at the same indent as the opening one. if indent[depth]: @@ -2382,8 +2452,7 @@ vi.append(indent_level + hang) # about the best we can do without look-ahead - if (indent_next and vi[0] == indent_level + 4 and - nrows == row + 1): + if indent_next and vi[0] == indent_level + 4 and nrows == row + 1: vi[0] += 4 if add_second_chances: @@ -2391,9 +2460,9 @@ min_indent = vi[0] for col, what in indent_chances.items(): if col > min_indent and ( - what is True or - (what == str and token_type == tokenize.STRING) or - (what == text and token_type == tokenize.OP) + what is True + or (what == str and token_type == tokenize.STRING) + or (what == text and token_type == tokenize.OP) ): vi.append(col) vi = sorted(vi) @@ -2404,33 +2473,31 @@ # pycodestyle. visual_indent = indent_chances.get(start[1]) last_indent = start - rel_indent[row] = ( - pycodestyle.expand_indent(line) - indent_level - ) + rel_indent[row] = pycodestyle.expand_indent(line) - indent_level hang = rel_indent[row] - rel_indent[open_row] - if token_type == tokenize.OP and text in ']})': + if token_type == tokenize.OP and text in "]})": pass elif visual_indent is True and not indent[depth]: indent[depth] = start[1] # line altered: comments shouldn't define a visual indent - if parens[row] and not indent[depth] and token_type not in ( - tokenize.NL, tokenize.COMMENT + if ( + parens[row] + and not indent[depth] + and token_type not in (tokenize.NL, tokenize.COMMENT) ): indent[depth] = start[1] indent_chances[start[1]] = True - elif token_type == tokenize.STRING or text in ( - 'u', 'ur', 'b', 'br' - ): + elif token_type == tokenize.STRING or text in ("u", "ur", "b", "br"): indent_chances[start[1]] = str if token_type == tokenize.OP: - if text in '([{': + if text in "([{": depth += 1 indent.append(0) parens[row] += 1 - elif text in ')]}' and depth > 0: + elif text in ")]}" and depth > 0: prev_indent = indent.pop() or last_indent[1] for d in range(depth): if indent[d] > prev_indent: @@ -2448,7 +2515,7 @@ if start[1] not in indent_chances: indent_chances[start[1]] = text - last_token_multiline = (start[0] != end[0]) + last_token_multiline = start[0] != end[0] return valid_indents @@ -2457,11 +2524,20 @@ """ Class used to shorten lines to a given maximum of characters. """ - def __init__(self, curLine, prevLine, nextLine, maxLength=88, eol="\n", - indentWord=" ", isDocString=False): + + def __init__( + self, + curLine, + prevLine, + nextLine, + maxLength=88, + eol="\n", + indentWord=" ", + isDocString=False, + ): """ Constructor - + @param curLine text to work on (string) @param prevLine line before the text to work on (string) @param nextLine line after the text to work on (string) @@ -2478,18 +2554,18 @@ self.__eol = eol self.__indentWord = indentWord self.__isDocString = isDocString - + def shorten(self): """ Public method to shorten the line wrapped by the class instance. - + @return tuple of a flag indicating successful shortening, the shortened line and the changed next line (boolean, string, string) """ # 1. check for comment - if self.__text.lstrip().startswith('#'): + if self.__text.lstrip().startswith("#"): lastComment = True - if self.__nextText.lstrip().startswith('#'): + if self.__nextText.lstrip().startswith("#"): lastComment = False # Wrap commented lines. @@ -2498,13 +2574,13 @@ return False, "", "" else: return True, newText, "" - elif '#' in self.__text: + elif "#" in self.__text: pos = self.__text.rfind("#") newText = ( - self.__text[:pos].rstrip() + - self.__eol + - self.__getIndent(self.__text) + - self.__text[pos:] + self.__text[:pos].rstrip() + + self.__eol + + self.__getIndent(self.__text) + + self.__text[pos:] ) if newText == self.__text: return False, "", "" @@ -2528,36 +2604,31 @@ # eric doc comment # create a new line and indent it newText = ( - first + - self.__eol + - self.__getIndent(first) + - self.__indentWord + - second + first + + self.__eol + + self.__getIndent(first) + + self.__indentWord + + second ) newNext = "" else: newText = first + self.__eol newNext = ( - self.__getIndent(self.__nextText) + - second.rstrip() + - " " + - self.__nextText.lstrip() + self.__getIndent(self.__nextText) + + second.rstrip() + + " " + + self.__nextText.lstrip() ) else: # empty line, add a new line - newText = ( - first + - self.__eol + - self.__getIndent(first) + - second - ) + newText = first + self.__eol + self.__getIndent(first) + second newNext = "" return True, newText, newNext - + indent = self.__getIndent(self.__text) - source = self.__text[len(indent):] + source = self.__text[len(indent) :] sio = StringIO(source) - + # Check for multi line string. try: tokens = list(tokenize.generate_tokens(sio.readline)) @@ -2566,10 +2637,10 @@ # just join the continuation line and let the next run # handle it once it tokenizes ok newText = ( - indent + - source.rstrip()[:-1].rstrip() + - " " + - self.__nextText.lstrip() + indent + + source.rstrip()[:-1].rstrip() + + " " + + self.__nextText.lstrip() ) if indent: newNext = indent @@ -2585,25 +2656,30 @@ # Handle statements by putting the right hand side on a line by itself. # This should let the next pass shorten it. - if source.startswith('return '): + if source.startswith("return "): newText = ( - indent + - 'return (' + - self.__eol + - indent + self.__indentWord + re.sub('^return ', '', source) + - indent + ')' + self.__eol + indent + + "return (" + + self.__eol + + indent + + self.__indentWord + + re.sub("^return ", "", source) + + indent + + ")" + + self.__eol ) return True, newText, "" - + candidates = self.__shortenLine(tokens, source, indent) if candidates: candidates = sorted( set(candidates).union([self.__text]), - key=lambda x: self.__lineShorteningRank(x)) + key=lambda x: self.__lineShorteningRank(x), + ) if candidates[0] == self.__text: return False, "", "" return True, candidates[0], "" - + source = self.__text rs = source.rstrip() if rs.endswith(("'", '"')) and " " in source: @@ -2616,91 +2692,103 @@ while blank > maxLen and blank != -1: blank = source.rfind(" ", 0, blank) if blank != -1: - if source[blank + 1:].startswith(quote): + if source[blank + 1 :].startswith(quote): first = source[:maxLen] second = source[maxLen:] else: first = source[:blank] - second = source[blank + 1:] + second = source[blank + 1 :] return ( True, - first + quote + " \\" + self.__eol + - indent + self.__indentWord + quote + second, - "") + first + + quote + + " \\" + + self.__eol + + indent + + self.__indentWord + + quote + + second, + "", + ) else: # Cannot break return False, "", "" - + return False, "", "" - + def __shortenComment(self, isLast): """ Private method to shorten a comment line. - + @param isLast flag indicating, that the line is the last comment line (boolean) @return shortened comment line (string) """ if len(self.__text) <= self.__maxLength: return self.__text - + newText = self.__text.rstrip() # PEP 8 recommends 72 characters for comment text. - indentation = self.__getIndent(newText) + '# ' - maxLength = min(self.__maxLength, - len(indentation) + 72) + indentation = self.__getIndent(newText) + "# " + maxLength = min(self.__maxLength, len(indentation) + 72) MIN_CHARACTER_REPEAT = 5 if ( - len(newText) - len(newText.rstrip(newText[-1])) >= - MIN_CHARACTER_REPEAT and - not newText[-1].isalnum() + len(newText) - len(newText.rstrip(newText[-1])) >= MIN_CHARACTER_REPEAT + and not newText[-1].isalnum() ): # Trim comments that end with things like --------- return newText[:maxLength] + self.__eol elif isLast and re.match(r"\s*#+\s*\w+", newText): import textwrap - splitLines = textwrap.wrap(newText.lstrip(" \t#"), - initial_indent=indentation, - subsequent_indent=indentation, - width=maxLength, - break_long_words=False, - break_on_hyphens=False) + + splitLines = textwrap.wrap( + newText.lstrip(" \t#"), + initial_indent=indentation, + subsequent_indent=indentation, + width=maxLength, + break_long_words=False, + break_on_hyphens=False, + ) return self.__eol.join(splitLines) + self.__eol else: return newText + self.__eol - + def __breakMultiline(self): """ Private method to break multi line strings. - + @return tuple of the shortened line and the changed next line (string, string) """ indentation = self.__getIndent(self.__text) # Handle special case. - for symbol in '([{': + for symbol in "([{": # Only valid if symbol is not on a line by itself. if ( - symbol in self.__text and - self.__text.strip() != symbol and - self.__text.rstrip().endswith((',', '%')) + symbol in self.__text + and self.__text.strip() != symbol + and self.__text.rstrip().endswith((",", "%")) ): index = 1 + self.__text.find(symbol) if index <= len(self.__indentWord) + len(indentation): continue - if self.__isProbablyInsideStringOrComment( - self.__text, index - 1): + if self.__isProbablyInsideStringOrComment(self.__text, index - 1): continue - return (self.__text[:index].rstrip() + self.__eol + - indentation + self.__indentWord + - self.__text[index:].lstrip(), "") - + return ( + self.__text[:index].rstrip() + + self.__eol + + indentation + + self.__indentWord + + self.__text[index:].lstrip(), + "", + ) + newText = self.__text newNext = self.__nextText blank = newText.rfind(" ") @@ -2717,29 +2805,23 @@ newNext = "" else: newNext = ( - self.__getIndent(newNext) + - second + - " " + - newNext.lstrip() + self.__getIndent(newNext) + second + " " + newNext.lstrip() ) else: # empty line, add a new line newText = first + self.__eol newNext = ( - self.__getIndent(newNext) + - second + - self.__eol + - newNext.lstrip() + self.__getIndent(newNext) + second + self.__eol + newNext.lstrip() ) return newText, newNext else: return None - + def __isProbablyInsideStringOrComment(self, line, index): """ Private method to check, if the given string might be inside a string or comment. - + @param line line to check (string) @param index position inside line to check (integer) @return flag indicating the possibility of being inside a string @@ -2752,16 +2834,16 @@ return True # Check against being in a comment. - pos = line.find('#') + pos = line.find("#") if pos != -1 and pos <= index: return True return False - + def __shortenLine(self, tokens, source, indent): """ Private method to shorten a line of code at an operator. - + @param tokens tokens of the line as generated by tokenize (list of token) @param source code string to work at (string) @@ -2769,85 +2851,89 @@ @return list of candidates (list of string) """ candidates = [] - + for tkn in tokens: tokenType = tkn[0] tokenString = tkn[1] - if ( - tokenType == tokenize.COMMENT and - not self.__prevText.rstrip().endswith('\\') + if tokenType == tokenize.COMMENT and not self.__prevText.rstrip().endswith( + "\\" ): # Move inline comments to previous line. offset = tkn[2][1] first = source[:offset] second = source[offset:] candidates.append( - indent + second.strip() + self.__eol + - indent + first.strip() + self.__eol) - elif tokenType == tokenize.OP and tokenString != '=': + indent + + second.strip() + + self.__eol + + indent + + first.strip() + + self.__eol + ) + elif tokenType == tokenize.OP and tokenString != "=": # Don't break on '=' after keyword as this violates PEP 8. offset = tkn[2][1] + 1 first = source[:offset] secondIndent = indent - if first.rstrip().endswith('('): + if first.rstrip().endswith("("): secondIndent += self.__indentWord - elif '(' in first: - secondIndent += ' ' * (1 + first.find('(')) + elif "(" in first: + secondIndent += " " * (1 + first.find("(")) else: secondIndent += self.__indentWord - second = (secondIndent + source[offset:].lstrip()) + second = secondIndent + source[offset:].lstrip() if not second.strip(): continue # Do not begin a line with a comma - if second.lstrip().startswith(','): + if second.lstrip().startswith(","): continue - + # Do end a line with a dot - if first.rstrip().endswith('.'): + if first.rstrip().endswith("."): continue - - if tokenString in '+-*/,': - newText = first + ' \\' + self.__eol + second + + if tokenString in "+-*/,": + newText = first + " \\" + self.__eol + second else: newText = first + self.__eol + second # Only fix if syntax is okay. if self.__checkSyntax(self.__normalizeMultiline(newText)): candidates.append(indent + newText) - + return candidates - + def __normalizeMultiline(self, text): """ Private method to remove multiline-related code that will cause syntax error. - + @param text code line to work on (string) @return normalized code line (string) """ - for quote in '\'"': + for quote in "'\"": dictPattern = r"^{q}[^{q}]*{q} *: *".format(q=quote) if re.match(dictPattern, text): - if not text.strip().endswith('}'): - text += '}' - return '{' + text - - if text.startswith('def ') and text.rstrip().endswith(':'): + if not text.strip().endswith("}"): + text += "}" + return "{" + text + + if text.startswith("def ") and text.rstrip().endswith(":"): # Do not allow ':' to be alone. That is invalid. splitText = [item.strip() for item in text.split(self.__eol)] - if ':' not in splitText and 'def' not in splitText: - return text[len('def'):].strip().rstrip(':') + if ":" not in splitText and "def" not in splitText: + return text[len("def") :].strip().rstrip(":") return text - + def __lineShorteningRank(self, candidate): """ Private method to rank a candidate. - + @param candidate candidate line to rank (string) @return rank of the candidate (integer) """ @@ -2856,27 +2942,24 @@ if candidate == self.__text: # give the original a disadvantage rank += 50 - + lines = candidate.split(self.__eol) offset = 0 - if lines[0].rstrip()[-1] not in '([{': - for symbol in '([{': + if lines[0].rstrip()[-1] not in "([{": + for symbol in "([{": offset = max(offset, 1 + lines[0].find(symbol)) maxLength = max(offset + len(x.strip()) for x in lines) rank += maxLength rank += len(lines) - badStartingSymbol = { - '(': ')', - '[': ']', - '{': '}'}.get(lines[0][-1], None) + badStartingSymbol = {"(": ")", "[": "]", "{": "}"}.get(lines[0][-1], None) if ( - len(lines) > 1 and - badStartingSymbol and - lines[1].lstrip().startswith(badStartingSymbol) + len(lines) > 1 + and badStartingSymbol + and lines[1].lstrip().startswith(badStartingSymbol) ): rank += 20 @@ -2885,65 +2968,64 @@ rank += 100 for currentLine in lines: - for badStart in ['.', '%', '+', '-', '/']: + for badStart in [".", "%", "+", "-", "/"]: if currentLine.startswith(badStart): rank += 100 - for ending in '([{': + for ending in "([{": # Avoid lonely opening. They result in longer lines. - if ( - currentLine.endswith(ending) and - len(currentLine.strip()) <= len(self.__indentWord) + if currentLine.endswith(ending) and len(currentLine.strip()) <= len( + self.__indentWord ): rank += 100 - if currentLine.endswith('%'): + if currentLine.endswith("%"): rank -= 20 # Try to break list comprehensions at the "for". - if currentLine.lstrip().startswith('for'): + if currentLine.lstrip().startswith("for"): rank -= 50 rank += 10 * self.__countUnbalancedBrackets(currentLine) else: rank = 100000 - + return max(0, rank) - + def __countUnbalancedBrackets(self, line): """ Private method to determine the number of unmatched open/close brackets. - + @param line line to work at (string) @return number of unmatched open/close brackets (integer) """ count = 0 - for opening, closing in ['()', '[]', '{}']: + for opening, closing in ["()", "[]", "{}"]: # __IGNORE_WARNING_M613__ count += abs(line.count(opening) - line.count(closing)) - + return count - + def __getIndent(self, line): """ Private method to get the indentation string. - + @param line line to determine the indentation string from (string) @return indentation string (string) """ # copied from CodeStyleFixer return line.replace(line.lstrip(), "") - + def __checkSyntax(self, code): """ Private method to check the syntax of the given code fragment. - + @param code code fragment to check (string) @return flag indicating syntax is ok (boolean) """ code = code.replace("\r\n", "\n").replace("\r", "\n") try: - return compile(code, '<string>', 'exec') + return compile(code, "<string>", "exec") except (SyntaxError, TypeError, UnicodeDecodeError): return False