54 class SimplifyNodeVisitor(ast.NodeVisitor): |
54 class SimplifyNodeVisitor(ast.NodeVisitor): |
55 """ |
55 """ |
56 Class to traverse the AST node tree and check for code that can be |
56 Class to traverse the AST node tree and check for code that can be |
57 simplified. |
57 simplified. |
58 """ |
58 """ |
|
59 |
59 def __init__(self, errorCallback): |
60 def __init__(self, errorCallback): |
60 """ |
61 """ |
61 Constructor |
62 Constructor |
62 |
63 |
63 @param errorCallback callback function to register an error |
64 @param errorCallback callback function to register an error |
64 @type func |
65 @type func |
65 """ |
66 """ |
66 super().__init__() |
67 super().__init__() |
67 |
68 |
68 self.__error = errorCallback |
69 self.__error = errorCallback |
69 |
70 |
70 self.__classDefinitionStack = [] |
71 self.__classDefinitionStack = [] |
71 |
72 |
72 def visit_Expr(self, node): |
73 def visit_Expr(self, node): |
73 """ |
74 """ |
74 Public method to process an Expr node. |
75 Public method to process an Expr node. |
75 |
76 |
76 @param node reference to the Expr node |
77 @param node reference to the Expr node |
77 @type ast.Expr |
78 @type ast.Expr |
78 """ |
79 """ |
79 self.__check112(node) |
80 self.__check112(node) |
80 |
81 |
81 self.generic_visit(node) |
82 self.generic_visit(node) |
82 |
83 |
83 def visit_Assign(self, node): |
84 def visit_Assign(self, node): |
84 """ |
85 """ |
85 Public method to process an Assign node. |
86 Public method to process an Assign node. |
86 |
87 |
87 @param node reference to the Assign node |
88 @param node reference to the Assign node |
88 @type ast.Assign |
89 @type ast.Assign |
89 """ |
90 """ |
90 self.__check181(node) |
91 self.__check181(node) |
91 |
92 |
92 self.generic_visit(node) |
93 self.generic_visit(node) |
93 |
94 |
94 def visit_BoolOp(self, node): |
95 def visit_BoolOp(self, node): |
95 """ |
96 """ |
96 Public method to process a BoolOp node. |
97 Public method to process a BoolOp node. |
97 |
98 |
98 @param node reference to the BoolOp node |
99 @param node reference to the BoolOp node |
99 @type ast.BoolOp |
100 @type ast.BoolOp |
100 """ |
101 """ |
101 self.__check101(node) |
102 self.__check101(node) |
102 self.__check109(node) |
103 self.__check109(node) |
103 self.__check221(node) |
104 self.__check221(node) |
104 self.__check222(node) |
105 self.__check222(node) |
105 self.__check223(node) |
106 self.__check223(node) |
106 self.__check224(node) |
107 self.__check224(node) |
107 |
108 |
108 self.generic_visit(node) |
109 self.generic_visit(node) |
109 |
110 |
110 def visit_If(self, node): |
111 def visit_If(self, node): |
111 """ |
112 """ |
112 Public method to process an If node. |
113 Public method to process an If node. |
113 |
114 |
114 @param node reference to the If node |
115 @param node reference to the If node |
115 @type ast.If |
116 @type ast.If |
116 """ |
117 """ |
117 self.__check102(node) |
118 self.__check102(node) |
118 self.__check103(node) |
119 self.__check103(node) |
119 self.__check106(node) |
120 self.__check106(node) |
120 self.__check108(node) |
121 self.__check108(node) |
121 self.__check114(node) |
122 self.__check114(node) |
122 self.__check116(node) |
123 self.__check116(node) |
123 self.__check122(node) |
124 self.__check122(node) |
124 |
125 |
125 self.generic_visit(node) |
126 self.generic_visit(node) |
126 |
127 |
127 def visit_IfExp(self, node): |
128 def visit_IfExp(self, node): |
128 """ |
129 """ |
129 Public method to process an IfExp node. |
130 Public method to process an IfExp node. |
130 |
131 |
131 @param node reference to the IfExp node |
132 @param node reference to the IfExp node |
132 @type ast.IfExp |
133 @type ast.IfExp |
133 """ |
134 """ |
134 self.__check211(node) |
135 self.__check211(node) |
135 self.__check212(node) |
136 self.__check212(node) |
136 self.__check213(node) |
137 self.__check213(node) |
137 |
138 |
138 self.generic_visit(node) |
139 self.generic_visit(node) |
139 |
140 |
140 def visit_For(self, node): |
141 def visit_For(self, node): |
141 """ |
142 """ |
142 Public method to process a For node. |
143 Public method to process a For node. |
143 |
144 |
144 @param node reference to the For node |
145 @param node reference to the For node |
145 @type ast.For |
146 @type ast.For |
146 """ |
147 """ |
147 self.__check104(node) |
148 self.__check104(node) |
148 self.__check110_111(node) |
149 self.__check110_111(node) |
149 self.__check113(node) |
150 self.__check113(node) |
150 self.__check118(node) |
151 self.__check118(node) |
151 |
152 |
152 self.generic_visit(node) |
153 self.generic_visit(node) |
153 |
154 |
154 def visit_Try(self, node): |
155 def visit_Try(self, node): |
155 """ |
156 """ |
156 Public method to process a Try node. |
157 Public method to process a Try node. |
157 |
158 |
158 @param node reference to the Try node |
159 @param node reference to the Try node |
159 @type ast.Try |
160 @type ast.Try |
160 """ |
161 """ |
161 self.__check105(node) |
162 self.__check105(node) |
162 self.__check107(node) |
163 self.__check107(node) |
163 |
164 |
164 self.generic_visit(node) |
165 self.generic_visit(node) |
165 |
166 |
166 def visit_Call(self, node): |
167 def visit_Call(self, node): |
167 """ |
168 """ |
168 Public method to process a Call node. |
169 Public method to process a Call node. |
169 |
170 |
170 @param node reference to the Call node |
171 @param node reference to the Call node |
171 @type ast.Call |
172 @type ast.Call |
172 """ |
173 """ |
173 self.__check115(node) |
174 self.__check115(node) |
174 self.__check182(node) |
175 self.__check182(node) |
175 self.__check401(node) |
176 self.__check401(node) |
176 self.__check402(node) |
177 self.__check402(node) |
177 |
178 |
178 self.generic_visit(node) |
179 self.generic_visit(node) |
179 |
180 |
180 def visit_With(self, node): |
181 def visit_With(self, node): |
181 """ |
182 """ |
182 Public method to process a With node. |
183 Public method to process a With node. |
183 |
184 |
184 @param node reference to the With node |
185 @param node reference to the With node |
185 @type ast.With |
186 @type ast.With |
186 """ |
187 """ |
187 self.__check117(node) |
188 self.__check117(node) |
188 |
189 |
189 self.generic_visit(node) |
190 self.generic_visit(node) |
190 |
191 |
191 def visit_Compare(self, node): |
192 def visit_Compare(self, node): |
192 """ |
193 """ |
193 Public method to process a Compare node. |
194 Public method to process a Compare node. |
194 |
195 |
195 @param node reference to the Compare node |
196 @param node reference to the Compare node |
196 @type ast.Compare |
197 @type ast.Compare |
197 """ |
198 """ |
198 self.__check118(node) |
199 self.__check118(node) |
199 self.__check301(node) |
200 self.__check301(node) |
200 |
201 |
201 self.generic_visit(node) |
202 self.generic_visit(node) |
202 |
203 |
203 def visit_ClassDef(self, node): |
204 def visit_ClassDef(self, node): |
204 """ |
205 """ |
205 Public method to process a ClassDef node. |
206 Public method to process a ClassDef node. |
206 |
207 |
207 @param node reference to the ClassDef node |
208 @param node reference to the ClassDef node |
208 @type ast.ClassDef |
209 @type ast.ClassDef |
209 """ |
210 """ |
210 # register the name of the class being defined |
211 # register the name of the class being defined |
211 self.__classDefinitionStack.append(node.name) |
212 self.__classDefinitionStack.append(node.name) |
212 |
213 |
213 self.__check119(node) |
214 self.__check119(node) |
214 self.__check120_121(node) |
215 self.__check120_121(node) |
215 |
216 |
216 self.generic_visit(node) |
217 self.generic_visit(node) |
217 |
218 |
218 self.__classDefinitionStack.pop() |
219 self.__classDefinitionStack.pop() |
219 |
220 |
220 def visit_UnaryOp(self, node): |
221 def visit_UnaryOp(self, node): |
221 """ |
222 """ |
222 Public method to process a UnaryOp node. |
223 Public method to process a UnaryOp node. |
223 |
224 |
224 @param node reference to the UnaryOp node |
225 @param node reference to the UnaryOp node |
225 @type ast.UnaryOp |
226 @type ast.UnaryOp |
226 """ |
227 """ |
227 self.__check201(node) |
228 self.__check201(node) |
228 self.__check202(node) |
229 self.__check202(node) |
413 op = ast.NotIn() |
411 op = ast.NotIn() |
414 elif isinstance(op, ast.NotIn): |
412 elif isinstance(op, ast.NotIn): |
415 op = ast.In() |
413 op = ast.In() |
416 newNode.ops = [op] |
414 newNode.ops = [op] |
417 return newNode |
415 return newNode |
418 |
416 |
419 ############################################################# |
417 ############################################################# |
420 ## Methods to check for possible code simplifications below |
418 ## Methods to check for possible code simplifications below |
421 ############################################################# |
419 ############################################################# |
422 |
420 |
423 def __check101(self, node): |
421 def __check101(self, node): |
424 """ |
422 """ |
425 Private method to check for duplicate isinstance() calls. |
423 Private method to check for duplicate isinstance() calls. |
426 |
424 |
427 @param node reference to the AST node to be checked |
425 @param node reference to the AST node to be checked |
428 @type ast.BoolOp |
426 @type ast.BoolOp |
429 """ |
427 """ |
430 if isinstance(node.op, ast.Or): |
428 if isinstance(node.op, ast.Or): |
431 for variable in self.__getDuplicatedIsinstanceCall(node): |
429 for variable in self.__getDuplicatedIsinstanceCall(node): |
432 self.__error(node.lineno - 1, node.col_offset, "Y101", |
430 self.__error(node.lineno - 1, node.col_offset, "Y101", variable) |
433 variable) |
431 |
434 |
|
435 def __check102(self, node): |
432 def __check102(self, node): |
436 """ |
433 """ |
437 Private method to check for nested if statements without else blocks. |
434 Private method to check for nested if statements without else blocks. |
438 |
435 |
439 @param node reference to the AST node to be checked |
436 @param node reference to the AST node to be checked |
440 @type ast.If |
437 @type ast.If |
441 """ |
438 """ |
442 # Don't treat 'if __name__ == "__main__":' as an issue. |
439 # Don't treat 'if __name__ == "__main__":' as an issue. |
443 if ( |
440 if ( |
444 isinstance(node.test, ast.Compare) and |
441 isinstance(node.test, ast.Compare) |
445 isinstance(node.test.left, ast.Name) and |
442 and isinstance(node.test.left, ast.Name) |
446 node.test.left.id == "__name__" and |
443 and node.test.left.id == "__name__" |
447 isinstance(node.test.ops[0], ast.Eq) and |
444 and isinstance(node.test.ops[0], ast.Eq) |
448 isinstance(node.test.comparators[0], ast.Constant) and |
445 and isinstance(node.test.comparators[0], ast.Constant) |
449 node.test.comparators[0].value == "__main__" |
446 and node.test.comparators[0].value == "__main__" |
450 ): |
447 ): |
451 return |
448 return |
452 |
449 |
453 # ## Pattern 1 |
450 # ## Pattern 1 |
454 # if a: <--- |
451 # if a: <--- |
455 # if b: <--- |
452 # if b: <--- |
456 # c |
453 # c |
457 isPattern1 = ( |
454 isPattern1 = ( |
458 node.orelse == [] and |
455 node.orelse == [] |
459 len(node.body) == 1 and |
456 and len(node.body) == 1 |
460 isinstance(node.body[0], ast.If) and |
457 and isinstance(node.body[0], ast.If) |
461 node.body[0].orelse == [] |
458 and node.body[0].orelse == [] |
462 ) |
459 ) |
463 # ## Pattern 2 |
460 # ## Pattern 2 |
464 # if a: < irrelevant for here |
461 # if a: < irrelevant for here |
465 # pass |
462 # pass |
466 # elif b: <--- this is treated like a nested block |
463 # elif b: <--- this is treated like a nested block |
467 # if c: <--- |
464 # if c: <--- |
468 # d |
465 # d |
469 if isPattern1: |
466 if isPattern1: |
470 self.__error(node.lineno - 1, node.col_offset, "Y102") |
467 self.__error(node.lineno - 1, node.col_offset, "Y102") |
471 |
468 |
472 def __check103(self, node): |
469 def __check103(self, node): |
473 """ |
470 """ |
474 Private method to check for calls that wrap a condition to return |
471 Private method to check for calls that wrap a condition to return |
475 a bool. |
472 a bool. |
476 |
473 |
477 @param node reference to the AST node to be checked |
474 @param node reference to the AST node to be checked |
478 @type ast.If |
475 @type ast.If |
479 """ |
476 """ |
480 # if cond: |
477 # if cond: |
481 # return True |
478 # return True |
482 # else: |
479 # else: |
483 # return False |
480 # return False |
484 if not ( |
481 if not ( |
485 len(node.body) != 1 or |
482 len(node.body) != 1 |
486 not isinstance(node.body[0], ast.Return) or |
483 or not isinstance(node.body[0], ast.Return) |
487 not isinstance(node.body[0].value, BOOL_CONST_TYPES) or |
484 or not isinstance(node.body[0].value, BOOL_CONST_TYPES) |
488 not ( |
485 or not ( |
489 node.body[0].value.value is True or |
486 node.body[0].value.value is True or node.body[0].value.value is False |
490 node.body[0].value.value is False |
487 ) |
491 ) or |
488 or len(node.orelse) != 1 |
492 len(node.orelse) != 1 or |
489 or not isinstance(node.orelse[0], ast.Return) |
493 not isinstance(node.orelse[0], ast.Return) or |
490 or not isinstance(node.orelse[0].value, BOOL_CONST_TYPES) |
494 not isinstance(node.orelse[0].value, BOOL_CONST_TYPES) or |
491 or not ( |
495 not ( |
492 node.orelse[0].value.value is True |
496 node.orelse[0].value.value is True or |
493 or node.orelse[0].value.value is False |
497 node.orelse[0].value.value is False |
|
498 ) |
494 ) |
499 ): |
495 ): |
500 condition = unparse(node.test) |
496 condition = unparse(node.test) |
501 self.__error(node.lineno - 1, node.col_offset, "Y103", condition) |
497 self.__error(node.lineno - 1, node.col_offset, "Y103", condition) |
502 |
498 |
503 def __check104(self, node): |
499 def __check104(self, node): |
504 """ |
500 """ |
505 Private method to check for "iterate and yield" patterns. |
501 Private method to check for "iterate and yield" patterns. |
506 |
502 |
507 @param node reference to the AST node to be checked |
503 @param node reference to the AST node to be checked |
508 @type ast.For |
504 @type ast.For |
509 """ |
505 """ |
510 # for item in iterable: |
506 # for item in iterable: |
511 # yield item |
507 # yield item |
512 if not ( |
508 if not ( |
513 len(node.body) != 1 or |
509 len(node.body) != 1 |
514 not isinstance(node.body[0], ast.Expr) or |
510 or not isinstance(node.body[0], ast.Expr) |
515 not isinstance(node.body[0].value, ast.Yield) or |
511 or not isinstance(node.body[0].value, ast.Yield) |
516 not isinstance(node.target, ast.Name) or |
512 or not isinstance(node.target, ast.Name) |
517 not isinstance(node.body[0].value.value, ast.Name) or |
513 or not isinstance(node.body[0].value.value, ast.Name) |
518 node.target.id != node.body[0].value.value.id or |
514 or node.target.id != node.body[0].value.value.id |
519 node.orelse != [] |
515 or node.orelse != [] |
520 ): |
516 ): |
521 iterable = unparse(node.iter) |
517 iterable = unparse(node.iter) |
522 self.__error(node.lineno - 1, node.col_offset, "Y104", iterable) |
518 self.__error(node.lineno - 1, node.col_offset, "Y104", iterable) |
523 |
519 |
524 def __check105(self, node): |
520 def __check105(self, node): |
525 """ |
521 """ |
526 Private method to check for "try-except-pass" patterns. |
522 Private method to check for "try-except-pass" patterns. |
527 |
523 |
528 @param node reference to the AST node to be checked |
524 @param node reference to the AST node to be checked |
529 @type ast.Try |
525 @type ast.Try |
530 """ |
526 """ |
531 # try: |
527 # try: |
532 # foo() |
528 # foo() |
533 # except ValueError: |
529 # except ValueError: |
534 # pass |
530 # pass |
535 if not ( |
531 if not ( |
536 len(node.handlers) != 1 or |
532 len(node.handlers) != 1 |
537 not isinstance(node.handlers[0], ast.ExceptHandler) or |
533 or not isinstance(node.handlers[0], ast.ExceptHandler) |
538 len(node.handlers[0].body) != 1 or |
534 or len(node.handlers[0].body) != 1 |
539 not isinstance(node.handlers[0].body[0], ast.Pass) or |
535 or not isinstance(node.handlers[0].body[0], ast.Pass) |
540 node.orelse != [] |
536 or node.orelse != [] |
541 ): |
537 ): |
542 if node.handlers[0].type is None: |
538 if node.handlers[0].type is None: |
543 exception = "Exception" |
539 exception = "Exception" |
544 elif isinstance(node.handlers[0].type, ast.Tuple): |
540 elif isinstance(node.handlers[0].type, ast.Tuple): |
545 exception = ", ".join( |
541 exception = ", ".join([unparse(n) for n in node.handlers[0].type.elts]) |
546 [unparse(n) for n in node.handlers[0].type.elts]) |
|
547 else: |
542 else: |
548 exception = unparse(node.handlers[0].type) |
543 exception = unparse(node.handlers[0].type) |
549 self.__error(node.lineno - 1, node.col_offset, "Y105", exception) |
544 self.__error(node.lineno - 1, node.col_offset, "Y105", exception) |
550 |
545 |
551 def __check106(self, node): |
546 def __check106(self, node): |
552 """ |
547 """ |
553 Private method to check for calls where an exception is raised in else. |
548 Private method to check for calls where an exception is raised in else. |
554 |
549 |
555 @param node reference to the AST node to be checked |
550 @param node reference to the AST node to be checked |
556 @type ast.If |
551 @type ast.If |
557 """ |
552 """ |
558 # if cond: |
553 # if cond: |
559 # return True |
554 # return True |
560 # else: |
555 # else: |
561 # raise Exception |
556 # raise Exception |
562 just_one = ( |
557 just_one = ( |
563 len(node.body) == 1 and |
558 len(node.body) == 1 |
564 len(node.orelse) >= 1 and |
559 and len(node.orelse) >= 1 |
565 isinstance(node.orelse[-1], ast.Raise) and |
560 and isinstance(node.orelse[-1], ast.Raise) |
566 not isinstance(node.body[-1], ast.Raise) |
561 and not isinstance(node.body[-1], ast.Raise) |
567 ) |
562 ) |
568 many = ( |
563 many = ( |
569 len(node.body) > 2 * len(node.orelse) and |
564 len(node.body) > 2 * len(node.orelse) |
570 len(node.orelse) >= 1 and |
565 and len(node.orelse) >= 1 |
571 isinstance(node.orelse[-1], ast.Raise) and |
566 and isinstance(node.orelse[-1], ast.Raise) |
572 not isinstance(node.body[-1], ast.Raise) |
567 and not isinstance(node.body[-1], ast.Raise) |
573 ) |
568 ) |
574 if just_one or many: |
569 if just_one or many: |
575 self.__error(node.lineno - 1, node.col_offset, "Y106") |
570 self.__error(node.lineno - 1, node.col_offset, "Y106") |
576 |
571 |
577 def __check107(self, node): |
572 def __check107(self, node): |
578 """ |
573 """ |
579 Private method to check for calls where try/except and finally have |
574 Private method to check for calls where try/except and finally have |
580 'return'. |
575 'return'. |
581 |
576 |
582 @param node reference to the AST node to be checked |
577 @param node reference to the AST node to be checked |
583 @type ast.Try |
578 @type ast.Try |
584 """ |
579 """ |
585 # def foo(): |
580 # def foo(): |
586 # try: |
581 # try: |
1011 # Pattern 2: |
1006 # Pattern 2: |
1012 # |
1007 # |
1013 # for key in dict.keys(): |
1008 # for key in dict.keys(): |
1014 # # do something |
1009 # # do something |
1015 if ( |
1010 if ( |
1016 isinstance(node, ast.Compare) and |
1011 isinstance(node, ast.Compare) |
1017 len(node.ops) == 1 and |
1012 and len(node.ops) == 1 |
1018 isinstance(node.ops[0], ast.In) and |
1013 and isinstance(node.ops[0], ast.In) |
1019 len(node.comparators) == 1 |
1014 and len(node.comparators) == 1 |
1020 ): |
1015 ): |
1021 callNode = node.comparators[0] |
1016 callNode = node.comparators[0] |
1022 elif ( |
1017 elif isinstance(node, ast.For): |
1023 isinstance(node, ast.For) |
|
1024 ): |
|
1025 callNode = node.iter |
1018 callNode = node.iter |
1026 else: |
1019 else: |
1027 callNode = None |
1020 callNode = None |
1028 |
1021 |
1029 if not isinstance(callNode, ast.Call): |
1022 if not isinstance(callNode, ast.Call): |
1030 return |
1023 return |
1031 |
1024 |
1032 attrNode = callNode.func |
1025 attrNode = callNode.func |
1033 if ( |
1026 if ( |
1034 isinstance(callNode.func, ast.Attribute) and |
1027 isinstance(callNode.func, ast.Attribute) |
1035 callNode.func.attr == "keys" and |
1028 and callNode.func.attr == "keys" |
1036 isinstance(callNode.func.ctx, ast.Load) |
1029 and isinstance(callNode.func.ctx, ast.Load) |
1037 ): |
1030 ): |
1038 if isinstance(node, ast.Compare): |
1031 if isinstance(node, ast.Compare): |
1039 keyStr = unparse(node.left) |
1032 keyStr = unparse(node.left) |
1040 else: |
1033 else: |
1041 keyStr = unparse(node.target) |
1034 keyStr = unparse(node.target) |
1042 dictStr = unparse(attrNode.value) |
1035 dictStr = unparse(attrNode.value) |
1043 self.__error(node.lineno - 1, node.col_offset, "Y118", |
1036 self.__error(node.lineno - 1, node.col_offset, "Y118", keyStr, dictStr) |
1044 keyStr, dictStr) |
1037 |
1045 |
|
1046 def __check119(self, node): |
1038 def __check119(self, node): |
1047 """ |
1039 """ |
1048 Private method to check for classes that should be "dataclasses". |
1040 Private method to check for classes that should be "dataclasses". |
1049 |
1041 |
1050 @param node reference to the AST node to be checked |
1042 @param node reference to the AST node to be checked |
1051 @type ast.ClassDef |
1043 @type ast.ClassDef |
1052 """ |
1044 """ |
1053 if ( |
1045 if len(node.decorator_list) == 0 and len(node.bases) == 0: |
1054 len(node.decorator_list) == 0 and |
|
1055 len(node.bases) == 0 |
|
1056 ): |
|
1057 dataclassFunctions = [ |
1046 dataclassFunctions = [ |
1058 "__init__", |
1047 "__init__", |
1059 "__eq__", |
1048 "__eq__", |
1060 "__hash__", |
1049 "__hash__", |
1061 "__repr__", |
1050 "__repr__", |
1062 "__str__", |
1051 "__str__", |
1063 ] |
1052 ] |
1064 hasOnlyConstructorMethod = True |
1053 hasOnlyConstructorMethod = True |
1065 for bodyElement in node.body: |
1054 for bodyElement in node.body: |
1066 if ( |
1055 if ( |
1067 isinstance(bodyElement, ast.FunctionDef) and |
1056 isinstance(bodyElement, ast.FunctionDef) |
1068 bodyElement.name not in dataclassFunctions |
1057 and bodyElement.name not in dataclassFunctions |
1069 ): |
1058 ): |
1070 hasOnlyConstructorMethod = False |
1059 hasOnlyConstructorMethod = False |
1071 break |
1060 break |
1072 |
1061 |
1073 if ( |
1062 if ( |
1074 hasOnlyConstructorMethod and |
1063 hasOnlyConstructorMethod |
1075 sum(1 for el in node.body |
1064 and sum(1 for el in node.body if isinstance(el, ast.FunctionDef)) > 0 |
1076 if isinstance(el, ast.FunctionDef)) > 0 |
|
1077 ): |
1065 ): |
1078 self.__error(node.lineno - 1, node.col_offset, "Y119", |
1066 self.__error(node.lineno - 1, node.col_offset, "Y119", node.name) |
1079 node.name) |
1067 |
1080 |
|
1081 def __check120_121(self, node): |
1068 def __check120_121(self, node): |
1082 """ |
1069 """ |
1083 Private method to check for classes that inherit from object. |
1070 Private method to check for classes that inherit from object. |
1084 |
1071 |
1085 @param node reference to the AST node to be checked |
1072 @param node reference to the AST node to be checked |
1086 @type ast.ClassDef |
1073 @type ast.ClassDef |
1087 """ |
1074 """ |
1088 # class FooBar(object): |
1075 # class FooBar(object): |
1089 # ... |
1076 # ... |
1090 if ( |
1077 if ( |
1091 len(node.bases) == 1 and |
1078 len(node.bases) == 1 |
1092 isinstance(node.bases[0], ast.Name) and |
1079 and isinstance(node.bases[0], ast.Name) |
1093 node.bases[0].id == "object" |
1080 and node.bases[0].id == "object" |
1094 ): |
1081 ): |
1095 self.__error(node.lineno - 1, node.col_offset, "Y120", |
1082 self.__error(node.lineno - 1, node.col_offset, "Y120", node.name) |
1096 node.name) |
1083 |
1097 |
|
1098 elif ( |
1084 elif ( |
1099 len(node.bases) > 1 and |
1085 len(node.bases) > 1 |
1100 isinstance(node.bases[-1], ast.Name) and |
1086 and isinstance(node.bases[-1], ast.Name) |
1101 node.bases[-1].id == "object" |
1087 and node.bases[-1].id == "object" |
1102 ): |
1088 ): |
1103 self.__error(node.lineno - 1, node.col_offset, "Y121", |
1089 self.__error( |
1104 node.name, ", ".join(b.id for b in node.bases[:-1])) |
1090 node.lineno - 1, |
1105 |
1091 node.col_offset, |
|
1092 "Y121", |
|
1093 node.name, |
|
1094 ", ".join(b.id for b in node.bases[:-1]), |
|
1095 ) |
|
1096 |
1106 def __check122(self, node): |
1097 def __check122(self, node): |
1107 """ |
1098 """ |
1108 Private method to check for all if-blocks which only check if a key |
1099 Private method to check for all if-blocks which only check if a key |
1109 is in a dictionary. |
1100 is in a dictionary. |
1110 |
1101 |
1111 @param node reference to the AST node to be checked |
1102 @param node reference to the AST node to be checked |
1112 @type ast.If |
1103 @type ast.If |
1113 """ |
1104 """ |
1114 if ( |
1105 if ( |
1115 isinstance(node.test, ast.Compare) and |
1106 isinstance(node.test, ast.Compare) |
1116 len(node.test.ops) == 1 and |
1107 and len(node.test.ops) == 1 |
1117 isinstance(node.test.ops[0], ast.In) and |
1108 and isinstance(node.test.ops[0], ast.In) |
1118 len(node.body) == 1 and |
1109 and len(node.body) == 1 |
1119 len(node.orelse) == 0 |
1110 and len(node.orelse) == 0 |
1120 ) and ( |
1111 ) and ( |
1121 # We might still be left with a check if a value is in a list or |
1112 # We might still be left with a check if a value is in a list or |
1122 # in the body the developer might remove the element from the list. |
1113 # in the body the developer might remove the element from the list. |
1123 # We need to have a look at the body. |
1114 # We need to have a look at the body. |
1124 isinstance(node.body[0], ast.Assign) and |
1115 isinstance(node.body[0], ast.Assign) |
1125 isinstance(node.body[0].value, ast.Subscript) and |
1116 and isinstance(node.body[0].value, ast.Subscript) |
1126 len(node.body[0].targets) == 1 and |
1117 and len(node.body[0].targets) == 1 |
1127 isinstance(node.body[0].targets[0], ast.Name) and |
1118 and isinstance(node.body[0].targets[0], ast.Name) |
1128 isinstance(node.body[0].value.value, ast.Name) and |
1119 and isinstance(node.body[0].value.value, ast.Name) |
1129 isinstance(node.test.comparators[0], ast.Name) and |
1120 and isinstance(node.test.comparators[0], ast.Name) |
1130 node.body[0].value.value.id == node.test.comparators[0].id |
1121 and node.body[0].value.value.id == node.test.comparators[0].id |
1131 ): |
1122 ): |
1132 key = unparse(node.test.left) |
1123 key = unparse(node.test.left) |
1133 dictname = unparse(node.test.comparators[0]) |
1124 dictname = unparse(node.test.comparators[0]) |
1134 self.__error(node.lineno - 1, node.col_offset, "Y122", |
1125 self.__error(node.lineno - 1, node.col_offset, "Y122", dictname, key) |
1135 dictname, key) |
1126 |
1136 |
|
1137 def __check181(self, node): |
1127 def __check181(self, node): |
1138 """ |
1128 """ |
1139 Private method to check for assignments that could be converted into |
1129 Private method to check for assignments that could be converted into |
1140 an augmented assignment. |
1130 an augmented assignment. |
1141 |
1131 |
1142 @param node reference to the AST node to be checked |
1132 @param node reference to the AST node to be checked |
1143 @type ast.Assign |
1133 @type ast.Assign |
1144 """ |
1134 """ |
1145 # a = a - b |
1135 # a = a - b |
1146 if ( |
1136 if ( |
1147 len(node.targets) == 1 and |
1137 len(node.targets) == 1 |
1148 isinstance(node.targets[0], ast.Name) and |
1138 and isinstance(node.targets[0], ast.Name) |
1149 isinstance(node.value, ast.BinOp) and |
1139 and isinstance(node.value, ast.BinOp) |
1150 isinstance(node.value.left, ast.Name) and |
1140 and isinstance(node.value.left, ast.Name) |
1151 node.value.left.id == node.targets[0].id and |
1141 and node.value.left.id == node.targets[0].id |
1152 not isinstance(node.value.right, ast.Tuple) |
1142 and not isinstance(node.value.right, ast.Tuple) |
1153 ): |
1143 ): |
1154 newNode = ast.AugAssign(node.targets[0], node.value.op, |
1144 newNode = ast.AugAssign(node.targets[0], node.value.op, node.value.right) |
1155 node.value.right) |
1145 self.__error( |
1156 self.__error(node.lineno - 1, node.col_offset, "Y181", |
1146 node.lineno - 1, |
1157 unparse(newNode), unparse(node)) |
1147 node.col_offset, |
1158 |
1148 "Y181", |
|
1149 unparse(newNode), |
|
1150 unparse(node), |
|
1151 ) |
|
1152 |
1159 def __check182(self, node): |
1153 def __check182(self, node): |
1160 """ |
1154 """ |
1161 Private method to check for calls of type 'super()' that could |
1155 Private method to check for calls of type 'super()' that could |
1162 be shortened to 'super()'. |
1156 be shortened to 'super()'. |
1163 |
1157 |
1164 @param node reference to the AST node to be checked |
1158 @param node reference to the AST node to be checked |
1165 @type ast.Call |
1159 @type ast.Call |
1166 """ |
1160 """ |
1167 # super() |
1161 # super() |
1168 if ( |
1162 if ( |
1169 self.__classDefinitionStack and |
1163 self.__classDefinitionStack |
1170 isinstance(node.func, ast.Name) and |
1164 and isinstance(node.func, ast.Name) |
1171 node.func.id == "super" and |
1165 and node.func.id == "super" |
1172 len(node.args) == 2 and |
1166 and len(node.args) == 2 |
1173 all(isinstance(arg, ast.Name) for arg in node.args) and |
1167 and all(isinstance(arg, ast.Name) for arg in node.args) |
1174 node.args[0].id == self.__classDefinitionStack[-1] and |
1168 and node.args[0].id == self.__classDefinitionStack[-1] |
1175 node.args[1].id == "self" |
1169 and node.args[1].id == "self" |
1176 ): |
1170 ): |
1177 self.__error(node.lineno - 1, node.col_offset, "Y182", |
1171 self.__error(node.lineno - 1, node.col_offset, "Y182", unparse(node)) |
1178 unparse(node)) |
1172 |
1179 |
|
1180 def __check201(self, node): |
1173 def __check201(self, node): |
1181 """ |
1174 """ |
1182 Private method to check for calls where an unary 'not' is used for |
1175 Private method to check for calls where an unary 'not' is used for |
1183 an unequality. |
1176 an unequality. |
1184 |
1177 |
1185 @param node reference to the UnaryOp node |
1178 @param node reference to the UnaryOp node |
1186 @type ast.UnaryOp |
1179 @type ast.UnaryOp |
1187 """ |
1180 """ |
1188 # not a == b |
1181 # not a == b |
1189 if not ( |
1182 if not ( |
1190 ( |
1183 ( |
1191 not isinstance(node.op, ast.Not) or |
1184 not isinstance(node.op, ast.Not) |
1192 not isinstance(node.operand, ast.Compare) or |
1185 or not isinstance(node.operand, ast.Compare) |
1193 len(node.operand.ops) != 1 or |
1186 or len(node.operand.ops) != 1 |
1194 not isinstance(node.operand.ops[0], ast.Eq) |
1187 or not isinstance(node.operand.ops[0], ast.Eq) |
1195 ) or |
1188 ) |
1196 isinstance(node.parent, ast.If) and |
1189 or isinstance(node.parent, ast.If) |
1197 self.__isExceptionCheck(node.parent) |
1190 and self.__isExceptionCheck(node.parent) |
1198 ): |
1191 ): |
1199 comparison = node.operand |
1192 comparison = node.operand |
1200 left = unparse(comparison.left) |
1193 left = unparse(comparison.left) |
1201 right = unparse(comparison.comparators[0]) |
1194 right = unparse(comparison.comparators[0]) |
1202 self.__error(node.lineno - 1, node.col_offset, "Y201", |
1195 self.__error(node.lineno - 1, node.col_offset, "Y201", left, right) |
1203 left, right) |
1196 |
1204 |
|
1205 def __check202(self, node): |
1197 def __check202(self, node): |
1206 """ |
1198 """ |
1207 Private method to check for calls where an unary 'not' is used for |
1199 Private method to check for calls where an unary 'not' is used for |
1208 an equality. |
1200 an equality. |
1209 |
1201 |
1210 @param node reference to the UnaryOp node |
1202 @param node reference to the UnaryOp node |
1211 @type ast.UnaryOp |
1203 @type ast.UnaryOp |
1212 """ |
1204 """ |
1213 # not a != b |
1205 # not a != b |
1214 if not ( |
1206 if not ( |
1215 ( |
1207 ( |
1216 not isinstance(node.op, ast.Not) or |
1208 not isinstance(node.op, ast.Not) |
1217 not isinstance(node.operand, ast.Compare) or |
1209 or not isinstance(node.operand, ast.Compare) |
1218 len(node.operand.ops) != 1 or |
1210 or len(node.operand.ops) != 1 |
1219 not isinstance(node.operand.ops[0], ast.NotEq) |
1211 or not isinstance(node.operand.ops[0], ast.NotEq) |
1220 ) or |
1212 ) |
1221 isinstance(node.parent, ast.If) and |
1213 or isinstance(node.parent, ast.If) |
1222 self.__isExceptionCheck(node.parent) |
1214 and self.__isExceptionCheck(node.parent) |
1223 ): |
1215 ): |
1224 comparison = node.operand |
1216 comparison = node.operand |
1225 left = unparse(comparison.left) |
1217 left = unparse(comparison.left) |
1226 right = unparse(comparison.comparators[0]) |
1218 right = unparse(comparison.comparators[0]) |
1227 self.__error(node.lineno - 1, node.col_offset, "Y202", |
1219 self.__error(node.lineno - 1, node.col_offset, "Y202", left, right) |
1228 left, right) |
1220 |
1229 |
|
1230 def __check203(self, node): |
1221 def __check203(self, node): |
1231 """ |
1222 """ |
1232 Private method to check for calls where an unary 'not' is used for |
1223 Private method to check for calls where an unary 'not' is used for |
1233 an in-check. |
1224 an in-check. |
1234 |
1225 |
1235 @param node reference to the UnaryOp node |
1226 @param node reference to the UnaryOp node |
1236 @type ast.UnaryOp |
1227 @type ast.UnaryOp |
1237 """ |
1228 """ |
1238 # not a in b |
1229 # not a in b |
1239 if not ( |
1230 if not ( |
1240 ( |
1231 ( |
1241 not isinstance(node.op, ast.Not) or |
1232 not isinstance(node.op, ast.Not) |
1242 not isinstance(node.operand, ast.Compare) or |
1233 or not isinstance(node.operand, ast.Compare) |
1243 len(node.operand.ops) != 1 or |
1234 or len(node.operand.ops) != 1 |
1244 not isinstance(node.operand.ops[0], ast.In) |
1235 or not isinstance(node.operand.ops[0], ast.In) |
1245 ) or |
1236 ) |
1246 isinstance(node.parent, ast.If) and |
1237 or isinstance(node.parent, ast.If) |
1247 self.__isExceptionCheck(node.parent) |
1238 and self.__isExceptionCheck(node.parent) |
1248 ): |
1239 ): |
1249 comparison = node.operand |
1240 comparison = node.operand |
1250 left = unparse(comparison.left) |
1241 left = unparse(comparison.left) |
1251 right = unparse(comparison.comparators[0]) |
1242 right = unparse(comparison.comparators[0]) |
1252 self.__error(node.lineno - 1, node.col_offset, "Y203", |
1243 self.__error(node.lineno - 1, node.col_offset, "Y203", left, right) |
1253 left, right) |
1244 |
1254 |
|
1255 def __check204(self, node): |
1245 def __check204(self, node): |
1256 """ |
1246 """ |
1257 Private method to check for calls of the type "not (a < b)". |
1247 Private method to check for calls of the type "not (a < b)". |
1258 |
1248 |
1259 @param node reference to the UnaryOp node |
1249 @param node reference to the UnaryOp node |
1260 @type ast.UnaryOp |
1250 @type ast.UnaryOp |
1261 """ |
1251 """ |
1262 # not a < b |
1252 # not a < b |
1263 if not ( |
1253 if not ( |
1264 ( |
1254 ( |
1265 not isinstance(node.op, ast.Not) or |
1255 not isinstance(node.op, ast.Not) |
1266 not isinstance(node.operand, ast.Compare) or |
1256 or not isinstance(node.operand, ast.Compare) |
1267 len(node.operand.ops) != 1 or |
1257 or len(node.operand.ops) != 1 |
1268 not isinstance(node.operand.ops[0], ast.Lt) |
1258 or not isinstance(node.operand.ops[0], ast.Lt) |
1269 ) or |
1259 ) |
1270 isinstance(node.parent, ast.If) and |
1260 or isinstance(node.parent, ast.If) |
1271 self.__isExceptionCheck(node.parent) |
1261 and self.__isExceptionCheck(node.parent) |
1272 ): |
1262 ): |
1273 comparison = node.operand |
1263 comparison = node.operand |
1274 left = unparse(comparison.left) |
1264 left = unparse(comparison.left) |
1275 right = unparse(comparison.comparators[0]) |
1265 right = unparse(comparison.comparators[0]) |
1276 self.__error(node.lineno - 1, node.col_offset, "Y204", |
1266 self.__error(node.lineno - 1, node.col_offset, "Y204", left, right) |
1277 left, right) |
1267 |
1278 |
|
1279 def __check205(self, node): |
1268 def __check205(self, node): |
1280 """ |
1269 """ |
1281 Private method to check for calls of the type "not (a <= b)". |
1270 Private method to check for calls of the type "not (a <= b)". |
1282 |
1271 |
1283 @param node reference to the UnaryOp node |
1272 @param node reference to the UnaryOp node |
1284 @type ast.UnaryOp |
1273 @type ast.UnaryOp |
1285 """ |
1274 """ |
1286 # not a <= b |
1275 # not a <= b |
1287 if not ( |
1276 if not ( |
1288 ( |
1277 ( |
1289 not isinstance(node.op, ast.Not) or |
1278 not isinstance(node.op, ast.Not) |
1290 not isinstance(node.operand, ast.Compare) or |
1279 or not isinstance(node.operand, ast.Compare) |
1291 len(node.operand.ops) != 1 or |
1280 or len(node.operand.ops) != 1 |
1292 not isinstance(node.operand.ops[0], ast.LtE) |
1281 or not isinstance(node.operand.ops[0], ast.LtE) |
1293 ) or |
1282 ) |
1294 isinstance(node.parent, ast.If) and |
1283 or isinstance(node.parent, ast.If) |
1295 self.__isExceptionCheck(node.parent) |
1284 and self.__isExceptionCheck(node.parent) |
1296 ): |
1285 ): |
1297 comparison = node.operand |
1286 comparison = node.operand |
1298 left = unparse(comparison.left) |
1287 left = unparse(comparison.left) |
1299 right = unparse(comparison.comparators[0]) |
1288 right = unparse(comparison.comparators[0]) |
1300 self.__error(node.lineno - 1, node.col_offset, "Y205", |
1289 self.__error(node.lineno - 1, node.col_offset, "Y205", left, right) |
1301 left, right) |
1290 |
1302 |
|
1303 def __check206(self, node): |
1291 def __check206(self, node): |
1304 """ |
1292 """ |
1305 Private method to check for calls of the type "not (a > b)". |
1293 Private method to check for calls of the type "not (a > b)". |
1306 |
1294 |
1307 @param node reference to the UnaryOp node |
1295 @param node reference to the UnaryOp node |
1308 @type ast.UnaryOp |
1296 @type ast.UnaryOp |
1309 """ |
1297 """ |
1310 # not a > b |
1298 # not a > b |
1311 if not ( |
1299 if not ( |
1312 ( |
1300 ( |
1313 not isinstance(node.op, ast.Not) or |
1301 not isinstance(node.op, ast.Not) |
1314 not isinstance(node.operand, ast.Compare) or |
1302 or not isinstance(node.operand, ast.Compare) |
1315 len(node.operand.ops) != 1 or |
1303 or len(node.operand.ops) != 1 |
1316 not isinstance(node.operand.ops[0], ast.Gt) |
1304 or not isinstance(node.operand.ops[0], ast.Gt) |
1317 ) or |
1305 ) |
1318 isinstance(node.parent, ast.If) and |
1306 or isinstance(node.parent, ast.If) |
1319 self.__isExceptionCheck(node.parent) |
1307 and self.__isExceptionCheck(node.parent) |
1320 ): |
1308 ): |
1321 comparison = node.operand |
1309 comparison = node.operand |
1322 left = unparse(comparison.left) |
1310 left = unparse(comparison.left) |
1323 right = unparse(comparison.comparators[0]) |
1311 right = unparse(comparison.comparators[0]) |
1324 self.__error(node.lineno - 1, node.col_offset, "Y206", |
1312 self.__error(node.lineno - 1, node.col_offset, "Y206", left, right) |
1325 left, right) |
1313 |
1326 |
|
1327 def __check207(self, node): |
1314 def __check207(self, node): |
1328 """ |
1315 """ |
1329 Private method to check for calls of the type "not (a >= b)". |
1316 Private method to check for calls of the type "not (a >= b)". |
1330 |
1317 |
1331 @param node reference to the UnaryOp node |
1318 @param node reference to the UnaryOp node |
1332 @type ast.UnaryOp |
1319 @type ast.UnaryOp |
1333 """ |
1320 """ |
1334 # not a >= b |
1321 # not a >= b |
1335 if not ( |
1322 if not ( |
1336 ( |
1323 ( |
1337 not isinstance(node.op, ast.Not) or |
1324 not isinstance(node.op, ast.Not) |
1338 not isinstance(node.operand, ast.Compare) or |
1325 or not isinstance(node.operand, ast.Compare) |
1339 len(node.operand.ops) != 1 or |
1326 or len(node.operand.ops) != 1 |
1340 not isinstance(node.operand.ops[0], ast.GtE) |
1327 or not isinstance(node.operand.ops[0], ast.GtE) |
1341 ) or |
1328 ) |
1342 isinstance(node.parent, ast.If) and |
1329 or isinstance(node.parent, ast.If) |
1343 self.__isExceptionCheck(node.parent) |
1330 and self.__isExceptionCheck(node.parent) |
1344 ): |
1331 ): |
1345 comparison = node.operand |
1332 comparison = node.operand |
1346 left = unparse(comparison.left) |
1333 left = unparse(comparison.left) |
1347 right = unparse(comparison.comparators[0]) |
1334 right = unparse(comparison.comparators[0]) |
1348 self.__error(node.lineno - 1, node.col_offset, "Y207", |
1335 self.__error(node.lineno - 1, node.col_offset, "Y207", left, right) |
1349 left, right) |
1336 |
1350 |
|
1351 def __check208(self, node): |
1337 def __check208(self, node): |
1352 """ |
1338 """ |
1353 Private method to check for calls of the type "not (not a)". |
1339 Private method to check for calls of the type "not (not a)". |
1354 |
1340 |
1355 @param node reference to the UnaryOp node |
1341 @param node reference to the UnaryOp node |
1356 @type ast.UnaryOp |
1342 @type ast.UnaryOp |
1357 """ |
1343 """ |
1358 # not (not a) |
1344 # not (not a) |
1359 if ( |
1345 if ( |
1360 isinstance(node.op, ast.Not) and |
1346 isinstance(node.op, ast.Not) |
1361 isinstance(node.operand, ast.UnaryOp) and |
1347 and isinstance(node.operand, ast.UnaryOp) |
1362 isinstance(node.operand.op, ast.Not) |
1348 and isinstance(node.operand.op, ast.Not) |
1363 ): |
1349 ): |
1364 var = unparse(node.operand.operand) |
1350 var = unparse(node.operand.operand) |
1365 self.__error(node.lineno - 1, node.col_offset, "Y208", var) |
1351 self.__error(node.lineno - 1, node.col_offset, "Y208", var) |
1366 |
1352 |
1367 def __check211(self, node): |
1353 def __check211(self, node): |
1368 """ |
1354 """ |
1369 Private method to check for calls of the type "True if a else False". |
1355 Private method to check for calls of the type "True if a else False". |
1370 |
1356 |
1371 @param node reference to the AST node to be checked |
1357 @param node reference to the AST node to be checked |
1372 @type ast.IfExp |
1358 @type ast.IfExp |
1373 """ |
1359 """ |
1374 # True if a else False |
1360 # True if a else False |
1375 if ( |
1361 if ( |
1376 isinstance(node.body, BOOL_CONST_TYPES) and |
1362 isinstance(node.body, BOOL_CONST_TYPES) |
1377 node.body.value is True and |
1363 and node.body.value is True |
1378 isinstance(node.orelse, BOOL_CONST_TYPES) and |
1364 and isinstance(node.orelse, BOOL_CONST_TYPES) |
1379 node.orelse.value is False |
1365 and node.orelse.value is False |
1380 ): |
1366 ): |
1381 cond = unparse(node.test) |
1367 cond = unparse(node.test) |
1382 if isinstance(node.test, ast.Name): |
1368 if isinstance(node.test, ast.Name): |
1383 newCond = "bool({0})".format(cond) |
1369 newCond = "bool({0})".format(cond) |
1384 else: |
1370 else: |
1385 newCond = cond |
1371 newCond = cond |
1386 self.__error(node.lineno - 1, node.col_offset, "Y211", |
1372 self.__error(node.lineno - 1, node.col_offset, "Y211", cond, newCond) |
1387 cond, newCond) |
1373 |
1388 |
|
1389 def __check212(self, node): |
1374 def __check212(self, node): |
1390 """ |
1375 """ |
1391 Private method to check for calls of the type "False if a else True". |
1376 Private method to check for calls of the type "False if a else True". |
1392 |
1377 |
1393 @param node reference to the AST node to be checked |
1378 @param node reference to the AST node to be checked |
1394 @type ast.IfExp |
1379 @type ast.IfExp |
1395 """ |
1380 """ |
1396 # False if a else True |
1381 # False if a else True |
1397 if ( |
1382 if ( |
1398 isinstance(node.body, BOOL_CONST_TYPES) and |
1383 isinstance(node.body, BOOL_CONST_TYPES) |
1399 node.body.value is False and |
1384 and node.body.value is False |
1400 isinstance(node.orelse, BOOL_CONST_TYPES) and |
1385 and isinstance(node.orelse, BOOL_CONST_TYPES) |
1401 node.orelse.value is True |
1386 and node.orelse.value is True |
1402 ): |
1387 ): |
1403 cond = unparse(node.test) |
1388 cond = unparse(node.test) |
1404 if isinstance(node.test, ast.Name): |
1389 if isinstance(node.test, ast.Name): |
1405 newCond = "not {0}".format(cond) |
1390 newCond = "not {0}".format(cond) |
1406 else: |
1391 else: |
1407 if len(node.test.ops) == 1: |
1392 if len(node.test.ops) == 1: |
1408 newCond = unparse(self.__negateTest(node.test)) |
1393 newCond = unparse(self.__negateTest(node.test)) |
1409 else: |
1394 else: |
1410 newCond = "not ({0})".format(cond) |
1395 newCond = "not ({0})".format(cond) |
1411 self.__error(node.lineno - 1, node.col_offset, "Y212", |
1396 self.__error(node.lineno - 1, node.col_offset, "Y212", cond, newCond) |
1412 cond, newCond) |
1397 |
1413 |
|
1414 def __check213(self, node): |
1398 def __check213(self, node): |
1415 """ |
1399 """ |
1416 Private method to check for calls of the type "b if not a else a". |
1400 Private method to check for calls of the type "b if not a else a". |
1417 |
1401 |
1418 @param node reference to the AST node to be checked |
1402 @param node reference to the AST node to be checked |
1419 @type ast.IfExp |
1403 @type ast.IfExp |
1420 """ |
1404 """ |
1421 # b if not a else a |
1405 # b if not a else a |
1422 if ( |
1406 if ( |
1423 isinstance(node.test, ast.UnaryOp) and |
1407 isinstance(node.test, ast.UnaryOp) |
1424 isinstance(node.test.op, ast.Not) and |
1408 and isinstance(node.test.op, ast.Not) |
1425 self.__isSameExpression(node.test.operand, node.orelse) |
1409 and self.__isSameExpression(node.test.operand, node.orelse) |
1426 ): |
1410 ): |
1427 a = unparse(node.test.operand) |
1411 a = unparse(node.test.operand) |
1428 b = unparse(node.body) |
1412 b = unparse(node.body) |
1429 self.__error(node.lineno - 1, node.col_offset, "Y213", a, b) |
1413 self.__error(node.lineno - 1, node.col_offset, "Y213", a, b) |
1430 |
1414 |
1431 def __check221(self, node): |
1415 def __check221(self, node): |
1432 """ |
1416 """ |
1433 Private method to check for calls of the type "a and not a". |
1417 Private method to check for calls of the type "a and not a". |
1434 |
1418 |
1435 @param node reference to the AST node to be checked |
1419 @param node reference to the AST node to be checked |
1436 @type ast.BoolOp |
1420 @type ast.BoolOp |
1437 """ |
1421 """ |
1438 # a and not a |
1422 # a and not a |
1439 if ( |
1423 if isinstance(node.op, ast.And) and len(node.values) >= 2: |
1440 isinstance(node.op, ast.And) and |
|
1441 len(node.values) >= 2 |
|
1442 ): |
|
1443 # We have a boolean And. Let's make sure there is two times the |
1424 # We have a boolean And. Let's make sure there is two times the |
1444 # same expression, but once with a "not" |
1425 # same expression, but once with a "not" |
1445 negatedExpressions = [] |
1426 negatedExpressions = [] |
1446 nonNegatedExpressions = [] |
1427 nonNegatedExpressions = [] |
1447 for exp in node.values: |
1428 for exp in node.values: |
1448 if ( |
1429 if isinstance(exp, ast.UnaryOp) and isinstance(exp.op, ast.Not): |
1449 isinstance(exp, ast.UnaryOp) and |
|
1450 isinstance(exp.op, ast.Not) |
|
1451 ): |
|
1452 negatedExpressions.append(exp.operand) |
1430 negatedExpressions.append(exp.operand) |
1453 else: |
1431 else: |
1454 nonNegatedExpressions.append(exp) |
1432 nonNegatedExpressions.append(exp) |
1455 for negatedExpression in negatedExpressions: |
1433 for negatedExpression in negatedExpressions: |
1456 for nonNegatedExpression in nonNegatedExpressions: |
1434 for nonNegatedExpression in nonNegatedExpressions: |
1457 if self.__isSameExpression( |
1435 if self.__isSameExpression(negatedExpression, nonNegatedExpression): |
1458 negatedExpression, nonNegatedExpression |
|
1459 ): |
|
1460 negExp = unparse(negatedExpression) |
1436 negExp = unparse(negatedExpression) |
1461 self.__error(node.lineno - 1, node.col_offset, "Y221", |
1437 self.__error(node.lineno - 1, node.col_offset, "Y221", negExp) |
1462 negExp) |
1438 |
1463 |
|
1464 def __check222(self, node): |
1439 def __check222(self, node): |
1465 """ |
1440 """ |
1466 Private method to check for calls of the type "a or not a". |
1441 Private method to check for calls of the type "a or not a". |
1467 |
1442 |
1468 @param node reference to the AST node to be checked |
1443 @param node reference to the AST node to be checked |
1469 @type ast.BoolOp |
1444 @type ast.BoolOp |
1470 """ |
1445 """ |
1471 # a or not a |
1446 # a or not a |
1472 if ( |
1447 if isinstance(node.op, ast.Or) and len(node.values) >= 2: |
1473 isinstance(node.op, ast.Or) and |
|
1474 len(node.values) >= 2 |
|
1475 ): |
|
1476 # We have a boolean And. Let's make sure there is two times the |
1448 # We have a boolean And. Let's make sure there is two times the |
1477 # same expression, but once with a "not" |
1449 # same expression, but once with a "not" |
1478 negatedExpressions = [] |
1450 negatedExpressions = [] |
1479 nonNegatedExpressions = [] |
1451 nonNegatedExpressions = [] |
1480 for exp in node.values: |
1452 for exp in node.values: |
1481 if ( |
1453 if isinstance(exp, ast.UnaryOp) and isinstance(exp.op, ast.Not): |
1482 isinstance(exp, ast.UnaryOp) and |
|
1483 isinstance(exp.op, ast.Not) |
|
1484 ): |
|
1485 negatedExpressions.append(exp.operand) |
1454 negatedExpressions.append(exp.operand) |
1486 else: |
1455 else: |
1487 nonNegatedExpressions.append(exp) |
1456 nonNegatedExpressions.append(exp) |
1488 for negatedExpression in negatedExpressions: |
1457 for negatedExpression in negatedExpressions: |
1489 for nonNegatedExpression in nonNegatedExpressions: |
1458 for nonNegatedExpression in nonNegatedExpressions: |
1490 if self.__isSameExpression( |
1459 if self.__isSameExpression(negatedExpression, nonNegatedExpression): |
1491 negatedExpression, nonNegatedExpression |
|
1492 ): |
|
1493 negExp = unparse(negatedExpression) |
1460 negExp = unparse(negatedExpression) |
1494 self.__error(node.lineno - 1, node.col_offset, "Y222", |
1461 self.__error(node.lineno - 1, node.col_offset, "Y222", negExp) |
1495 negExp) |
1462 |
1496 |
|
1497 def __check223(self, node): |
1463 def __check223(self, node): |
1498 """ |
1464 """ |
1499 Private method to check for calls of the type "... or True". |
1465 Private method to check for calls of the type "... or True". |
1500 |
1466 |
1501 @param node reference to the AST node to be checked |
1467 @param node reference to the AST node to be checked |
1502 @type ast.BoolOp |
1468 @type ast.BoolOp |
1503 """ |
1469 """ |
1504 # a or True |
1470 # a or True |
1505 if isinstance(node.op, ast.Or): |
1471 if isinstance(node.op, ast.Or): |
1506 for exp in node.values: |
1472 for exp in node.values: |
1507 if isinstance(exp, BOOL_CONST_TYPES) and exp.value is True: |
1473 if isinstance(exp, BOOL_CONST_TYPES) and exp.value is True: |
1508 self.__error(node.lineno - 1, node.col_offset, "Y223") |
1474 self.__error(node.lineno - 1, node.col_offset, "Y223") |
1509 |
1475 |
1510 def __check224(self, node): |
1476 def __check224(self, node): |
1511 """ |
1477 """ |
1512 Private method to check for calls of the type "... and False". |
1478 Private method to check for calls of the type "... and False". |
1513 |
1479 |
1514 @param node reference to the AST node to be checked |
1480 @param node reference to the AST node to be checked |
1515 @type ast.BoolOp |
1481 @type ast.BoolOp |
1516 """ |
1482 """ |
1517 # a and False |
1483 # a and False |
1518 if isinstance(node.op, ast.And): |
1484 if isinstance(node.op, ast.And): |
1519 for exp in node.values: |
1485 for exp in node.values: |
1520 if isinstance(exp, BOOL_CONST_TYPES) and exp.value is False: |
1486 if isinstance(exp, BOOL_CONST_TYPES) and exp.value is False: |
1521 self.__error(node.lineno - 1, node.col_offset, "Y224") |
1487 self.__error(node.lineno - 1, node.col_offset, "Y224") |
1522 |
1488 |
1523 def __check301(self, node): |
1489 def __check301(self, node): |
1524 """ |
1490 """ |
1525 Private method to check for Yoda conditions. |
1491 Private method to check for Yoda conditions. |
1526 |
1492 |
1527 @param node reference to the AST node to be checked |
1493 @param node reference to the AST node to be checked |
1528 @type ast.Compare |
1494 @type ast.Compare |
1529 """ |
1495 """ |
1530 # 42 == age |
1496 # 42 == age |
1531 if ( |
1497 if ( |
1532 isinstance(node.left, AST_CONST_TYPES) and |
1498 isinstance(node.left, AST_CONST_TYPES) |
1533 len(node.ops) == 1 and |
1499 and len(node.ops) == 1 |
1534 isinstance(node.ops[0], ast.Eq) |
1500 and isinstance(node.ops[0], ast.Eq) |
1535 ): |
1501 ): |
1536 left = unparse(node.left) |
1502 left = unparse(node.left) |
1537 isPy37Str = isinstance(node.left, ast.Str) |
1503 isPy37Str = isinstance(node.left, ast.Str) |
1538 isPy38Str = ( |
1504 isPy38Str = isinstance(node.left, ast.Constant) and isinstance( |
1539 isinstance(node.left, ast.Constant) and |
1505 node.left.value, str |
1540 isinstance(node.left.value, str) |
|
1541 ) |
1506 ) |
1542 if isPy37Str or isPy38Str: |
1507 if isPy37Str or isPy38Str: |
1543 left = f"'{left}'" |
1508 left = f"'{left}'" |
1544 right = unparse(node.comparators[0]) |
1509 right = unparse(node.comparators[0]) |
1545 self.__error(node.lineno - 1, node.col_offset, "Y301", |
1510 self.__error(node.lineno - 1, node.col_offset, "Y301", left, right) |
1546 left, right) |
1511 |
1547 |
|
1548 def __check401(self, node): |
1512 def __check401(self, node): |
1549 """ |
1513 """ |
1550 Private method to check for bare boolean function arguments. |
1514 Private method to check for bare boolean function arguments. |
1551 |
1515 |
1552 @param node reference to the AST node to be checked |
1516 @param node reference to the AST node to be checked |
1553 @type ast.Call |
1517 @type ast.Call |
1554 """ |
1518 """ |
1555 # foo(a, b, True) |
1519 # foo(a, b, True) |
1556 hasBareBool = any( |
1520 hasBareBool = any( |
1557 isinstance(callArg, ast.Constant) and |
1521 isinstance(callArg, ast.Constant) |
1558 (callArg.value is True or callArg.value is False) |
1522 and (callArg.value is True or callArg.value is False) |
1559 for callArg in node.args |
1523 for callArg in node.args |
1560 ) |
1524 ) |
1561 |
1525 |
1562 isException = ( |
1526 isException = isinstance(node.func, ast.Attribute) and node.func.attr in ["get"] |
1563 isinstance(node.func, ast.Attribute) and |
1527 |
1564 node.func.attr in ["get"] |
|
1565 ) |
|
1566 |
|
1567 if hasBareBool and not isException: |
1528 if hasBareBool and not isException: |
1568 self.__error(node.lineno - 1, node.col_offset, "Y401") |
1529 self.__error(node.lineno - 1, node.col_offset, "Y401") |
1569 |
1530 |
1570 def __check402(self, node): |
1531 def __check402(self, node): |
1571 """ |
1532 """ |
1572 Private method to check for bare numeric function arguments. |
1533 Private method to check for bare numeric function arguments. |
1573 |
1534 |
1574 @param node reference to the AST node to be checked |
1535 @param node reference to the AST node to be checked |
1575 @type ast.Call |
1536 @type ast.Call |
1576 """ |
1537 """ |
1577 # foo(a, b, 123123) |
1538 # foo(a, b, 123123) |
1578 hasBareNumeric = any( |
1539 hasBareNumeric = any( |
1579 isinstance(callArg, ast.Constant) and |
1540 isinstance(callArg, ast.Constant) and type(callArg.value) in (float, int) |
1580 type(callArg.value) in (float, int) |
|
1581 for callArg in node.args |
1541 for callArg in node.args |
1582 ) |
1542 ) |
1583 |
1543 |
1584 isException = ( |
1544 isException = isinstance(node.func, ast.Name) and node.func.id == "range" |
1585 isinstance(node.func, ast.Name) and |
1545 isException = isException or ( |
1586 node.func.id == "range" |
1546 isinstance(node.func, ast.Attribute) and node.func.attr in ("get", "insert") |
1587 ) |
1547 ) |
1588 isException = isException or ( |
1548 |
1589 isinstance(node.func, ast.Attribute) and |
|
1590 node.func.attr in ("get", "insert") |
|
1591 ) |
|
1592 |
|
1593 if hasBareNumeric and not isException: |
1549 if hasBareNumeric and not isException: |
1594 self.__error(node.lineno - 1, node.col_offset, "Y402") |
1550 self.__error(node.lineno - 1, node.col_offset, "Y402") |
1595 |
1551 |
|
1552 |
1596 # |
1553 # |
1597 # eflag: noqa = M891 |
1554 # eflag: noqa = M891 |