307 |
307 |
308 # collection of detected errors |
308 # collection of detected errors |
309 self.errors = [] |
309 self.errors = [] |
310 |
310 |
311 checkersWithCodes = [ |
311 checkersWithCodes = [ |
312 (self.__checkCoding, ("M101", "M102")), |
312 (self.__checkCoding, ("M-101", "M-102")), |
313 (self.__checkCopyright, ("M111", "M112")), |
313 (self.__checkCopyright, ("M-111", "M-112")), |
314 (self.__checkBuiltins, ("M131", "M132")), |
314 (self.__checkBuiltins, ("M-131", "M-132")), |
315 ( |
315 ( |
316 self.__checkComprehensions, |
316 self.__checkComprehensions, |
317 ( |
317 ( |
318 "M180", |
318 "M-180", |
319 "M181", |
319 "M-181", |
320 "M182", |
320 "M-182", |
321 "M183", |
321 "M-183", |
322 "M184", |
322 "M-184", |
323 "M185", |
323 "M-185", |
324 "M186", |
324 "M-186", |
325 "M188", |
325 "M-188", |
326 "M189", |
326 "M-189", |
327 "M189a", |
327 "M-189a", |
328 "M189b", |
328 "M-189b", |
329 "M190", |
329 "M-190", |
330 "M190a", |
330 "M-190a", |
331 "M190b", |
331 "M-190b", |
332 "M191", |
332 "M-191", |
333 "M193", |
333 "M-193", |
334 "M193a", |
334 "M-193a", |
335 "M193b", |
335 "M-193b", |
336 "M193c", |
336 "M-193c", |
337 "M194", |
337 "M-194", |
338 "M195", |
338 "M-195", |
339 "M196", |
339 "M-196", |
340 "M197", |
340 "M-197", |
341 "M198", |
341 "M-198", |
342 "M199", |
342 "M-199", |
343 "M200", |
343 "M-200", |
344 ), |
344 ), |
345 ), |
345 ), |
346 (self.__checkDictWithSortedKeys, ("M251",)), |
346 (self.__checkDictWithSortedKeys, ("M-251",)), |
347 ( |
347 ( |
348 self.__checkProperties, |
348 self.__checkProperties, |
349 ("M260", "M261", "M262", "M263", "M264", "M265", "M266", "M267"), |
349 ("M-260", "M-261", "M-262", "M-263", "M-264", "M-265", "M-266", "M-267"), |
350 ), |
350 ), |
351 ( |
351 ( |
352 self.__checkDateTime, |
352 self.__checkDateTime, |
353 ( |
353 ( |
354 "M301", |
354 "M-301", |
355 "M302", |
355 "M-302", |
356 "M303", |
356 "M-303", |
357 "M304", |
357 "M-304", |
358 "M305", |
358 "M-305", |
359 "M306", |
359 "M-306", |
360 "M307", |
360 "M-307", |
361 "M308", |
361 "M-308", |
362 "M311", |
362 "M-311", |
363 "M312", |
363 "M-312", |
364 "M313", |
364 "M-313", |
365 "M314", |
365 "M-314", |
366 "M315", |
366 "M-315", |
367 "M321", |
367 "M-321", |
368 ), |
368 ), |
369 ), |
369 ), |
370 ( |
370 ( |
371 self.__checkSysVersion, |
371 self.__checkSysVersion, |
372 ( |
372 ( |
373 "M401", |
373 "M-401", |
374 "M402", |
374 "M-402", |
375 "M403", |
375 "M-403", |
376 "M411", |
376 "M-411", |
377 "M412", |
377 "M-412", |
378 "M413", |
378 "M-413", |
379 "M414", |
379 "M-414", |
380 "M421", |
380 "M-421", |
381 "M422", |
381 "M-422", |
382 "M423", |
382 "M-423", |
383 ), |
383 ), |
384 ), |
384 ), |
385 ( |
385 ( |
386 self.__checkBugBear, |
386 self.__checkBugBear, |
387 ( |
387 ( |
388 "M501", |
388 "M-501", |
389 "M502", |
389 "M-502", |
390 "M503", |
390 "M-503", |
391 "M504", |
391 "M-504", |
392 "M505", |
392 "M-505", |
393 "M506", |
393 "M-506", |
394 "M507", |
394 "M-507", |
395 "M508", |
395 "M-508", |
396 "M509", |
396 "M-509", |
397 "M510", |
397 "M-510", |
398 "M511", |
398 "M-511", |
399 "M512", |
399 "M-512", |
400 "M513", |
400 "M-513", |
401 "M514", |
401 "M-514", |
402 "M515", |
402 "M-515", |
403 "M516", |
403 "M-516", |
404 "M517", |
404 "M-517", |
405 "M518", |
405 "M-518", |
406 "M519", |
406 "M-519", |
407 "M520", |
407 "M-520", |
408 "M521", |
408 "M-521", |
409 "M522", |
409 "M-522", |
410 "M523", |
410 "M-523", |
411 "M524", |
411 "M-524", |
412 "M525", |
412 "M-525", |
413 "M526", |
413 "M-526", |
414 "M527", |
414 "M-527", |
415 "M528", |
415 "M-528", |
416 "M529", |
416 "M-529", |
417 "M530", |
417 "M-530", |
418 "M531", |
418 "M-531", |
419 "M532", |
419 "M-532", |
420 "M533", |
420 "M-533", |
421 "M534", |
421 "M-534", |
422 "M535", |
422 "M-535", |
423 "M536", |
423 "M-536", |
424 "M537", |
424 "M-537", |
425 "M539", |
425 "M-539", |
426 "M540", |
426 "M-540", |
427 "M541", |
427 "M-541", |
428 "M569", |
428 "M-569", |
429 "M581", |
429 "M-581", |
430 "M582", |
430 "M-582", |
431 ), |
431 ), |
432 ), |
432 ), |
433 (self.__checkPep3101, ("M601",)), |
433 (self.__checkPep3101, ("M-601",)), |
434 ( |
434 ( |
435 self.__checkFormatString, |
435 self.__checkFormatString, |
436 ( |
436 ( |
437 "M611", |
437 "M-611", |
438 "M612", |
438 "M-612", |
439 "M613", |
439 "M-613", |
440 "M621", |
440 "M-621", |
441 "M622", |
441 "M-622", |
442 "M623", |
442 "M-623", |
443 "M624", |
443 "M-624", |
444 "M625", |
444 "M-625", |
445 "M631", |
445 "M-631", |
446 "M632", |
446 "M-632", |
447 ), |
447 ), |
448 ), |
448 ), |
449 (self.__checkFuture, ("M701", "M702")), |
449 (self.__checkFuture, ("M-701", "M-702")), |
450 (self.__checkGettext, ("M711",)), |
450 (self.__checkGettext, ("M-711",)), |
451 (self.__checkPrintStatements, ("M801",)), |
451 (self.__checkPrintStatements, ("M-801",)), |
452 (self.__checkTuple, ("M811",)), |
452 (self.__checkTuple, ("M-811",)), |
453 (self.__checkReturn, ("M831", "M832", "M833", "M834")), |
453 (self.__checkReturn, ("M-831", "M-832", "M-833", "M-834")), |
454 (self.__checkLineContinuation, ("M841",)), |
454 (self.__checkLineContinuation, ("M-841",)), |
455 (self.__checkImplicitStringConcat, ("M851", "M852")), |
455 (self.__checkImplicitStringConcat, ("M-851", "M-852")), |
456 (self.__checkExplicitStringConcat, ("M853",)), |
456 (self.__checkExplicitStringConcat, ("M-853",)), |
457 (self.__checkCommentedCode, ("M891",)), |
457 (self.__checkCommentedCode, ("M-891",)), |
458 (self.__checkDefaultMatchCase, ("M901", "M902")), |
458 (self.__checkDefaultMatchCase, ("M-901", "M-902")), |
459 ] |
459 ] |
460 |
460 |
461 # the eradicate whitelist |
461 # the eradicate whitelist |
462 commentedCodeCheckerArgs = self.__args.get( |
462 commentedCodeCheckerArgs = self.__args.get( |
463 "CommentedCodeChecker", |
463 "CommentedCodeChecker", |
601 if len(topOfSource) < copyrightMinFileSize: |
601 if len(topOfSource) < copyrightMinFileSize: |
602 return |
602 return |
603 |
603 |
604 copyrightRe = re.compile(copyrightRegexStr.format(author=r".*"), re.IGNORECASE) |
604 copyrightRe = re.compile(copyrightRegexStr.format(author=r".*"), re.IGNORECASE) |
605 if not copyrightRe.search(topOfSource): |
605 if not copyrightRe.search(topOfSource): |
606 self.__error(0, 0, "M111") |
606 self.__error(0, 0, "M-111") |
607 return |
607 return |
608 |
608 |
609 if copyrightAuthor: |
609 if copyrightAuthor: |
610 copyrightAuthorRe = re.compile( |
610 copyrightAuthorRe = re.compile( |
611 copyrightRegexStr.format(author=copyrightAuthor), re.IGNORECASE |
611 copyrightRegexStr.format(author=copyrightAuthor), re.IGNORECASE |
612 ) |
612 ) |
613 if not copyrightAuthorRe.search(topOfSource): |
613 if not copyrightAuthorRe.search(topOfSource): |
614 self.__error(0, 0, "M112") |
614 self.__error(0, 0, "M-112") |
615 |
615 |
616 def __checkCommentedCode(self): |
616 def __checkCommentedCode(self): |
617 """ |
617 """ |
618 Private method to check for commented code. |
618 Private method to check for commented code. |
619 """ |
619 """ |
650 for lineIndex, line in enumerate(stripped): |
650 for lineIndex, line in enumerate(stripped): |
651 strippedLine = line.strip() |
651 strippedLine = line.strip() |
652 if strippedLine.endswith("\\") and not strippedLine.startswith( |
652 if strippedLine.endswith("\\") and not strippedLine.startswith( |
653 ("assert", "with") |
653 ("assert", "with") |
654 ): |
654 ): |
655 self.__error(lineIndex, len(line), "M841") |
655 self.__error(lineIndex, len(line), "M-841") |
656 |
656 |
657 def __checkPrintStatements(self): |
657 def __checkPrintStatements(self): |
658 """ |
658 """ |
659 Private method to check for print statements. |
659 Private method to check for print statements. |
660 """ |
660 """ |
661 for node in ast.walk(self.__tree): |
661 for node in ast.walk(self.__tree): |
662 if ( |
662 if ( |
663 isinstance(node, ast.Call) and getattr(node.func, "id", None) == "print" |
663 isinstance(node, ast.Call) and getattr(node.func, "id", None) == "print" |
664 ) or (hasattr(ast, "Print") and isinstance(node, ast.Print)): |
664 ) or (hasattr(ast, "Print") and isinstance(node, ast.Print)): |
665 self.__error(node.lineno - 1, node.col_offset, "M801") |
665 self.__error(node.lineno - 1, node.col_offset, "M-801") |
666 |
666 |
667 def __checkTuple(self): |
667 def __checkTuple(self): |
668 """ |
668 """ |
669 Private method to check for one element tuples. |
669 Private method to check for one element tuples. |
670 """ |
670 """ |
671 for node in ast.walk(self.__tree): |
671 for node in ast.walk(self.__tree): |
672 if isinstance(node, ast.Tuple) and len(node.elts) == 1: |
672 if isinstance(node, ast.Tuple) and len(node.elts) == 1: |
673 self.__error(node.lineno - 1, node.col_offset, "M811") |
673 self.__error(node.lineno - 1, node.col_offset, "M-811") |
674 |
674 |
675 def __checkFuture(self): |
675 def __checkFuture(self): |
676 """ |
676 """ |
677 Private method to check the __future__ imports. |
677 Private method to check the __future__ imports. |
678 """ |
678 """ |
759 except UnicodeDecodeError: |
759 except UnicodeDecodeError: |
760 continue |
760 continue |
761 fields, implicit, explicit = self.__getFields(text) |
761 fields, implicit, explicit = self.__getFields(text) |
762 if implicit: |
762 if implicit: |
763 if node in visitor.calls: |
763 if node in visitor.calls: |
764 self.__error(node.lineno - 1, node.col_offset, "M611") |
764 self.__error(node.lineno - 1, node.col_offset, "M-611") |
765 else: |
765 else: |
766 if node.is_docstring: |
766 if node.is_docstring: |
767 self.__error(node.lineno - 1, node.col_offset, "M612") |
767 self.__error(node.lineno - 1, node.col_offset, "M-612") |
768 else: |
768 else: |
769 self.__error(node.lineno - 1, node.col_offset, "M613") |
769 self.__error(node.lineno - 1, node.col_offset, "M-613") |
770 |
770 |
771 if node in visitor.calls: |
771 if node in visitor.calls: |
772 call, strArgs = visitor.calls[node] |
772 call, strArgs = visitor.calls[node] |
773 |
773 |
774 numbers = set() |
774 numbers = set() |
802 |
802 |
803 # if starargs or kwargs is not None, it can't count the |
803 # if starargs or kwargs is not None, it can't count the |
804 # parameters but at least check if the args are used |
804 # parameters but at least check if the args are used |
805 if hasKwArgs and not names: |
805 if hasKwArgs and not names: |
806 # No names but kwargs |
806 # No names but kwargs |
807 self.__error(call.lineno - 1, call.col_offset, "M623") |
807 self.__error(call.lineno - 1, call.col_offset, "M-623") |
808 if hasStarArgs and not numbers: |
808 if hasStarArgs and not numbers: |
809 # No numbers but args |
809 # No numbers but args |
810 self.__error(call.lineno - 1, call.col_offset, "M624") |
810 self.__error(call.lineno - 1, call.col_offset, "M-624") |
811 |
811 |
812 if not hasKwArgs and not hasStarArgs: |
812 if not hasKwArgs and not hasStarArgs: |
813 # can actually verify numbers and names |
813 # can actually verify numbers and names |
814 for number in sorted(numbers): |
814 for number in sorted(numbers): |
815 if number >= numArgs: |
815 if number >= numArgs: |
816 self.__error( |
816 self.__error( |
817 call.lineno - 1, call.col_offset, "M621", number |
817 call.lineno - 1, call.col_offset, "M-621", number |
818 ) |
818 ) |
819 |
819 |
820 for name in sorted(names): |
820 for name in sorted(names): |
821 if name not in keywords: |
821 if name not in keywords: |
822 self.__error(call.lineno - 1, call.col_offset, "M622", name) |
822 self.__error(call.lineno - 1, call.col_offset, "M-622", name) |
823 |
823 |
824 for arg in range(numArgs): |
824 for arg in range(numArgs): |
825 if arg not in numbers: |
825 if arg not in numbers: |
826 self.__error(call.lineno - 1, call.col_offset, "M631", arg) |
826 self.__error(call.lineno - 1, call.col_offset, "M-631", arg) |
827 |
827 |
828 for keyword in keywords: |
828 for keyword in keywords: |
829 if keyword not in names: |
829 if keyword not in names: |
830 self.__error(call.lineno - 1, call.col_offset, "M632", keyword) |
830 self.__error(call.lineno - 1, call.col_offset, "M-632", keyword) |
831 |
831 |
832 if implicit and explicit: |
832 if implicit and explicit: |
833 self.__error(call.lineno - 1, call.col_offset, "M625") |
833 self.__error(call.lineno - 1, call.col_offset, "M-625") |
834 |
834 |
835 def __getFields(self, string): |
835 def __getFields(self, string): |
836 """ |
836 """ |
837 Private method to extract the format field information. |
837 Private method to extract the format field information. |
838 |
838 |
889 and value.id in ignoreBuiltinAssignments[element.id] |
889 and value.id in ignoreBuiltinAssignments[element.id] |
890 ): |
890 ): |
891 # ignore compatibility assignments |
891 # ignore compatibility assignments |
892 continue |
892 continue |
893 self.__error( |
893 self.__error( |
894 element.lineno - 1, element.col_offset, "M131", element.id |
894 element.lineno - 1, element.col_offset, "M-131", element.id |
895 ) |
895 ) |
896 elif isinstance(element, (ast.Tuple, ast.List)): |
896 elif isinstance(element, (ast.Tuple, ast.List)): |
897 for tupleElement in element.elts: |
897 for tupleElement in element.elts: |
898 if ( |
898 if ( |
899 isinstance(tupleElement, ast.Name) |
899 isinstance(tupleElement, ast.Name) |
900 and tupleElement.id in self.__builtins |
900 and tupleElement.id in self.__builtins |
901 ): |
901 ): |
902 self.__error( |
902 self.__error( |
903 tupleElement.lineno - 1, |
903 tupleElement.lineno - 1, |
904 tupleElement.col_offset, |
904 tupleElement.col_offset, |
905 "M131", |
905 "M-131", |
906 tupleElement.id, |
906 tupleElement.id, |
907 ) |
907 ) |
908 elif isinstance(node, ast.For): |
908 elif isinstance(node, ast.For): |
909 # for loop |
909 # for loop |
910 target = node.target |
910 target = node.target |
911 if isinstance(target, ast.Name) and target.id in self.__builtins: |
911 if isinstance(target, ast.Name) and target.id in self.__builtins: |
912 self.__error( |
912 self.__error( |
913 target.lineno - 1, target.col_offset, "M131", target.id |
913 target.lineno - 1, target.col_offset, "M-131", target.id |
914 ) |
914 ) |
915 elif isinstance(target, (ast.Tuple, ast.List)): |
915 elif isinstance(target, (ast.Tuple, ast.List)): |
916 for element in target.elts: |
916 for element in target.elts: |
917 if ( |
917 if ( |
918 isinstance(element, ast.Name) |
918 isinstance(element, ast.Name) |
919 and element.id in self.__builtins |
919 and element.id in self.__builtins |
920 ): |
920 ): |
921 self.__error( |
921 self.__error( |
922 element.lineno - 1, |
922 element.lineno - 1, |
923 element.col_offset, |
923 element.col_offset, |
924 "M131", |
924 "M-131", |
925 element.id, |
925 element.id, |
926 ) |
926 ) |
927 elif any(isinstance(node, functionDef) for functionDef in functionDefs): |
927 elif any(isinstance(node, functionDef) for functionDef in functionDefs): |
928 # (asynchronous) function definition |
928 # (asynchronous) function definition |
929 for arg in node.args.args: |
929 for arg in node.args.args: |
930 if isinstance(arg, ast.arg) and arg.arg in self.__builtins: |
930 if isinstance(arg, ast.arg) and arg.arg in self.__builtins: |
931 self.__error(arg.lineno - 1, arg.col_offset, "M132", arg.arg) |
931 self.__error(arg.lineno - 1, arg.col_offset, "M-132", arg.arg) |
932 |
932 |
933 def __checkComprehensions(self): |
933 def __checkComprehensions(self): |
934 """ |
934 """ |
935 Private method to check some comprehension related things. |
935 Private method to check some comprehension related things. |
936 |
936 |
968 and isinstance(node.args[0], (ast.GeneratorExp, ast.ListComp)) |
968 and isinstance(node.args[0], (ast.GeneratorExp, ast.ListComp)) |
969 and isinstance(node.args[0].elt, ast.Tuple) |
969 and isinstance(node.args[0].elt, ast.Tuple) |
970 and len(node.args[0].elt.elts) == 2 |
970 and len(node.args[0].elt.elts) == 2 |
971 ): |
971 ): |
972 if isinstance(node.args[0], ast.GeneratorExp): |
972 if isinstance(node.args[0], ast.GeneratorExp): |
973 errorCode = "M182" |
973 errorCode = "M-182" |
974 else: |
974 else: |
975 errorCode = "M184" |
975 errorCode = "M-184" |
976 self.__error(node.lineno - 1, node.col_offset, errorCode) |
976 self.__error(node.lineno - 1, node.col_offset, errorCode) |
977 |
977 |
978 elif ( |
978 elif ( |
979 numPositionalArgs == 1 |
979 numPositionalArgs == 1 |
980 and isinstance(node.args[0], ast.ListComp) |
980 and isinstance(node.args[0], ast.ListComp) |
981 and node.func.id in ("list", "set", "any", "all") |
981 and node.func.id in ("list", "set", "any", "all") |
982 ): |
982 ): |
983 errorCode = { |
983 errorCode = { |
984 "list": "M191", |
984 "list": "M-191", |
985 "set": "M183", |
985 "set": "M-183", |
986 "any": "M199", |
986 "any": "M-199", |
987 "all": "M199", |
987 "all": "M-199", |
988 }[node.func.id] |
988 }[node.func.id] |
989 self.__error( |
989 self.__error( |
990 node.lineno - 1, node.col_offset, errorCode, node.func.id |
990 node.lineno - 1, node.col_offset, errorCode, node.func.id |
991 ) |
991 ) |
992 |
992 |
1145 and isinstance(node.args[0].slice.step, ast.UnaryOp) |
1145 and isinstance(node.args[0].slice.step, ast.UnaryOp) |
1146 and isinstance(node.args[0].slice.step.op, ast.USub) |
1146 and isinstance(node.args[0].slice.step.op, ast.USub) |
1147 and isinstance(node.args[0].slice.step.operand, ast.Constant) |
1147 and isinstance(node.args[0].slice.step.operand, ast.Constant) |
1148 and node.args[0].slice.step.operand.n == 1 |
1148 and node.args[0].slice.step.operand.n == 1 |
1149 ): |
1149 ): |
1150 self.__error(node.lineno - 1, node.col_offset, "M195", node.func.id) |
1150 self.__error(node.lineno - 1, node.col_offset, "M-195", node.func.id) |
1151 |
1151 |
1152 elif ( |
1152 elif ( |
1153 node.func.id == "map" |
1153 node.func.id == "map" |
1154 and node not in visitedMapCalls |
1154 and node not in visitedMapCalls |
1155 and len(node.args) == 2 |
1155 and len(node.args) == 2 |
1156 and isinstance(node.args[0], ast.Lambda) |
1156 and isinstance(node.args[0], ast.Lambda) |
1157 ): |
1157 ): |
1158 self.__error( |
1158 self.__error( |
1159 node.lineno - 1, node.col_offset, "M197", "generator expression" |
1159 node.lineno - 1, node.col_offset, "M-197", "generator expression" |
1160 ) |
1160 ) |
1161 |
1161 |
1162 elif ( |
1162 elif ( |
1163 node.func.id in ("list", "set", "dict") |
1163 node.func.id in ("list", "set", "dict") |
1164 and len(node.args) == 1 |
1164 and len(node.args) == 1 |
1392 if node.name != decorator.value.id: |
1392 if node.name != decorator.value.id: |
1393 if node.name in properties: |
1393 if node.name in properties: |
1394 self.__error( |
1394 self.__error( |
1395 node.lineno - 1, |
1395 node.lineno - 1, |
1396 node.col_offset, |
1396 node.col_offset, |
1397 "M266", |
1397 "M-266", |
1398 node.name, |
1398 node.name, |
1399 decorator.value.id, |
1399 decorator.value.id, |
1400 ) |
1400 ) |
1401 else: |
1401 else: |
1402 self.__error( |
1402 self.__error( |
1403 node.lineno - 1, |
1403 node.lineno - 1, |
1404 node.col_offset, |
1404 node.col_offset, |
1405 "M264", |
1405 "M-264", |
1406 decorator.value.id, |
1406 decorator.value.id, |
1407 node.name, |
1407 node.name, |
1408 ) |
1408 ) |
1409 if len(node.args.args) != 1: |
1409 if len(node.args.args) != 1: |
1410 self.__error( |
1410 self.__error( |
1411 node.lineno - 1, |
1411 node.lineno - 1, |
1412 node.col_offset, |
1412 node.col_offset, |
1413 "M262", |
1413 "M-262", |
1414 len(node.args.args), |
1414 len(node.args.args), |
1415 ) |
1415 ) |
1416 |
1416 |
1417 if propertyCount > 1: |
1417 if propertyCount > 1: |
1418 self.__error(node.lineno - 1, node.col_offset, "M267", node.name) |
1418 self.__error(node.lineno - 1, node.col_offset, "M-267", node.name) |
1419 |
1419 |
1420 ####################################################################### |
1420 ####################################################################### |
1421 ## The following methods check for implicitly concatenated strings. |
1421 ## The following methods check for implicitly concatenated strings. |
1422 ## |
1422 ## |
1423 ## These methods are adapted from: flake8-implicit-str-concat v0.5.0 |
1423 ## These methods are adapted from: flake8-implicit-str-concat v0.5.0 |
2092 self.__M540CaughtException = M540CaughtException(node.name, False) |
2092 self.__M540CaughtException = M540CaughtException(node.name, False) |
2093 |
2093 |
2094 names = self.__checkForM513_M514_M529_M530(node) |
2094 names = self.__checkForM513_M514_M529_M530(node) |
2095 |
2095 |
2096 if "BaseException" in names and not ExceptBaseExceptionVisitor(node).reRaised(): |
2096 if "BaseException" in names and not ExceptBaseExceptionVisitor(node).reRaised(): |
2097 self.violations.append((node, "M536")) |
2097 self.violations.append((node, "M-536")) |
2098 |
2098 |
2099 self.generic_visit(node) |
2099 self.generic_visit(node) |
2100 |
2100 |
2101 if ( |
2101 if ( |
2102 self.__M540CaughtException is not None |
2102 self.__M540CaughtException is not None |
2103 and self.__M540CaughtException.hasNote |
2103 and self.__M540CaughtException.hasNote |
2104 ): |
2104 ): |
2105 self.violations.append((node, "M540")) |
2105 self.violations.append((node, "M-540")) |
2106 self.__M540CaughtException = oldM540CaughtException |
2106 self.__M540CaughtException = oldM540CaughtException |
2107 |
2107 |
2108 def visit_UAdd(self, node): |
2108 def visit_UAdd(self, node): |
2109 """ |
2109 """ |
2110 Public method to handle unary additions. |
2110 Public method to handle unary additions. |
2141 and isinstance(args[0], ast.Attribute) |
2141 and isinstance(args[0], ast.Attribute) |
2142 and isinstance(args[0].value, ast.Name) |
2142 and isinstance(args[0].value, ast.Name) |
2143 and args[0].value.id == "self" |
2143 and args[0].value.id == "self" |
2144 and args[0].attr == "__class__" |
2144 and args[0].attr == "__class__" |
2145 ): |
2145 ): |
2146 self.violations.append((node, "M582")) |
2146 self.violations.append((node, "M-582")) |
2147 |
2147 |
2148 # bad getattr and setattr |
2148 # bad getattr and setattr |
2149 if ( |
2149 if ( |
2150 node.func.id in ("getattr", "hasattr") |
2150 node.func.id in ("getattr", "hasattr") |
2151 and node.args[1].value == "__call__" |
2151 and node.args[1].value == "__call__" |
2152 ): |
2152 ): |
2153 self.violations.append((node, "M504")) |
2153 self.violations.append((node, "M-504")) |
2154 if ( |
2154 if ( |
2155 node.func.id == "getattr" |
2155 node.func.id == "getattr" |
2156 and len(node.args) == 2 |
2156 and len(node.args) == 2 |
2157 and self.__isIdentifier(node.args[1]) |
2157 and self.__isIdentifier(node.args[1]) |
2158 and iskeyword(AstUtilities.getValue(node.args[1])) |
2158 and iskeyword(AstUtilities.getValue(node.args[1])) |
2159 ): |
2159 ): |
2160 self.violations.append((node, "M509")) |
2160 self.violations.append((node, "M-509")) |
2161 elif ( |
2161 elif ( |
2162 node.func.id == "setattr" |
2162 node.func.id == "setattr" |
2163 and len(node.args) == 3 |
2163 and len(node.args) == 3 |
2164 and self.__isIdentifier(node.args[1]) |
2164 and self.__isIdentifier(node.args[1]) |
2165 and iskeyword(AstUtilities.getValue(node.args[1])) |
2165 and iskeyword(AstUtilities.getValue(node.args[1])) |
2166 ): |
2166 ): |
2167 self.violations.append((node, "M510")) |
2167 self.violations.append((node, "M-510")) |
2168 |
2168 |
2169 self.__checkForM526(node) |
2169 self.__checkForM526(node) |
2170 |
2170 |
2171 self.__checkForM528(node) |
2171 self.__checkForM528(node) |
2172 self.__checkForM534(node) |
2172 self.__checkForM534(node) |
2510 return # stripping just one character |
2510 return # stripping just one character |
2511 |
2511 |
2512 if len(value) == len(set(value)): |
2512 if len(value) == len(set(value)): |
2513 return # no characters appear more than once |
2513 return # no characters appear more than once |
2514 |
2514 |
2515 self.violations.append((node, "M505")) |
2515 self.violations.append((node, "M-505")) |
2516 |
2516 |
2517 def __checkForM506_M508(self, node): |
2517 def __checkForM506_M508(self, node): |
2518 """ |
2518 """ |
2519 Private method to check the use of mutable literals, comprehensions and calls. |
2519 Private method to check the use of mutable literals, comprehensions and calls. |
2520 |
2520 |
2521 @param node reference to the node to be processed |
2521 @param node reference to the node to be processed |
2522 @type ast.AsyncFunctionDef or ast.FunctionDef |
2522 @type ast.AsyncFunctionDef or ast.FunctionDef |
2523 """ |
2523 """ |
2524 visitor = FunctionDefDefaultsVisitor("M506", "M508") |
2524 visitor = FunctionDefDefaultsVisitor("M-506", "M-508") |
2525 visitor.visit(node.args.defaults + node.args.kw_defaults) |
2525 visitor.visit(node.args.defaults + node.args.kw_defaults) |
2526 self.violations.extend(visitor.errors) |
2526 self.violations.extend(visitor.errors) |
2527 |
2527 |
2528 def __checkForM507(self, node): |
2528 def __checkForM507(self, node): |
2529 """ |
2529 """ |
2557 |
2557 |
2558 if isinstance(node, (ast.While, ast.For)): |
2558 if isinstance(node, (ast.While, ast.For)): |
2559 badNodeTypes = (ast.Return,) |
2559 badNodeTypes = (ast.Return,) |
2560 |
2560 |
2561 elif isinstance(node, badNodeTypes): |
2561 elif isinstance(node, badNodeTypes): |
2562 self.violations.append((node, "M512", self.__inTryStar)) |
2562 self.violations.append((node, "M-512", self.__inTryStar)) |
2563 |
2563 |
2564 for child in ast.iter_child_nodes(node): |
2564 for child in ast.iter_child_nodes(node): |
2565 _loop(child, badNodeTypes) |
2565 _loop(child, badNodeTypes) |
2566 |
2566 |
2567 for child in node.finalbody: |
2567 for child in node.finalbody: |
2591 elif isinstance(handler, (ast.Call, ast.Starred)): |
2591 elif isinstance(handler, (ast.Call, ast.Starred)): |
2592 ignoredHandlers.append(handler) |
2592 ignoredHandlers.append(handler) |
2593 else: |
2593 else: |
2594 badHandlers.append(handler) |
2594 badHandlers.append(handler) |
2595 if badHandlers: |
2595 if badHandlers: |
2596 self.violations.append((node, "M530")) |
2596 self.violations.append((node, "M-530")) |
2597 if len(names) == 0 and not badHandlers and not ignoredHandlers: |
2597 if len(names) == 0 and not badHandlers and not ignoredHandlers: |
2598 self.violations.append((node, "M529", self.__inTryStar)) |
2598 self.violations.append((node, "M-529", self.__inTryStar)) |
2599 elif ( |
2599 elif ( |
2600 len(names) == 1 |
2600 len(names) == 1 |
2601 and not badHandlers |
2601 and not badHandlers |
2602 and not ignoredHandlers |
2602 and not ignoredHandlers |
2603 and isinstance(node.type, ast.Tuple) |
2603 and isinstance(node.type, ast.Tuple) |
2604 ): |
2604 ): |
2605 self.violations.append((node, "M513", *names, self.__inTryStar)) |
2605 self.violations.append((node, "M-513", *names, self.__inTryStar)) |
2606 else: |
2606 else: |
2607 maybeError = self.__checkRedundantExcepthandlers( |
2607 maybeError = self.__checkRedundantExcepthandlers( |
2608 names, node, self.__inTryStar |
2608 names, node, self.__inTryStar |
2609 ) |
2609 ) |
2610 if maybeError is not None: |
2610 if maybeError is not None: |
2858 if suspiciousVariables: |
2858 if suspiciousVariables: |
2859 reassignedInLoop = set(self.__getAssignedNames(loopNode)) |
2859 reassignedInLoop = set(self.__getAssignedNames(loopNode)) |
2860 |
2860 |
2861 for err in sorted(suspiciousVariables): |
2861 for err in sorted(suspiciousVariables): |
2862 if reassignedInLoop.issuperset(err[2]): |
2862 if reassignedInLoop.issuperset(err[2]): |
2863 self.violations.append((err[3], "M523", err[2])) |
2863 self.violations.append((err[3], "M-523", err[2])) |
2864 |
2864 |
2865 def __checkForM524_M527(self, node): |
2865 def __checkForM524_M527(self, node): |
2866 """ |
2866 """ |
2867 Private method to check for inheritance from abstract classes in abc and lack of |
2867 Private method to check for inheritance from abstract classes in abc and lack of |
2868 any methods decorated with abstract*. |
2868 any methods decorated with abstract*. |
2939 if ( |
2939 if ( |
2940 not hasAbstractDecorator |
2940 not hasAbstractDecorator |
2941 and emptyBody(stmt.body) |
2941 and emptyBody(stmt.body) |
2942 and not any(map(isOverload, stmt.decorator_list)) |
2942 and not any(map(isOverload, stmt.decorator_list)) |
2943 ): |
2943 ): |
2944 self.violations.append((stmt, "M527", stmt.name)) |
2944 self.violations.append((stmt, "M-527", stmt.name)) |
2945 |
2945 |
2946 if hasMethod and not hasAbstractMethod: |
2946 if hasMethod and not hasAbstractMethod: |
2947 self.violations.append((node, "M524", node.name)) |
2947 self.violations.append((node, "M-524", node.name)) |
2948 |
2948 |
2949 def __checkForM525(self, node): |
2949 def __checkForM525(self, node): |
2950 """ |
2950 """ |
2951 Private method to check for exceptions being handled multiple times. |
2951 Private method to check for exceptions being handled multiple times. |
2952 |
2952 |
3049 for nestedNode in self.__walkList(node.body): |
3049 for nestedNode in self.__walkList(node.body): |
3050 if ( |
3050 if ( |
3051 isinstance(nestedNode, ast.Name) |
3051 isinstance(nestedNode, ast.Name) |
3052 and nestedNode.id == groupName |
3052 and nestedNode.id == groupName |
3053 ): |
3053 ): |
3054 self.violations.append((nestedNode, "M531")) |
3054 self.violations.append((nestedNode, "M-531")) |
3055 |
3055 |
3056 # Handle multiple uses |
3056 # Handle multiple uses |
3057 if isinstance(node, ast.Name) and node.id == groupName: |
3057 if isinstance(node, ast.Name) and node.id == groupName: |
3058 numUsages += 1 |
3058 numUsages += 1 |
3059 if numUsages > 1: |
3059 if numUsages > 1: |
3060 self.violations.append((nestedNode, "M531")) |
3060 self.violations.append((nestedNode, "M-531")) |
3061 |
3061 |
3062 def __checkForM532(self, node): |
3062 def __checkForM532(self, node): |
3063 """ |
3063 """ |
3064 Private method to check for possible unintentional typing annotation. |
3064 Private method to check for possible unintentional typing annotation. |
3065 |
3065 |
3129 |
3129 |
3130 @param node reference to the node to be processed |
3130 @param node reference to the node to be processed |
3131 @type ast.DictComp |
3131 @type ast.DictComp |
3132 """ |
3132 """ |
3133 if isinstance(node.key, ast.Constant): |
3133 if isinstance(node.key, ast.Constant): |
3134 self.violations.append((node, "M535", node.key.value)) |
3134 self.violations.append((node, "M-535", node.key.value)) |
3135 elif isinstance( |
3135 elif isinstance( |
3136 node.key, ast.Name |
3136 node.key, ast.Name |
3137 ) and node.key.id not in self.__getDictCompLoopAndNamedExprVarNames(node): |
3137 ) and node.key.id not in self.__getDictCompLoopAndNamedExprVarNames(node): |
3138 self.violations.append((node, "M535", node.key.id)) |
3138 self.violations.append((node, "M-535", node.key.id)) |
3139 |
3139 |
3140 def __checkForM539(self, node): |
3140 def __checkForM539(self, node): |
3141 """ |
3141 """ |
3142 Private method to check for correct ContextVar usage. |
3142 Private method to check for correct ContextVar usage. |
3143 |
3143 |
3943 """ |
3943 """ |
3944 Private method to check for implicit return values. |
3944 Private method to check for implicit return values. |
3945 """ |
3945 """ |
3946 for node in self.returns: |
3946 for node in self.returns: |
3947 if not node.value: |
3947 if not node.value: |
3948 self.violations.append((node, "M832")) |
3948 self.violations.append((node, "M-832")) |
3949 |
3949 |
3950 def __checkUnnecessaryReturnNone(self): |
3950 def __checkUnnecessaryReturnNone(self): |
3951 """ |
3951 """ |
3952 Private method to check for an unnecessary 'return None' statement. |
3952 Private method to check for an unnecessary 'return None' statement. |
3953 """ |
3953 """ |
3954 for node in self.returns: |
3954 for node in self.returns: |
3955 if self.__isNone(node.value): |
3955 if self.__isNone(node.value): |
3956 self.violations.append((node, "M831")) |
3956 self.violations.append((node, "M-831")) |
3957 |
3957 |
3958 def __checkImplicitReturn(self, node): |
3958 def __checkImplicitReturn(self, node): |
3959 """ |
3959 """ |
3960 Private method to check for an implicit return statement. |
3960 Private method to check for an implicit return statement. |
3961 |
3961 |
3962 @param node reference to the node to check |
3962 @param node reference to the node to check |
3963 @type ast.AST |
3963 @type ast.AST |
3964 """ |
3964 """ |
3965 if isinstance(node, ast.If): |
3965 if isinstance(node, ast.If): |
3966 if not node.body or not node.orelse: |
3966 if not node.body or not node.orelse: |
3967 self.violations.append((node, "M833")) |
3967 self.violations.append((node, "M-833")) |
3968 return |
3968 return |
3969 |
3969 |
3970 self.__checkImplicitReturn(node.body[-1]) |
3970 self.__checkImplicitReturn(node.body[-1]) |
3971 self.__checkImplicitReturn(node.orelse[-1]) |
3971 self.__checkImplicitReturn(node.orelse[-1]) |
3972 return |
3972 return |
3985 try: |
3985 try: |
3986 okNodes = (ast.Return, ast.Raise, ast.While, ast.Try) |
3986 okNodes = (ast.Return, ast.Raise, ast.While, ast.Try) |
3987 except AttributeError: |
3987 except AttributeError: |
3988 okNodes = (ast.Return, ast.Raise, ast.While) |
3988 okNodes = (ast.Return, ast.Raise, ast.While) |
3989 if not isinstance(node, okNodes): |
3989 if not isinstance(node, okNodes): |
3990 self.violations.append((node, "M833")) |
3990 self.violations.append((node, "M-833")) |
3991 |
3991 |
3992 def __checkUnnecessaryAssign(self, node): |
3992 def __checkUnnecessaryAssign(self, node): |
3993 """ |
3993 """ |
3994 Private method to check for an unnecessary assign statement. |
3994 Private method to check for an unnecessary assign statement. |
3995 |
3995 |
4124 AstUtilities.isNameConstant(tzinfoKeyword.value) |
4124 AstUtilities.isNameConstant(tzinfoKeyword.value) |
4125 and AstUtilities.getValue(tzinfoKeyword.value) is None |
4125 and AstUtilities.getValue(tzinfoKeyword.value) is None |
4126 ) |
4126 ) |
4127 |
4127 |
4128 if not (isCase1 or isCase2): |
4128 if not (isCase1 or isCase2): |
4129 self.violations.append((node, "M301")) |
4129 self.violations.append((node, "M-301")) |
4130 |
4130 |
4131 elif node.func.attr == "time": |
4131 elif node.func.attr == "time": |
4132 # time(12, 10, 45, 0, datetime.timezone.utc) |
4132 # time(12, 10, 45, 0, datetime.timezone.utc) |
4133 isCase1 = len(node.args) >= 5 and not ( |
4133 isCase1 = len(node.args) >= 5 and not ( |
4134 AstUtilities.isNameConstant(node.args[4]) |
4134 AstUtilities.isNameConstant(node.args[4]) |
4141 AstUtilities.isNameConstant(tzinfoKeyword.value) |
4141 AstUtilities.isNameConstant(tzinfoKeyword.value) |
4142 and AstUtilities.getValue(tzinfoKeyword.value) is None |
4142 and AstUtilities.getValue(tzinfoKeyword.value) is None |
4143 ) |
4143 ) |
4144 |
4144 |
4145 if not (isCase1 or isCase2): |
4145 if not (isCase1 or isCase2): |
4146 self.violations.append((node, "M321")) |
4146 self.violations.append((node, "M-321")) |
4147 |
4147 |
4148 elif node.func.attr == "date": |
4148 elif node.func.attr == "date": |
4149 self.violations.append((node, "M311")) |
4149 self.violations.append((node, "M-311")) |
4150 |
4150 |
4151 if isDateTimeClass or isDateTimeModuleAndClass: |
4151 if isDateTimeClass or isDateTimeModuleAndClass: |
4152 if node.func.attr == "today": |
4152 if node.func.attr == "today": |
4153 self.violations.append((node, "M302")) |
4153 self.violations.append((node, "M-302")) |
4154 |
4154 |
4155 elif node.func.attr == "utcnow": |
4155 elif node.func.attr == "utcnow": |
4156 self.violations.append((node, "M303")) |
4156 self.violations.append((node, "M-303")) |
4157 |
4157 |
4158 elif node.func.attr == "utcfromtimestamp": |
4158 elif node.func.attr == "utcfromtimestamp": |
4159 self.violations.append((node, "M304")) |
4159 self.violations.append((node, "M-304")) |
4160 |
4160 |
4161 elif node.func.attr in "now": |
4161 elif node.func.attr in "now": |
4162 # datetime.now(UTC) |
4162 # datetime.now(UTC) |
4163 isCase1 = ( |
4163 isCase1 = ( |
4164 len(node.args) == 1 |
4164 len(node.args) == 1 |
4196 AstUtilities.isNameConstant(tzKeyword.value) |
4196 AstUtilities.isNameConstant(tzKeyword.value) |
4197 and AstUtilities.getValue(tzKeyword.value) is None |
4197 and AstUtilities.getValue(tzKeyword.value) is None |
4198 ) |
4198 ) |
4199 |
4199 |
4200 if not (isCase1 or isCase2): |
4200 if not (isCase1 or isCase2): |
4201 self.violations.append((node, "M306")) |
4201 self.violations.append((node, "M-306")) |
4202 |
4202 |
4203 elif node.func.attr == "strptime": |
4203 elif node.func.attr == "strptime": |
4204 # datetime.strptime(...).replace(tzinfo=UTC) |
4204 # datetime.strptime(...).replace(tzinfo=UTC) |
4205 parent = getattr(node, "_dtCheckerParent", None) |
4205 parent = getattr(node, "_dtCheckerParent", None) |
4206 pparent = getattr(parent, "_dtCheckerParent", None) |
4206 pparent = getattr(parent, "_dtCheckerParent", None) |
4237 and node.func.value.value.id == "datetime" |
4237 and node.func.value.value.id == "datetime" |
4238 ) |
4238 ) |
4239 |
4239 |
4240 if isDateClass or isDateModuleAndClass: |
4240 if isDateClass or isDateModuleAndClass: |
4241 if node.func.attr == "today": |
4241 if node.func.attr == "today": |
4242 self.violations.append((node, "M312")) |
4242 self.violations.append((node, "M-312")) |
4243 |
4243 |
4244 elif node.func.attr == "fromtimestamp": |
4244 elif node.func.attr == "fromtimestamp": |
4245 self.violations.append((node, "M313")) |
4245 self.violations.append((node, "M-313")) |
4246 |
4246 |
4247 elif node.func.attr == "fromordinal": |
4247 elif node.func.attr == "fromordinal": |
4248 self.violations.append((node, "M314")) |
4248 self.violations.append((node, "M-314")) |
4249 |
4249 |
4250 elif node.func.attr == "fromisoformat": |
4250 elif node.func.attr == "fromisoformat": |
4251 self.violations.append((node, "M315")) |
4251 self.violations.append((node, "M-315")) |
4252 |
4252 |
4253 self.generic_visit(node) |
4253 self.generic_visit(node) |
4254 |
4254 |
4255 |
4255 |
4256 class SysVersionVisitor(ast.NodeVisitor): |
4256 class SysVersionVisitor(ast.NodeVisitor): |
4335 |
4335 |
4336 @param node reference to the node to be processed |
4336 @param node reference to the node to be processed |
4337 @type ast.Subscript |
4337 @type ast.Subscript |
4338 """ |
4338 """ |
4339 if self.__isSysVersionUpperSlice(node, 1): |
4339 if self.__isSysVersionUpperSlice(node, 1): |
4340 self.violations.append((node.value, "M423")) |
4340 self.violations.append((node.value, "M-423")) |
4341 elif self.__isSysVersionUpperSlice(node, 3): |
4341 elif self.__isSysVersionUpperSlice(node, 3): |
4342 self.violations.append((node.value, "M401")) |
4342 self.violations.append((node.value, "M-401")) |
4343 elif ( |
4343 elif ( |
4344 self.__isSys("version", node.value) |
4344 self.__isSys("version", node.value) |
4345 and isinstance(node.slice, ast.Index) |
4345 and isinstance(node.slice, ast.Index) |
4346 and AstUtilities.isNumber(node.slice.value) |
4346 and AstUtilities.isNumber(node.slice.value) |
4347 and AstUtilities.getValue(node.slice.value) == 2 |
4347 and AstUtilities.getValue(node.slice.value) == 2 |
4348 ): |
4348 ): |
4349 self.violations.append((node.value, "M402")) |
4349 self.violations.append((node.value, "M-402")) |
4350 elif ( |
4350 elif ( |
4351 self.__isSys("version", node.value) |
4351 self.__isSys("version", node.value) |
4352 and isinstance(node.slice, ast.Index) |
4352 and isinstance(node.slice, ast.Index) |
4353 and AstUtilities.isNumber(node.slice.value) |
4353 and AstUtilities.isNumber(node.slice.value) |
4354 and AstUtilities.getValue(node.slice.value) == 0 |
4354 and AstUtilities.getValue(node.slice.value) == 0 |
4355 ): |
4355 ): |
4356 self.violations.append((node.value, "M421")) |
4356 self.violations.append((node.value, "M-421")) |
4357 |
4357 |
4358 self.generic_visit(node) |
4358 self.generic_visit(node) |
4359 |
4359 |
4360 def visit_Compare(self, node): |
4360 def visit_Compare(self, node): |
4361 """ |
4361 """ |
4373 and len(node.ops) == 1 |
4373 and len(node.ops) == 1 |
4374 and isinstance(node.ops[0], ast.Eq) |
4374 and isinstance(node.ops[0], ast.Eq) |
4375 and AstUtilities.isNumber(node.comparators[0]) |
4375 and AstUtilities.isNumber(node.comparators[0]) |
4376 and AstUtilities.getValue(node.comparators[0]) == 3 |
4376 and AstUtilities.getValue(node.comparators[0]) == 3 |
4377 ): |
4377 ): |
4378 self.violations.append((node.left, "M411")) |
4378 self.violations.append((node.left, "M-411")) |
4379 elif ( |
4379 elif ( |
4380 self.__isSys("version", node.left) |
4380 self.__isSys("version", node.left) |
4381 and len(node.ops) == 1 |
4381 and len(node.ops) == 1 |
4382 and isinstance(node.ops[0], (ast.Lt, ast.LtE, ast.Gt, ast.GtE)) |
4382 and isinstance(node.ops[0], (ast.Lt, ast.LtE, ast.Gt, ast.GtE)) |
4383 and AstUtilities.isString(node.comparators[0]) |
4383 and AstUtilities.isString(node.comparators[0]) |
4384 ): |
4384 ): |
4385 if len(AstUtilities.getValue(node.comparators[0])) == 1: |
4385 if len(AstUtilities.getValue(node.comparators[0])) == 1: |
4386 errorCode = "M422" |
4386 errorCode = "M-422" |
4387 else: |
4387 else: |
4388 errorCode = "M403" |
4388 errorCode = "M-403" |
4389 self.violations.append((node.left, errorCode)) |
4389 self.violations.append((node.left, errorCode)) |
4390 elif ( |
4390 elif ( |
4391 isinstance(node.left, ast.Subscript) |
4391 isinstance(node.left, ast.Subscript) |
4392 and self.__isSys("version_info", node.left.value) |
4392 and self.__isSys("version_info", node.left.value) |
4393 and isinstance(node.left.slice, ast.Index) |
4393 and isinstance(node.left.slice, ast.Index) |
4395 and AstUtilities.getValue(node.left.slice.value) == 1 |
4395 and AstUtilities.getValue(node.left.slice.value) == 1 |
4396 and len(node.ops) == 1 |
4396 and len(node.ops) == 1 |
4397 and isinstance(node.ops[0], (ast.Lt, ast.LtE, ast.Gt, ast.GtE)) |
4397 and isinstance(node.ops[0], (ast.Lt, ast.LtE, ast.Gt, ast.GtE)) |
4398 and AstUtilities.isNumber(node.comparators[0]) |
4398 and AstUtilities.isNumber(node.comparators[0]) |
4399 ): |
4399 ): |
4400 self.violations.append((node, "M413")) |
4400 self.violations.append((node, "M-413")) |
4401 elif ( |
4401 elif ( |
4402 isinstance(node.left, ast.Attribute) |
4402 isinstance(node.left, ast.Attribute) |
4403 and self.__isSys("version_info", node.left.value) |
4403 and self.__isSys("version_info", node.left.value) |
4404 and node.left.attr == "minor" |
4404 and node.left.attr == "minor" |
4405 and len(node.ops) == 1 |
4405 and len(node.ops) == 1 |
4406 and isinstance(node.ops[0], (ast.Lt, ast.LtE, ast.Gt, ast.GtE)) |
4406 and isinstance(node.ops[0], (ast.Lt, ast.LtE, ast.Gt, ast.GtE)) |
4407 and AstUtilities.isNumber(node.comparators[0]) |
4407 and AstUtilities.isNumber(node.comparators[0]) |
4408 ): |
4408 ): |
4409 self.violations.append((node, "M414")) |
4409 self.violations.append((node, "M-414")) |
4410 |
4410 |
4411 self.generic_visit(node) |
4411 self.generic_visit(node) |
4412 |
4412 |
4413 def visit_Attribute(self, node): |
4413 def visit_Attribute(self, node): |
4414 """ |
4414 """ |
4477 @ytype tyuple of (ast.AST, str) |
4477 @ytype tyuple of (ast.AST, str) |
4478 """ |
4478 """ |
4479 for case in node.cases: |
4479 for case in node.cases: |
4480 if self.__emptyMatchDefault(case): |
4480 if self.__emptyMatchDefault(case): |
4481 if self.__lastStatementDoesNotRaise(case): |
4481 if self.__lastStatementDoesNotRaise(case): |
4482 yield self.__findBadNode(case), "M901" |
4482 yield self.__findBadNode(case), "M-901" |
4483 elif self.__returnPrecedesExceptionRaising(case): |
4483 elif self.__returnPrecedesExceptionRaising(case): |
4484 yield self.__findBadNode(case), "M902" |
4484 yield self.__findBadNode(case), "M-902" |
4485 |
4485 |
4486 def __emptyMatchDefault(self, case): |
4486 def __emptyMatchDefault(self, case): |
4487 """ |
4487 """ |
4488 Private method to check for an empty default match case. |
4488 Private method to check for an empty default match case. |
4489 |
4489 |