5 |
5 |
6 """ |
6 """ |
7 Module implementing a class to fix certain code style issues. |
7 Module implementing a class to fix certain code style issues. |
8 """ |
8 """ |
9 |
9 |
10 from __future__ import unicode_literals |
10 try: |
11 |
11 # Python 2 |
|
12 from StringIO import StringIO # __IGNORE_EXCEPTION__ |
|
13 except ImportError: |
|
14 # Python 3 |
|
15 from io import StringIO # __IGNORE_WARNING__ |
12 import os |
16 import os |
13 import re |
17 import re |
|
18 import sys |
14 import tokenize |
19 import tokenize |
15 import io |
20 |
16 |
21 # CodeStyleCheckerDialog tries to import FixableCodeStyleIssues what fail under |
17 from PyQt4.QtCore import QObject |
22 # Python3. So ignore it. |
18 |
23 try: |
19 from E5Gui import E5MessageBox |
24 import pep8 |
20 |
25 except ImportError: |
21 from . import pep8 |
26 pass |
22 |
|
23 import Utilities |
|
24 |
27 |
25 FixableCodeStyleIssues = [ |
28 FixableCodeStyleIssues = [ |
26 "D111", "D112", "D113", "D121", "D131", "D141", |
29 "D111", "D112", "D113", "D121", "D131", "D141", |
27 "D142", "D143", "D144", "D145", |
30 "D142", "D143", "D144", "D145", |
28 "D221", "D222", "D231", "D242", "D243", "D244", |
31 "D221", "D222", "D231", "D242", "D243", "D244", |
38 "N804", "N805", "N806", |
41 "N804", "N805", "N806", |
39 "W191", "W291", "W292", "W293", "W391", "W603", |
42 "W191", "W291", "W292", "W293", "W391", "W603", |
40 ] |
43 ] |
41 |
44 |
42 |
45 |
43 class CodeStyleFixer(QObject): |
46 class CodeStyleFixer(object): |
44 """ |
47 """ |
45 Class implementing a fixer for certain code style issues. |
48 Class implementing a fixer for certain code style issues. |
46 """ |
49 """ |
47 def __init__(self, project, filename, sourceLines, fixCodes, noFixCodes, |
50 def __init__(self, filename, sourceLines, fixCodes, noFixCodes, |
48 maxLineLength, inPlace): |
51 maxLineLength, inPlace, eol): |
49 """ |
52 """ |
50 Constructor |
53 Constructor |
51 |
54 |
52 @param project reference to the project object (Project) |
|
53 @param filename name of the file to be fixed (string) |
55 @param filename name of the file to be fixed (string) |
54 @param sourceLines list of source lines including eol marker |
56 @param sourceLines list of source lines including eol marker |
55 (list of string) |
57 (list of string) |
56 @param fixCodes list of codes to be fixed as a comma separated |
58 @param fixCodes list of codes to be fixed as a comma separated |
57 string (string) |
59 string (string) |
58 @param noFixCodes list of codes not to be fixed as a comma |
60 @param noFixCodes list of codes not to be fixed as a comma |
59 separated string (string) |
61 separated string (string) |
60 @param maxLineLength maximum allowed line length (integer) |
62 @param maxLineLength maximum allowed line length (integer) |
61 @param inPlace flag indicating to modify the file in place (boolean) |
63 @param inPlace flag indicating to modify the file in place (boolean) |
|
64 @param eol end of line character(s) (string) |
62 """ |
65 """ |
63 super(CodeStyleFixer, self).__init__() |
66 super(CodeStyleFixer, self).__init__() |
64 |
67 |
65 self.__project = project |
|
66 self.__filename = filename |
68 self.__filename = filename |
67 self.__origName = "" |
69 self.__origName = "" |
68 self.__source = sourceLines[:] # save a copy |
70 self.__source = sourceLines[:] # save a copy |
69 self.__fixCodes = [c.strip() for c in fixCodes.split(",") if c.strip()] |
71 self.__fixCodes = [c.strip() for c in fixCodes.split(",") if c.strip()] |
70 self.__noFixCodes = [ |
72 self.__noFixCodes = [ |
71 c.strip() for c in noFixCodes.split(",") if c.strip()] |
73 c.strip() for c in noFixCodes.split(",") if c.strip()] |
72 self.__maxLineLength = maxLineLength |
74 self.__maxLineLength = maxLineLength |
73 self.fixed = 0 |
75 self.fixed = 0 |
74 |
76 |
75 self.__reindenter = None |
77 self.__reindenter = None |
76 self.__eol = "" |
|
77 self.__indentWord = self.__getIndentWord() |
78 self.__indentWord = self.__getIndentWord() |
78 |
79 |
79 if not inPlace: |
80 if inPlace: |
|
81 # TODO: Do a backup before any changes |
|
82 pass |
|
83 else: |
80 self.__origName = self.__filename |
84 self.__origName = self.__filename |
81 self.__filename = os.path.join( |
85 self.__filename = os.path.join( |
82 os.path.dirname(self.__filename), |
86 os.path.dirname(self.__filename), |
83 "fixed_" + os.path.basename(self.__filename)) |
87 "fixed_" + os.path.basename(self.__filename)) |
84 |
88 self.__eol = eol |
|
89 |
85 self.__fixes = { |
90 self.__fixes = { |
86 "D111": self.__fixD111, |
91 "D111": self.__fixD111, |
87 "D112": self.__fixD112, |
92 "D112": self.__fixD112, |
88 "D113": self.__fixD112, |
93 "D113": self.__fixD112, |
89 "D121": self.__fixD121, |
94 "D121": self.__fixD121, |
172 def saveFile(self, encoding): |
177 def saveFile(self, encoding): |
173 """ |
178 """ |
174 Public method to save the modified file. |
179 Public method to save the modified file. |
175 |
180 |
176 @param encoding encoding of the source file (string) |
181 @param encoding encoding of the source file (string) |
177 @return flag indicating success (boolean) |
182 @return error message on failure (tuple of str) |
178 """ |
183 """ |
|
184 import codecs |
|
185 |
179 if not self.__modified: |
186 if not self.__modified: |
180 # no need to write |
187 # no need to write |
181 return True |
188 return |
182 |
189 |
183 txt = "".join(self.__source) |
190 txt = "".join(self.__source) |
184 try: |
191 try: |
185 Utilities.writeEncodedFile(self.__filename, txt, encoding) |
192 if sys.version_info[0] == 3: |
186 except (IOError, Utilities.CodingError, UnicodeError) as err: |
193 txt = txt.encode(encoding) |
187 E5MessageBox.critical( |
194 if encoding == 'utf-8-bom': |
188 self, |
195 txt = codecs.BOM_UTF8 + txt |
189 self.trUtf8("Fix Code Style Issues"), |
196 |
190 self.trUtf8( |
197 with open(self.__filename, "wb") as fp: |
191 """<p>Could not save the file <b>{0}</b>.""" |
198 fp.write(txt) |
192 """ Skipping it.</p><p>Reason: {1}</p>""") |
199 except (IOError, UnicodeError) as err: |
193 .format(self.__filename, str(err)) |
200 # Could not save the file! Skipping it. Reason: {0} |
194 ) |
201 return ("FWRITE_ERROR", (str(err),)) |
195 return False |
202 return |
196 |
|
197 return True |
|
198 |
203 |
199 def __codeMatch(self, code): |
204 def __codeMatch(self, code): |
200 """ |
205 """ |
201 Private method to check, if the code should be fixed. |
206 Private method to check, if the code should be fixed. |
202 |
207 |
235 @param message message text (string) |
240 @param message message text (string) |
236 @return value indicating an applied/deferred fix (-1, 0, 1), |
241 @return value indicating an applied/deferred fix (-1, 0, 1), |
237 a message for the fix (string) and an ID for a deferred |
242 a message for the fix (string) and an ID for a deferred |
238 fix (integer) |
243 fix (integer) |
239 """ |
244 """ |
240 code = message.split(None, 1)[0].strip() |
245 if isinstance(message, (tuple, list)): |
|
246 code = message[0].strip() |
|
247 else: |
|
248 code = message.split(None, 1)[0].strip() |
241 |
249 |
242 if line <= len(self.__source) and \ |
250 if line <= len(self.__source) and \ |
243 self.__codeMatch(code) and \ |
251 self.__codeMatch(code) and \ |
244 code in self.__fixes: |
252 code in self.__fixes: |
245 res = self.__fixes[code](code, line, pos) |
253 res = self.__fixes[code](code, line, pos) |
403 string and a set of line numbers belonging to a multi line |
393 string and a set of line numbers belonging to a multi line |
404 documentation string (tuple of two set of integer) |
394 documentation string (tuple of two set of integer) |
405 """ |
395 """ |
406 if self.__multiLineNumbers is None: |
396 if self.__multiLineNumbers is None: |
407 source = "".join(self.__source) |
397 source = "".join(self.__source) |
408 sio = io.StringIO(source) |
398 sio = StringIO(source) |
409 self.__multiLineNumbers = set() |
399 self.__multiLineNumbers = set() |
410 self.__docLineNumbers = set() |
400 self.__docLineNumbers = set() |
411 previousTokenType = '' |
401 previousTokenType = '' |
412 try: |
402 try: |
413 for t in tokenize.generate_tokens(sio.readline): |
403 for t in tokenize.generate_tokens(sio.readline): |
539 return (0, "", 0) |
526 return (0, "", 0) |
540 |
527 |
541 newText = self.__getIndent(self.__source[line]) + \ |
528 newText = self.__getIndent(self.__source[line]) + \ |
542 insertChar + self.__source[line].lstrip() |
529 insertChar + self.__source[line].lstrip() |
543 self.__source[line] = newText |
530 self.__source[line] = newText |
544 return ( |
531 # Introductory quotes corrected to be {0}""" |
545 1, |
532 return (1, ('FD112', (insertChar,)), 0) |
546 self.trUtf8('Introductory quotes corrected to be {0}"""') |
|
547 .format(insertChar), |
|
548 0) |
|
549 |
533 |
550 def __fixD121(self, code, line, pos, apply=False): |
534 def __fixD121(self, code, line, pos, apply=False): |
551 """ |
535 """ |
552 Private method to fix a single line docstring on multiple lines. |
536 Private method to fix a single line docstring on multiple lines. |
553 |
537 |
570 return (0, "", 0) |
554 return (0, "", 0) |
571 |
555 |
572 docstring = self.__source[line].rstrip() + \ |
556 docstring = self.__source[line].rstrip() + \ |
573 self.__source[line + 1].strip() |
557 self.__source[line + 1].strip() |
574 if docstring.endswith('"""'): |
558 if docstring.endswith('"""'): |
575 docstring += self.__getEol() |
559 docstring += self.__eol |
576 else: |
560 else: |
577 docstring += self.__source[line + 2].lstrip() |
561 docstring += self.__source[line + 2].lstrip() |
578 self.__source[line + 2] = "" |
562 self.__source[line + 2] = "" |
579 |
563 |
580 self.__source[line] = docstring |
564 self.__source[line] = docstring |
581 self.__source[line + 1] = "" |
565 self.__source[line + 1] = "" |
582 return ( |
566 # Single line docstring put on one line. |
583 1, |
567 return (1, "FD121", 0) |
584 self.trUtf8("Single line docstring put on one line."), |
|
585 0) |
|
586 else: |
568 else: |
587 id = self.__getID() |
569 id = self.__getID() |
588 self.__stack.append((id, code, line, pos)) |
570 self.__stack.append((id, code, line, pos)) |
589 return (-1, "", id) |
571 return (-1, "", id) |
590 |
572 |
606 newText = "" |
588 newText = "" |
607 if self.__source[line].rstrip().endswith(('"""', "'''")) and \ |
589 if self.__source[line].rstrip().endswith(('"""', "'''")) and \ |
608 self.__source[line].lstrip().startswith(('"""', 'r"""', 'u"""')): |
590 self.__source[line].lstrip().startswith(('"""', 'r"""', 'u"""')): |
609 # it is a one-liner |
591 # it is a one-liner |
610 newText = self.__source[line].rstrip()[:-3].rstrip() + "." + \ |
592 newText = self.__source[line].rstrip()[:-3].rstrip() + "." + \ |
611 self.__source[line].rstrip()[-3:] + self.__getEol() |
593 self.__source[line].rstrip()[-3:] + self.__eol |
612 else: |
594 else: |
613 if line < len(self.__source) - 1 and \ |
595 if line < len(self.__source) - 1 and \ |
614 (not self.__source[line + 1].strip() or |
596 (not self.__source[line + 1].strip() or |
615 self.__source[line + 1].lstrip().startswith("@") or |
597 self.__source[line + 1].lstrip().startswith("@") or |
616 (self.__source[line + 1].strip() in ('"""', "'''") and |
598 (self.__source[line + 1].strip() in ('"""', "'''") and |
617 not self.__source[line].lstrip().startswith("@"))): |
599 not self.__source[line].lstrip().startswith("@"))): |
618 newText = self.__source[line].rstrip() + "." + self.__getEol() |
600 newText = self.__source[line].rstrip() + "." + self.__eol |
619 |
601 |
620 if newText: |
602 if newText: |
621 self.__source[line] = newText |
603 self.__source[line] = newText |
622 return (1, self.trUtf8("Period added to summary line."), 0) |
604 # Period added to summary line. |
|
605 return (1, "FD131", 0) |
623 else: |
606 else: |
624 return (0, "", 0) |
607 return (0, "", 0) |
625 |
608 |
626 def __fixD141(self, code, line, pos, apply=False): |
609 def __fixD141(self, code, line, pos, apply=False): |
627 """ |
610 """ |
796 else: |
767 else: |
797 first, second = source[:3], source[3:].strip() |
768 first, second = source[:3], source[3:].strip() |
798 else: |
769 else: |
799 # trailing |
770 # trailing |
800 first, second = source[:-3].strip(), source[-3:] |
771 first, second = source[:-3].strip(), source[-3:] |
801 newText = indent + first + self.__getEol() + \ |
772 newText = indent + first + self.__eol + \ |
802 indent + second + self.__getEol() |
773 indent + second + self.__eol |
803 self.__source[line] = newText |
774 self.__source[line] = newText |
804 if code == "D221": |
775 if code == "D221": |
805 msg = self.trUtf8("Leading quotes put on separate line.") |
776 # Leading quotes put on separate line. |
|
777 msg = "FD221" |
806 else: |
778 else: |
807 msg = self.trUtf8("Trailing quotes put on separate line.") |
779 # Trailing quotes put on separate line. |
|
780 msg = "FD222" |
808 return (1, msg, 0) |
781 return (1, msg, 0) |
809 else: |
782 else: |
810 id = self.__getID() |
783 id = self.__getID() |
811 self.__stack.append((id, code, line, pos)) |
784 self.__stack.append((id, code, line, pos)) |
812 return (-1, "", id) |
785 return (-1, "", id) |
917 self.__reindenter.run() |
890 self.__reindenter.run() |
918 fixedLine = self.__reindenter.fixedLine(line - 1) |
891 fixedLine = self.__reindenter.fixedLine(line - 1) |
919 if fixedLine is not None and fixedLine != self.__source[line - 1]: |
892 if fixedLine is not None and fixedLine != self.__source[line - 1]: |
920 self.__source[line - 1] = fixedLine |
893 self.__source[line - 1] = fixedLine |
921 if code in ["E101", "W191"]: |
894 if code in ["E101", "W191"]: |
922 msg = self.trUtf8("Tab converted to 4 spaces.") |
895 # Tab converted to 4 spaces. |
|
896 msg = "FE101" |
923 else: |
897 else: |
924 msg = self.trUtf8( |
898 # Indentation adjusted to be a multiple of four. |
925 "Indentation adjusted to be a multiple of four.") |
899 msg = "FE111" |
926 return (1, msg, 0) |
900 return (1, msg, 0) |
927 else: |
901 else: |
928 return (0, "", 0) |
902 return (0, "", 0) |
929 |
903 |
930 def __fixE121(self, code, line, pos, apply=False): |
904 def __fixE121(self, code, line, pos, apply=False): |
1218 if newText == text: |
1190 if newText == text: |
1219 return (0, "", 0) |
1191 return (0, "", 0) |
1220 |
1192 |
1221 self.__source[line] = newText |
1193 self.__source[line] = newText |
1222 if code in ["E225", "E226", "E227", "E228"]: |
1194 if code in ["E225", "E226", "E227", "E228"]: |
1223 return (1, self.trUtf8("Missing whitespace added."), 0) |
1195 # Missing whitespace added. |
1224 else: |
1196 return (1, "", 0) |
1225 return (1, self.trUtf8("Extraneous whitespace removed."), 0) |
1197 else: |
|
1198 # Extraneous whitespace removed. |
|
1199 return (1, "", 0) |
1226 |
1200 |
1227 def __fixE231(self, code, line, pos): |
1201 def __fixE231(self, code, line, pos): |
1228 """ |
1202 """ |
1229 Private method to fix missing whitespace after ',;:'. |
1203 Private method to fix missing whitespace after ',;:'. |
1230 |
1204 |
1276 if newText.endswith(('=\\\n', '=\\\r\n', '=\\\r')): |
1251 if newText.endswith(('=\\\n', '=\\\r\n', '=\\\r')): |
1277 self.__source[line] = newText.rstrip("\n\r \t\\") |
1252 self.__source[line] = newText.rstrip("\n\r \t\\") |
1278 self.__source[line + 1] = self.__source[line + 1].lstrip() |
1253 self.__source[line + 1] = self.__source[line + 1].lstrip() |
1279 else: |
1254 else: |
1280 self.__source[line] = newText |
1255 self.__source[line] = newText |
1281 return (1, self.trUtf8("Extraneous whitespace removed."), 0) |
1256 # Extraneous whitespace removed. |
|
1257 return (1, "FE251", 0) |
1282 |
1258 |
1283 def __fixE261(self, code, line, pos): |
1259 def __fixE261(self, code, line, pos): |
1284 """ |
1260 """ |
1285 Private method to fix whitespace before or after inline comment. |
1261 Private method to fix whitespace before or after inline comment. |
1286 |
1262 |
1297 text = self.__source[line] |
1273 text = self.__source[line] |
1298 left = text[:pos].rstrip(' \t#') |
1274 left = text[:pos].rstrip(' \t#') |
1299 right = text[pos:].lstrip(' \t#') |
1275 right = text[pos:].lstrip(' \t#') |
1300 newText = left + (" # " + right if right.strip() else right) |
1276 newText = left + (" # " + right if right.strip() else right) |
1301 self.__source[line] = newText |
1277 self.__source[line] = newText |
1302 return (1, self.trUtf8("Whitespace around comment sign corrected."), 0) |
1278 # Whitespace around comment sign corrected. |
|
1279 return (1, "FE261", 0) |
1303 |
1280 |
1304 def __fixE301(self, code, line, pos, apply=False): |
1281 def __fixE301(self, code, line, pos, apply=False): |
1305 """ |
1282 """ |
1306 Private method to fix the need for one blank line. |
1283 Private method to fix the need for one blank line. |
1307 |
1284 |
1315 @return value indicating an applied/deferred fix (-1, 0, 1), |
1292 @return value indicating an applied/deferred fix (-1, 0, 1), |
1316 a message for the fix (string) and an ID for a deferred |
1293 a message for the fix (string) and an ID for a deferred |
1317 fix (integer) |
1294 fix (integer) |
1318 """ |
1295 """ |
1319 if apply: |
1296 if apply: |
1320 self.__source.insert(line - 1, self.__getEol()) |
1297 self.__source.insert(line - 1, self.__eol) |
1321 return (1, self.trUtf8("One blank line inserted."), 0) |
1298 # One blank line inserted. |
|
1299 return (1, "FE301", 0) |
1322 else: |
1300 else: |
1323 id = self.__getID() |
1301 id = self.__getID() |
1324 self.__stack.append((id, code, line, pos)) |
1302 self.__stack.append((id, code, line, pos)) |
1325 return (-1, "", id) |
1303 return (-1, "", id) |
1326 |
1304 |
1353 |
1331 |
1354 line -= 1 |
1332 line -= 1 |
1355 if delta < 0: |
1333 if delta < 0: |
1356 # insert blank lines (one or two) |
1334 # insert blank lines (one or two) |
1357 while delta < 0: |
1335 while delta < 0: |
1358 self.__source.insert(line, self.__getEol()) |
1336 self.__source.insert(line, self.__eol) |
1359 delta += 1 |
1337 delta += 1 |
1360 changed = True |
1338 # %n blank line(s) inserted. |
|
1339 return (1, ("FE302+", 2 - blanks), 0) |
1361 elif delta > 0: |
1340 elif delta > 0: |
1362 # delete superfluous blank lines |
1341 # delete superfluous blank lines |
1363 while delta > 0: |
1342 while delta > 0: |
1364 del self.__source[line - 1] |
1343 del self.__source[line - 1] |
1365 line -= 1 |
1344 line -= 1 |
1366 delta -= 1 |
1345 delta -= 1 |
1367 changed = True |
1346 # %n superfluous line(s) removed. |
|
1347 return (1, ("FE302-", blanks - 2), 0) |
1368 else: |
1348 else: |
1369 changed = False |
1349 return (0, "", 0) |
1370 |
|
1371 if changed: |
|
1372 if delta < 0: |
|
1373 msg = self.trUtf8( |
|
1374 "%n blank line(s) inserted.", "", -delta) |
|
1375 elif delta > 0: |
|
1376 msg = self.trUtf8( |
|
1377 "%n superfluous lines removed", "", delta) |
|
1378 else: |
|
1379 msg = "" |
|
1380 return (1, msg, 0) |
|
1381 return (0, "", 0) |
|
1382 else: |
1350 else: |
1383 id = self.__getID() |
1351 id = self.__getID() |
1384 self.__stack.append((id, code, line, pos)) |
1352 self.__stack.append((id, code, line, pos)) |
1385 return (-1, "", id) |
1353 return (-1, "", id) |
1386 |
1354 |
1470 # statement followed by a semicolon and some unrelated |
1438 # statement followed by a semicolon and some unrelated |
1471 # statement with commas in it. |
1439 # statement with commas in it. |
1472 if ';' in text: |
1440 if ';' in text: |
1473 return (0, "", 0) |
1441 return (0, "", 0) |
1474 |
1442 |
1475 newText = text[:pos].rstrip("\t ,") + self.__getEol() + \ |
1443 newText = text[:pos].rstrip("\t ,") + self.__eol + \ |
1476 self.__getIndent(text) + "import " + text[pos:].lstrip("\t ,") |
1444 self.__getIndent(text) + "import " + text[pos:].lstrip("\t ,") |
1477 self.__source[line] = newText |
1445 self.__source[line] = newText |
1478 return (1, self.trUtf8("Imports were put on separate lines."), 0) |
1446 # Imports were put on separate lines. |
|
1447 return (1, "FE401", 0) |
1479 else: |
1448 else: |
1480 id = self.__getID() |
1449 id = self.__getID() |
1481 self.__stack.append((id, code, line, pos)) |
1450 self.__stack.append((id, code, line, pos)) |
1482 return (-1, "", id) |
1451 return (-1, "", id) |
1483 |
1452 |
1510 nextText = self.__source[line + 1] |
1479 nextText = self.__source[line + 1] |
1511 else: |
1480 else: |
1512 nextText = "" |
1481 nextText = "" |
1513 shortener = LineShortener( |
1482 shortener = LineShortener( |
1514 text, prevText, nextText, |
1483 text, prevText, nextText, |
1515 maxLength=self.__maxLineLength, eol=self.__getEol(), |
1484 maxLength=self.__maxLineLength, eol=self.__eol, |
1516 indentWord=self.__indentWord, isDocString=isDocString) |
1485 indentWord=self.__indentWord, isDocString=isDocString) |
1517 changed, newText, newNextText = shortener.shorten() |
1486 changed, newText, newNextText = shortener.shorten() |
1518 if changed: |
1487 if changed: |
1519 if newText != text: |
1488 if newText != text: |
1520 self.__source[line] = newText |
1489 self.__source[line] = newText |
1521 if newNextText and newNextText != nextText: |
1490 if newNextText and newNextText != nextText: |
1522 if newNextText == " ": |
1491 if newNextText == " ": |
1523 newNextText = "" |
1492 newNextText = "" |
1524 self.__source[line + 1] = newNextText |
1493 self.__source[line + 1] = newNextText |
1525 return (1, self.trUtf8("Long lines have been shortened."), 0) |
1494 # Long lines have been shortened. |
|
1495 return (1, "FE501", 0) |
1526 else: |
1496 else: |
1527 return (0, "", 0) |
1497 return (0, "", 0) |
1528 else: |
1498 else: |
1529 id = self.__getID() |
1499 id = self.__getID() |
1530 self.__stack.append((id, code, line, pos)) |
1500 self.__stack.append((id, code, line, pos)) |
1542 @return value indicating an applied/deferred fix (-1, 0, 1), |
1512 @return value indicating an applied/deferred fix (-1, 0, 1), |
1543 a message for the fix (string) and an ID for a deferred |
1513 a message for the fix (string) and an ID for a deferred |
1544 fix (integer) |
1514 fix (integer) |
1545 """ |
1515 """ |
1546 self.__source[line - 1] = \ |
1516 self.__source[line - 1] = \ |
1547 self.__source[line - 1].rstrip("\n\r \t\\") + self.__getEol() |
1517 self.__source[line - 1].rstrip("\n\r \t\\") + self.__eol |
1548 return (1, self.trUtf8("Redundant backslash in brackets removed."), 0) |
1518 # Redundant backslash in brackets removed. |
|
1519 return (1, "FE502", 0) |
1549 |
1520 |
1550 def __fixE701(self, code, line, pos, apply=False): |
1521 def __fixE701(self, code, line, pos, apply=False): |
1551 """ |
1522 """ |
1552 Private method to fix colon-separated compound statements. |
1523 Private method to fix colon-separated compound statements. |
1553 |
1524 |
1599 if text.rstrip().endswith("\\"): |
1571 if text.rstrip().endswith("\\"): |
1600 # normalize '1; \\\n2' into '1; 2' |
1572 # normalize '1; \\\n2' into '1; 2' |
1601 self.__source[line] = text.rstrip("\n\r \t\\") |
1573 self.__source[line] = text.rstrip("\n\r \t\\") |
1602 self.__source[line + 1] = self.__source[line + 1].lstrip() |
1574 self.__source[line + 1] = self.__source[line + 1].lstrip() |
1603 elif text.rstrip().endswith(";"): |
1575 elif text.rstrip().endswith(";"): |
1604 self.__source[line] = text.rstrip("\n\r \t;") + self.__getEol() |
1576 self.__source[line] = text.rstrip("\n\r \t;") + self.__eol |
1605 else: |
1577 else: |
1606 first = text[:pos].rstrip("\n\r \t;") + self.__getEol() |
1578 first = text[:pos].rstrip("\n\r \t;") + self.__eol |
1607 second = text[pos:].lstrip("\n\r \t;") |
1579 second = text[pos:].lstrip("\n\r \t;") |
1608 self.__source[line] = first + self.__getIndent(text) + second |
1580 self.__source[line] = first + self.__getIndent(text) + second |
1609 return (1, self.trUtf8("Compound statement corrected."), 0) |
1581 # Compound statement corrected. |
|
1582 return (1, "FE702", 0) |
1610 else: |
1583 else: |
1611 id = self.__getID() |
1584 id = self.__getID() |
1612 self.__stack.append((id, code, line, pos)) |
1585 self.__stack.append((id, code, line, pos)) |
1613 return (-1, "", id) |
1586 return (-1, "", id) |
1614 |
1587 |
1673 else: |
1647 else: |
1674 arg = "self" |
1648 arg = "self" |
1675 |
1649 |
1676 if text.rstrip().endswith("("): |
1650 if text.rstrip().endswith("("): |
1677 newText = text + self.__getIndent(text) + \ |
1651 newText = text + self.__getIndent(text) + \ |
1678 self.__indentWord + arg + "," + self.__getEol() |
1652 self.__indentWord + arg + "," + self.__eol |
1679 else: |
1653 else: |
1680 index = text.find("(") + 1 |
1654 index = text.find("(") + 1 |
1681 left = text[:index] |
1655 left = text[:index] |
1682 right = text[index:] |
1656 right = text[index:] |
1683 if right.startswith(")"): |
1657 if right.startswith(")"): |
1684 center = arg |
1658 center = arg |
1685 else: |
1659 else: |
1686 center = arg + ", " |
1660 center = arg + ", " |
1687 newText = left + center + right |
1661 newText = left + center + right |
1688 self.__source[line] = newText |
1662 self.__source[line] = newText |
1689 return (1, self.trUtf8("'{0}' argument added.").format(arg), 0) |
1663 # '{0}' argument added. |
|
1664 return (1, ("FN804", (arg,)), 0) |
1690 else: |
1665 else: |
1691 id = self.__getID() |
1666 id = self.__getID() |
1692 self.__stack.append((id, code, line, pos)) |
1667 self.__stack.append((id, code, line, pos)) |
1693 return (-1, "", id) |
1668 return (-1, "", id) |
1694 |
1669 |
1765 a message for the fix (string) and an ID for a deferred |
1741 a message for the fix (string) and an ID for a deferred |
1766 fix (integer) |
1742 fix (integer) |
1767 """ |
1743 """ |
1768 self.__source[line - 1] = re.sub(r'[\t ]+(\r?)$', r"\1", |
1744 self.__source[line - 1] = re.sub(r'[\t ]+(\r?)$', r"\1", |
1769 self.__source[line - 1]) |
1745 self.__source[line - 1]) |
1770 return (1, self.trUtf8("Whitespace stripped from end of line."), 0) |
1746 # Whitespace stripped from end of line. |
|
1747 return (1, "FW291", 0) |
1771 |
1748 |
1772 def __fixW292(self, code, line, pos): |
1749 def __fixW292(self, code, line, pos): |
1773 """ |
1750 """ |
1774 Private method to fix a missing newline at the end of file. |
1751 Private method to fix a missing newline at the end of file. |
1775 |
1752 |
1780 @param pos position inside line (integer) |
1757 @param pos position inside line (integer) |
1781 @return value indicating an applied/deferred fix (-1, 0, 1), |
1758 @return value indicating an applied/deferred fix (-1, 0, 1), |
1782 a message for the fix (string) and an ID for a deferred |
1759 a message for the fix (string) and an ID for a deferred |
1783 fix (integer) |
1760 fix (integer) |
1784 """ |
1761 """ |
1785 self.__source[line - 1] += self.__getEol() |
1762 self.__source[line - 1] += self.__eol |
1786 return (1, self.trUtf8("newline added to end of file."), 0) |
1763 # newline added to end of file. |
|
1764 return (1, "FW292", 0) |
1787 |
1765 |
1788 def __fixW391(self, code, line, pos): |
1766 def __fixW391(self, code, line, pos): |
1789 """ |
1767 """ |
1790 Private method to fix trailing blank lines. |
1768 Private method to fix trailing blank lines. |
1791 |
1769 |
1820 @return value indicating an applied/deferred fix (-1, 0, 1), |
1798 @return value indicating an applied/deferred fix (-1, 0, 1), |
1821 a message for the fix (string) and an ID for a deferred |
1799 a message for the fix (string) and an ID for a deferred |
1822 fix (integer) |
1800 fix (integer) |
1823 """ |
1801 """ |
1824 self.__source[line - 1] = self.__source[line - 1].replace("<>", "!=") |
1802 self.__source[line - 1] = self.__source[line - 1].replace("<>", "!=") |
1825 return (1, self.trUtf8("'<>' replaced by '!='."), 0) |
1803 # '<>' replaced by '!='. |
|
1804 return (1, "FW603", 0) |
1826 |
1805 |
1827 |
1806 |
1828 class Reindenter(object): |
1807 class Reindenter(object): |
1829 """ |
1808 """ |
1830 Class to reindent badly-indented code to uniformly use four-space |
1809 Class to reindent badly-indented code to uniformly use four-space |
2336 return True, newText, newNext |
2315 return True, newText, newNext |
2337 |
2316 |
2338 indent = self.__getIndent(self.__text) |
2317 indent = self.__getIndent(self.__text) |
2339 source = self.__text[len(indent):] |
2318 source = self.__text[len(indent):] |
2340 assert source.lstrip() == source |
2319 assert source.lstrip() == source |
2341 sio = io.StringIO(source) |
2320 sio = StringIO(source) |
2342 |
2321 |
2343 # Check for multi line string. |
2322 # Check for multi line string. |
2344 try: |
2323 try: |
2345 tokens = list(tokenize.generate_tokens(sio.readline)) |
2324 tokens = list(tokenize.generate_tokens(sio.readline)) |
2346 except (SyntaxError, tokenize.TokenError): |
2325 except (SyntaxError, tokenize.TokenError): |