85 # collection of detected errors |
85 # collection of detected errors |
86 self.errors = [] |
86 self.errors = [] |
87 |
87 |
88 self.__checkersWithCodes = { |
88 self.__checkersWithCodes = { |
89 "classdef": [ |
89 "classdef": [ |
90 (self.__checkClassName, ("N801", "N818")), |
90 (self.__checkClassName, ("N-801", "N-818")), |
91 (self.__checkNameToBeAvoided, ("N831",)), |
91 (self.__checkNameToBeAvoided, ("N-831",)), |
92 ], |
92 ], |
93 "module": [ |
93 "module": [ |
94 (self.__checkModule, ("N807", "N808")), |
94 (self.__checkModule, ("N-807", "N-808")), |
95 ], |
95 ], |
96 } |
96 } |
97 for name in ("functiondef", "asyncfunctiondef"): |
97 for name in ("functiondef", "asyncfunctiondef"): |
98 self.__checkersWithCodes[name] = [ |
98 self.__checkersWithCodes[name] = [ |
99 (self.__checkFunctionName, ("N802", "N809")), |
99 (self.__checkFunctionName, ("N-802", "N-809")), |
100 (self.__checkFunctionArgumentNames, ("N803", "N804", "N805", "N806")), |
100 (self.__checkFunctionArgumentNames, ("N-803", "N-804", "N-805", "N-806")), |
101 (self.__checkNameToBeAvoided, ("N831",)), |
101 (self.__checkNameToBeAvoided, ("N-831",)), |
102 ] |
102 ] |
103 for name in ("assign", "namedexpr", "annassign"): |
103 for name in ("assign", "namedexpr", "annassign"): |
104 self.__checkersWithCodes[name] = [ |
104 self.__checkersWithCodes[name] = [ |
105 (self.__checkVariableNames, ("N821",)), |
105 (self.__checkVariableNames, ("N-821",)), |
106 (self.__checkNameToBeAvoided, ("N831",)), |
106 (self.__checkNameToBeAvoided, ("N-831",)), |
107 ] |
107 ] |
108 for name in ( |
108 for name in ( |
109 "with", |
109 "with", |
110 "asyncwith", |
110 "asyncwith", |
111 "for", |
111 "for", |
115 "listcomp", |
115 "listcomp", |
116 "dictcomp", |
116 "dictcomp", |
117 "setcomp", |
117 "setcomp", |
118 ): |
118 ): |
119 self.__checkersWithCodes[name] = [ |
119 self.__checkersWithCodes[name] = [ |
120 (self.__checkVariableNames, ("N821",)), |
120 (self.__checkVariableNames, ("N-821",)), |
121 ] |
121 ] |
122 for name in ("import", "importfrom"): |
122 for name in ("import", "importfrom"): |
123 self.__checkersWithCodes[name] = [ |
123 self.__checkersWithCodes[name] = [ |
124 (self.__checkImportAs, ("N811", "N812", "N813", "N814", "N815")), |
124 (self.__checkImportAs, ("N-811", "N-812", "N-813", "N-814", "N-815")), |
125 ] |
125 ] |
126 |
126 |
127 self.__checkers = collections.defaultdict(list) |
127 self.__checkers = collections.defaultdict(list) |
128 for key, checkers in self.__checkersWithCodes.items(): |
128 for key, checkers in self.__checkersWithCodes.items(): |
129 for checker, codes in checkers: |
129 for checker, codes in checkers: |
333 @type list of ast.AST |
333 @type list of ast.AST |
334 """ |
334 """ |
335 if isinstance(node, (ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef)): |
335 if isinstance(node, (ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef)): |
336 name = node.name |
336 name = node.name |
337 if self.__isNameToBeAvoided(name): |
337 if self.__isNameToBeAvoided(name): |
338 self.__error(node, "N831") |
338 self.__error(node, "N-831") |
339 |
339 |
340 elif isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): |
340 elif isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): |
341 argNames = self.__getArgNames(node) |
341 argNames = self.__getArgNames(node) |
342 for arg in argNames: |
342 for arg in argNames: |
343 if self.__isNameToBeAvoided(arg): |
343 if self.__isNameToBeAvoided(arg): |
344 self.__error(node, "N831") |
344 self.__error(node, "N-831") |
345 |
345 |
346 elif isinstance(node, (ast.Assign, ast.NamedExpr, ast.AnnAssign)): |
346 elif isinstance(node, (ast.Assign, ast.NamedExpr, ast.AnnAssign)): |
347 if isinstance(node, ast.Assign): |
347 if isinstance(node, ast.Assign): |
348 targets = node.targets |
348 targets = node.targets |
349 else: |
349 else: |
350 targets = [node.target] |
350 targets = [node.target] |
351 for target in targets: |
351 for target in targets: |
352 if isinstance(target, ast.Name): |
352 if isinstance(target, ast.Name): |
353 name = target.id |
353 name = target.id |
354 if bool(name) and self.__isNameToBeAvoided(name): |
354 if bool(name) and self.__isNameToBeAvoided(name): |
355 self.__error(node, "N831") |
355 self.__error(node, "N-831") |
356 |
356 |
357 elif isinstance(target, (ast.Tuple, ast.List)): |
357 elif isinstance(target, (ast.Tuple, ast.List)): |
358 for element in target.elts: |
358 for element in target.elts: |
359 if isinstance(element, ast.Name): |
359 if isinstance(element, ast.Name): |
360 name = element.id |
360 name = element.id |
361 if bool(name) and self.__isNameToBeAvoided(name): |
361 if bool(name) and self.__isNameToBeAvoided(name): |
362 self.__error(node, "N831") |
362 self.__error(node, "N-831") |
363 |
363 |
364 def __getClassdef(self, name, parents): |
364 def __getClassdef(self, name, parents): |
365 """ |
365 """ |
366 Private method to extract the class definition. |
366 Private method to extract the class definition. |
367 |
367 |
420 @type list of ast.AST |
420 @type list of ast.AST |
421 """ |
421 """ |
422 name = node.name |
422 name = node.name |
423 strippedName = name.strip("_") |
423 strippedName = name.strip("_") |
424 if not strippedName[:1].isupper() or "_" in strippedName: |
424 if not strippedName[:1].isupper() or "_" in strippedName: |
425 self.__error(node, "N801") |
425 self.__error(node, "N-801") |
426 |
426 |
427 superClasses = self.__superClassNames(name, parents) |
427 superClasses = self.__superClassNames(name, parents) |
428 if "Exception" in superClasses and not name.endswith("Error"): |
428 if "Exception" in superClasses and not name.endswith("Error"): |
429 self.__error(node, "N818") |
429 self.__error(node, "N-818") |
430 |
430 |
431 def __checkFunctionName(self, node, _parents): |
431 def __checkFunctionName(self, node, _parents): |
432 """ |
432 """ |
433 Private class to check the given node for function name |
433 Private class to check the given node for function name |
434 conventions (N802, N809). |
434 conventions (N802, N809). |
449 |
449 |
450 if name in ("__dir__", "__getattr__"): |
450 if name in ("__dir__", "__getattr__"): |
451 return |
451 return |
452 |
452 |
453 if name.lower() != name: |
453 if name.lower() != name: |
454 self.__error(node, "N802") |
454 self.__error(node, "N-802") |
455 if functionType == "function" and name[:2] == "__" and name[-2:] == "__": |
455 if functionType == "function" and name[:2] == "__" and name[-2:] == "__": |
456 self.__error(node, "N809") |
456 self.__error(node, "N-809") |
457 |
457 |
458 def __checkFunctionArgumentNames(self, node, _parents): |
458 def __checkFunctionArgumentNames(self, node, _parents): |
459 """ |
459 """ |
460 Private class to check the argument names of functions |
460 Private class to check the argument names of functions |
461 (N803, N804, N805, N806). |
461 (N803, N804, N805, N806). |
470 @type list of ast.AST |
470 @type list of ast.AST |
471 """ |
471 """ |
472 if node.args.kwarg is not None: |
472 if node.args.kwarg is not None: |
473 kwarg = node.args.kwarg.arg |
473 kwarg = node.args.kwarg.arg |
474 if kwarg.lower() != kwarg: |
474 if kwarg.lower() != kwarg: |
475 self.__error(node, "N803") |
475 self.__error(node, "N-803") |
476 |
476 |
477 elif node.args.vararg is not None: |
477 elif node.args.vararg is not None: |
478 vararg = node.args.vararg.arg |
478 vararg = node.args.vararg.arg |
479 if vararg.lower() != vararg: |
479 if vararg.lower() != vararg: |
480 self.__error(node, "N803") |
480 self.__error(node, "N-803") |
481 |
481 |
482 else: |
482 else: |
483 argNames = self.__getArgNames(node) |
483 argNames = self.__getArgNames(node) |
484 functionType = getattr(node, "function_type", "function") |
484 functionType = getattr(node, "function_type", "function") |
485 |
485 |
486 if not argNames: |
486 if not argNames: |
487 if functionType == "method": |
487 if functionType == "method": |
488 self.__error(node, "N805") |
488 self.__error(node, "N-805") |
489 elif functionType == "classmethod": |
489 elif functionType == "classmethod": |
490 self.__error(node, "N804") |
490 self.__error(node, "N-804") |
491 |
491 |
492 elif functionType == "method" and argNames[0] != "self": |
492 elif functionType == "method" and argNames[0] != "self": |
493 self.__error(node, "N805") |
493 self.__error(node, "N-805") |
494 elif functionType == "classmethod" and argNames[0] != "cls": |
494 elif functionType == "classmethod" and argNames[0] != "cls": |
495 self.__error(node, "N804") |
495 self.__error(node, "N-804") |
496 elif functionType == "staticmethod" and argNames[0] in ("cls", "self"): |
496 elif functionType == "staticmethod" and argNames[0] in ("cls", "self"): |
497 self.__error(node, "N806") |
497 self.__error(node, "N-806") |
498 for arg in argNames: |
498 for arg in argNames: |
499 if arg.lower() != arg: |
499 if arg.lower() != arg: |
500 self.__error(node, "N803") |
500 self.__error(node, "N-803") |
501 break |
501 break |
502 |
502 |
503 def __checkVariableNames(self, node, parents): |
503 def __checkVariableNames(self, node, parents): |
504 """ |
504 """ |
505 Private method to check variable names in function, class and global scope |
505 Private method to check variable names in function, class and global scope |
673 @type list of ast.AST |
673 @type list of ast.AST |
674 """ |
674 """ |
675 if self.__filename: |
675 if self.__filename: |
676 moduleName = os.path.splitext(os.path.basename(self.__filename))[0] |
676 moduleName = os.path.splitext(os.path.basename(self.__filename))[0] |
677 if moduleName.lower() != moduleName: |
677 if moduleName.lower() != moduleName: |
678 self.__error(node, "N807") |
678 self.__error(node, "N-807") |
679 |
679 |
680 if moduleName == "__init__": |
680 if moduleName == "__init__": |
681 # we got a package |
681 # we got a package |
682 packageName = os.path.split(os.path.dirname(self.__filename))[1] |
682 packageName = os.path.split(os.path.dirname(self.__filename))[1] |
683 if packageName.lower() != packageName: |
683 if packageName.lower() != packageName: |
684 self.__error(node, "N808") |
684 self.__error(node, "N-808") |
685 |
685 |
686 def __checkImportAs(self, node, _parents): |
686 def __checkImportAs(self, node, _parents): |
687 """ |
687 """ |
688 Private method to check that imports don't change the |
688 Private method to check that imports don't change the |
689 naming convention (N811, N812, N813, N814, N815). |
689 naming convention (N811, N812, N813, N814, N815). |
699 continue |
699 continue |
700 |
700 |
701 originalName = name.name |
701 originalName = name.name |
702 if originalName.isupper(): |
702 if originalName.isupper(): |
703 if not asname.isupper(): |
703 if not asname.isupper(): |
704 self.__error(node, "N811") |
704 self.__error(node, "N-811") |
705 elif originalName.islower(): |
705 elif originalName.islower(): |
706 if asname.lower() != asname: |
706 if asname.lower() != asname: |
707 self.__error(node, "N812") |
707 self.__error(node, "N-812") |
708 elif asname.islower(): |
708 elif asname.islower(): |
709 self.__error(node, "N813") |
709 self.__error(node, "N-813") |
710 elif asname.isupper(): |
710 elif asname.isupper(): |
711 if "".join(filter(str.isupper, originalName)) == asname: |
711 if "".join(filter(str.isupper, originalName)) == asname: |
712 self.__error(node, "N815") |
712 self.__error(node, "N-815") |
713 else: |
713 else: |
714 self.__error(node, "N814") |
714 self.__error(node, "N-814") |