45 self.__origName = "" |
51 self.__origName = "" |
46 self.__source = sourceLines[:] # save a copy |
52 self.__source = sourceLines[:] # save a copy |
47 self.__fixCodes = [c.strip() for c in fixCodes.split(",") if c.strip()] |
53 self.__fixCodes = [c.strip() for c in fixCodes.split(",") if c.strip()] |
48 self.fixed = 0 |
54 self.fixed = 0 |
49 |
55 |
|
56 self.__reindenter = None |
|
57 self.__eol = "" |
|
58 self.__indentWord = self.__getIndentWord() |
|
59 |
50 if not inPlace: |
60 if not inPlace: |
51 self.__origName = self.__filename |
61 self.__origName = self.__filename |
52 self.__filename = os.path.join(os.path.dirname(self.__filename), |
62 self.__filename = os.path.join(os.path.dirname(self.__filename), |
53 "fixed_" + os.path.basename(self.__filename)) |
63 "fixed_" + os.path.basename(self.__filename)) |
54 |
64 |
55 self.__fixes = { |
65 self.__fixes = { |
56 "E101": self.__fixTabs, |
66 "E101": self.__fixE101, |
57 "W191": self.__fixTabs, |
67 "E111": self.__fixE101, |
58 "E201": self.__fixWhitespaceAfter, |
68 "W191": self.__fixE101, |
59 "E202": self.__fixWhitespaceBefore, |
69 "E201": self.__fixE201, |
60 "E203": self.__fixWhitespaceBefore, |
70 "E202": self.__fixE201, |
61 "E211": self.__fixWhitespaceBefore, |
71 "E203": self.__fixE201, |
62 "E221": self.__fixWhitespaceAroundOperator, |
72 "E211": self.__fixE201, |
63 "E222": self.__fixWhitespaceAroundOperator, |
73 "E221": self.__fixE221, |
64 "E225": self.__fixMissingWhitespaceAroundOperator, |
74 "E222": self.__fixE221, |
65 "E231": self.__fixMissingWhitespaceAfter, |
75 "E223": self.__fixE221, |
66 "E241": self.__fixWhitespaceAroundOperator, |
76 "E224": self.__fixE221, |
67 "E251": self.__fixWhitespaceAroundEquals, |
77 "E225": self.__fixE221, |
68 "E261": self.__fixWhitespaceBeforeInline, |
78 "E226": self.__fixE221, |
69 "E262": self.__fixWhitespaceAfterInline, |
79 "E227": self.__fixE221, |
70 "W291": self.__fixWhitespace, |
80 "E228": self.__fixE221, |
71 "W292": self.__fixNewline, |
81 "E231": self.__fixE231, |
72 "W293": self.__fixWhitespace, |
82 "E241": self.__fixE221, |
73 "E301": self.__fixOneBlankLine, |
83 "E242": self.__fixE221, |
74 "E302": self.__fixTwoBlankLines, |
84 "E251": self.__fixE251, |
75 "E303": self.__fixTooManyBlankLines, |
85 "E261": self.__fixE261, |
76 "E304": self.__fixBlankLinesAfterDecorator, |
86 "E262": self.__fixE261, |
77 "W391": self.__fixTrailingBlankLines, |
87 "E271": self.__fixE221, |
78 "W603": self.__fixNotEqual, |
88 "E272": self.__fixE221, |
|
89 "E273": self.__fixE221, |
|
90 "E274": self.__fixE221, |
|
91 "W291": self.__fixW291, |
|
92 "W292": self.__fixW292, |
|
93 "W293": self.__fixW291, |
|
94 "E301": self.__fixE301, |
|
95 "E302": self.__fixE302, |
|
96 "E303": self.__fixE303, |
|
97 "E304": self.__fixE304, |
|
98 "W391": self.__fixW391, |
|
99 "E401": self.__fixE401, |
|
100 "E502": self.__fixE502, |
|
101 "W603": self.__fixW603, |
|
102 "E701": self.__fixE701, |
|
103 "E702": self.__fixE702, |
|
104 "E703": self.__fixE702, |
|
105 "E711": self.__fixE711, |
|
106 "E712": self.__fixE711, |
79 } |
107 } |
80 self.__modified = False |
108 self.__modified = False |
81 self.__stack = [] # these need to be fixed before the file is saved |
109 self.__stack = [] # these need to be fixed before the file is saved |
82 # but after all inline fixes |
110 # but after all inline fixes |
83 |
111 |
145 """ |
173 """ |
146 Private method to get the applicable eol string. |
174 Private method to get the applicable eol string. |
147 |
175 |
148 @return eol string (string) |
176 @return eol string (string) |
149 """ |
177 """ |
150 if self.__origName: |
178 if not self.__eol: |
151 fn = self.__origName |
179 if self.__origName: |
152 else: |
180 fn = self.__origName |
153 fn = self.__filename |
|
154 |
|
155 if self.__project.isOpen() and self.__project.isProjectFile(fn): |
|
156 eol = self.__project.getEolString() |
|
157 else: |
|
158 eol = Utilities.linesep() |
|
159 return eol |
|
160 |
|
161 def __fixTabs(self, code, line, pos): |
|
162 """ |
|
163 Private method to fix obsolete tab usage. |
|
164 |
|
165 @param code code of the issue (string) |
|
166 @param line line number of the issue (integer) |
|
167 @param pos position inside line (integer) |
|
168 @return flag indicating an applied fix (boolean) and a message for |
|
169 the fix (string) |
|
170 """ |
|
171 self.__source[line - 1] = self.__source[line - 1].replace("\t", " ") |
|
172 return (True, self.trUtf8("Tab converted to 4 spaces.")) |
|
173 |
|
174 def __fixWhitespace(self, code, line, pos): |
|
175 """ |
|
176 Private method to fix trailing whitespace. |
|
177 |
|
178 @param code code of the issue (string) |
|
179 @param line line number of the issue (integer) |
|
180 @param pos position inside line (integer) |
|
181 @return flag indicating an applied fix (boolean) and a message for |
|
182 the fix (string) |
|
183 """ |
|
184 self.__source[line - 1] = re.sub(r'[\t ]+(\r?)$', r"\1", |
|
185 self.__source[line - 1]) |
|
186 return (True, self.trUtf8("Whitespace stripped from end of line.")) |
|
187 |
|
188 def __fixNewline(self, code, line, pos): |
|
189 """ |
|
190 Private method to fix a missing newline at the end of file. |
|
191 |
|
192 @param code code of the issue (string) |
|
193 @param line line number of the issue (integer) |
|
194 @param pos position inside line (integer) |
|
195 @return flag indicating an applied fix (boolean) and a message for |
|
196 the fix (string) |
|
197 """ |
|
198 self.__source[line - 1] += self.__getEol() |
|
199 return (True, self.trUtf8("newline added to end of file.")) |
|
200 |
|
201 def __fixTrailingBlankLines(self, code, line, pos): |
|
202 """ |
|
203 Private method to fix trailing blank lines. |
|
204 |
|
205 @param code code of the issue (string) |
|
206 @param line line number of the issue (integer) |
|
207 @param pos position inside line (integer) |
|
208 @return flag indicating an applied fix (boolean) and a message for |
|
209 the fix (string) |
|
210 """ |
|
211 index = line - 1 |
|
212 while index: |
|
213 if self.__source[index].strip() == "": |
|
214 del self.__source[index] |
|
215 index -= 1 |
|
216 else: |
181 else: |
217 break |
182 fn = self.__filename |
218 return (True, self.trUtf8( |
183 |
219 "Superfluous trailing blank lines removed from end of file.")) |
184 if self.__project.isOpen() and self.__project.isProjectFile(fn): |
220 |
185 self.__eol = self.__project.getEolString() |
221 def __fixNotEqual(self, code, line, pos): |
186 else: |
222 """ |
187 self.__eol = Utilities.linesep() |
223 Private method to fix the not equal notation. |
188 return self.__eol |
224 |
189 |
225 @param code code of the issue (string) |
190 def __getIndentWord(self): |
226 @param line line number of the issue (integer) |
191 """ |
227 @param pos position inside line (integer) |
192 Private method to determine the indentation type. |
228 @return flag indicating an applied fix (boolean) and a message for |
193 |
229 the fix (string) |
194 @return string to be used for an indentation (string) |
230 """ |
195 """ |
231 self.__source[line - 1] = self.__source[line - 1].replace("<>", "!=") |
196 sio = io.StringIO("".join(self.__source)) |
232 return (True, self.trUtf8("'<>' replaced by '!='.")) |
197 indentWord = " " # default in case of failure |
233 |
198 try: |
234 def __fixBlankLinesAfterDecorator(self, code, line, pos, apply=False): |
199 for token in tokenize.generate_tokens(sio.readline): |
235 """ |
200 if token[0] == tokenize.INDENT: |
236 Private method to fix superfluous blank lines after a function |
201 indentWord = token[1] |
237 decorator. |
202 break |
|
203 except (SyntaxError, tokenize.TokenError): |
|
204 pass |
|
205 return indentWord |
|
206 |
|
207 def __getIndent(self, line): |
|
208 """ |
|
209 Private method to get the indentation string. |
|
210 |
|
211 @param line line to determine the indentation string from (string) |
|
212 @return indentation string (string) |
|
213 """ |
|
214 return line.replace(line.lstrip(), "") |
|
215 |
|
216 def __fixWhitespace(self, line, offset, replacement): |
|
217 """ |
|
218 Private method to correct whitespace at the given offset. |
|
219 |
|
220 @param line line to be corrected (string) |
|
221 @param offset offset within line (integer) |
|
222 @param replacement replacement string (string) |
|
223 @return corrected line |
|
224 """ |
|
225 left = line[:offset].rstrip(" \t") |
|
226 right = line[offset:].lstrip(" \t") |
|
227 if right.startswith("#"): |
|
228 return line |
|
229 else: |
|
230 return left + replacement + right |
|
231 |
|
232 def __fixE101(self, code, line, pos): |
|
233 """ |
|
234 Private method to fix obsolete tab usage and indentation errors |
|
235 (E101, E111, W191). |
|
236 |
|
237 @param code code of the issue (string) |
|
238 @param line line number of the issue (integer) |
|
239 @param pos position inside line (integer) |
|
240 @return flag indicating an applied fix (boolean) and a message for |
|
241 the fix (string) |
|
242 """ |
|
243 if self.__reindenter is None: |
|
244 self.__reindenter = Pep8Reindenter(self.__source) |
|
245 self.__reindenter.run() |
|
246 fixedLine = self.__reindenter.fixedLine(line - 1) |
|
247 if fixedLine is not None: |
|
248 self.__source[line - 1] = fixedLine |
|
249 if code in ["E101", "W191"]: |
|
250 msg = self.trUtf8("Tab converted to 4 spaces.") |
|
251 else: |
|
252 msg = self.trUtf8("Indentation adjusted to be a multiple of four.") |
|
253 return (True, msg) |
|
254 else: |
|
255 return (False, self.trUtf8("Fix for {0} failed.").format(code)) |
|
256 |
|
257 def __fixE201(self, code, line, pos): |
|
258 """ |
|
259 Private method to fix extraneous whitespace (E201, E202, |
|
260 E203, E211). |
|
261 |
|
262 @param code code of the issue (string) |
|
263 @param line line number of the issue (integer) |
|
264 @param pos position inside line (integer) |
|
265 @return flag indicating an applied fix (boolean) and a message for |
|
266 the fix (string) |
|
267 """ |
|
268 line = line - 1 |
|
269 text = self.__source[line] |
|
270 |
|
271 if '"""' in text or "'''" in text or text.rstrip().endswith('\\'): |
|
272 return (False, self.trUtf8("Extraneous whitespace cannot be removed.")) |
|
273 |
|
274 newText = self.__fixWhitespace(text, pos, '') |
|
275 if newText == text: |
|
276 return (False, "") |
|
277 |
|
278 self.__source[line] = newText |
|
279 return (True, self.trUtf8("Extraneous whitespace removed.")) |
|
280 |
|
281 def __fixE221(self, code, line, pos): |
|
282 """ |
|
283 Private method to fix extraneous whitespace around operator or |
|
284 keyword (E221, E222, E223, E224, E225, E226, E227, E228, E241, |
|
285 E242, E271, E272, E273, E274). |
|
286 |
|
287 @param code code of the issue (string) |
|
288 @param line line number of the issue (integer) |
|
289 @param pos position inside line (integer) |
|
290 @return flag indicating an applied fix (boolean) and a message for |
|
291 the fix (string) |
|
292 """ |
|
293 line = line - 1 |
|
294 text = self.__source[line] |
|
295 |
|
296 if '"""' in text or "'''" in text or text.rstrip().endswith('\\'): |
|
297 return (False, self.trUtf8("Extraneous whitespace cannot be removed.")) |
|
298 |
|
299 newText = self.__fixWhitespace(text, pos, ' ') |
|
300 if newText == text: |
|
301 return (False, "") |
|
302 |
|
303 self.__source[line] = newText |
|
304 if code in ["E225", "E226", "E227", "E228"]: |
|
305 return (True, self.trUtf8("Missing whitespace added.")) |
|
306 else: |
|
307 return (True, self.trUtf8("Extraneous whitespace removed.")) |
|
308 |
|
309 def __fixE231(self, code, line, pos): |
|
310 """ |
|
311 Private method to fix missing whitespace after ',;:'. |
|
312 |
|
313 @param code code of the issue (string) |
|
314 @param line line number of the issue (integer) |
|
315 @param pos position inside line (integer) |
|
316 @return flag indicating an applied fix (boolean) and a message for |
|
317 the fix (string) |
|
318 """ |
|
319 line = line - 1 |
|
320 pos = pos + 1 |
|
321 self.__source[line] = self.__source[line][:pos] + \ |
|
322 " " + \ |
|
323 self.__source[line][pos:] |
|
324 return (True, self.trUtf8("Missing whitespace added.")) |
|
325 |
|
326 def __fixE251(self, code, line, pos): |
|
327 """ |
|
328 Private method to fix extraneous whitespace around keyword and |
|
329 default parameter equals (E251). |
|
330 |
|
331 @param code code of the issue (string) |
|
332 @param line line number of the issue (integer) |
|
333 @param pos position inside line (integer) |
|
334 @return flag indicating an applied fix (boolean) and a message for |
|
335 the fix (string) |
|
336 """ |
|
337 line = line - 1 |
|
338 text = self.__source[line] |
|
339 |
|
340 # This is necessary since pep8 sometimes reports columns that goes |
|
341 # past the end of the physical line. This happens in cases like, |
|
342 # foo(bar\n=None) |
|
343 col = min(pos, len(text) - 1) |
|
344 if text[col].strip(): |
|
345 newText = text |
|
346 else: |
|
347 newText = text[:col].rstrip() + text[col:].lstrip() |
|
348 |
|
349 # There could be an escaped newline |
|
350 # |
|
351 # def foo(a=\ |
|
352 # 1) |
|
353 if newText.endswith(('=\\\n', '=\\\r\n', '=\\\r')): |
|
354 self.__source[line] = newText.rstrip("\n\r \t\\") |
|
355 self.__source[line + 1] = self.__source[line + 1].lstrip() |
|
356 else: |
|
357 self.__source[line] = newText |
|
358 return (True, self.trUtf8("Extraneous whitespace removed.")) |
|
359 |
|
360 def __fixE261(self, code, line, pos): |
|
361 """ |
|
362 Private method to fix whitespace before or after inline comment |
|
363 (E261, E262). |
|
364 |
|
365 @param code code of the issue (string) |
|
366 @param line line number of the issue (integer) |
|
367 @param pos position inside line (integer) |
|
368 @return flag indicating an applied fix (boolean) and a message for |
|
369 the fix (string) |
|
370 """ |
|
371 line = line - 1 |
|
372 text = self.__source[line] |
|
373 left = text[:pos].rstrip(' \t#') |
|
374 right = text[pos:].lstrip(' \t#') |
|
375 newText = left + (" # " + right if right.strip() else right) |
|
376 self.__source[line] = newText |
|
377 return (True, self.trUtf8("Whitespace around comment sign corrected.")) |
|
378 |
|
379 def __fixE301(self, code, line, pos, apply=False): |
|
380 """ |
|
381 Private method to fix the need for one blank line (E301). |
238 |
382 |
239 @param code code of the issue (string) |
383 @param code code of the issue (string) |
240 @param line line number of the issue (integer) |
384 @param line line number of the issue (integer) |
241 @param pos position inside line (integer) |
385 @param pos position inside line (integer) |
242 @keyparam apply flag indicating, that the fix should be applied |
386 @keyparam apply flag indicating, that the fix should be applied |
243 (boolean) |
387 (boolean) |
244 @return flag indicating an applied fix (boolean) and a message for |
388 @return flag indicating an applied fix (boolean) and a message for |
245 the fix (string) |
389 the fix (string) |
246 """ |
390 """ |
247 if apply: |
391 if apply: |
248 index = line - 2 |
|
249 while index: |
|
250 if self.__source[index].strip() == "": |
|
251 del self.__source[index] |
|
252 index -= 1 |
|
253 else: |
|
254 break |
|
255 else: |
|
256 self.__stack.append((code, line, pos)) |
|
257 return (True, self.trUtf8( |
|
258 "Superfluous blank lines after function decorator removed.")) |
|
259 |
|
260 def __fixTooManyBlankLines(self, code, line, pos, apply=False): |
|
261 """ |
|
262 Private method to fix superfluous blank lines. |
|
263 |
|
264 @param code code of the issue (string) |
|
265 @param line line number of the issue (integer) |
|
266 @param pos position inside line (integer) |
|
267 @keyparam apply flag indicating, that the fix should be applied |
|
268 (boolean) |
|
269 @return flag indicating an applied fix (boolean) and a message for |
|
270 the fix (string) |
|
271 """ |
|
272 if apply: |
|
273 index = line - 3 |
|
274 while index: |
|
275 if self.__source[index].strip() == "": |
|
276 del self.__source[index] |
|
277 index -= 1 |
|
278 else: |
|
279 break |
|
280 else: |
|
281 self.__stack.append((code, line, pos)) |
|
282 return (True, self.trUtf8("Superfluous blank lines removed.")) |
|
283 |
|
284 def __fixOneBlankLine(self, code, line, pos, apply=False): |
|
285 """ |
|
286 Private method to fix the need for one blank line. |
|
287 |
|
288 @param code code of the issue (string) |
|
289 @param line line number of the issue (integer) |
|
290 @param pos position inside line (integer) |
|
291 @keyparam apply flag indicating, that the fix should be applied |
|
292 (boolean) |
|
293 @return flag indicating an applied fix (boolean) and a message for |
|
294 the fix (string) |
|
295 """ |
|
296 if apply: |
|
297 self.__source.insert(line - 1, self.__getEol()) |
392 self.__source.insert(line - 1, self.__getEol()) |
298 else: |
393 else: |
299 self.__stack.append((code, line, pos)) |
394 self.__stack.append((code, line, pos)) |
300 return (True, self.trUtf8("One blank line inserted.")) |
395 return (True, self.trUtf8("One blank line inserted.")) |
301 |
396 |
302 def __fixTwoBlankLines(self, code, line, pos, apply=False): |
397 def __fixE302(self, code, line, pos, apply=False): |
303 """ |
398 """ |
304 Private method to fix the need for two blank lines. |
399 Private method to fix the need for two blank lines (E302). |
305 """ |
400 """ |
306 # count blank lines |
401 # count blank lines |
307 index = line - 1 |
402 index = line - 1 |
308 blanks = 0 |
403 blanks = 0 |
309 while index: |
404 while index: |
336 msg = self.trUtf8("%n superfluous lines removed", "", delta) |
431 msg = self.trUtf8("%n superfluous lines removed", "", delta) |
337 else: |
432 else: |
338 msg = "" |
433 msg = "" |
339 return (True, msg) |
434 return (True, msg) |
340 |
435 |
341 def __fixWhitespaceAfter(self, code, line, pos, apply=False): |
436 def __fixE303(self, code, line, pos, apply=False): |
342 """ |
437 """ |
343 Private method to fix superfluous whitespace after '([{'. |
438 Private method to fix superfluous blank lines (E303). |
344 |
439 |
345 @param code code of the issue (string) |
440 @param code code of the issue (string) |
346 @param line line number of the issue (integer) |
441 @param line line number of the issue (integer) |
347 @param pos position inside line (integer) |
442 @param pos position inside line (integer) |
348 @keyparam apply flag indicating, that the fix should be applied |
443 @keyparam apply flag indicating, that the fix should be applied |
349 (boolean) |
444 (boolean) |
350 @return flag indicating an applied fix (boolean) and a message for |
445 @return flag indicating an applied fix (boolean) and a message for |
351 the fix (string) |
446 the fix (string) |
352 """ |
447 """ |
353 line = line - 1 |
448 if apply: |
354 pos = pos - 1 |
449 index = line - 3 |
355 while self.__source[line][pos] in [" ", "\t"]: |
450 while index: |
356 self.__source[line] = self.__source[line][:pos] + \ |
451 if self.__source[index].strip() == "": |
357 self.__source[line][pos + 1:] |
452 del self.__source[index] |
358 return (True, self.trUtf8("Superfluous whitespace removed.")) |
453 index -= 1 |
359 |
454 else: |
360 def __fixWhitespaceBefore(self, code, line, pos, apply=False): |
455 break |
361 """ |
456 else: |
362 Private method to fix superfluous whitespace before '}])', |
457 self.__stack.append((code, line, pos)) |
363 ',;:' and '(['. |
458 return (True, self.trUtf8("Superfluous blank lines removed.")) |
|
459 |
|
460 def __fixE304(self, code, line, pos, apply=False): |
|
461 """ |
|
462 Private method to fix superfluous blank lines after a function |
|
463 decorator (E304). |
364 |
464 |
365 @param code code of the issue (string) |
465 @param code code of the issue (string) |
366 @param line line number of the issue (integer) |
466 @param line line number of the issue (integer) |
367 @param pos position inside line (integer) |
467 @param pos position inside line (integer) |
368 @keyparam apply flag indicating, that the fix should be applied |
468 @keyparam apply flag indicating, that the fix should be applied |
369 (boolean) |
469 (boolean) |
370 @return flag indicating an applied fix (boolean) and a message for |
470 @return flag indicating an applied fix (boolean) and a message for |
371 the fix (string) |
471 the fix (string) |
372 """ |
472 """ |
373 line = line - 1 |
473 if apply: |
374 pos = pos - 1 |
474 index = line - 2 |
375 while self.__source[line][pos] in [" ", "\t"]: |
475 while index: |
376 self.__source[line] = self.__source[line][:pos] + \ |
476 if self.__source[index].strip() == "": |
377 self.__source[line][pos + 1:] |
477 del self.__source[index] |
378 pos -= 1 |
478 index -= 1 |
379 return (True, self.trUtf8("Superfluous whitespace removed.")) |
479 else: |
380 |
480 break |
381 def __fixMissingWhitespaceAfter(self, code, line, pos, apply=False): |
481 else: |
382 """ |
482 self.__stack.append((code, line, pos)) |
383 Private method to fix missing whitespace after ',;:'. |
483 return (True, self.trUtf8( |
|
484 "Superfluous blank lines after function decorator removed.")) |
|
485 |
|
486 def __fixE401(self, code, line, pos, apply=False): |
|
487 """ |
|
488 Private method to fix multiple imports on one line (E401). |
384 |
489 |
385 @param code code of the issue (string) |
490 @param code code of the issue (string) |
386 @param line line number of the issue (integer) |
491 @param line line number of the issue (integer) |
387 @param pos position inside line (integer) |
492 @param pos position inside line (integer) |
388 @keyparam apply flag indicating, that the fix should be applied |
493 @keyparam apply flag indicating, that the fix should be applied |
389 (boolean) |
494 (boolean) |
390 @return flag indicating an applied fix (boolean) and a message for |
495 @return flag indicating an applied fix (boolean) and a message for |
391 the fix (string) |
496 the fix (string) |
392 """ |
497 """ |
393 line = line - 1 |
498 if apply: |
394 self.__source[line] = self.__source[line][:pos] + \ |
499 line = line - 1 |
395 " " + \ |
500 text = self.__source[line] |
396 self.__source[line][pos:] |
501 if not text.lstrip().startswith("import"): |
397 return (True, self.trUtf8("Missing whitespace added.")) |
502 return (False, "") |
398 |
503 |
399 def __fixWhitespaceAroundOperator(self, code, line, pos, apply=False): |
504 # pep8 (1.3.1) reports false positive if there is an import |
400 """ |
505 # statement followed by a semicolon and some unrelated |
401 Private method to fix extraneous whitespace around operator. |
506 # statement with commas in it. |
|
507 if ';' in text: |
|
508 return (False, "") |
|
509 |
|
510 newText = text[:pos].rstrip("\t ,") + self.__getEol() + \ |
|
511 self.__getIndent(text) + "import " + text[pos:].lstrip("\t ,") |
|
512 self.__source[line] = newText |
|
513 else: |
|
514 self.__stack.append((code, line, pos)) |
|
515 return (True, self.trUtf8("Imports were put on separate lines.")) |
|
516 |
|
517 def __fixE502(self, code, line, pos): |
|
518 """ |
|
519 Private method to fix redundant backslash within brackets (E502). |
|
520 |
|
521 @param code code of the issue (string) |
|
522 @param line line number of the issue (integer) |
|
523 @param pos position inside line (integer) |
|
524 @return flag indicating an applied fix (boolean) and a message for |
|
525 the fix (string) |
|
526 """ |
|
527 self.__source[line - 1] = self.__source[line - 1].rstrip("\n\r \t\\") + \ |
|
528 self.__getEol() |
|
529 return (True, self.trUtf8("Redundant backslash in brackets removed.")) |
|
530 |
|
531 def __fixE701(self, code, line, pos, apply=False): |
|
532 """ |
|
533 Private method to fix colon-separated compund statements (E701). |
402 |
534 |
403 @param code code of the issue (string) |
535 @param code code of the issue (string) |
404 @param line line number of the issue (integer) |
536 @param line line number of the issue (integer) |
405 @param pos position inside line (integer) |
537 @param pos position inside line (integer) |
406 @keyparam apply flag indicating, that the fix should be applied |
538 @keyparam apply flag indicating, that the fix should be applied |
407 (boolean) |
539 (boolean) |
408 @return flag indicating an applied fix (boolean) and a message for |
540 @return flag indicating an applied fix (boolean) and a message for |
409 the fix (string) |
541 the fix (string) |
410 """ |
542 """ |
411 line = line - 1 |
543 if apply: |
412 while self.__source[line][pos - 1] in [" ", "\t"]: |
544 line = line - 1 |
413 self.__source[line] = self.__source[line][:pos - 1] + \ |
545 text = self.__source[line] |
414 self.__source[line][pos:] |
546 pos = pos + 1 |
415 pos -= 1 |
547 |
416 return (True, self.trUtf8("Extraneous whitespace removed.")) |
548 newText = text[:pos] + self.__getEol() + self.__getIndent(text) + \ |
417 |
549 self.__indentWord + text[pos:].lstrip("\n\r \t\\") + \ |
418 def __fixMissingWhitespaceAroundOperator(self, code, line, pos, |
550 self.__getEol() |
419 apply=False): |
551 self.__source[line] = newText |
420 """ |
552 else: |
421 Private method to fix missing whitespace after ',;:'. |
553 self.__stack.append((code, line, pos)) |
|
554 return (True, self.trUtf8("Compound statement corrected.")) |
|
555 |
|
556 def __fixE702(self, code, line, pos, apply=False): |
|
557 """ |
|
558 Private method to fix semicolon-separated compound statements |
|
559 (E702, E703). |
422 |
560 |
423 @param code code of the issue (string) |
561 @param code code of the issue (string) |
424 @param line line number of the issue (integer) |
562 @param line line number of the issue (integer) |
425 @param pos position inside line (integer) |
563 @param pos position inside line (integer) |
426 @keyparam apply flag indicating, that the fix should be applied |
564 @keyparam apply flag indicating, that the fix should be applied |
427 (boolean) |
565 (boolean) |
428 @return flag indicating an applied fix (boolean) and a message for |
566 @return flag indicating an applied fix (boolean) and a message for |
429 the fix (string) |
567 the fix (string) |
430 """ |
568 """ |
|
569 if apply: |
|
570 line = line - 1 |
|
571 text = self.__source[line] |
|
572 |
|
573 if text.rstrip().endswith("\\"): |
|
574 # normalize '1; \\\n2' into '1; 2' |
|
575 self.__source[line] = text.rstrip("\n\r \t\\") |
|
576 self.__source[line + 1] = self.__source[line + 1].lstrip() |
|
577 elif text.rstrip().endswith(";"): |
|
578 self.__source[line] = text.rstrip("\n\r \t;") + self.__getEol() |
|
579 else: |
|
580 first = text[:pos].rstrip("\n\r \t;") + self.__getEol() |
|
581 second = text[pos:].lstrip("\n\r \t;") |
|
582 self.__source[line] = first + self.__getIndent(text) + second |
|
583 else: |
|
584 self.__stack.append((code, line, pos)) |
|
585 return (True, self.trUtf8("Compound statement corrected.")) |
|
586 |
|
587 def __fixE711(self, code, line, pos): |
|
588 """ |
|
589 Private method to fix comparison with None (E711, E712). |
|
590 |
|
591 @param code code of the issue (string) |
|
592 @param line line number of the issue (integer) |
|
593 @param pos position inside line (integer) |
|
594 @return flag indicating an applied fix (boolean) and a message for |
|
595 the fix (string) |
|
596 """ |
431 line = line - 1 |
597 line = line - 1 |
432 pos = pos - 1 |
598 text = self.__source[line] |
433 self.__source[line] = self.__source[line][:pos] + \ |
599 |
434 " " + \ |
600 rightPos = pos + 2 |
435 self.__source[line][pos:] |
601 if rightPos >= len(text): |
436 return (True, self.trUtf8("Missing whitespace added.")) |
602 return (False, "") |
437 |
603 |
438 def __fixWhitespaceAroundEquals(self, code, line, pos, apply=False): |
604 left = text[:pos].rstrip() |
439 """ |
605 center = text[pos:rightPos] |
440 Private method to fix extraneous whitespace around keyword and |
606 right = text[rightPos:].lstrip() |
441 default parameter equals. |
607 |
442 |
608 if not right.startswith(("None", "True", "False")): |
443 @param code code of the issue (string) |
609 return (False, "") |
444 @param line line number of the issue (integer) |
610 |
445 @param pos position inside line (integer) |
611 if center.strip() == "==": |
446 @keyparam apply flag indicating, that the fix should be applied |
612 center = "is" |
447 (boolean) |
613 elif center.strip() == "!=": |
448 @return flag indicating an applied fix (boolean) and a message for |
614 center = "is not" |
449 the fix (string) |
615 else: |
450 """ |
616 return (False, "") |
451 line = line - 1 |
617 |
452 if self.__source[line][pos + 1] == " ": |
618 self.__source[line] = " ".join([left, center, right]) |
453 self.__source[line] = self.__source[line][:pos + 1] + \ |
619 return (True, self.trUtf8("Comparison to None/True/False corrected.")) |
454 self.__source[line][pos + 2:] |
620 |
455 if self.__source[line][pos - 1] == " ": |
621 def __fixW291(self, code, line, pos): |
456 self.__source[line] = self.__source[line][:pos - 1] + \ |
622 """ |
457 self.__source[line][pos:] |
623 Private method to fix trailing whitespace (W291, W293). |
458 return (True, self.trUtf8("Extraneous whitespace removed.")) |
624 |
459 |
625 @param code code of the issue (string) |
460 def __fixWhitespaceBeforeInline(self, code, line, pos, apply=False): |
626 @param line line number of the issue (integer) |
461 """ |
627 @param pos position inside line (integer) |
462 Private method to fix missing whitespace before inline comment. |
628 @return flag indicating an applied fix (boolean) and a message for |
463 |
629 the fix (string) |
464 @param code code of the issue (string) |
630 """ |
465 @param line line number of the issue (integer) |
631 self.__source[line - 1] = re.sub(r'[\t ]+(\r?)$', r"\1", |
466 @param pos position inside line (integer) |
632 self.__source[line - 1]) |
467 @keyparam apply flag indicating, that the fix should be applied |
633 return (True, self.trUtf8("Whitespace stripped from end of line.")) |
468 (boolean) |
634 |
469 @return flag indicating an applied fix (boolean) and a message for |
635 def __fixW292(self, code, line, pos): |
470 the fix (string) |
636 """ |
471 """ |
637 Private method to fix a missing newline at the end of file (W292). |
472 line = line - 1 |
638 |
473 pos = pos - 1 |
639 @param code code of the issue (string) |
474 if self.__source[line][pos] == " ": |
640 @param line line number of the issue (integer) |
475 count = 1 |
641 @param pos position inside line (integer) |
476 else: |
642 @return flag indicating an applied fix (boolean) and a message for |
477 count = 2 |
643 the fix (string) |
478 self.__source[line] = self.__source[line][:pos] + \ |
644 """ |
479 count * " " + \ |
645 self.__source[line - 1] += self.__getEol() |
480 self.__source[line][pos:] |
646 return (True, self.trUtf8("newline added to end of file.")) |
481 return (True, self.trUtf8("Missing whitespace added.")) |
647 |
482 |
648 def __fixW391(self, code, line, pos): |
483 def __fixWhitespaceAfterInline(self, code, line, pos, apply=False): |
649 """ |
484 """ |
650 Private method to fix trailing blank lines (W391). |
485 Private method to fix whitespace after inline comment. |
651 |
486 |
652 @param code code of the issue (string) |
487 @param code code of the issue (string) |
653 @param line line number of the issue (integer) |
488 @param line line number of the issue (integer) |
654 @param pos position inside line (integer) |
489 @param pos position inside line (integer) |
655 @return flag indicating an applied fix (boolean) and a message for |
490 @keyparam apply flag indicating, that the fix should be applied |
656 the fix (string) |
491 (boolean) |
657 """ |
492 @return flag indicating an applied fix (boolean) and a message for |
658 index = line - 1 |
493 the fix (string) |
659 while index: |
494 """ |
660 if self.__source[index].strip() == "": |
495 line = line - 1 |
661 del self.__source[index] |
496 if self.__source[line][pos] == " ": |
662 index -= 1 |
497 pos += 1 |
663 else: |
498 while self.__source[line][pos] == " ": |
664 break |
499 self.__source[line] = self.__source[line][:pos] + \ |
|
500 self.__source[line][pos + 1:] |
|
501 else: |
|
502 self.__source[line] = self.__source[line][:pos] + \ |
|
503 " " + \ |
|
504 self.__source[line][pos:] |
|
505 return (True, self.trUtf8( |
665 return (True, self.trUtf8( |
506 "Whitespace after inline comment sign corrected.")) |
666 "Superfluous trailing blank lines removed from end of file.")) |
|
667 |
|
668 def __fixW603(self, code, line, pos): |
|
669 """ |
|
670 Private method to fix the not equal notation (W603). |
|
671 |
|
672 @param code code of the issue (string) |
|
673 @param line line number of the issue (integer) |
|
674 @param pos position inside line (integer) |
|
675 @return flag indicating an applied fix (boolean) and a message for |
|
676 the fix (string) |
|
677 """ |
|
678 self.__source[line - 1] = self.__source[line - 1].replace("<>", "!=") |
|
679 return (True, self.trUtf8("'<>' replaced by '!='.")) |
|
680 |
|
681 |
|
682 class Pep8Reindenter(object): |
|
683 """ |
|
684 Class to reindent badly-indented code to uniformly use four-space indentation. |
|
685 |
|
686 Released to the public domain, by Tim Peters, 03 October 2000. |
|
687 """ |
|
688 def __init__(self, sourceLines): |
|
689 """ |
|
690 Constructor |
|
691 |
|
692 @param sourceLines list of source lines including eol marker |
|
693 (list of string) |
|
694 """ |
|
695 # Raw file lines. |
|
696 self.raw = sourceLines |
|
697 self.after = [] |
|
698 |
|
699 # File lines, rstripped & tab-expanded. Dummy at start is so |
|
700 # that we can use tokenize's 1-based line numbering easily. |
|
701 # Note that a line is all-blank iff it's "\n". |
|
702 self.lines = [line.rstrip().expandtabs() + "\n" |
|
703 for line in self.raw] |
|
704 self.lines.insert(0, None) |
|
705 self.index = 1 # index into self.lines of next line |
|
706 |
|
707 # List of (lineno, indentlevel) pairs, one for each stmt and |
|
708 # comment line. indentlevel is -1 for comment lines, as a |
|
709 # signal that tokenize doesn't know what to do about them; |
|
710 # indeed, they're our headache! |
|
711 self.stats = [] |
|
712 |
|
713 def run(self): |
|
714 """ |
|
715 Public method to run the re-indenter. |
|
716 """ |
|
717 try: |
|
718 stats = self.__genStats(tokenize.generate_tokens(self.getline)) |
|
719 except (SyntaxError, tokenize.TokenError): |
|
720 return False |
|
721 |
|
722 # Remove trailing empty lines. |
|
723 lines = self.lines |
|
724 while lines and lines[-1] == "\n": |
|
725 lines.pop() |
|
726 # Sentinel. |
|
727 stats.append((len(lines), 0)) |
|
728 # Map count of leading spaces to # we want. |
|
729 have2want = {} |
|
730 # Program after transformation. |
|
731 after = self.after = [] |
|
732 # Copy over initial empty lines -- there's nothing to do until |
|
733 # we see a line with *something* on it. |
|
734 i = stats[0][0] |
|
735 after.extend(lines[1:i]) |
|
736 for i in range(len(stats)-1): |
|
737 thisstmt, thislevel = stats[i] |
|
738 nextstmt = stats[i+1][0] |
|
739 have = self.__getlspace(lines[thisstmt]) |
|
740 want = thislevel * 4 |
|
741 if want < 0: |
|
742 # A comment line. |
|
743 if have: |
|
744 # An indented comment line. If we saw the same |
|
745 # indentation before, reuse what it most recently |
|
746 # mapped to. |
|
747 want = have2want.get(have, -1) |
|
748 if want < 0: |
|
749 # Then it probably belongs to the next real stmt. |
|
750 for j in range(i + 1, len(stats) - 1): |
|
751 jline, jlevel = stats[j] |
|
752 if jlevel >= 0: |
|
753 if have == self.__getlspace(lines[jline]): |
|
754 want = jlevel * 4 |
|
755 break |
|
756 if want < 0: # Maybe it's a hanging |
|
757 # comment like this one, |
|
758 # in which case we should shift it like its base |
|
759 # line got shifted. |
|
760 for j in range(i - 1, -1, -1): |
|
761 jline, jlevel = stats[j] |
|
762 if jlevel >= 0: |
|
763 want = have + self.__getlspace(after[jline-1]) - \ |
|
764 self.__getlspace(lines[jline]) |
|
765 break |
|
766 if want < 0: |
|
767 # Still no luck -- leave it alone. |
|
768 want = have |
|
769 else: |
|
770 want = 0 |
|
771 assert want >= 0 |
|
772 have2want[have] = want |
|
773 diff = want - have |
|
774 if diff == 0 or have == 0: |
|
775 after.extend(lines[thisstmt:nextstmt]) |
|
776 else: |
|
777 for line in lines[thisstmt:nextstmt]: |
|
778 if diff > 0: |
|
779 if line == "\n": |
|
780 after.append(line) |
|
781 else: |
|
782 after.append(" " * diff + line) |
|
783 else: |
|
784 remove = min(self.__getlspace(line), -diff) |
|
785 after.append(line[remove:]) |
|
786 return self.raw != self.after |
|
787 |
|
788 def fixedLine(self, line): |
|
789 """ |
|
790 Public method to get a fixed line. |
|
791 |
|
792 @param line number of the line to retrieve (integer) |
|
793 @return fixed line (string) |
|
794 """ |
|
795 if line < len(self.after): |
|
796 return self.after[line] |
|
797 |
|
798 def getline(self): |
|
799 """ |
|
800 Public method to get a line of text for tokenize. |
|
801 |
|
802 @return line of text (string) |
|
803 """ |
|
804 if self.index >= len(self.lines): |
|
805 line = "" |
|
806 else: |
|
807 line = self.lines[self.index] |
|
808 self.index += 1 |
|
809 return line |
|
810 |
|
811 def __genStats(self, tokens): |
|
812 """ |
|
813 Private method to generate the re-indent statistics. |
|
814 |
|
815 @param tokens tokens generator (tokenize._tokenize) |
|
816 """ |
|
817 find_stmt = True # next token begins a fresh stmt? |
|
818 level = 0 # current indent level |
|
819 stats = [] |
|
820 |
|
821 for t in tokens: |
|
822 token_type = t[0] |
|
823 sline = t[2][0] |
|
824 line = t[4] |
|
825 |
|
826 if token_type == tokenize.NEWLINE: |
|
827 # A program statement, or ENDMARKER, will eventually follow, |
|
828 # after some (possibly empty) run of tokens of the form |
|
829 # (NL | COMMENT)* (INDENT | DEDENT+)? |
|
830 self.find_stmt = True |
|
831 |
|
832 elif token_type == tokenize.INDENT: |
|
833 find_stmt = True |
|
834 level += 1 |
|
835 |
|
836 elif token_type == tokenize.DEDENT: |
|
837 find_stmt = True |
|
838 level -= 1 |
|
839 |
|
840 elif token_type == tokenize.COMMENT: |
|
841 if find_stmt: |
|
842 stats.append((sline, -1)) |
|
843 # but we're still looking for a new stmt, so leave |
|
844 # find_stmt alone |
|
845 |
|
846 elif token_type == tokenize.NL: |
|
847 pass |
|
848 |
|
849 elif find_stmt: |
|
850 # This is the first "real token" following a NEWLINE, so it |
|
851 # must be the first token of the next program statement, or an |
|
852 # ENDMARKER. |
|
853 find_stmt = False |
|
854 if line: # not endmarker |
|
855 stats.append((sline, level)) |
|
856 |
|
857 return stats |
|
858 |
|
859 def __getlspace(self, line): |
|
860 """ |
|
861 Private method to count number of leading blanks. |
|
862 |
|
863 @param line line to check (string) |
|
864 @return number of leading blanks (integer) |
|
865 """ |
|
866 i = 0 |
|
867 n = len(line) |
|
868 while i < n and line[i] == " ": |
|
869 i += 1 |
|
870 return i |