eric7/Plugins/CheckerPlugins/CodeStyleChecker/Imports/ImportsChecker.py

Wed, 01 Dec 2021 20:09:57 +0100

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Wed, 01 Dec 2021 20:09:57 +0100
branch
eric7
changeset 8801
8fbb21be8579
parent 8789
b165effc3c62
child 8802
129a973fc33e
permissions
-rw-r--r--

Continued implementing a checker for import statements.

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

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

"""
Module implementing a checker for import statements.
"""

import copy
import sys


class ImportsChecker:
    """
    Class implementing a checker for import statements.
    """
    Codes = [
        ## Local imports
        "I101", "I102", "I103",
    ]

    def __init__(self, source, filename, tree, select, ignore, expected,
                 repeat, args):
        """
        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 select list of selected codes
        @type list of str
        @param ignore 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
        @param args dictionary of arguments for the various checks
        @type dict
        """
        self.__select = tuple(select)
        self.__ignore = ("",) if select else tuple(ignore)
        self.__expected = expected[:]
        self.__repeat = repeat
        self.__filename = filename
        self.__source = source[:]
        self.__tree = copy.deepcopy(tree)
        self.__args = args
        
        # statistics counters
        self.counters = {}
        
        # collection of detected errors
        self.errors = []
        
        checkersWithCodes = [
            (self.__checkLocalImports, ("I101", "I102", "I103")),
        ]
        
        self.__checkers = []
        for checker, codes in checkersWithCodes:
            if any(not (code and self.__ignoreCode(code))
                    for code in codes):
                self.__checkers.append(checker)
    
    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.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
        
        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):
            # record the issue with one based line number
            self.errors.append(
                {
                    "file": self.__filename,
                    "line": lineNumber + 1,
                    "offset": offset,
                    "code": code,
                    "args": args,
                }
            )
    
    def run(self):
        """
        Public method to check the given source against miscellaneous
        conditions.
        """
        if not self.__filename:
            # don't do anything, if essential data is missing
            return
        
        if not self.__checkers:
            # don't do anything, if no codes were selected
            return
        
        for check in self.__checkers:
            check()
    
    def getStandardModules(self):
        """
        Public method to get a list of modules of the standard library.
        
        @return set of builtin modules
        @rtype set of str
        """
        try:
            return sys.stdlib_module_names
        except AttributeError:
            return {
                "__future__", "__main__", "_dummy_thread", "_thread", "abc",
                "aifc", "argparse", "array", "ast", "asynchat", "asyncio",
                "asyncore", "atexit", "audioop", "base64", "bdb", "binascii",
                "binhex", "bisect", "builtins", "bz2", "calendar", "cgi",
                "cgitb", "chunk", "cmath", "cmd", "code", "codecs", "codeop",
                "collections", "colorsys", "compileall", "concurrent",
                "configparser", "contextlib", "contextvars", "copy", "copyreg",
                "cProfile", "crypt", "csv", "ctypes", "curses", "dataclasses",
                "datetime", "dbm", "decimal", "difflib", "dis", "distutils",
                "doctest", "dummy_threading", "email", "encodings",
                "ensurepip", "enum", "errno", "faulthandler", "fcntl",
                "filecmp", "fileinput", "fnmatch", "formatter", "fractions",
                "ftplib", "functools", "gc", "getopt", "getpass", "gettext",
                "glob", "grp", "gzip", "hashlib", "heapq", "hmac", "html",
                "http", "imaplib", "imghdr", "imp", "importlib", "inspect",
                "io", "ipaddress", "itertools", "json", "keyword", "lib2to3",
                "linecache", "locale", "logging", "lzma", "mailbox", "mailcap",
                "marshal", "math", "mimetypes", "mmap", "modulefinder",
                "msilib", "msvcrt", "multiprocessing", "netrc", "nis",
                "nntplib", "numbers", "operator", "optparse", "os",
                "ossaudiodev", "parser", "pathlib", "pdb", "pickle",
                "pickletools", "pipes", "pkgutil", "platform", "plistlib",
                "poplib", "posix", "pprint", "profile", "pstats", "pty", "pwd",
                "py_compile", "pyclbr", "pydoc", "queue", "quopri", "random",
                "re", "readline", "reprlib", "resource", "rlcompleter",
                "runpy", "sched", "secrets", "select", "selectors", "shelve",
                "shlex", "shutil", "signal", "site", "smtpd", "smtplib",
                "sndhdr", "socket", "socketserver", "spwd", "sqlite3", "ssl",
                "stat", "statistics", "string", "stringprep", "struct",
                "subprocess", "sunau", "symbol", "symtable", "sys",
                "sysconfig", "syslog", "tabnanny", "tarfile", "telnetlib",
                "tempfile", "termios", "test", "textwrap", "threading", "time",
                "timeit", "tkinter", "token", "tokenize", "trace", "traceback",
                "tracemalloc", "tty", "turtle", "turtledemo", "types",
                "typing", "unicodedata", "unittest", "urllib", "uu", "uuid",
                "venv", "warnings", "wave", "weakref", "webbrowser", "winreg",
                "winsound", "wsgiref", "xdrlib", "xml", "xmlrpc", "zipapp",
                "zipfile", "zipimport", "zlib", "zoneinfo",
            }
    
    #######################################################################
    ## Local imports
    ##
    ## adapted from: flake8-local-import v1.0.6
    #######################################################################
    
    def __checkLocalImports(self):
        """
        Private method to check local imports.
        """
        from .LocalImportVisitor import LocalImportVisitor
        
        visitor = LocalImportVisitor(self.__args, self)
        visitor.visit(copy.deepcopy(self.__tree))
        for violation in visitor.violations:
            node = violation[0]
            reason = violation[1]
            self.__error(node.lineno - 1, node.col_offset, reason)

eric ide

mercurial