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

Mon, 24 Feb 2025 15:11:18 +0100

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Mon, 24 Feb 2025 15:11:18 +0100
branch
eric7
changeset 11147
dee6e106b4d3
parent 11145
d328a7b74fd8
child 11150
73d80859079c
permissions
-rw-r--r--

Modified the code style checker such, that the issue category and issue number are separated by a '-' to make up the issue code (e.g E-901).

# -*- coding: utf-8 -*-

# Copyright (c) 2021 - 2025 Detlev Offenbach <detlev@die-offenbachs.de>
#

"""
Module implementing the checker for simplifying Python code.
"""

import ast
import copy

from .SimplifyNodeVisitor import SimplifyNodeVisitor


class SimplifyChecker:
    """
    Class implementing a checker for to help simplifying Python code.
    """

    Codes = [
        # Python-specifics
        "Y-101",
        "Y-102",
        "Y-103",
        "Y-104",
        "Y-105",
        "Y-106",
        "Y-107",
        "Y-108",
        "Y-109",
        "Y-110",
        "Y-111",
        "Y-112",
        "Y-113",
        "Y-114",
        "Y-115",
        "Y-116",
        "Y-117",
        "Y-118",
        "Y-119",
        "Y-120",
        "Y-121",
        "Y-122",
        "Y-123",
        # Python-specifics not part of flake8-simplify
        "Y-181",
        "Y-182",
        # Comparations
        "Y-201",
        "Y-202",
        "Y-203",
        "Y-204",
        "Y-205",
        "Y-206",
        "Y-207",
        "Y-208",
        "Y-211",
        "Y-212",
        "Y-213",
        "Y-221",
        "Y-222",
        "Y-223",
        "Y-224",
        # Opinionated
        "Y-301",
        # General Code Style
        "Y-401",
        "Y-402",
        # f-Strings
        "Y-411",
        # Additional Checks
        "Y-901",
        "Y-904",
        "Y-905",
        "Y-906",
        "Y-907",
        "Y-909",
        "Y-910",
        "Y-911",
    ]

    def __init__(self, source, filename, tree, selected, ignored, expected, repeat):
        """
        Constructor

        @param source source code to be checked
        @type list of str
        @param filename name of the source file
        @type str
        @param tree AST tree of the source code
        @type ast.Module
        @param selected list of selected codes
        @type list of str
        @param ignored list of codes to be ignored
        @type list of str
        @param expected list of expected codes
        @type list of str
        @param repeat flag indicating to report each occurrence of a code
        @type bool
        """
        self.__select = tuple(selected)
        self.__ignore = tuple(ignored)
        self.__expected = expected[:]
        self.__repeat = repeat
        self.__filename = filename
        self.__source = source[:]
        self.__tree = copy.deepcopy(tree)

        # statistics counters
        self.counters = {}

        # collection of detected errors
        self.errors = []

        self.__checkCodes = (code for code in self.Codes if not self.__ignoreCode(code))

    def __ignoreCode(self, code):
        """
        Private method to check if the message code should be ignored.

        @param code message code to check for
        @type str
        @return flag indicating to ignore the given code
        @rtype bool
        """
        return code in self.__ignore or (
            code.startswith(self.__ignore) and not code.startswith(self.__select)
        )

    def __error(self, lineNumber, offset, code, *args):
        """
        Private method to record an issue.

        @param lineNumber line number of the issue
        @type int
        @param offset position within line of the issue
        @type int
        @param code message code
        @type str
        @param args arguments for the message
        @type list
        """
        if self.__ignoreCode(code):
            return

        # record the issue with one based line number
        errorInfo = {
            "file": self.__filename,
            "line": lineNumber + 1,
            "offset": offset,
            "code": code,
            "args": args,
        }

        if errorInfo not in self.errors:
            # this issue was not seen before
            if code in self.counters:
                self.counters[code] += 1
            else:
                self.counters[code] = 1

            # Don't care about expected codes
            if code in self.__expected:
                return

            if code and (self.counters[code] == 1 or self.__repeat):
                self.errors.append(errorInfo)

    def run(self):
        """
        Public method to check the given source against functions
        to be replaced by 'pathlib' equivalents.
        """
        if not self.__filename:
            # don't do anything, if essential data is missing
            return

        if not self.__checkCodes:
            # don't do anything, if no codes were selected
            return

        # Add parent information
        self.__addMeta(self.__tree)

        visitor = SimplifyNodeVisitor(self.__error)
        visitor.visit(self.__tree)

    def __addMeta(self, root, level=0):
        """
        Private method to amend the nodes of the given AST tree with backward and
        forward references.

        @param root reference to the root node of the tree
        @type ast.AST
        @param level nesting level (defaults to 0)
        @type int (optional)
        """
        previousSibling = None
        for node in ast.iter_child_nodes(root):
            if level == 0:
                node.parent = root
            node.previous_sibling = previousSibling
            node.next_sibling = None
            if previousSibling:
                node.previous_sibling.next_sibling = node
            previousSibling = node
            for child in ast.iter_child_nodes(node):
                child.parent = node
            self.__addMeta(node, level=level + 1)

eric ide

mercurial