15 from collections import defaultdict |
15 from collections import defaultdict |
16 import tokenize |
16 import tokenize |
17 |
17 |
18 import AstUtilities |
18 import AstUtilities |
19 |
19 |
|
20 from eradicate import Eradicator |
|
21 |
|
22 from .MiscellaneousDefaults import MiscellaneousCheckerDefaultArgs |
|
23 |
20 |
24 |
21 def composeCallPath(node): |
25 def composeCallPath(node): |
22 """ |
26 """ |
23 Generator function to assemble the call path of a given node. |
27 Generator function to assemble the call path of a given node. |
24 |
28 |
25 @param node node to assemble call path for |
29 @param node node to assemble call path for |
26 @type ast.Node |
30 @type ast.Node |
27 @return call path components |
31 @yield call path components |
28 @rtype str |
32 @ytype str |
29 """ |
33 """ |
30 if isinstance(node, ast.Attribute): |
34 if isinstance(node, ast.Attribute): |
31 for v in composeCallPath(node.value): |
35 for v in composeCallPath(node.value): |
32 yield v |
36 yield v |
33 yield node.attr |
37 yield node.attr |
152 r'^(?:[^\'"]*[\'"][^\'"]*[\'"])*\s*%|^\s*%') |
156 r'^(?:[^\'"]*[\'"][^\'"]*[\'"])*\s*%|^\s*%') |
153 |
157 |
154 import builtins |
158 import builtins |
155 self.__builtins = [b for b in dir(builtins) |
159 self.__builtins = [b for b in dir(builtins) |
156 if b not in self.BuiltinsWhiteList] |
160 if b not in self.BuiltinsWhiteList] |
|
161 |
|
162 self.__eradicator = Eradicator() |
157 |
163 |
158 # statistics counters |
164 # statistics counters |
159 self.counters = {} |
165 self.counters = {} |
160 |
166 |
161 # collection of detected errors |
167 # collection of detected errors |
193 (self.__checkReturn, ("M831", "M832", "M833", "M834")), |
199 (self.__checkReturn, ("M831", "M832", "M833", "M834")), |
194 (self.__checkLineContinuation, ("M841",)), |
200 (self.__checkLineContinuation, ("M841",)), |
195 (self.__checkCommentedCode, ("M891",)), |
201 (self.__checkCommentedCode, ("M891",)), |
196 ] |
202 ] |
197 |
203 |
198 self.__defaultArgs = { |
204 # the eradicate whitelist |
199 "BuiltinsChecker": { |
205 commentedCodeCheckerArgs = self.__args.get( |
200 "chr": ["unichr", ], |
206 "CommentedCodeChecker", |
201 "str": ["unicode", ], |
207 MiscellaneousCheckerDefaultArgs["CommentedCodeChecker"]) |
202 }, |
208 commentedCodeCheckerWhitelist = commentedCodeCheckerArgs.get( |
203 "CodingChecker": 'latin-1, utf-8', |
209 "WhiteList", |
204 "CopyrightChecker": { |
210 MiscellaneousCheckerDefaultArgs[ |
205 "Author": "", |
211 "CommentedCodeChecker"]["WhiteList"]) |
206 "MinFilesize": 0, |
212 self.__eradicator.update_whitelist(commentedCodeCheckerWhitelist, |
207 }, |
213 extend_default=False) |
208 "CommentedCodeChecker": { |
|
209 "Aggressive": False, |
|
210 } |
|
211 } |
|
212 |
214 |
213 self.__checkers = [] |
215 self.__checkers = [] |
214 for checker, codes in checkersWithCodes: |
216 for checker, codes in checkersWithCodes: |
215 if any(not (code and self.__ignoreCode(code)) |
217 if any(not (code and self.__ignoreCode(code)) |
216 for code in codes): |
218 for code in codes): |
334 if len(self.__source) == 0: |
336 if len(self.__source) == 0: |
335 return |
337 return |
336 |
338 |
337 encodings = [e.lower().strip() |
339 encodings = [e.lower().strip() |
338 for e in self.__args.get( |
340 for e in self.__args.get( |
339 "CodingChecker", self.__defaultArgs["CodingChecker"]) |
341 "CodingChecker", |
|
342 MiscellaneousCheckerDefaultArgs["CodingChecker"]) |
340 .split(",")] |
343 .split(",")] |
341 lineno, coding = self.__getCoding() |
344 lineno, coding = self.__getCoding() |
342 if coding: |
345 if coding: |
343 if coding.lower() not in encodings: |
346 if coding.lower() not in encodings: |
344 self.__error(lineno, 0, "M102", coding) |
347 self.__error(lineno, 0, "M102", coding) |
349 """ |
352 """ |
350 Private method to check the presence of a copyright statement. |
353 Private method to check the presence of a copyright statement. |
351 """ |
354 """ |
352 source = "".join(self.__source) |
355 source = "".join(self.__source) |
353 copyrightArgs = self.__args.get( |
356 copyrightArgs = self.__args.get( |
354 "CopyrightChecker", self.__defaultArgs["CopyrightChecker"]) |
357 "CopyrightChecker", |
|
358 MiscellaneousCheckerDefaultArgs["CopyrightChecker"]) |
355 copyrightMinFileSize = copyrightArgs.get( |
359 copyrightMinFileSize = copyrightArgs.get( |
356 "MinFilesize", |
360 "MinFilesize", |
357 self.__defaultArgs["CopyrightChecker"]["MinFilesize"]) |
361 MiscellaneousCheckerDefaultArgs["CopyrightChecker"]["MinFilesize"]) |
358 copyrightAuthor = copyrightArgs.get( |
362 copyrightAuthor = copyrightArgs.get( |
359 "Author", |
363 "Author", |
360 self.__defaultArgs["CopyrightChecker"]["Author"]) |
364 MiscellaneousCheckerDefaultArgs["CopyrightChecker"]["Author"]) |
361 copyrightRegexStr = ( |
365 copyrightRegexStr = ( |
362 r"Copyright\s+(\(C\)\s+)?(\d{{4}}\s+-\s+)?\d{{4}}\s+{author}" |
366 r"Copyright\s+(\(C\)\s+)?(\d{{4}}\s+-\s+)?\d{{4}}\s+{author}" |
363 ) |
367 ) |
364 |
368 |
365 tocheck = max(1024, copyrightMinFileSize) |
369 tocheck = max(1024, copyrightMinFileSize) |
382 |
386 |
383 def __checkCommentedCode(self): |
387 def __checkCommentedCode(self): |
384 """ |
388 """ |
385 Private method to check for commented code. |
389 Private method to check for commented code. |
386 """ |
390 """ |
387 from eradicate import commented_out_code_line_numbers |
|
388 |
|
389 source = "".join(self.__source) |
391 source = "".join(self.__source) |
390 commentedCodeCheckerArgs = self.__args.get( |
392 commentedCodeCheckerArgs = self.__args.get( |
391 "CommentedCodeChecker", self.__defaultArgs["CommentedCodeChecker"]) |
393 "CommentedCodeChecker", |
|
394 MiscellaneousCheckerDefaultArgs["CommentedCodeChecker"]) |
392 aggressive = commentedCodeCheckerArgs.get( |
395 aggressive = commentedCodeCheckerArgs.get( |
393 "Aggressive", |
396 "Aggressive", |
394 self.__defaultArgs["CommentedCodeChecker"]["Aggressive"]) |
397 MiscellaneousCheckerDefaultArgs[ |
395 for markedLine in commented_out_code_line_numbers( |
398 "CommentedCodeChecker"]["Aggressive"]) |
|
399 for markedLine in self.__eradicator.commented_out_code_line_numbers( |
396 source, aggressive=aggressive): |
400 source, aggressive=aggressive): |
397 self.__error(markedLine - 1, 0, "M891") |
401 self.__error(markedLine - 1, 0, "M891") |
398 |
402 |
399 def __checkLineContinuation(self): |
403 def __checkLineContinuation(self): |
400 """ |
404 """ |
556 |
560 |
557 keywords = {keyword.arg for keyword in call.keywords} |
561 keywords = {keyword.arg for keyword in call.keywords} |
558 numArgs = len(call.args) |
562 numArgs = len(call.args) |
559 if strArgs: |
563 if strArgs: |
560 numArgs -= 1 |
564 numArgs -= 1 |
561 if sys.version_info < (3, 5): |
565 hasKwArgs = any(kw.arg is None for kw in call.keywords) |
562 hasKwArgs = bool(call.kwargs) |
566 hasStarArgs = sum(1 for arg in call.args |
563 hasStarArgs = bool(call.starargs) |
567 if isinstance(arg, ast.Starred)) |
564 else: |
568 |
565 hasKwArgs = any(kw.arg is None for kw in call.keywords) |
569 if hasKwArgs: |
566 hasStarArgs = sum(1 for arg in call.args |
570 keywords.discard(None) |
567 if isinstance(arg, ast.Starred)) |
571 if hasStarArgs: |
568 |
572 numArgs -= 1 |
569 if hasKwArgs: |
|
570 keywords.discard(None) |
|
571 if hasStarArgs: |
|
572 numArgs -= 1 |
|
573 |
573 |
574 # if starargs or kwargs is not None, it can't count the |
574 # if starargs or kwargs is not None, it can't count the |
575 # parameters but at least check if the args are used |
575 # parameters but at least check if the args are used |
576 if hasKwArgs: |
576 if hasKwArgs: |
577 if not names: |
577 if not names: |
647 functionDefs.append(ast.AsyncFunctionDef) |
647 functionDefs.append(ast.AsyncFunctionDef) |
648 except AttributeError: |
648 except AttributeError: |
649 pass |
649 pass |
650 |
650 |
651 ignoreBuiltinAssignments = self.__args.get( |
651 ignoreBuiltinAssignments = self.__args.get( |
652 "BuiltinsChecker", self.__defaultArgs["BuiltinsChecker"]) |
652 "BuiltinsChecker", |
|
653 MiscellaneousCheckerDefaultArgs["BuiltinsChecker"]) |
653 |
654 |
654 for node in ast.walk(self.__tree): |
655 for node in ast.walk(self.__tree): |
655 if isinstance(node, ast.Assign): |
656 if isinstance(node, ast.Assign): |
656 # assign statement |
657 # assign statement |
657 for element in node.targets: |
658 for element in node.targets: |
1292 Public method to handle f-string arguments. |
1293 Public method to handle f-string arguments. |
1293 |
1294 |
1294 @param node reference to the node to be processed |
1295 @param node reference to the node to be processed |
1295 @type ast.JoinedStr |
1296 @type ast.JoinedStr |
1296 """ |
1297 """ |
1297 if sys.version_info >= (3, 6): |
1298 if self.__withinLoggingStatement(): |
1298 if self.__withinLoggingStatement(): |
1299 if any(isinstance(i, ast.FormattedValue) for i in node.values): |
1299 if any(isinstance(i, ast.FormattedValue) for i in node.values): |
1300 if self.__withinLoggingArgument(): |
1300 if self.__withinLoggingArgument(): |
1301 self.violations.append((node, "M654")) |
1301 self.violations.append((node, "M654")) |
1302 |
1302 |
1303 super(LoggingVisitor, self).generic_visit(node) |
1303 super(LoggingVisitor, self).generic_visit(node) |
|
1304 |
1304 |
1305 |
1305 |
1306 class BugBearVisitor(ast.NodeVisitor): |
1306 class BugBearVisitor(ast.NodeVisitor): |
1307 """ |
1307 """ |
1308 Class implementing a node visitor to check for various topics. |
1308 Class implementing a node visitor to check for various topics. |
1502 Public method to handle f-string arguments. |
1502 Public method to handle f-string arguments. |
1503 |
1503 |
1504 @param node reference to the node to be processed |
1504 @param node reference to the node to be processed |
1505 @type ast.JoinedStr |
1505 @type ast.JoinedStr |
1506 """ |
1506 """ |
1507 if sys.version_info >= (3, 6): |
1507 for value in node.values: |
1508 for value in node.values: |
1508 if isinstance(value, ast.FormattedValue): |
1509 if isinstance(value, ast.FormattedValue): |
1509 return |
1510 return |
1510 |
1511 |
1511 self.violations.append((node, "M508")) |
1512 self.violations.append((node, "M508")) |
|
1513 |
1512 |
1514 def __checkForM502(self, node): |
1513 def __checkForM502(self, node): |
1515 """ |
1514 """ |
1516 Private method to check the use of *strip(). |
1515 Private method to check the use of *strip(). |
1517 |
1516 |