diff --git a/.gitignore b/.gitignore index fe89362..3e876ea 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,14 @@ -*.pyc -.tox -.eggs *.egg *.egg-info -build -dist +*.log +*.pyc +*.sw* *.zip .cache -*.sw* -*.log -docs/build/html/* .coverage +.eggs +.tox +/.mypy_cache +build +dist +docs/build/html/* diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 0000000..3d1cd43 --- /dev/null +++ b/mypy.ini @@ -0,0 +1,2 @@ +[mypy] +ignore_missing_imports = true diff --git a/setup.py b/setup.py index cc5b872..528fc73 100644 --- a/setup.py +++ b/setup.py @@ -20,13 +20,14 @@ requires = [ # And in which releases we will update those ranges here: # http://flake8.pycqa.org/en/latest/internal/releases.html#releasing-flake8 "entrypoints >= 0.2.3, < 0.3.0", - "pyflakes >= 2.0.0, < 2.1.0", + "pyflakes >= 2.1.0, < 2.2.0", "pycodestyle >= 2.4.0, < 2.5.0", "mccabe >= 0.6.0, < 0.7.0", ] extras_require = { ":python_version<'3.4'": ['enum34'], + ":python_version<'3.5'": ['typing'], ":python_version<'3.2'": ['configparser'], } diff --git a/src/flake8/checker.py b/src/flake8/checker.py index b0e0644..ce33737 100644 --- a/src/flake8/checker.py +++ b/src/flake8/checker.py @@ -5,6 +5,7 @@ import logging import signal import sys import tokenize +from typing import List, Optional # noqa: F401 (until flake8 3.7) try: import multiprocessing @@ -195,7 +196,7 @@ class Manager(object): ) def make_checkers(self, paths=None): - # type: (List[str]) -> NoneType + # type: (List[str]) -> None """Create checkers for each file.""" if paths is None: paths = self.arguments @@ -411,7 +412,7 @@ class FileChecker(object): return None def report(self, error_code, line_number, column, text, line=None): - # type: (str, int, int, str) -> str + # type: (str, int, int, str, Optional[str]) -> str """Report an error by storing it in the results list.""" if error_code is None: error_code, text = text.split(" ", 1) diff --git a/src/flake8/main/application.py b/src/flake8/main/application.py index ae33b13..86be4bd 100644 --- a/src/flake8/main/application.py +++ b/src/flake8/main/application.py @@ -4,6 +4,8 @@ from __future__ import print_function import logging import sys import time +from typing import List, Optional, Sequence # noqa: F401 (until flake8 3.7) +from typing import Type, TYPE_CHECKING # noqa: F401 (until flake8 3.7) import flake8 from flake8 import checker @@ -16,6 +18,12 @@ from flake8.options import aggregator, config from flake8.options import manager from flake8.plugins import manager as plugin_manager +if TYPE_CHECKING: + # fmt: off + from flake8.formatting.base import BaseFormatter # noqa: F401, E501 (until flake8 3.7) + # fmt: on + + LOG = logging.getLogger(__name__) @@ -23,7 +31,7 @@ class Application(object): """Abstract our application into a class.""" def __init__(self, program="flake8", version=flake8.__version__): - # type: (str, str) -> NoneType + # type: (str, str) -> None """Initialize our application. :param str program: @@ -132,7 +140,7 @@ class Application(object): self.prelim_opts, self.prelim_args = opts, args def exit(self): - # type: () -> NoneType + # type: () -> None """Handle finalization and exiting the program. This should be the last thing called on the application instance. It @@ -159,7 +167,7 @@ class Application(object): ) def find_plugins(self): - # type: () -> NoneType + # type: () -> None """Find and load the plugins for this application. If :attr:`check_plugins`, or :attr:`formatting_plugins` are ``None`` @@ -191,14 +199,14 @@ class Application(object): self.formatting_plugins.load_plugins() def register_plugin_options(self): - # type: () -> NoneType + # type: () -> None """Register options provided by plugins to our option manager.""" self.check_plugins.register_options(self.option_manager) self.check_plugins.register_plugin_versions(self.option_manager) self.formatting_plugins.register_options(self.option_manager) def parse_configuration_and_cli(self, argv=None): - # type: (Union[NoneType, List[str]]) -> NoneType + # type: (Optional[List[str]]) -> None """Parse configuration files and the CLI options. :param list argv: @@ -238,7 +246,7 @@ class Application(object): return formatter_plugin.execute def make_formatter(self, formatter_class=None): - # type: () -> NoneType + # type: (Optional[Type[BaseFormatter]]) -> None """Initialize a formatter based on the parsed options.""" if self.formatter is None: format_plugin = self.options.format @@ -253,7 +261,7 @@ class Application(object): self.formatter = formatter_class(self.options) def make_guide(self): - # type: () -> NoneType + # type: () -> None """Initialize our StyleGuide.""" if self.guide is None: self.guide = style_guide.StyleGuideManager( @@ -264,7 +272,7 @@ class Application(object): self.guide.add_diff_ranges(self.parsed_diff) def make_file_checker_manager(self): - # type: () -> NoneType + # type: () -> None """Initialize our FileChecker Manager.""" if self.file_checker_manager is None: self.file_checker_manager = checker.Manager( @@ -274,7 +282,7 @@ class Application(object): ) def run_checks(self, files=None): - # type: (Union[List[str], NoneType]) -> NoneType + # type: (Optional[List[str]]) -> None """Run the actual checks with the FileChecker Manager. This method encapsulates the logic to make a @@ -315,7 +323,7 @@ class Application(object): self.formatter.show_benchmarks(statistics) def report_errors(self): - # type: () -> NoneType + # type: () -> None """Report all the errors found by flake8 3.0. This also updates the :attr:`result_count` attribute with the total @@ -338,7 +346,7 @@ class Application(object): self.formatter.show_statistics(self.guide.stats) def initialize(self, argv): - # type: () -> NoneType + # type: (Sequence[str]) -> None """Initialize the application to be run. This finds the plugins, registers their options, and parses the @@ -367,13 +375,13 @@ class Application(object): self.formatter.stop() def _run(self, argv): - # type: (Union[NoneType, List[str]]) -> NoneType + # type: (Optional[List[str]]) -> None self.initialize(argv) self.run_checks() self.report() def run(self, argv=None): - # type: (Union[NoneType, List[str]]) -> NoneType + # type: (Optional[List[str]]) -> None """Run our application. This method will also handle KeyboardInterrupt exceptions for the @@ -389,7 +397,7 @@ class Application(object): self.catastrophic_failure = True except exceptions.ExecutionError as exc: print("There was a critical error during execution of Flake8:") - print(exc.message) + print(exc) LOG.exception(exc) self.catastrophic_failure = True except exceptions.EarlyQuit: diff --git a/src/flake8/main/cli.py b/src/flake8/main/cli.py index 8ba6288..d6cb933 100644 --- a/src/flake8/main/cli.py +++ b/src/flake8/main/cli.py @@ -1,9 +1,11 @@ """Command-line implementation of flake8.""" +from typing import List, Optional # noqa: F401 (until flake8 3.7) + from flake8.main import application def main(argv=None): - # type: (Union[NoneType, List[str]]) -> NoneType + # type: (Optional[List[str]]) -> None """Execute the main bit of the application. This handles the creation of an instance of :class:`Application`, runs it, diff --git a/src/flake8/main/setuptools_command.py b/src/flake8/main/setuptools_command.py index 0a76057..a3ccf03 100644 --- a/src/flake8/main/setuptools_command.py +++ b/src/flake8/main/setuptools_command.py @@ -1,5 +1,6 @@ """The logic for Flake8's integration with setuptools.""" import os +from typing import List # noqa: F401 (until flake8 3.7) import setuptools @@ -19,7 +20,7 @@ class Flake8(setuptools.Command): # of options, and since this will break when users use plugins that # provide command-line options, we are leaving this empty. If users want # to configure this command, they can do so through config files. - user_options = [] + user_options = [] # type: List[str] def initialize_options(self): """Override this method to initialize our application.""" diff --git a/src/flake8/plugins/manager.py b/src/flake8/plugins/manager.py index 045cfd7..28b4b35 100644 --- a/src/flake8/plugins/manager.py +++ b/src/flake8/plugins/manager.py @@ -315,7 +315,7 @@ class PluginManager(object): # pylint: disable=too-few-public-methods def version_for(plugin): - # (Plugin) -> Union[str, NoneType] + # (Plugin) -> Optional[str] """Determine the version of a plugin by its module. :param plugin: diff --git a/src/flake8/plugins/pyflakes.py b/src/flake8/plugins/pyflakes.py index 880373a..4435380 100644 --- a/src/flake8/plugins/pyflakes.py +++ b/src/flake8/plugins/pyflakes.py @@ -30,6 +30,8 @@ FLAKE8_PYFLAKES_CODES = { "TooManyExpressionsInStarredAssignment": "F621", "TwoStarredExpressions": "F622", "AssertTuple": "F631", + "IsLiteral": "F632", + "InvalidPrintSyntax": "F633", "BreakOutsideLoop": "F701", "ContinueOutsideLoop": "F702", "ContinueInFinally": "F703", @@ -39,6 +41,7 @@ FLAKE8_PYFLAKES_CODES = { "DefaultExceptNotLast": "F707", "DoctestSyntaxError": "F721", "ForwardAnnotationSyntaxError": "F722", + "CommentAnnotationSyntaxError": "F723", "RedefinedWhileUnused": "F811", "RedefinedInListComp": "F812", "UndefinedName": "F821", @@ -72,7 +75,7 @@ class FlakesChecker(pyflakes.checker.Checker): include_in_doctest = [] exclude_from_doctest = [] - def __init__(self, tree, filename): + def __init__(self, tree, file_tokens, filename): """Initialize the PyFlakes plugin with an AST tree and filename.""" filename = utils.normalize_paths(filename)[0] with_doctest = self.with_doctest @@ -97,7 +100,10 @@ class FlakesChecker(pyflakes.checker.Checker): with_doctest = True super(FlakesChecker, self).__init__( - tree, filename=filename, withDoctest=with_doctest + tree, + filename=filename, + withDoctest=with_doctest, + file_tokens=file_tokens, ) @classmethod diff --git a/src/flake8/processor.py b/src/flake8/processor.py index c31a5c4..47f5cd2 100644 --- a/src/flake8/processor.py +++ b/src/flake8/processor.py @@ -3,6 +3,7 @@ import contextlib import logging import sys import tokenize +from typing import List # noqa: F401 (until flake8 3.7) import flake8 from flake8 import defaults @@ -348,7 +349,7 @@ class FileProcessor(object): return False def strip_utf_bom(self): - # type: () -> NoneType + # type: () -> None """Strip the UTF bom from the lines of the file.""" if not self.lines: # If we have nothing to analyze quit early diff --git a/src/flake8/style_guide.py b/src/flake8/style_guide.py index 01d85d7..4626fd6 100644 --- a/src/flake8/style_guide.py +++ b/src/flake8/style_guide.py @@ -7,6 +7,7 @@ import functools import itertools import linecache import logging +from typing import Optional, Union # noqa: F401 (until flake8 3.7) from flake8 import defaults from flake8 import statistics @@ -391,7 +392,7 @@ class StyleGuideManager(object): text, physical_line=None, ): - # type: (str, str, int, int, str) -> int + # type: (str, str, int, int, str, Optional[str]) -> int """Handle an error reported by a check. :param str code: @@ -511,7 +512,7 @@ class StyleGuide(object): text, physical_line=None, ): - # type: (str, str, int, int, str) -> int + # type: (str, str, int, int, str, Optional[str]) -> int """Handle an error reported by a check. :param str code: diff --git a/src/flake8/utils.py b/src/flake8/utils.py index 0c97a39..502e1ca 100644 --- a/src/flake8/utils.py +++ b/src/flake8/utils.py @@ -8,6 +8,13 @@ import platform import re import sys import tokenize +from typing import Callable, Dict, Generator # noqa: F401 (until flake8 3.7) +from typing import List, Pattern, Sequence # noqa: F401 (until flake8 3,7) +from typing import Tuple, TYPE_CHECKING # noqa: F401 (until flake8 3.7) +from typing import Union # noqa: F401 (until flake8 3.7) + +if TYPE_CHECKING: + from flake8.plugins.manager import Plugin # noqa: F401 (until flake8 3.7) DIFF_HUNK_REGEXP = re.compile(r"^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@.*$") COMMA_SEPARATED_LIST_RE = re.compile(r"[,\s]") @@ -15,7 +22,7 @@ LOCAL_PLUGIN_LIST_RE = re.compile(r"[,\t\n\r\f\v]") def parse_comma_separated_list(value, regexp=COMMA_SEPARATED_LIST_RE): - # type: (Union[Sequence[str], str]) -> List[str] + # type: (Union[Sequence[str], str], Pattern[str]) -> List[str] """Parse a comma-separated list. :param value: @@ -321,7 +328,7 @@ def _default_predicate(*args): def filenames_from(arg, predicate=None): - # type: (str, callable) -> Generator + # type: (str, Callable[[str], bool]) -> Generator """Generate filenames from an argument. :param str arg: @@ -382,7 +389,7 @@ def fnmatch(filename, patterns, default=True): def parameters_for(plugin): - # type: (flake8.plugins.manager.Plugin) -> Dict[str, bool] + # type: (Plugin) -> Dict[str, bool] """Return the parameters for the plugin. This will inspect the plugin and return either the function parameters diff --git a/tox.ini b/tox.ini index ae0545f..5d94e5c 100644 --- a/tox.ini +++ b/tox.ini @@ -74,9 +74,9 @@ commands = basepython = python3 skip_install = true deps = - mypy-lang + mypy commands = - mypy flake8 + mypy src [testenv:bandit] basepython = python3