--- a/UtilitiesPython2/NamingStyleCheckerPy2.py Sun Jan 05 23:22:17 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,426 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright (c) 2013 - 2014 Detlev Offenbach <detlev@die-offenbachs.de> -# - -""" -Module implementing a checker for naming conventions for Python2. -""" - -import collections -import ast -import re -import os - - -class NamingStyleChecker(object): - """ - Class implementing a checker for naming conventions for Python2. - """ - LowercaseRegex = re.compile(r"[_a-z][_a-z0-9]*$") - UppercaseRegexp = re.compile(r"[_A-Z][_A-Z0-9]*$") - CamelcaseRegexp = re.compile(r"_?[A-Z][a-zA-Z0-9]*$") - MixedcaseRegexp = re.compile(r"_?[a-z][a-zA-Z0-9]*$") - - Codes = [ - "N801", "N802", "N803", "N804", "N805", "N806", "N807", "N808", - "N811", "N812", "N813", "N814", "N821", "N831" - ] - - def __init__(self, tree, filename, options): - """ - Constructor (according to 'extended' pep8.py API) - - @param tree AST tree of the source file - @param filename name of the source file (string) - @param options options as parsed by pep8.StyleGuide - """ - self.__parents = collections.deque() - self.__tree = tree - self.__filename = filename - - self.__checkersWithCodes = { - "classdef": [ - (self.__checkClassName, ("N801",)), - (self.__checkNameToBeAvoided, ("N831",)), - ], - "functiondef": [ - (self.__checkFuntionName, ("N802",)), - (self.__checkFunctionArgumentNames, - ("N803", "N804", "N805", "N806")), - (self.__checkNameToBeAvoided, ("N831",)), - ], - "assign": [ - (self.__checkVariablesInFunction, ("N821",)), - (self.__checkNameToBeAvoided, ("N831",)), - ], - "importfrom": [ - (self.__checkImportAs, ("N811", "N812", "N813", "N814")), - ], - "module": [ - (self.__checkModule, ("N807", "N808")), - ], - } - - self.__checkers = {} - for key, checkers in self.__checkersWithCodes.items(): - for checker, codes in checkers: - if any(not (code and options.ignore_code(code)) - for code in codes): - if key not in self.__checkers: - self.__checkers[key] = [] - self.__checkers[key].append(checker) - - def run(self): - """ - Public method run by the pep8.py checker. - - @return tuple giving line number, offset within line, code and - checker function - """ - if self.__tree: - return self.__visitTree(self.__tree) - else: - return () - - def __visitTree(self, node): - """ - Private method to scan the given AST tree. - - @param node AST tree node to scan - @return tuple giving line number, offset within line, code and - checker function - """ - for error in self.__visitNode(node): - yield error - self.__parents.append(node) - for child in ast.iter_child_nodes(node): - for error in self.__visitTree(child): - yield error - self.__parents.pop() - - def __visitNode(self, node): - """ - Private method to inspect the given AST node. - - @param node AST tree node to inspect - @return tuple giving line number, offset within line, code and - checker function - """ - if isinstance(node, ast.ClassDef): - self.__tagClassFunctions(node) - elif isinstance(node, ast.FunctionDef): - self.__findGlobalDefs(node) - - checkerName = node.__class__.__name__.lower() - if checkerName in self.__checkers: - for checker in self.__checkers[checkerName]: - for error in checker(node, self.__parents): - yield error + (self.__checkers[checkerName],) - - def __tagClassFunctions(self, classNode): - """ - Private method to tag functions if they are methods, class methods or - static methods. - - @param classNode AST tree node to tag - """ - # try to find all 'old style decorators' like - # m = staticmethod(m) - lateDecoration = {} - for node in ast.iter_child_nodes(classNode): - if not (isinstance(node, ast.Assign) and - isinstance(node.value, ast.Call) and - isinstance(node.value.func, ast.Name)): - continue - funcName = node.value.func.id - if funcName in ("classmethod", "staticmethod"): - meth = (len(node.value.args) == 1 and node.value.args[0]) - if isinstance(meth, ast.Name): - lateDecoration[meth.id] = funcName - - # iterate over all functions and tag them - for node in ast.iter_child_nodes(classNode): - if not isinstance(node, ast.FunctionDef): - continue - - node.function_type = 'method' - if node.name == "__new__": - node.function_type = "classmethod" - - if node.name in lateDecoration: - node.function_type = lateDecoration[node.name] - elif node.decorator_list: - names = [d.id for d in node.decorator_list - if isinstance(d, ast.Name) and - d.id in ("classmethod", "staticmethod")] - if names: - node.function_type = names[0] - - def __findGlobalDefs(self, functionNode): - """ - Private method amend a node with global definitions information. - - @param functionNode AST tree node to amend - """ - globalNames = set() - nodesToCheck = collections.deque(ast.iter_child_nodes(functionNode)) - while nodesToCheck: - node = nodesToCheck.pop() - if isinstance(node, ast.Global): - globalNames.update(node.names) - - if not isinstance(node, (ast.FunctionDef, ast.ClassDef)): - nodesToCheck.extend(ast.iter_child_nodes(node)) - functionNode.global_names = globalNames - - def __getArgNames(self, node): - """ - Private method to get the argument names of a function node. - - @param node AST node to extract arguments names from - @return list of argument names (list of string) - """ - def unpackArgs(args): - """ - Local helper function to unpack function argument names. - - @param args list of AST node arguments - @return list of argument names (list of string) - """ - ret = [] - for arg in args: - if isinstance(arg, ast.Tuple): - ret.extend(unpackArgs(arg.elts)) - else: - ret.append(arg.id) - return ret - - return unpackArgs(node.args.args) - - def __error(self, node, code): - """ - Private method to build the error information. - - @param node AST node to report an error for - @param code error code to report (string) - @return tuple giving line number, offset within line and error code - (integer, integer, string) - """ - if isinstance(node, ast.Module): - lineno = 0 - offset = 0 - else: - lineno = node.lineno - offset = node.col_offset - if isinstance(node, ast.ClassDef): - lineno += len(node.decorator_list) - offset += 6 - elif isinstance(node, ast.FunctionDef): - lineno += len(node.decorator_list) - offset += 4 - return (lineno, offset, code) - - def __isNameToBeAvoided(self, name): - """ - Private method to check, if the given name should be avoided. - - @param name name to be checked (string) - @return flag indicating to avoid it (boolen) - """ - return name in ("l", "O", "I") - - def __checkNameToBeAvoided(self, node, parents): - """ - Private class to check the given node for a name to be avoided (N831). - - @param node AST note to check - @param parents list of parent nodes - @return tuple giving line number, offset within line and error code - (integer, integer, string) - """ - if isinstance(node, (ast.ClassDef, ast.FunctionDef)): - name = node.name - if self.__isNameToBeAvoided(name): - yield self.__error(node, "N831") - return - - if isinstance(node, ast.FunctionDef): - argNames = self.__getArgNames(node) - for arg in argNames: - if self.__isNameToBeAvoided(arg): - yield self.__error(node, "N831") - return - - if isinstance(node, ast.Assign): - for target in node.targets: - name = isinstance(target, ast.Name) and target.id - if not name: - return - - if self.__isNameToBeAvoided(name): - yield self.__error(node, "N831") - return - - def __checkClassName(self, node, parents): - """ - Private class to check the given node for class name - conventions (N801). - - Almost without exception, class names use the CapWords convention. - Classes for internal use have a leading underscore in addition. - - @param node AST note to check - @param parents list of parent nodes - @return tuple giving line number, offset within line and error code - (integer, integer, string) - """ - if not self.CamelcaseRegexp.match(node.name): - yield self.__error(node, "N801") - - def __checkFuntionName(self, node, parents): - """ - Private class to check the given node for function name - conventions (N802). - - Function names should be lowercase, with words separated by underscores - as necessary to improve readability. Functions <b>not</b> being - methods '__' in front and back are not allowed. Mixed case is allowed - only in contexts where that's already the prevailing style - (e.g. threading.py), to retain backwards compatibility. - - @param node AST note to check - @param parents list of parent nodes - @return tuple giving line number, offset within line and error code - (integer, integer, string) - """ - functionType = getattr(node, "function_type", "function") - name = node.name - if (functionType == "function" and "__" in (name[:2], name[-2:])) or \ - not self.LowercaseRegex.match(name): - yield self.__error(node, "N802") - - def __checkFunctionArgumentNames(self, node, parents): - """ - Private class to check the argument names of functions - (N803, N804, N805, N806). - - The argument names of a function should be lowercase, with words - separated by underscores. A class method should have 'cls' as the - first argument. A method should have 'self' as the first argument. - - @param node AST note to check - @param parents list of parent nodes - @return tuple giving line number, offset within line and error code - (integer, integer, string) - """ - if node.args.kwarg is not None: - if not self.LowercaseRegex.match(node.args.kwarg): - yield self.__error(node, "N803") - return - - if node.args.vararg is not None: - if not self.LowercaseRegex.match(node.args.vararg): - yield self.__error(node, "N803") - return - - argNames = self.__getArgNames(node) - functionType = getattr(node, "function_type", "function") - - if not argNames: - if functionType == "method": - yield self.__error(node, "N805") - elif functionType == "classmethod": - yield self.__error(node, "N804") - return - - if functionType == "method": - if argNames[0] != "self": - yield self.__error(node, "N805") - elif functionType == "classmethod": - if argNames[0] != "cls": - yield self.__error(node, "N804") - elif functionType == "staticmethod": - if argNames[0] in ("cls", "self"): - yield self.__error(node, "N806") - for arg in argNames: - if not self.LowercaseRegex.match(arg): - yield self.__error(node, "N803") - return - - def __checkVariablesInFunction(self, node, parents): - """ - Private method to check local variables in functions (N821). - - Local variables in functions should be lowercase. - - @param node AST note to check - @param parents list of parent nodes - @return tuple giving line number, offset within line and error code - (integer, integer, string) - """ - for parentFunc in reversed(parents): - if isinstance(parentFunc, ast.ClassDef): - return - if isinstance(parentFunc, ast.FunctionDef): - break - else: - return - for target in node.targets: - name = isinstance(target, ast.Name) and target.id - if not name or name in parentFunc.global_names: - return - - if not self.LowercaseRegex.match(name) and name[:1] != '_': - yield self.__error(target, "N821") - - def __checkModule(self, node, parents): - """ - Private method to check module naming conventions (N807, N808). - - Module and package names should be lowercase. - - @param node AST note to check - @param parents list of parent nodes - @return tuple giving line number, offset within line and error code - (integer, integer, string) - """ - if self.__filename: - moduleName = os.path.splitext(os.path.basename(self.__filename))[0] - if moduleName.lower() != moduleName: - yield self.__error(node, "N807") - - if moduleName == "__init__": - # we got a package - packageName = \ - os.path.split(os.path.dirname(self.__filename))[1] - if packageName.lower != packageName: - yield self.__error(node, "N808") - - def __checkImportAs(self, node, parents): - """ - Private method to check that imports don't change the - naming convention (N811, N812, N813, N814). - - @param node AST note to check - @param parents list of parent nodes - @return tuple giving line number, offset within line and error code - (integer, integer, string) - """ - for name in node.names: - if not name.asname: - continue - - if self.UppercaseRegexp.match(name.name): - if not self.UppercaseRegexp.match(name.asname): - yield self.__error(node, "N811") - elif self.LowercaseRegex.match(name.name): - if not self.LowercaseRegex.match(name.asname): - yield self.__error(node, "N812") - elif self.LowercaseRegex.match(name.asname): - yield self.__error(node, "N813") - elif self.UppercaseRegexp.match(name.asname): - yield self.__error(node, "N814") - -# -# eflag: FileType = Python2