src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Simplify/SimplifyNodeVisitor.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9209
b99e7fd55fd3
child 9274
86fab0c74430
equal deleted inserted replaced
9220:e9e7eca7efee 9221:bf71ee032bb4
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)
230 self.__check204(node) 231 self.__check204(node)
231 self.__check205(node) 232 self.__check205(node)
232 self.__check206(node) 233 self.__check206(node)
233 self.__check207(node) 234 self.__check207(node)
234 self.__check208(node) 235 self.__check208(node)
235 236
236 self.generic_visit(node) 237 self.generic_visit(node)
237 238
238 ############################################################# 239 #############################################################
239 ## Helper methods for the various checkers below 240 ## Helper methods for the various checkers below
240 ############################################################# 241 #############################################################
241 242
242 def __getDuplicatedIsinstanceCall(self, node): 243 def __getDuplicatedIsinstanceCall(self, node):
243 """ 244 """
244 Private method to get a list of isinstance arguments which could 245 Private method to get a list of isinstance arguments which could
245 be combined. 246 be combined.
246 247
247 @param node reference to the AST node to be inspected 248 @param node reference to the AST node to be inspected
248 @type ast.BoolOp 249 @type ast.BoolOp
249 @return list of variable names of duplicated isinstance calls 250 @return list of variable names of duplicated isinstance calls
250 @rtype list of str 251 @rtype list of str
251 """ 252 """
252 counter = collections.defaultdict(int) 253 counter = collections.defaultdict(int)
253 254
254 for call in node.values: 255 for call in node.values:
255 # Ensure this is a call of the built-in isinstance() function. 256 # Ensure this is a call of the built-in isinstance() function.
256 if not isinstance(call, ast.Call) or len(call.args) != 2: 257 if not isinstance(call, ast.Call) or len(call.args) != 2:
257 continue 258 continue
258 functionName = unparse(call.func) 259 functionName = unparse(call.func)
259 if functionName != "isinstance": 260 if functionName != "isinstance":
260 continue 261 continue
261 262
262 arg0Name = unparse(call.args[0]) 263 arg0Name = unparse(call.args[0])
263 counter[arg0Name] += 1 264 counter[arg0Name] += 1
264 265
265 return [name for name, count in counter.items() if count > 1] 266 return [name for name, count in counter.items() if count > 1]
266 267
267 def __isConstantIncrease(self, expression): 268 def __isConstantIncrease(self, expression):
268 """ 269 """
269 Private method check the given expression for being a constant 270 Private method check the given expression for being a constant
270 increase. 271 increase.
271 272
272 @param expression reference to the expression node 273 @param expression reference to the expression node
273 @type ast.AugAssign 274 @type ast.AugAssign
274 @return flag indicating a constant increase 275 @return flag indicating a constant increase
275 @rtype bool 276 @rtype bool
276 """ 277 """
277 return ( 278 return isinstance(expression.op, ast.Add) and (
278 isinstance(expression.op, ast.Add) and ( 279 (
279 (isinstance(expression.value, ast.Constant) and 280 isinstance(expression.value, ast.Constant)
280 isinstance(expression.value.value, int)) or 281 and isinstance(expression.value.value, int)
281 isinstance(expression.value, ast.Num) 282 )
282 ) 283 or isinstance(expression.value, ast.Num)
283 ) 284 )
284 285
285 def __getIfBodyPairs(self, node): 286 def __getIfBodyPairs(self, node):
286 """ 287 """
287 Private method to extract a list of pairs of test and body for an 288 Private method to extract a list of pairs of test and body for an
288 If node. 289 If node.
289 290
290 @param node reference to the If node to be processed 291 @param node reference to the If node to be processed
291 @type ast.If 292 @type ast.If
292 @return list of pairs of test and body 293 @return list of pairs of test and body
293 @rtype list of tuples of (ast.expr, [ast.stmt]) 294 @rtype list of tuples of (ast.expr, [ast.stmt])
294 """ 295 """
295 pairs = [(node.test, node.body)] 296 pairs = [(node.test, node.body)]
296 orelse = node.orelse 297 orelse = node.orelse
297 while ( 298 while (
298 isinstance(orelse, list) and 299 isinstance(orelse, list)
299 len(orelse) == 1 and 300 and len(orelse) == 1
300 isinstance(orelse[0], ast.If) 301 and isinstance(orelse[0], ast.If)
301 ): 302 ):
302 pairs.append((orelse[0].test, orelse[0].body)) 303 pairs.append((orelse[0].test, orelse[0].body))
303 orelse = orelse[0].orelse 304 orelse = orelse[0].orelse
304 return pairs 305 return pairs
305 306
306 def __isSameBody(self, body1, body2): 307 def __isSameBody(self, body1, body2):
307 """ 308 """
308 Private method check, if the given bodies are equivalent. 309 Private method check, if the given bodies are equivalent.
309 310
310 @param body1 list of statements of the first body 311 @param body1 list of statements of the first body
311 @type list of ast.stmt 312 @type list of ast.stmt
312 @param body2 list of statements of the second body 313 @param body2 list of statements of the second body
313 @type list of ast.stmt 314 @type list of ast.stmt
314 @return flag indicating identical bodies 315 @return flag indicating identical bodies
321 statementEqual = self.__isStatementEqual(a, b) 322 statementEqual = self.__isStatementEqual(a, b)
322 except RecursionError: # maximum recursion depth 323 except RecursionError: # maximum recursion depth
323 statementEqual = False 324 statementEqual = False
324 if not statementEqual: 325 if not statementEqual:
325 return False 326 return False
326 327
327 return True 328 return True
328 329
329 def __isSameExpression(self, a, b): 330 def __isSameExpression(self, a, b):
330 """ 331 """
331 Private method to check, if two expressions are equal. 332 Private method to check, if two expressions are equal.
332 333
333 @param a first expression to be checked 334 @param a first expression to be checked
334 @type ast.expr 335 @type ast.expr
335 @param b second expression to be checked 336 @param b second expression to be checked
336 @type ast.expr 337 @type ast.expr
337 @return flag indicating equal expressions 338 @return flag indicating equal expressions
339 """ 340 """
340 if isinstance(a, ast.Name) and isinstance(b, ast.Name): 341 if isinstance(a, ast.Name) and isinstance(b, ast.Name):
341 return a.id == b.id 342 return a.id == b.id
342 else: 343 else:
343 return False 344 return False
344 345
345 def __isStatementEqual(self, a, b): 346 def __isStatementEqual(self, a, b):
346 """ 347 """
347 Private method to check, if two statements are equal. 348 Private method to check, if two statements are equal.
348 349
349 @param a reference to the first statement 350 @param a reference to the first statement
350 @type ast.stmt 351 @type ast.stmt
351 @param b reference to the second statement 352 @param b reference to the second statement
352 @type ast.stmt 353 @type ast.stmt
353 @return flag indicating if the two statements are equal 354 @return flag indicating if the two statements are equal
354 @rtype bool 355 @rtype bool
355 """ 356 """
356 if type(a) is not type(b): 357 if type(a) is not type(b):
357 return False 358 return False
358 359
359 if isinstance(a, ast.AST): 360 if isinstance(a, ast.AST):
360 for k, v in vars(a).items(): 361 for k, v in vars(a).items():
361 if k in ("lineno", "col_offset", "ctx", "end_lineno", 362 if k in ("lineno", "col_offset", "ctx", "end_lineno", "parent"):
362 "parent"):
363 continue 363 continue
364 if not self.__isStatementEqual(v, getattr(b, k)): 364 if not self.__isStatementEqual(v, getattr(b, k)):
365 return False 365 return False
366 return True 366 return True
367 elif isinstance(a, list): 367 elif isinstance(a, list):
368 return all(itertools.starmap(self.__isStatementEqual, zip(a, b))) 368 return all(itertools.starmap(self.__isStatementEqual, zip(a, b)))
369 else: 369 else:
370 return a == b 370 return a == b
371 371
372 def __isExceptionCheck(self, node): 372 def __isExceptionCheck(self, node):
373 """ 373 """
374 Private method to check, if the node is checking an exception. 374 Private method to check, if the node is checking an exception.
375 375
376 @param node reference to the node to be checked 376 @param node reference to the node to be checked
377 @type ast.If 377 @type ast.If
378 @return flag indicating an exception check 378 @return flag indicating an exception check
379 @rtype bool 379 @rtype bool
380 """ 380 """
381 return ( 381 return len(node.body) == 1 and isinstance(node.body[0], ast.Raise)
382 len(node.body) == 1 and isinstance(node.body[0], ast.Raise) 382
383 )
384
385 def __negateTest(self, node): 383 def __negateTest(self, node):
386 """ 384 """
387 Private method negate the given Compare node. 385 Private method negate the given Compare node.
388 386
389 @param node reference to the node to be negated 387 @param node reference to the node to be negated
390 @type ast.Compare 388 @type ast.Compare
391 @return node with negated logic 389 @return node with negated logic
392 @rtype ast.Compare 390 @rtype ast.Compare
393 """ 391 """
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:
609 finallyHasReturn = True 604 finallyHasReturn = True
610 finallyReturn = stmt 605 finallyReturn = stmt
611 break 606 break
612 607
613 if ( 608 if (
614 (tryHasReturn or exceptHasReturn) and 609 (tryHasReturn or exceptHasReturn)
615 finallyHasReturn and 610 and finallyHasReturn
616 finallyReturn is not None 611 and finallyReturn is not None
617 ): 612 ):
618 self.__error(finallyReturn.lineno - 1, 613 self.__error(finallyReturn.lineno - 1, finallyReturn.col_offset, "Y107")
619 finallyReturn.col_offset, "Y107") 614
620
621 def __check108(self, node): 615 def __check108(self, node):
622 """ 616 """
623 Private method to check for if-elses which could be a ternary 617 Private method to check for if-elses which could be a ternary
624 operator assignment. 618 operator assignment.
625 619
626 @param node reference to the AST node to be checked 620 @param node reference to the AST node to be checked
627 @type ast.If 621 @type ast.If
628 """ 622 """
629 # if a: 623 # if a:
630 # b = c 624 # b = c
637 # elif c: 631 # elif c:
638 # b = e 632 # b = e
639 # else: 633 # else:
640 # b = d 634 # b = d
641 if ( 635 if (
642 len(node.body) == 1 and 636 len(node.body) == 1
643 len(node.orelse) == 1 and 637 and len(node.orelse) == 1
644 isinstance(node.body[0], ast.Assign) and 638 and isinstance(node.body[0], ast.Assign)
645 isinstance(node.orelse[0], ast.Assign) and 639 and isinstance(node.orelse[0], ast.Assign)
646 len(node.body[0].targets) == 1 and 640 and len(node.body[0].targets) == 1
647 len(node.orelse[0].targets) == 1 and 641 and len(node.orelse[0].targets) == 1
648 isinstance(node.body[0].targets[0], ast.Name) and 642 and isinstance(node.body[0].targets[0], ast.Name)
649 isinstance(node.orelse[0].targets[0], ast.Name) and 643 and isinstance(node.orelse[0].targets[0], ast.Name)
650 node.body[0].targets[0].id == node.orelse[0].targets[0].id and 644 and node.body[0].targets[0].id == node.orelse[0].targets[0].id
651 not isinstance(node.parent, ast.If) 645 and not isinstance(node.parent, ast.If)
652 ): 646 ):
653 assign = unparse(node.body[0].targets[0]) 647 assign = unparse(node.body[0].targets[0])
654 body = unparse(node.body[0].value) 648 body = unparse(node.body[0].value)
655 cond = unparse(node.test) 649 cond = unparse(node.test)
656 orelse = unparse(node.orelse[0].value) 650 orelse = unparse(node.orelse[0].value)
657 651
658 self.__error(node.lineno - 1, node.col_offset, "Y108", 652 self.__error(
659 assign, body, cond, orelse) 653 node.lineno - 1, node.col_offset, "Y108", assign, body, cond, orelse
660 654 )
655
661 def __check109(self, node): 656 def __check109(self, node):
662 """ 657 """
663 Private method to check for multiple equalities with the same value 658 Private method to check for multiple equalities with the same value
664 are combined via "or". 659 are combined via "or".
665 660
666 @param node reference to the AST node to be checked 661 @param node reference to the AST node to be checked
667 @type ast.BoolOp 662 @type ast.BoolOp
668 """ 663 """
669 # if a == b or a == c: 664 # if a == b or a == c:
670 # d 665 # d
671 if isinstance(node.op, ast.Or): 666 if isinstance(node.op, ast.Or):
672 equalities = [ 667 equalities = [
673 value 668 value
674 for value in node.values 669 for value in node.values
675 if isinstance(value, ast.Compare) and 670 if isinstance(value, ast.Compare)
676 len(value.ops) == 1 and 671 and len(value.ops) == 1
677 isinstance(value.ops[0], ast.Eq) 672 and isinstance(value.ops[0], ast.Eq)
678 ] 673 ]
679 ids = [] # (name, compared_to) 674 ids = [] # (name, compared_to)
680 for eq in equalities: 675 for eq in equalities:
681 if isinstance(eq.left, ast.Name): 676 if isinstance(eq.left, ast.Name):
682 ids.append((eq.left, eq.comparators[0])) 677 ids.append((eq.left, eq.comparators[0]))
683 if ( 678 if len(eq.comparators) == 1 and isinstance(eq.comparators[0], ast.Name):
684 len(eq.comparators) == 1 and
685 isinstance(eq.comparators[0], ast.Name)
686 ):
687 ids.append((eq.comparators[0], eq.left)) 679 ids.append((eq.comparators[0], eq.left))
688 680
689 id2count = {} 681 id2count = {}
690 for identifier, comparedTo in ids: 682 for identifier, comparedTo in ids:
691 if identifier.id not in id2count: 683 if identifier.id not in id2count:
692 id2count[identifier.id] = [] 684 id2count[identifier.id] = []
693 id2count[identifier.id].append(comparedTo) 685 id2count[identifier.id].append(comparedTo)
694 for value, values in id2count.items(): 686 for value, values in id2count.items():
695 if len(values) == 1: 687 if len(values) == 1:
696 continue 688 continue
697 689
698 self.__error(node.lineno - 1, node.col_offset, "Y109", 690 self.__error(
699 value, unparse(ast.Tuple(elts=values)), 691 node.lineno - 1,
700 unparse(node)) 692 node.col_offset,
701 693 "Y109",
694 value,
695 unparse(ast.Tuple(elts=values)),
696 unparse(node),
697 )
698
702 def __check110_111(self, node): 699 def __check110_111(self, node):
703 """ 700 """
704 Private method to check if any / all could be used. 701 Private method to check if any / all could be used.
705 702
706 @param node reference to the AST node to be checked 703 @param node reference to the AST node to be checked
707 @type ast.For 704 @type ast.For
708 """ 705 """
709 # for x in iterable: 706 # for x in iterable:
710 # if check(x): 707 # if check(x):
714 # for x in iterable: 711 # for x in iterable:
715 # if check(x): 712 # if check(x):
716 # return False 713 # return False
717 # return True 714 # return True
718 if ( 715 if (
719 len(node.body) == 1 and 716 len(node.body) == 1
720 isinstance(node.body[0], ast.If) and 717 and isinstance(node.body[0], ast.If)
721 len(node.body[0].body) == 1 and 718 and len(node.body[0].body) == 1
722 isinstance(node.body[0].body[0], ast.Return) and 719 and isinstance(node.body[0].body[0], ast.Return)
723 isinstance(node.body[0].body[0].value, BOOL_CONST_TYPES) and 720 and isinstance(node.body[0].body[0].value, BOOL_CONST_TYPES)
724 hasattr(node.body[0].body[0].value, "value") 721 and hasattr(node.body[0].body[0].value, "value")
725 ): 722 ):
726 check = unparse(node.body[0].test) 723 check = unparse(node.body[0].test)
727 target = unparse(node.target) 724 target = unparse(node.target)
728 iterable = unparse(node.iter) 725 iterable = unparse(node.iter)
729 if node.body[0].body[0].value.value is True: 726 if node.body[0].body[0].value.value is True:
730 self.__error(node.lineno - 1, node.col_offset, "Y110", 727 self.__error(
731 check, target, iterable) 728 node.lineno - 1, node.col_offset, "Y110", check, target, iterable
729 )
732 elif node.body[0].body[0].value.value is False: 730 elif node.body[0].body[0].value.value is False:
733 check = "not " + check 731 check = "not " + check
734 if check.startswith("not not "): 732 if check.startswith("not not "):
735 check = check[len("not not "):] 733 check = check[len("not not ") :]
736 self.__error(node.lineno - 1, node.col_offset, "Y111", 734 self.__error(
737 check, target, iterable) 735 node.lineno - 1, node.col_offset, "Y111", check, target, iterable
738 736 )
737
739 def __check112(self, node): 738 def __check112(self, node):
740 """ 739 """
741 Private method to check for non-capitalized calls to environment 740 Private method to check for non-capitalized calls to environment
742 variables. 741 variables.
743 742
744 @param node reference to the AST node to be checked 743 @param node reference to the AST node to be checked
745 @type ast.Expr 744 @type ast.Expr
746 """ 745 """
747 # os.environ["foo"] 746 # os.environ["foo"]
748 # os.environ.get("bar") 747 # os.environ.get("bar")
749 isIndexCall = ( 748 isIndexCall = (
750 isinstance(node.value, ast.Subscript) and 749 isinstance(node.value, ast.Subscript)
751 isinstance(node.value.value, ast.Attribute) and 750 and isinstance(node.value.value, ast.Attribute)
752 isinstance(node.value.value.value, ast.Name) and 751 and isinstance(node.value.value.value, ast.Name)
753 node.value.value.value.id == "os" and 752 and node.value.value.value.id == "os"
754 node.value.value.attr == "environ" and 753 and node.value.value.attr == "environ"
755 ( 754 and (
756 ( 755 (
757 isinstance(node.value.slice, ast.Index) and 756 isinstance(node.value.slice, ast.Index)
758 isinstance(node.value.slice.value, STR_TYPES) 757 and isinstance(node.value.slice.value, STR_TYPES)
759 ) or 758 )
760 isinstance(node.value.slice, ast.Constant) 759 or isinstance(node.value.slice, ast.Constant)
761 ) 760 )
762 ) 761 )
763 if isIndexCall: 762 if isIndexCall:
764 subscript = node.value 763 subscript = node.value
765 slice_ = subscript.slice 764 slice_ = subscript.slice
776 775
777 # Check if this has a change 776 # Check if this has a change
778 hasChange = envName != envName.upper() 777 hasChange = envName != envName.upper()
779 778
780 isGetCall = ( 779 isGetCall = (
781 isinstance(node.value, ast.Call) and 780 isinstance(node.value, ast.Call)
782 isinstance(node.value.func, ast.Attribute) and 781 and isinstance(node.value.func, ast.Attribute)
783 isinstance(node.value.func.value, ast.Attribute) and 782 and isinstance(node.value.func.value, ast.Attribute)
784 isinstance(node.value.func.value.value, ast.Name) and 783 and isinstance(node.value.func.value.value, ast.Name)
785 node.value.func.value.value.id == "os" and 784 and node.value.func.value.value.id == "os"
786 node.value.func.value.attr == "environ" and 785 and node.value.func.value.attr == "environ"
787 node.value.func.attr == "get" and 786 and node.value.func.attr == "get"
788 len(node.value.args) in [1, 2] and 787 and len(node.value.args) in [1, 2]
789 isinstance(node.value.args[0], STR_TYPES) 788 and isinstance(node.value.args[0], STR_TYPES)
790 ) 789 )
791 if isGetCall: 790 if isGetCall:
792 call = node.value 791 call = node.value
793 stringPart = call.args[0] 792 stringPart = call.args[0]
794 if isinstance(stringPart, ast.Str): 793 if isinstance(stringPart, ast.Str):
806 original = unparse(node) 805 original = unparse(node)
807 if len(node.value.args) == 1: 806 if len(node.value.args) == 1:
808 expected = f"os.environ.get('{envName.upper()}')" 807 expected = f"os.environ.get('{envName.upper()}')"
809 else: 808 else:
810 defaultValue = unparse(node.value.args[1]) 809 defaultValue = unparse(node.value.args[1])
811 expected = ( 810 expected = f"os.environ.get('{envName.upper()}', '{defaultValue}')"
812 f"os.environ.get('{envName.upper()}', '{defaultValue}')"
813 )
814 else: 811 else:
815 return 812 return
816 813
817 self.__error(node.lineno - 1, node.col_offset, "Y112", expected, 814 self.__error(node.lineno - 1, node.col_offset, "Y112", expected, original)
818 original) 815
819
820 def __check113(self, node): 816 def __check113(self, node):
821 """ 817 """
822 Private method to check for loops in which "enumerate" should be 818 Private method to check for loops in which "enumerate" should be
823 used. 819 used.
824 820
825 @param node reference to the AST node to be checked 821 @param node reference to the AST node to be checked
826 @type ast.For 822 @type ast.For
827 """ 823 """
828 # idx = 0 824 # idx = 0
829 # for el in iterable: 825 # for el in iterable:
830 # ... 826 # ...
831 # idx += 1 827 # idx += 1
832 variableCandidates = [] 828 variableCandidates = []
833 for expression in node.body: 829 for expression in node.body:
834 if ( 830 if (
835 isinstance(expression, ast.AugAssign) and 831 isinstance(expression, ast.AugAssign)
836 self.__isConstantIncrease(expression) and 832 and self.__isConstantIncrease(expression)
837 isinstance(expression.target, ast.Name) 833 and isinstance(expression.target, ast.Name)
838 ): 834 ):
839 variableCandidates.append(expression.target) 835 variableCandidates.append(expression.target)
840 836
841 for candidate in variableCandidates: 837 for candidate in variableCandidates:
842 self.__error(candidate.lineno - 1, candidate.col_offset, "Y113", 838 self.__error(
843 unparse(candidate)) 839 candidate.lineno - 1, candidate.col_offset, "Y113", unparse(candidate)
844 840 )
841
845 def __check114(self, node): 842 def __check114(self, node):
846 """ 843 """
847 Private method to check for alternative if clauses with identical 844 Private method to check for alternative if clauses with identical
848 bodies. 845 bodies.
849 846
850 @param node reference to the AST node to be checked 847 @param node reference to the AST node to be checked
851 @type ast.If 848 @type ast.If
852 """ 849 """
853 # if a: 850 # if a:
854 # b 851 # b
858 errorPairs = [] 855 errorPairs = []
859 for ifbody1, ifbody2 in itertools.combinations(ifBodyPairs, 2): 856 for ifbody1, ifbody2 in itertools.combinations(ifBodyPairs, 2):
860 if self.__isSameBody(ifbody1[1], ifbody2[1]): 857 if self.__isSameBody(ifbody1[1], ifbody2[1]):
861 errorPairs.append((ifbody1, ifbody2)) 858 errorPairs.append((ifbody1, ifbody2))
862 for ifbody1, ifbody2 in errorPairs: 859 for ifbody1, ifbody2 in errorPairs:
863 self.__error(ifbody1[0].lineno - 1, ifbody1[0].col_offset, "Y114", 860 self.__error(
864 unparse(ifbody1[0]), unparse(ifbody2[0])) 861 ifbody1[0].lineno - 1,
865 862 ifbody1[0].col_offset,
863 "Y114",
864 unparse(ifbody1[0]),
865 unparse(ifbody2[0]),
866 )
867
866 def __check115(self, node): 868 def __check115(self, node):
867 """ 869 """
868 Private method to to check for places where open() is called without 870 Private method to to check for places where open() is called without
869 a context handler. 871 a context handler.
870 872
871 @param node reference to the AST node to be checked 873 @param node reference to the AST node to be checked
872 @type ast.Call 874 @type ast.Call
873 """ 875 """
874 # f = open(...) 876 # f = open(...)
875 #. .. # (do something with f) 877 # . .. # (do something with f)
876 # f.close() 878 # f.close()
877 if ( 879 if (
878 isinstance(node.func, ast.Name) and 880 isinstance(node.func, ast.Name)
879 node.func.id == "open" and 881 and node.func.id == "open"
880 not isinstance(node.parent, ast.withitem) 882 and not isinstance(node.parent, ast.withitem)
881 ): 883 ):
882 self.__error(node.lineno - 1, node.col_offset, "Y115") 884 self.__error(node.lineno - 1, node.col_offset, "Y115")
883 885
884 def __check116(self, node): 886 def __check116(self, node):
885 """ 887 """
886 Private method to check for places with 3 or more consecutive 888 Private method to check for places with 3 or more consecutive
887 if-statements with direct returns. 889 if-statements with direct returns.
888 890
889 * Each if-statement must be a check for equality with the 891 * Each if-statement must be a check for equality with the
890 same variable 892 same variable
891 * Each if-statement must just have a "return" 893 * Each if-statement must just have a "return"
892 * Else must also just have a return 894 * Else must also just have a return
893 895
894 @param node reference to the AST node to be checked 896 @param node reference to the AST node to be checked
895 @type ast.If 897 @type ast.If
896 """ 898 """
897 # if a == "foo": 899 # if a == "foo":
898 # return "bar" 900 # return "bar"
901 # elif a == "boo": 903 # elif a == "boo":
902 # return "ooh" 904 # return "ooh"
903 # else: 905 # else:
904 # return 42 906 # return 42
905 if ( 907 if (
906 isinstance(node.test, ast.Compare) and 908 isinstance(node.test, ast.Compare)
907 isinstance(node.test.left, ast.Name) and 909 and isinstance(node.test.left, ast.Name)
908 len(node.test.ops) == 1 and 910 and len(node.test.ops) == 1
909 isinstance(node.test.ops[0], ast.Eq) and 911 and isinstance(node.test.ops[0], ast.Eq)
910 len(node.test.comparators) == 1 and 912 and len(node.test.comparators) == 1
911 isinstance(node.test.comparators[0], AST_CONST_TYPES) and 913 and isinstance(node.test.comparators[0], AST_CONST_TYPES)
912 len(node.body) == 1 and 914 and len(node.body) == 1
913 isinstance(node.body[0], ast.Return) and 915 and isinstance(node.body[0], ast.Return)
914 len(node.orelse) == 1 and 916 and len(node.orelse) == 1
915 isinstance(node.orelse[0], ast.If) 917 and isinstance(node.orelse[0], ast.If)
916 ): 918 ):
917 variable = node.test.left 919 variable = node.test.left
918 child = node.orelse[0] 920 child = node.orelse[0]
919 elseValue = None 921 elseValue = None
920 if node.body[0].value is not None: 922 if node.body[0].value is not None:
921 bodyValueStr = unparse(node.body[0].value).strip("'") 923 bodyValueStr = unparse(node.body[0].value).strip("'")
922 else: 924 else:
923 bodyValueStr = "None" 925 bodyValueStr = "None"
924 if isinstance(node.test.comparators[0], ast.Str): 926 if isinstance(node.test.comparators[0], ast.Str):
925 keyValuePairs = { 927 keyValuePairs = {node.test.comparators[0].s: bodyValueStr}
926 node.test.comparators[0].s: bodyValueStr
927 }
928 elif isinstance(node.test.comparators[0], ast.Num): 928 elif isinstance(node.test.comparators[0], ast.Num):
929 keyValuePairs = { 929 keyValuePairs = {
930 node.test.comparators[0].n: bodyValueStr, 930 node.test.comparators[0].n: bodyValueStr,
931 } 931 }
932 else: 932 else:
933 keyValuePairs = { 933 keyValuePairs = {node.test.comparators[0].value: bodyValueStr}
934 node.test.comparators[0].value: bodyValueStr
935 }
936 while child: 934 while child:
937 if not ( 935 if not (
938 isinstance(child.test, ast.Compare) and 936 isinstance(child.test, ast.Compare)
939 isinstance(child.test.left, ast.Name) and 937 and isinstance(child.test.left, ast.Name)
940 child.test.left.id == variable.id and 938 and child.test.left.id == variable.id
941 len(child.test.ops) == 1 and 939 and len(child.test.ops) == 1
942 isinstance(child.test.ops[0], ast.Eq) and 940 and isinstance(child.test.ops[0], ast.Eq)
943 len(child.test.comparators) == 1 and 941 and len(child.test.comparators) == 1
944 isinstance(child.test.comparators[0], AST_CONST_TYPES) and 942 and isinstance(child.test.comparators[0], AST_CONST_TYPES)
945 len(child.body) == 1 and 943 and len(child.body) == 1
946 isinstance(child.body[0], ast.Return) and 944 and isinstance(child.body[0], ast.Return)
947 len(child.orelse) <= 1 945 and len(child.orelse) <= 1
948 ): 946 ):
949 return 947 return
950 948
951 if isinstance(child.test.comparators[0], ast.Str): 949 if isinstance(child.test.comparators[0], ast.Str):
952 key = child.test.comparators[0].s 950 key = child.test.comparators[0].s
953 elif isinstance(child.test.comparators[0], ast.Num): 951 elif isinstance(child.test.comparators[0], ast.Num):
954 key = child.test.comparators[0].n 952 key = child.test.comparators[0].n
955 else: 953 else:
963 child = None 961 child = None
964 else: 962 else:
965 return 963 return
966 else: 964 else:
967 child = None 965 child = None
968 966
969 if len(keyValuePairs) < 3: 967 if len(keyValuePairs) < 3:
970 return 968 return
971 969
972 if elseValue: 970 if elseValue:
973 ret = f"{keyValuePairs}.get({variable.id}, {elseValue})" 971 ret = f"{keyValuePairs}.get({variable.id}, {elseValue})"
974 else: 972 else:
975 ret = f"{keyValuePairs}.get({variable.id})" 973 ret = f"{keyValuePairs}.get({variable.id})"
976 974
977 self.__error(node.lineno - 1, node.col_offset, "Y116", ret) 975 self.__error(node.lineno - 1, node.col_offset, "Y116", ret)
978 976
979 def __check117(self, node): 977 def __check117(self, node):
980 """ 978 """
981 Private method to check for multiple with-statements with same scope. 979 Private method to check for multiple with-statements with same scope.
982 980
983 @param node reference to the AST node to be checked 981 @param node reference to the AST node to be checked
984 @type ast.With 982 @type ast.With
985 """ 983 """
986 # with A() as a: 984 # with A() as a:
987 # with B() as b: 985 # with B() as b:
988 # print("hello") 986 # print("hello")
989 if ( 987 if len(node.body) == 1 and isinstance(node.body[0], ast.With):
990 len(node.body) == 1 and
991 isinstance(node.body[0], ast.With)
992 ):
993 withItems = [] 988 withItems = []
994 for withitem in node.items + node.body[0].items: 989 for withitem in node.items + node.body[0].items:
995 withItems.append(f"{unparse(withitem)}") 990 withItems.append(f"{unparse(withitem)}")
996 mergedWith = f"with {', '.join(withItems)}:" 991 mergedWith = f"with {', '.join(withItems)}:"
997 self.__error(node.lineno - 1, node.col_offset, "Y117", mergedWith) 992 self.__error(node.lineno - 1, node.col_offset, "Y117", mergedWith)
998 993
999 def __check118(self, node): 994 def __check118(self, node):
1000 """ 995 """
1001 Private method to check for usages of "key in dict.keys()". 996 Private method to check for usages of "key in dict.keys()".
1002 997
1003 @param node reference to the AST node to be checked 998 @param node reference to the AST node to be checked
1004 @type ast.Compare or ast.For 999 @type ast.Compare or ast.For
1005 """ 1000 """
1006 # Pattern 1: 1001 # Pattern 1:
1007 # 1002 #
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

eric ide

mercurial