예제 #1
0
 def run(self):
     """
     Public method to check the given source for code complexity.
     """
     if not self.__filename or not self.__source:
         # don't do anything, if essential data is missing
         return
     
     if self.__ignoreCode("C101"):
         # don't do anything, if this should be ignored
         return
     
     try:
         tree = compile(''.join(self.__source), self.__filename, 'exec',
                        ast.PyCF_ONLY_AST)
     except (SyntaxError, TypeError):
         self.__reportInvalidSyntax()
         return
     
     visitor = PathGraphingAstVisitor()
     visitor.preorder(tree, visitor)
     for graph in visitor.graphs.values():
         if graph.complexity() > self.__maxComplexity:
             self.__error(graph.lineno, 0, "C101",
                          graph.entity, graph.complexity())
예제 #2
0
    def run(self):
        messages = []

        for code_file in self._code_files:
            try:
                tree = ast.parse(open(code_file, "r").read(), filename=code_file)
            except (SyntaxError, TypeError):
                location = Location(path=code_file, module=None, function=None, line=1, character=0)
                message = Message(source="mccabe", code="MC0000", location=location, message="Could not parse file")
                messages.append(message)
                continue

            visitor = PathGraphingAstVisitor()
            visitor.preorder(tree, visitor)

            for graph in visitor.graphs.values():
                complexity = graph.complexity()
                if complexity > self.max_complexity:
                    location = Location(
                        path=code_file,
                        module=None,
                        function=graph.entity,
                        line=graph.lineno,
                        character=0,
                        absolute_path=True,
                    )
                    message = Message(
                        source="mccabe",
                        code="MC0001",
                        location=location,
                        message="%s is too complex (%s)" % (graph.entity, complexity),
                    )
                    messages.append(message)

        return self.filter_messages(messages)
예제 #3
0
    def run(self, found_files):
        messages = []

        for code_file in found_files.iter_module_paths():
            try:
                contents = read_py_file(code_file)
                tree = ast.parse(
                    contents,
                    filename=code_file,
                )
            except CouldNotHandleEncoding as err:
                messages.append(
                    make_tool_error_message(
                        code_file,
                        'mccabe',
                        'MC0000',
                        message='Could not handle the encoding of this file: %s'
                        % err.encoding))
                continue
            except SyntaxError as err:
                messages.append(
                    make_tool_error_message(code_file,
                                            'mccabe',
                                            'MC0000',
                                            line=err.lineno,
                                            character=err.offset,
                                            message='Syntax Error'))
                continue
            except TypeError:
                messages.append(
                    make_tool_error_message(code_file,
                                            'mccabe',
                                            'MC0000',
                                            message='Unable to parse file'))
                continue

            visitor = PathGraphingAstVisitor()
            visitor.preorder(tree, visitor)

            for graph in visitor.graphs.values():
                complexity = graph.complexity()
                if complexity > self.max_complexity:
                    location = Location(path=code_file,
                                        module=None,
                                        function=graph.entity,
                                        line=graph.lineno,
                                        character=0,
                                        absolute_path=True)
                    message = Message(
                        source='mccabe',
                        code='MC0001',
                        location=location,
                        message='%s is too complex (%s)' % (
                            graph.entity,
                            complexity,
                        ),
                    )
                    messages.append(message)

        return self.filter_messages(messages)
예제 #4
0
파일: mccabe.py 프로젝트: douardda/tidypy
    def execute(self, finder):
        issues = []
        if CODE in self.config['disabled']:
            return issues

        for filepath in finder.files(self.config['filters']):
            try:
                tree = parse_python_file(filepath)
            except (SyntaxError, TypeError) as exc:
                issues.append(ParseIssue(exc, filepath))
                continue
            except EnvironmentError as exc:
                issues.append(AccessIssue(exc, filepath))
                continue

            visitor = PathGraphingAstVisitor()
            visitor.preorder(tree, visitor)

            for graph in itervalues(visitor.graphs):
                complexity = graph.complexity()
                if complexity > self.config['options']['max-complexity']:
                    issues.append(
                        McCabeIssue(
                            'complex',
                            DESCRIPTION.format(
                                entity=graph.entity,
                                score=complexity,
                            ),
                            filepath,
                            graph.lineno,
                            graph.column + 1,
                        ))

        return issues
예제 #5
0
 def _cyclomatic(self, filename):
     with open(filename, 'r') as fp:
         code = fp.read()
     tree = compile(code, filename, 'exec', ast.PyCF_ONLY_AST)
     visitor = PathGraphingAstVisitor()
     visitor.preorder(tree, visitor)
     ret = {}
     for name, graph in visitor.graphs.items():
         ret[name] = graph.complexity()
     return ret
예제 #6
0
 def calcule(self, node):
     visitor = PathGraphingAstVisitor()
     visitor.preorder(node, visitor)
     for graph in visitor.graphs.values():
         if graph.complexity() > self.max_complexity:
             split = graph.entity.split('.')
             if len(split) == 2:
                 entity, method = split
                 if self.result.has_key(entity):
                     self.result[entity].extend([method])
                 else:
                     self.result[entity] = [method]
     return self.result
예제 #7
0
    def run(self):
        messages = []

        for code_file in self._code_files:
            try:
                tree = ast.parse(
                    open(code_file, 'r').read(),
                    filename=code_file,
                )
            except (SyntaxError, TypeError):
                location = Location(
                    path=code_file,
                    module=None,
                    function=None,
                    line=1,
                    character=0,
                )
                message = Message(
                    source='mccabe',
                    code='MC0000',
                    location=location,
                    message='Could not parse file',
                )
                messages.append(message)
                continue

            visitor = PathGraphingAstVisitor()
            visitor.preorder(tree, visitor)

            for graph in visitor.graphs.values():
                complexity = graph.complexity()
                if complexity > self.max_complexity:
                    location = Location(
                        path=code_file,
                        module=None,
                        function=graph.entity,
                        line=graph.lineno,
                        character=0,
                    )
                    message = Message(
                        source='mccabe',
                        code='MC0001',
                        location=location,
                        message='%s is too complex (%s)' % (
                            graph.entity,
                            complexity,
                        ),
                    )
                    messages.append(message)

        return self.filter_messages(messages)
예제 #8
0
def parse_code(source, filepath):
    """
    Parse some code into its constituent Thing objects.

    Parse a string containing Python source into the various Thing objects that it represents
    and are interesting to this library.

    Args:
      source(str): A string representing an entire block of Python code which should be inspected.
      filepath(str): The path containing this code or some representation of its location at least.
    """
    # Both the AST and tokens are required, as the AST module does not return regular comments
    # but the tokenize module does not have quite enough structure information. The asttoken
    # library is used to annotate AST nodes with location information, which can then be used in
    # combination with the tokens to attach comments which may have been removed. Additionally,
    # the mccabe library is then used to annotate nodes with complexity information.
    tree = compile(source, filepath, "exec", ast.PyCF_ONLY_AST)
    astok = asttokens.ASTTokens(source, tree=tree)

    # For the purposes of this library, a particular documentable thing is considered to have coordinates
    # of the location of the first token making up its node in the AST, which forms the key in this dictionary
    things = defaultdict(list)

    # split the AST into the nodes we care about and discard the ones we don't care about
    for node in ast.walk(astok.tree):
        if isinstance(node, ast.FunctionDef):
            coords = (node.lineno, node.col_offset)
            things[coords].append(MethodOrFunction(filepath, node))

    # now attach inline comments because the number of comments should be appropriate for the McCabe complexity
    # of certain types of node
    tokens = tokenize.generate_tokens(StringIO(source).readline)
    for token in tokens:
        if token[0] == tokenize.COMMENT:
            for thing_list in things.values():
                for thing in thing_list:
                    # allow the Thing object to determine whether to pay attention to this comment or not
                    thing.add_comment(token)

    # now attach complexity information to them
    visitor = PathGraphingAstVisitor()
    visitor.preorder(astok.tree, visitor)

    for graph in visitor.graphs.values():
        complexity = graph.complexity()
        coords = (graph.lineno, graph.column)
        for thing in things[coords]:
            if isinstance(thing, MethodOrFunction):
                thing.set_complexity(complexity)

    return [x for t in things.values() for x in t]
예제 #9
0
def get_mccabe_violations_for_file(filepath, max_complexity):
    code = _read(filepath)
    tree = compile(code, filepath, "exec", ast.PyCF_ONLY_AST)
    visitor = PathGraphingAstVisitor()
    visitor.preorder(tree, visitor)

    violations = []
    for graph in visitor.graphs.values():
        if graph.complexity() >= max_complexity:
            complex_function_name = graph.entity
            if complex_function_name.startswith('If '):
                complex_function_name = 'if __name__ == "__main__"'
            violations.append(complex_function_name)
    return violations
예제 #10
0
 def __checkMcCabeComplexity(self):
     """
     Private method to check the McCabe code complexity.
     """
     try:
         # create the AST again because it is modified by the checker
         tree = compile(''.join(self.__source), self.__filename, 'exec',
                        ast.PyCF_ONLY_AST)
     except (SyntaxError, TypeError):
         # compile errors are already reported by the run() method
         return
     
     maxComplexity = self.__args.get("McCabeComplexity",
                                     self.__defaultArgs["McCabeComplexity"])
     
     visitor = PathGraphingAstVisitor()
     visitor.preorder(tree, visitor)
     for graph in visitor.graphs.values():
         if graph.complexity() > maxComplexity:
             self.__error(graph.lineno, 0, "C101",
                          graph.entity, graph.complexity())
예제 #11
0
def get_cyclomatic_complexity(some_funcdef: ast.FunctionDef) -> int:
    visitor = PathGraphingAstVisitor()
    visitor.preorder(some_funcdef, visitor)
    return list(visitor.graphs.values())[0].complexity()
예제 #12
0
    def run(self, found_files):
        messages = []

        for code_file in found_files.iter_module_paths():
            try:
                tree = ast.parse(
                    open(code_file, 'r').read(),
                    filename=code_file,
                )
            except SyntaxError as e:
                location = Location(
                    path=code_file,
                    module=None,
                    function=None,
                    line=e.lineno,
                    character=e.offset,
                )
                message = Message(
                    source='mccabe',
                    code='MC0000',
                    location=location,
                    message='Syntax error',
                )
                messages.append(message)
                continue
            except TypeError:
                location = Location(
                    path=code_file,
                    module=None,
                    function=None,
                    line=0,
                    character=0,
                )
                message = Message(
                    source='mccabe',
                    code='MC0000',
                    location=location,
                    message='Unable to parse file',
                )
                messages.append(message)
                continue

            visitor = PathGraphingAstVisitor()
            visitor.preorder(tree, visitor)

            for graph in visitor.graphs.values():
                complexity = graph.complexity()
                if complexity > self.max_complexity:
                    location = Location(path=code_file,
                                        module=None,
                                        function=graph.entity,
                                        line=graph.lineno,
                                        character=0,
                                        absolute_path=True)
                    message = Message(
                        source='mccabe',
                        code='MC0001',
                        location=location,
                        message='%s is too complex (%s)' % (
                            graph.entity,
                            complexity,
                        ),
                    )
                    messages.append(message)

        return self.filter_messages(messages)
예제 #13
0
    def run(self, found_files):
        messages = []

        for code_file in found_files.iter_module_paths():
            try:
                tree = ast.parse(
                    open(code_file, 'r').read(),
                    filename=code_file,
                )
            except SyntaxError as e:
                location = Location(
                    path=code_file,
                    module=None,
                    function=None,
                    line=e.lineno,
                    character=e.offset,
                )
                message = Message(
                    source='mccabe',
                    code='MC0000',
                    location=location,
                    message='Syntax error',
                )
                messages.append(message)
                continue
            except TypeError:
                location = Location(
                    path=code_file,
                    module=None,
                    function=None,
                    line=0,
                    character=0,
                )
                message = Message(
                    source='mccabe',
                    code='MC0000',
                    location=location,
                    message='Unable to parse file',
                    )
                messages.append(message)
                continue

            visitor = PathGraphingAstVisitor()
            visitor.preorder(tree, visitor)

            for graph in visitor.graphs.values():
                complexity = graph.complexity()
                if complexity > self.max_complexity:
                    location = Location(
                        path=code_file,
                        module=None,
                        function=graph.entity,
                        line=graph.lineno,
                        character=0,
                        absolute_path=True
                    )
                    message = Message(
                        source='mccabe',
                        code='MC0001',
                        location=location,
                        message='%s is too complex (%s)' % (
                            graph.entity,
                            complexity,
                        ),
                    )
                    messages.append(message)

        return self.filter_messages(messages)