57 self.__tree = tree |
57 self.__tree = tree |
58 self.__filename = filename |
58 self.__filename = filename |
59 |
59 |
60 self.__checkersWithCodes = { |
60 self.__checkersWithCodes = { |
61 "classdef": [ |
61 "classdef": [ |
62 (self.__checkClassName, ("N801",)), |
62 (self.__checkClassName, ("N801", "N818")), |
63 (self.__checkNameToBeAvoided, ("N831",)), |
63 (self.__checkNameToBeAvoided, ("N831",)), |
64 ], |
|
65 "functiondef": [ |
|
66 (self.__checkFunctionName, ("N802",)), |
|
67 (self.__checkFunctionArgumentNames, ("N803", "N804", "N805", "N806")), |
|
68 (self.__checkNameToBeAvoided, ("N831",)), |
|
69 ], |
|
70 "assign": [ |
|
71 (self.__checkVariablesInFunction, ("N821",)), |
|
72 (self.__checkNameToBeAvoided, ("N831",)), |
|
73 ], |
|
74 "importfrom": [ |
|
75 (self.__checkImportAs, ("N811", "N812", "N813", "N814")), |
|
76 ], |
64 ], |
77 "module": [ |
65 "module": [ |
78 (self.__checkModule, ("N807", "N808")), |
66 (self.__checkModule, ("N807", "N808")), |
79 ], |
67 ], |
80 } |
68 } |
|
69 for name in ("functiondef", "asyncfunctiondef"): |
|
70 self.__checkersWithCodes[name] = [ |
|
71 (self.__checkFunctionName, ("N802", "N809")), |
|
72 (self.__checkFunctionArgumentNames, ("N803", "N804", "N805", "N806")), |
|
73 (self.__checkNameToBeAvoided, ("N831",)), |
|
74 ] |
|
75 for name in ("assign", "namedexpr", "annassign"): |
|
76 self.__checkersWithCodes[name] = [ |
|
77 (self.__checkVariableNames, ("N821",)), |
|
78 (self.__checkNameToBeAvoided, ("N831",)), |
|
79 ] |
|
80 for name in ( |
|
81 "with", |
|
82 "asyncwith", |
|
83 "for", |
|
84 "asyncfor", |
|
85 "excepthandler", |
|
86 "generatorexp", |
|
87 "listcomp", |
|
88 "dictcomp", |
|
89 "setcomp", |
|
90 ): |
|
91 self.__checkersWithCodes[name] = [ |
|
92 (self.__checkVariableNames, ("N821",)), |
|
93 ] |
|
94 for name in ("import", "importfrom"): |
|
95 self.__checkersWithCodes[name] = [ |
|
96 (self.__checkImportAs, ("N811", "N812", "N813", "N814", "N815")), |
|
97 ] |
81 |
98 |
82 self.__checkers = {} |
99 self.__checkers = {} |
83 for key, checkers in self.__checkersWithCodes.items(): |
100 for key, checkers in self.__checkersWithCodes.items(): |
84 for checker, codes in checkers: |
101 for checker, codes in checkers: |
85 if any(not (code and options.ignore_code(code)) for code in codes): |
102 if any(not (code and options.ignore_code(code)) for code in codes): |
249 """ |
266 """ |
250 if isinstance(node, (ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef)): |
267 if isinstance(node, (ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef)): |
251 name = node.name |
268 name = node.name |
252 if self.__isNameToBeAvoided(name): |
269 if self.__isNameToBeAvoided(name): |
253 yield self.__error(node, "N831") |
270 yield self.__error(node, "N831") |
254 return |
271 |
255 |
272 elif isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): |
256 if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): |
|
257 argNames = self.__getArgNames(node) |
273 argNames = self.__getArgNames(node) |
258 for arg in argNames: |
274 for arg in argNames: |
259 if self.__isNameToBeAvoided(arg): |
275 if self.__isNameToBeAvoided(arg): |
260 yield self.__error(node, "N831") |
276 yield self.__error(node, "N831") |
261 return |
277 |
262 |
278 elif isinstance(node, (ast.Assign, ast.NamedExpr, ast.AnnAssign)): |
263 if isinstance(node, ast.Assign): |
279 if isinstance(node, ast.Assign): |
264 for target in node.targets: |
280 targets = node.targets |
|
281 else: |
|
282 targets = [node.target] |
|
283 for target in targets: |
265 if isinstance(target, ast.Name): |
284 if isinstance(target, ast.Name): |
266 name = target.id |
285 name = target.id |
267 if not name: |
286 if bool(name) and self.__isNameToBeAvoided(name): |
268 return |
|
269 |
|
270 if self.__isNameToBeAvoided(name): |
|
271 yield self.__error(node, "N831") |
287 yield self.__error(node, "N831") |
272 return |
|
273 |
288 |
274 elif isinstance(target, (ast.Tuple, ast.List)): |
289 elif isinstance(target, (ast.Tuple, ast.List)): |
275 for element in target.elts: |
290 for element in target.elts: |
276 if isinstance(element, ast.Name): |
291 if isinstance(element, ast.Name): |
277 name = element.id |
292 name = element.id |
278 if not name: |
293 if bool(name) and self.__isNameToBeAvoided(name): |
279 return |
|
280 |
|
281 if self.__isNameToBeAvoided(name): |
|
282 yield self.__error(node, "N831") |
294 yield self.__error(node, "N831") |
283 return |
295 |
|
296 def __getClassdef(self, name, parents): |
|
297 """ |
|
298 Private method to extract the class definition. |
|
299 |
|
300 @param name name of the class |
|
301 @type str |
|
302 @param parents list of parent nodes |
|
303 @type ast.AST |
|
304 @return node containing the class definition |
|
305 @rtype ast.ClassDef |
|
306 """ |
|
307 for parent in parents: |
|
308 for node in parent.body: |
|
309 if isinstance(node, ast.ClassDef) and node.name == name: |
|
310 return node |
|
311 |
|
312 return None |
|
313 |
|
314 def __superClassNames(self, name, parents, names=None): |
|
315 """ |
|
316 Private method to extract the names of all super classes. |
|
317 |
|
318 @param name name of the class |
|
319 @type str |
|
320 @param parents list of parent nodes |
|
321 @type ast.AST |
|
322 @param names set of collected class names (defaults to None) |
|
323 @type set of str (optional) |
|
324 @return set of class names |
|
325 @rtype set of str |
|
326 """ |
|
327 if names is None: |
|
328 # initialize recursive search with empty set |
|
329 names = set() |
|
330 |
|
331 classdef = self.__getClassdef(name, parents) |
|
332 if not classdef: |
|
333 return names |
|
334 |
|
335 for base in classdef.bases: |
|
336 if isinstance(base, ast.Name) and base.id not in names: |
|
337 names.add(base.id) |
|
338 names.update(self.__superClassNames(base.id, parents, names)) |
|
339 return names |
284 |
340 |
285 def __checkClassName(self, node, parents): |
341 def __checkClassName(self, node, parents): |
286 """ |
342 """ |
287 Private class to check the given node for class name |
343 Private class to check the given node for class name |
288 conventions (N801). |
344 conventions (N801, N818). |
289 |
345 |
290 Almost without exception, class names use the CapWords convention. |
346 Almost without exception, class names use the CapWords convention. |
291 Classes for internal use have a leading underscore in addition. |
347 Classes for internal use have a leading underscore in addition. |
292 |
348 |
293 @param node AST note to check |
349 @param node AST note to check |
294 @param parents list of parent nodes |
350 @param parents list of parent nodes |
295 @yield tuple giving line number, offset within line and error code |
351 @yield tuple giving line number, offset within line and error code |
296 @ytype tuple of (int, int, str) |
352 @ytype tuple of (int, int, str) |
297 """ |
353 """ |
298 if not self.CamelcaseRegexp.match(node.name): |
354 name = node.name |
|
355 strippedName = name.strip("_") |
|
356 if not strippedName[:1].isupper() or "_" in strippedName: |
299 yield self.__error(node, "N801") |
357 yield self.__error(node, "N801") |
300 |
358 |
|
359 superClasses = self.__superClassNames(name, parents) |
|
360 if "Exception" in superClasses and not name.endswith("Error"): |
|
361 yield self.__error(node, "N818") |
|
362 |
301 def __checkFunctionName(self, node, parents): |
363 def __checkFunctionName(self, node, parents): |
302 """ |
364 """ |
303 Private class to check the given node for function name |
365 Private class to check the given node for function name |
304 conventions (N802). |
366 conventions (N802, N809). |
305 |
367 |
306 Function names should be lowercase, with words separated by underscores |
368 Function names should be lowercase, with words separated by underscores |
307 as necessary to improve readability. Functions <b>not</b> being |
369 as necessary to improve readability. Functions <b>not</b> being |
308 methods '__' in front and back are not allowed. Mixed case is allowed |
370 methods '__' in front and back are not allowed. Mixed case is allowed |
309 only in contexts where that's already the prevailing style |
371 only in contexts where that's already the prevailing style |
335 @yield tuple giving line number, offset within line and error code |
401 @yield tuple giving line number, offset within line and error code |
336 @ytype tuple of (int, int, str) |
402 @ytype tuple of (int, int, str) |
337 """ |
403 """ |
338 if node.args.kwarg is not None: |
404 if node.args.kwarg is not None: |
339 kwarg = node.args.kwarg.arg |
405 kwarg = node.args.kwarg.arg |
340 if not self.LowercaseRegex.match(kwarg): |
406 if kwarg.lower() != kwarg: |
341 yield self.__error(node, "N803") |
407 yield self.__error(node, "N803") |
|
408 |
|
409 elif node.args.vararg is not None: |
|
410 vararg = node.args.vararg.arg |
|
411 if vararg.lower() != vararg: |
|
412 yield self.__error(node, "N803") |
|
413 |
|
414 else: |
|
415 argNames = self.__getArgNames(node) |
|
416 functionType = getattr(node, "function_type", "function") |
|
417 |
|
418 if not argNames: |
|
419 if functionType == "method": |
|
420 yield self.__error(node, "N805") |
|
421 elif functionType == "classmethod": |
|
422 yield self.__error(node, "N804") |
|
423 |
|
424 elif functionType == "method" and argNames[0] != "self": |
|
425 yield self.__error(node, "N805") |
|
426 elif functionType == "classmethod" and argNames[0] != "cls": |
|
427 yield self.__error(node, "N804") |
|
428 elif functionType == "staticmethod" and argNames[0] in ("cls", "self"): |
|
429 yield self.__error(node, "N806") |
|
430 for arg in argNames: |
|
431 if arg.lower() != arg: |
|
432 yield self.__error(node, "N803") |
|
433 break |
|
434 |
|
435 def __checkVariableNames(self, node, parents): |
|
436 """ |
|
437 Private method to check variable names in function, class and global scope |
|
438 (N821, N822, N823). |
|
439 |
|
440 Local variables in functions should be lowercase. |
|
441 |
|
442 @param node AST note to check |
|
443 @param parents list of parent nodes |
|
444 @yield tuple giving line number, offset within line and error code |
|
445 @ytype tuple of (int, int, str) |
|
446 """ |
|
447 nodeType = type(node) |
|
448 if nodeType is ast.Assign: |
|
449 if self.__isNamedTupel(node.value): |
342 return |
450 return |
343 |
451 for target in node.targets: |
344 if node.args.vararg is not None: |
452 yield from self.__findVariableNameErrors(target, parents) |
345 vararg = node.args.vararg.arg |
453 |
346 if not self.LowercaseRegex.match(vararg): |
454 elif nodeType in (ast.NamedExpr, ast.AnnAssign): |
347 yield self.__error(node, "N803") |
455 if self.__isNamedTupel(node.value): |
348 return |
456 return |
349 |
457 yield from self.__findVariableNameErrors(node.target, parents) |
350 argNames = self.__getArgNames(node) |
458 |
351 functionType = getattr(node, "function_type", "function") |
459 elif nodeType in (ast.With, ast.AsyncWith): |
352 |
460 for item in node.items: |
353 if not argNames: |
461 yield from self.__findVariableNameErrors(item.optional_vars, parents) |
354 if functionType == "method": |
462 |
355 yield self.__error(node, "N805") |
463 elif nodeType in (ast.For, ast.AsyncFor): |
356 elif functionType == "classmethod": |
464 yield from self.__findVariableNameErrors(node.target, parents) |
357 yield self.__error(node, "N804") |
465 |
358 return |
466 elif nodeType is ast.ExceptHandler: |
359 |
467 if node.name: |
360 if functionType == "method" and argNames[0] != "self": |
468 yield from self.__findVariableNameErrors(node, parents) |
361 yield self.__error(node, "N805") |
469 |
362 elif functionType == "classmethod" and argNames[0] != "cls": |
470 elif nodeType in (ast.GeneratorExp, ast.ListComp, ast.DictComp, ast.SetComp): |
363 yield self.__error(node, "N804") |
471 for gen in node.generators: |
364 elif functionType == "staticmethod" and argNames[0] in ("cls", "self"): |
472 yield from self.__findVariableNameErrors(gen.target, parents) |
365 yield self.__error(node, "N806") |
473 |
366 for arg in argNames: |
474 def __findVariableNameErrors(self, assignmentTarget, parents): |
367 if not self.LowercaseRegex.match(arg): |
475 """ |
368 yield self.__error(node, "N803") |
476 Private method to check, if there is a variable name error. |
369 return |
477 |
370 |
478 @param assignmentTarget target node of the assignment |
371 def __checkVariablesInFunction(self, node, parents): |
479 @type ast.Name, ast.Tuple, ast.List or ast.ExceptHandler |
372 """ |
480 @param parents list of parent nodes |
373 Private method to check local variables in functions (N821). |
481 @type ast.AST |
374 |
|
375 Local variables in functions should be lowercase. |
|
376 |
|
377 @param node AST note to check |
|
378 @param parents list of parent nodes |
|
379 @yield tuple giving line number, offset within line and error code |
482 @yield tuple giving line number, offset within line and error code |
380 @ytype tuple of (int, int, str) |
483 @ytype tuple of (int, int, str) |
381 """ |
484 """ |
382 for parentFunc in reversed(parents): |
485 for parentFunc in reversed(parents): |
383 if isinstance(parentFunc, ast.ClassDef): |
486 if isinstance(parentFunc, ast.ClassDef): |
384 return |
487 checker = self.__classVariableCheck |
|
488 break |
385 if isinstance(parentFunc, (ast.FunctionDef, ast.AsyncFunctionDef)): |
489 if isinstance(parentFunc, (ast.FunctionDef, ast.AsyncFunctionDef)): |
|
490 checker = functools.partial(self.__functionVariableCheck, parentFunc) |
386 break |
491 break |
387 else: |
492 else: |
388 return |
493 checker = self.__globalVariableCheck |
389 for target in node.targets: |
494 for name in self.__extractNames(assignmentTarget): |
390 name = isinstance(target, ast.Name) and target.id |
495 errorCode = checker(name) |
391 if not name or name in parentFunc.global_names: |
496 if errorCode: |
392 return |
497 yield self.__error(assignmentTarget, errorCode) |
393 |
498 |
394 if not self.LowercaseRegex.match(name) and name[:1] != "_": |
499 def __extractNames(self, assignmentTarget): |
395 yield self.__error(target, "N821") |
500 """ |
|
501 Private method to extract the names from the target node. |
|
502 |
|
503 @param assignmentTarget target node of the assignment |
|
504 @type ast.Name, ast.Tuple, ast.List or ast.ExceptHandler |
|
505 @yield name of the variable |
|
506 @ytype str |
|
507 """ |
|
508 targetType = type(assignmentTarget) |
|
509 if targetType is ast.Name: |
|
510 yield assignmentTarget.id |
|
511 elif targetType in (ast.Tuple, ast.List): |
|
512 for element in assignmentTarget.elts: |
|
513 elementType = type(element) |
|
514 if elementType is ast.Name: |
|
515 yield element.id |
|
516 elif elementType in (ast.Tuple, ast.List): |
|
517 yield from self.__extractNames(element) |
|
518 elif elementType is ast.Starred: # PEP 3132 |
|
519 yield from self.__extractNames(element.value) |
|
520 elif isinstance(assignmentTarget, ast.ExceptHandler): |
|
521 yield assignmentTarget.name |
|
522 |
|
523 def __isMixedCase(self, name): |
|
524 """ |
|
525 Private method to check, if the given name is mixed case. |
|
526 |
|
527 @param name variable name to be checked |
|
528 @type str |
|
529 @return flag indicating mixed case |
|
530 @rtype bool |
|
531 """ |
|
532 return name.lower() != name and name.lstrip("_")[:1].islower() |
|
533 |
|
534 def __globalVariableCheck(self, name): |
|
535 """ |
|
536 Private method to determine the error code for a variable in global scope. |
|
537 |
|
538 @param name variable name to be checked |
|
539 @type str |
|
540 @return error code or None |
|
541 @rtype str or None |
|
542 """ |
|
543 if self.__isMixedCase(name): |
|
544 return "N823" |
|
545 |
|
546 return None |
|
547 |
|
548 def __classVariableCheck(self, name): |
|
549 """ |
|
550 Private method to determine the error code for a variable in class scope. |
|
551 |
|
552 @param name variable name to be checked |
|
553 @type str |
|
554 @return error code or None |
|
555 @rtype str or None |
|
556 """ |
|
557 if self.__isMixedCase(name): |
|
558 return "N822" |
|
559 |
|
560 return None |
|
561 |
|
562 def __functionVariableCheck(self, func, varName): |
|
563 """ |
|
564 Private method to determine the error code for a variable in class scope. |
|
565 |
|
566 @param func reference to the function definition node |
|
567 @type ast.FunctionDef or ast.AsyncFunctionDef |
|
568 @param varName variable name to be checked |
|
569 @type str |
|
570 @return error code or None |
|
571 @rtype str or None |
|
572 """ |
|
573 if varName not in func.global_names and varName.lower() != varName: |
|
574 return "N821" |
|
575 |
|
576 return None |
|
577 |
|
578 def __isNamedTupel(self, nodeValue): |
|
579 """ |
|
580 Private method to check, if a node is a named tuple. |
|
581 |
|
582 @param nodeValue node to be checked |
|
583 @type ast.AST |
|
584 @return flag indicating a nemd tuple |
|
585 @rtype bool |
|
586 """ |
|
587 return isinstance(nodeValue, ast.Call) and ( |
|
588 ( |
|
589 isinstance(nodeValue.func, ast.Attribute) |
|
590 and nodeValue.func.attr == "namedtuple" |
|
591 ) |
|
592 or ( |
|
593 isinstance(nodeValue.func, ast.Name) |
|
594 and nodeValue.func.id == "namedtuple" |
|
595 ) |
|
596 ) |
396 |
597 |
397 def __checkModule(self, node, parents): |
598 def __checkModule(self, node, parents): |
398 """ |
599 """ |
399 Private method to check module naming conventions (N807, N808). |
600 Private method to check module naming conventions (N807, N808). |
400 |
601 |
417 yield self.__error(node, "N808") |
618 yield self.__error(node, "N808") |
418 |
619 |
419 def __checkImportAs(self, node, parents): |
620 def __checkImportAs(self, node, parents): |
420 """ |
621 """ |
421 Private method to check that imports don't change the |
622 Private method to check that imports don't change the |
422 naming convention (N811, N812, N813, N814). |
623 naming convention (N811, N812, N813, N814, N815). |
423 |
624 |
424 @param node AST note to check |
625 @param node AST note to check |
425 @param parents list of parent nodes |
626 @param parents list of parent nodes |
426 @yield tuple giving line number, offset within line and error code |
627 @yield tuple giving line number, offset within line and error code |
427 @ytype tuple of (int, int, str) |
628 @ytype tuple of (int, int, str) |
428 """ |
629 """ |
429 for name in node.names: |
630 for name in node.names: |
430 if not name.asname: |
631 asname = name.asname |
|
632 if not asname: |
431 continue |
633 continue |
432 |
634 |
433 if self.UppercaseRegexp.match(name.name): |
635 originalName = name.name |
434 if not self.UppercaseRegexp.match(name.asname): |
636 if originalName.isupper(): |
|
637 if not asname.isupper(): |
435 yield self.__error(node, "N811") |
638 yield self.__error(node, "N811") |
436 elif self.LowercaseRegex.match(name.name): |
639 elif originalName.islower(): |
437 if not self.LowercaseRegex.match(name.asname): |
640 if asname.lower() != asname: |
438 yield self.__error(node, "N812") |
641 yield self.__error(node, "N812") |
439 elif self.LowercaseRegex.match(name.asname): |
642 elif asname.islower(): |
440 yield self.__error(node, "N813") |
643 yield self.__error(node, "N813") |
441 elif self.UppercaseRegexp.match(name.asname): |
644 elif asname.isupper(): |
442 yield self.__error(node, "N814") |
645 if "".join(filter(str.isupper, originalName)) == asname: |
|
646 yield self.__error(node, "N815") |
|
647 else: |
|
648 yield self.__error(node, "N814") |