25 |
42 |
26 "M191", "M192", "M193", "M194", |
43 "M191", "M192", "M193", "M194", |
27 "M195", "M196", "M197", "M198", |
44 "M195", "M196", "M197", "M198", |
28 |
45 |
29 "M201", |
46 "M201", |
|
47 |
|
48 "M501", "M502", "M503", "M504", "M505", |
|
49 "M511", "M512", "M513", |
30 |
50 |
31 "M601", |
51 "M601", |
32 "M611", "M612", "M613", |
52 "M611", "M612", "M613", |
33 "M621", "M622", "M623", "M624", "M625", |
53 "M621", "M622", "M623", "M624", "M625", |
34 "M631", "M632", |
54 "M631", "M632", |
108 (self.__checkDictWithSortedKeys, ("M201",)), |
128 (self.__checkDictWithSortedKeys, ("M201",)), |
109 (self.__checkPep3101, ("M601",)), |
129 (self.__checkPep3101, ("M601",)), |
110 (self.__checkFormatString, ("M611", "M612", "M613", |
130 (self.__checkFormatString, ("M611", "M612", "M613", |
111 "M621", "M622", "M623", "M624", "M625", |
131 "M621", "M622", "M623", "M624", "M625", |
112 "M631", "M632")), |
132 "M631", "M632")), |
|
133 (self.__checkBugBear, ("M501", "M502", "M503", "M504", "M505", "M511", "M512", "M513",)), |
113 (self.__checkLogging, ("M651", "M652", "M653", "M654", "M655")), |
134 (self.__checkLogging, ("M651", "M652", "M653", "M654", "M655")), |
114 (self.__checkFuture, ("M701", "M702")), |
135 (self.__checkFuture, ("M701", "M702")), |
115 (self.__checkGettext, ("M711",)), |
136 (self.__checkGettext, ("M711",)), |
116 (self.__checkPrintStatements, ("M801",)), |
137 (self.__checkPrintStatements, ("M801",)), |
117 (self.__checkTuple, ("M811", )), |
138 (self.__checkTuple, ("M811", )), |
598 |
619 |
599 def __checkMutableDefault(self): |
620 def __checkMutableDefault(self): |
600 """ |
621 """ |
601 Private method to check for use of mutable types as default arguments. |
622 Private method to check for use of mutable types as default arguments. |
602 """ |
623 """ |
603 mutableTypes = [ |
624 mutableTypes = ( |
604 ast.Call, |
625 ast.Call, |
605 ast.Dict, |
626 ast.Dict, |
606 ast.List, |
627 ast.List, |
607 ast.Set, |
628 ast.Set, |
608 ] |
629 ) |
|
630 mutableCalls = ( |
|
631 "Counter", |
|
632 "OrderedDict", |
|
633 "collections.Counter", |
|
634 "collections.OrderedDict", |
|
635 "collections.defaultdict", |
|
636 "collections.deque", |
|
637 "defaultdict", |
|
638 "deque", |
|
639 "dict", |
|
640 "list", |
|
641 "set", |
|
642 ) |
609 |
643 |
610 for node in ast.walk(self.__tree): |
644 for node in ast.walk(self.__tree): |
611 if isinstance(node, ast.FunctionDef): |
645 if isinstance(node, ast.FunctionDef): |
612 for default in node.args.defaults: |
646 for default in node.args.defaults: |
613 if any(isinstance(default, mutableType) |
647 if any(isinstance(default, mutableType) |
614 for mutableType in mutableTypes): |
648 for mutableType in mutableTypes): |
615 typeName = type(default).__name__ |
649 typeName = type(default).__name__ |
616 if isinstance(default, ast.Call): |
650 if isinstance(default, ast.Call): |
617 errorCode = "M822" |
651 callPath = '.'.join(composeCallPath(default.func)) |
|
652 if callPath in mutableCalls: |
|
653 self.__error(default.lineno - 1, |
|
654 default.col_offset, |
|
655 "M823", callPath + "()") |
|
656 else: |
|
657 self.__error(default.lineno - 1, |
|
658 default.col_offset, |
|
659 "M822", typeName) |
618 else: |
660 else: |
619 errorCode = "M821" |
661 self.__error(default.lineno - 1, |
620 self.__error(default.lineno - 1, default.col_offset, |
662 default.col_offset, |
621 errorCode, typeName) |
663 "M821", typeName) |
622 |
664 |
623 def __dictShouldBeChecked(self, node): |
665 def __dictShouldBeChecked(self, node): |
624 """ |
666 """ |
625 Private function to test, if the node should be checked. |
667 Private function to test, if the node should be checked. |
626 |
668 |
665 for node in ast.walk(self.__tree): |
707 for node in ast.walk(self.__tree): |
666 if isinstance(node, ast.ImportFrom) and \ |
708 if isinstance(node, ast.ImportFrom) and \ |
667 any(name.asname == '_' for name in node.names): |
709 any(name.asname == '_' for name in node.names): |
668 self.__error(node.lineno - 1, node.col_offset, "M711", |
710 self.__error(node.lineno - 1, node.col_offset, "M711", |
669 node.names[0].name) |
711 node.names[0].name) |
|
712 |
|
713 def __checkBugBear(self): |
|
714 """ |
|
715 Private method to bugbear checks. |
|
716 """ |
|
717 visitor = BugBearVisitor() |
|
718 visitor.visit(self.__tree) |
|
719 for node, reason in visitor.violations: |
|
720 self.__error(node.lineno - 1, node.col_offset, reason) |
670 |
721 |
671 |
722 |
672 class TextVisitor(ast.NodeVisitor): |
723 class TextVisitor(ast.NodeVisitor): |
673 """ |
724 """ |
674 Class implementing a node visitor for bytes and str instances. |
725 Class implementing a node visitor for bytes and str instances. |
977 if self.__withinLoggingArgument(): |
1028 if self.__withinLoggingArgument(): |
978 self.violations.append((node, "M654")) |
1029 self.violations.append((node, "M654")) |
979 |
1030 |
980 super(LoggingVisitor, self).generic_visit(node) |
1031 super(LoggingVisitor, self).generic_visit(node) |
981 |
1032 |
|
1033 |
|
1034 class BugBearVisitor(ast.NodeVisitor): |
|
1035 """ |
|
1036 Class implementing a node visitor to check for various topics. |
|
1037 """ |
|
1038 # |
|
1039 # This class was implemented along the BugBear flake8 extension (v 18.2.0). |
|
1040 # Original: Copyright (c) 2016 Ćukasz Langa |
|
1041 # |
|
1042 |
|
1043 NodeWindowSize = 4 |
|
1044 |
|
1045 def __init__(self): |
|
1046 """ |
|
1047 Constructor |
|
1048 """ |
|
1049 super(BugBearVisitor, self).__init__() |
|
1050 |
|
1051 self.__nodeStack = [] |
|
1052 self.__nodeWindow = [] |
|
1053 self.violations = [] |
|
1054 |
|
1055 def visit(self, node): |
|
1056 """ |
|
1057 Public method to traverse a given AST node. |
|
1058 |
|
1059 @param node AST node to be traversed |
|
1060 @type ast.Node |
|
1061 """ |
|
1062 self.__nodeStack.append(node) |
|
1063 self.__nodeWindow.append(node) |
|
1064 self.__nodeWindow = \ |
|
1065 self.__nodeWindow[-BugBearVisitor.NodeWindowSize:] |
|
1066 |
|
1067 super(BugBearVisitor, self).visit(node) |
|
1068 |
|
1069 self.__nodeStack.pop() |
|
1070 |
|
1071 def visit_UAdd(self, node): |
|
1072 """ |
|
1073 Public method to handle unary additions. |
|
1074 |
|
1075 @param node reference to the node to be processed |
|
1076 @type ast.UAdd |
|
1077 """ |
|
1078 trailingNodes = list(map(type, self.__nodeWindow[-4:])) |
|
1079 if trailingNodes == [ast.UnaryOp, ast.UAdd, ast.UnaryOp, ast.UAdd]: |
|
1080 originator = self.__nodeWindow[-4] |
|
1081 self.violations.append((originator, "M501")) |
|
1082 |
|
1083 self.generic_visit(node) |
|
1084 |
|
1085 def visit_Call(self, node): |
|
1086 """ |
|
1087 Public method to handle a function call. |
|
1088 |
|
1089 @param node reference to the node to be processed |
|
1090 @type ast.Call |
|
1091 """ |
|
1092 if sys.version_info >= (3, 0): |
|
1093 validPaths = ("six", "future.utils", "builtins") |
|
1094 methodsDict = { |
|
1095 "M511": ("iterkeys", "itervalues", "iteritems", "iterlists"), |
|
1096 "M512": ("viewkeys", "viewvalues", "viewitems", "viewlists"), |
|
1097 "M513": ("next",), |
|
1098 } |
|
1099 else: |
|
1100 validPaths = () |
|
1101 methodsDict = {} |
|
1102 |
|
1103 if isinstance(node.func, ast.Attribute): |
|
1104 for code, methods in methodsDict.items(): |
|
1105 if node.func.attr in methods: |
|
1106 callPath = ".".join(composeCallPath(node.func.value)) |
|
1107 if callPath not in validPaths: |
|
1108 self.violations.append((node, code)) |
|
1109 break |
|
1110 else: |
|
1111 self.__checkForM502(node) |
|
1112 else: |
|
1113 try: |
|
1114 if ( |
|
1115 node.func.id in ("getattr", "hasattr") and |
|
1116 node.args[1].s == "__call__" |
|
1117 ): |
|
1118 self.violations.append((node, "M503")) |
|
1119 except (AttributeError, IndexError): |
|
1120 pass |
|
1121 |
|
1122 self.generic_visit(node) |
|
1123 |
|
1124 def visit_Attribute(self, node): |
|
1125 """ |
|
1126 Public method to handle attributes. |
|
1127 |
|
1128 @param node reference to the node to be processed |
|
1129 @type ast.Attribute |
|
1130 """ |
|
1131 callPath = list(composeCallPath(node)) |
|
1132 |
|
1133 if '.'.join(callPath) == 'sys.maxint' and sys.version_info >= (3, 0): |
|
1134 self.violations.append((node, "M504")) |
|
1135 |
|
1136 elif len(callPath) == 2 and callPath[1] == 'message' and \ |
|
1137 sys.version_info >= (2, 6): |
|
1138 name = callPath[0] |
|
1139 for elem in reversed(self.__nodeStack[:-1]): |
|
1140 if isinstance(elem, ast.ExceptHandler) and elem.name == name: |
|
1141 self.violations.append((node, "M505")) |
|
1142 break |
|
1143 |
|
1144 def __checkForM502(self, node): |
|
1145 """ |
|
1146 Private method to check the use of *strip(). |
|
1147 |
|
1148 @param node reference to the node to be processed |
|
1149 @type ast.Call |
|
1150 """ |
|
1151 if node.func.attr not in ("lstrip", "rstrip", "strip"): |
|
1152 return # method name doesn't match |
|
1153 |
|
1154 if len(node.args) != 1 or not isinstance(node.args[0], ast.Str): |
|
1155 return # used arguments don't match the builtin strip |
|
1156 |
|
1157 s = node.args[0].s |
|
1158 if len(s) == 1: |
|
1159 return # stripping just one character |
|
1160 |
|
1161 if len(s) == len(set(s)): |
|
1162 return # no characters appear more than once |
|
1163 |
|
1164 self.violations.append((node, "M502")) |
|
1165 |
982 # |
1166 # |
983 # eflag: noqa = M702 |
1167 # eflag: noqa = M702 |