관리-도구
편집 파일: pyflakes.py
"""Plugin built-in to Flake8 to treat pyflakes as a plugin.""" import argparse import ast import os import tokenize from typing import Any from typing import Generator from typing import List from typing import Tuple from typing import Type import pyflakes.checker from flake8 import utils from flake8.options.manager import OptionManager FLAKE8_PYFLAKES_CODES = { "UnusedImport": "F401", "ImportShadowedByLoopVar": "F402", "ImportStarUsed": "F403", "LateFutureImport": "F404", "ImportStarUsage": "F405", "ImportStarNotPermitted": "F406", "FutureFeatureNotDefined": "F407", "PercentFormatInvalidFormat": "F501", "PercentFormatExpectedMapping": "F502", "PercentFormatExpectedSequence": "F503", "PercentFormatExtraNamedArguments": "F504", "PercentFormatMissingArgument": "F505", "PercentFormatMixedPositionalAndNamed": "F506", "PercentFormatPositionalCountMismatch": "F507", "PercentFormatStarRequiresSequence": "F508", "PercentFormatUnsupportedFormatCharacter": "F509", "StringDotFormatInvalidFormat": "F521", "StringDotFormatExtraNamedArguments": "F522", "StringDotFormatExtraPositionalArguments": "F523", "StringDotFormatMissingArgument": "F524", "StringDotFormatMixingAutomatic": "F525", "FStringMissingPlaceholders": "F541", "MultiValueRepeatedKeyLiteral": "F601", "MultiValueRepeatedKeyVariable": "F602", "TooManyExpressionsInStarredAssignment": "F621", "TwoStarredExpressions": "F622", "AssertTuple": "F631", "IsLiteral": "F632", "InvalidPrintSyntax": "F633", "IfTuple": "F634", "BreakOutsideLoop": "F701", "ContinueOutsideLoop": "F702", "ContinueInFinally": "F703", "YieldOutsideFunction": "F704", "ReturnOutsideFunction": "F706", "DefaultExceptNotLast": "F707", "DoctestSyntaxError": "F721", "ForwardAnnotationSyntaxError": "F722", "CommentAnnotationSyntaxError": "F723", "RedefinedWhileUnused": "F811", "UndefinedName": "F821", "UndefinedExport": "F822", "UndefinedLocal": "F823", "DuplicateArgument": "F831", "UnusedVariable": "F841", "RaiseNotImplemented": "F901", } class FlakesChecker(pyflakes.checker.Checker): """Subclass the Pyflakes checker to conform with the flake8 API.""" with_doctest = False include_in_doctest: List[str] = [] exclude_from_doctest: List[str] = [] def __init__( self, tree: ast.AST, file_tokens: List[tokenize.TokenInfo], filename: str, ) -> None: """Initialize the PyFlakes plugin with an AST tree and filename.""" filename = utils.normalize_path(filename) with_doctest = self.with_doctest included_by = [ include for include in self.include_in_doctest if include != "" and filename.startswith(include) ] if included_by: with_doctest = True for exclude in self.exclude_from_doctest: if exclude != "" and filename.startswith(exclude): with_doctest = False overlaped_by = [ include for include in included_by if include.startswith(exclude) ] if overlaped_by: with_doctest = True super().__init__( tree, filename=filename, withDoctest=with_doctest, file_tokens=file_tokens, ) @classmethod def add_options(cls, parser: OptionManager) -> None: """Register options for PyFlakes on the Flake8 OptionManager.""" parser.add_option( "--builtins", parse_from_config=True, comma_separated_list=True, help="define more built-ins, comma separated", ) parser.add_option( "--doctests", default=False, action="store_true", parse_from_config=True, help="also check syntax of the doctests", ) parser.add_option( "--include-in-doctest", default="", dest="include_in_doctest", parse_from_config=True, comma_separated_list=True, normalize_paths=True, help="Run doctests only on these files", ) parser.add_option( "--exclude-from-doctest", default="", dest="exclude_from_doctest", parse_from_config=True, comma_separated_list=True, normalize_paths=True, help="Skip these files when running doctests", ) @classmethod def parse_options(cls, options: argparse.Namespace) -> None: """Parse option values from Flake8's OptionManager.""" if options.builtins: cls.builtIns = cls.builtIns.union(options.builtins) cls.with_doctest = options.doctests included_files = [] for included_file in options.include_in_doctest: if included_file == "": continue if not included_file.startswith((os.sep, "./", "~/")): included_files.append(f"./{included_file}") else: included_files.append(included_file) cls.include_in_doctest = utils.normalize_paths(included_files) excluded_files = [] for excluded_file in options.exclude_from_doctest: if excluded_file == "": continue if not excluded_file.startswith((os.sep, "./", "~/")): excluded_files.append(f"./{excluded_file}") else: excluded_files.append(excluded_file) cls.exclude_from_doctest = utils.normalize_paths(excluded_files) inc_exc = set(cls.include_in_doctest).intersection( cls.exclude_from_doctest ) if inc_exc: raise ValueError( f"{inc_exc!r} was specified in both the " f"include-in-doctest and exclude-from-doctest " f"options. You are not allowed to specify it in " f"both for doctesting." ) def run(self) -> Generator[Tuple[int, int, str, Type[Any]], None, None]: """Run the plugin.""" for message in self.messages: col = getattr(message, "col", 0) yield ( message.lineno, col, "{} {}".format( FLAKE8_PYFLAKES_CODES.get(type(message).__name__, "F999"), message.message % message.message_args, ), message.__class__, )