summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnthony Sottile <asottile@umich.edu>2019-05-19 17:01:14 -0700
committerAnthony Sottile <asottile@umich.edu>2019-05-19 17:31:04 -0700
commitfb7e9338cd06760a2f9096f976f0e246fc36a09e (patch)
treec2a2a2a907d7540eef0dbd633d6cb52cc50da14a
parentb6ba6d4d03109965d3cf5174d5c2e6868d7d92bb (diff)
downloadflake8-fb7e9338cd06760a2f9096f976f0e246fc36a09e.tar.gz
mypy now passes
-rw-r--r--.gitlab-ci.yml7
-rw-r--r--.pre-commit-config.yaml6
-rw-r--r--mypy.ini2
-rw-r--r--setup.cfg25
-rw-r--r--src/flake8/__init__.py5
-rw-r--r--src/flake8/checker.py46
-rw-r--r--src/flake8/exceptions.py47
-rw-r--r--src/flake8/formatting/base.py35
-rw-r--r--src/flake8/formatting/default.py28
-rw-r--r--src/flake8/main/application.py37
-rw-r--r--src/flake8/main/git.py4
-rw-r--r--src/flake8/main/mercurial.py3
-rw-r--r--src/flake8/main/setuptools_command.py4
-rw-r--r--src/flake8/main/vcs.py2
-rw-r--r--src/flake8/options/config.py11
-rw-r--r--src/flake8/options/manager.py21
-rw-r--r--src/flake8/plugins/manager.py21
-rw-r--r--src/flake8/plugins/pyflakes.py5
-rw-r--r--src/flake8/processor.py10
-rw-r--r--src/flake8/statistics.py22
-rw-r--r--src/flake8/style_guide.py16
-rw-r--r--src/flake8/utils.py57
-rw-r--r--tests/__init__.py1
-rw-r--r--tests/integration/test_checker.py10
-rw-r--r--tests/unit/test_base_formatter.py5
-rw-r--r--tests/unit/test_debug.py3
-rw-r--r--tests/unit/test_decision_engine.py3
-rw-r--r--tests/unit/test_option.py2
-rw-r--r--tests/unit/test_plugin_type_manager.py14
-rw-r--r--tests/unit/test_statistics.py2
-rw-r--r--tests/unit/test_utils.py6
-rw-r--r--tox.ini7
32 files changed, 255 insertions, 212 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 4b1c28d..cbccb83 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -35,7 +35,7 @@ python36:
script: tox -e py36
python37:
- image: python:3.7-rc
+ image: python:3.7
stage: test
script: tox -e py37
@@ -44,6 +44,11 @@ linters:
stage: test
script: tox -e linters
+pre-commit:
+ image: python:3.7
+ stage: test
+ script: tox -e pre-commit
+
docs:
stage: test
script: tox -e docs
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000..48f8a1d
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,6 @@
+repos:
+- repo: https://github.com/pre-commit/mirrors-mypy
+ rev: v0.701
+ hooks:
+ - id: mypy
+ exclude: ^(docs/|example-plugin/|tests/fixtures)
diff --git a/mypy.ini b/mypy.ini
deleted file mode 100644
index 3d1cd43..0000000
--- a/mypy.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[mypy]
-ignore_missing_imports = true
diff --git a/setup.cfg b/setup.cfg
index e887ea8..109a253 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -16,3 +16,28 @@ requires-dist =
pyflakes >= 2.1.0, < 2.2.0
pycodestyle >= 2.5.0, < 2.6.0
mccabe >= 0.6.0, < 0.7.0
+
+[mypy]
+check_untyped_defs = true
+disallow_any_generics = true
+disallow_incomplete_defs = true
+# TODO: disallow_untyped_defs = true
+no_implicit_optional = true
+warn_unused_ignores = true
+
+# TODO: until we opt in all the modules
+[mypy-flake8.defaults]
+disallow_untyped_defs = true
+[mypy-flake8.exceptions]
+disallow_untyped_defs = true
+[mypy-flake8.formatting.*]
+disallow_untyped_defs = true
+[mypy-flake8.main.cli]
+disallow_untyped_defs = true
+[mypy-flake8.statistics]
+disallow_untyped_defs = true
+[mypy-flake8.utils]
+disallow_untyped_defs = true
+
+[mypy-tests.*]
+disallow_untyped_defs = false
diff --git a/src/flake8/__init__.py b/src/flake8/__init__.py
index 73ec63a..8394276 100644
--- a/src/flake8/__init__.py
+++ b/src/flake8/__init__.py
@@ -12,6 +12,9 @@ This module
import logging
import sys
+if False: # `typing.TYPE_CHECKING` was introduced in 3.5.2
+ from typing import Type # `typing.Type` was introduced in 3.5.2
+
LOG = logging.getLogger(__name__)
LOG.addHandler(logging.NullHandler())
@@ -61,7 +64,7 @@ def configure_logging(verbosity, filename=None, logformat=LOG_FORMAT):
if not filename or filename in ("stderr", "stdout"):
fileobj = getattr(sys, filename or "stderr")
- handler_cls = logging.StreamHandler
+ handler_cls = logging.StreamHandler # type: Type[logging.Handler]
else:
fileobj = filename
handler_cls = logging.FileHandler
diff --git a/src/flake8/checker.py b/src/flake8/checker.py
index 4fb106e..fd5658a 100644
--- a/src/flake8/checker.py
+++ b/src/flake8/checker.py
@@ -3,14 +3,13 @@ import collections
import errno
import logging
import signal
-import sys
import tokenize
-from typing import List, Optional, Tuple
+from typing import Dict, List, Optional, Tuple
try:
import multiprocessing
except ImportError:
- multiprocessing = None
+ multiprocessing = None # type: ignore
from flake8 import defaults
from flake8 import exceptions
@@ -73,8 +72,7 @@ class Manager(object):
self.options = style_guide.options
self.checks = checker_plugins
self.jobs = self._job_count()
- self.processes = []
- self.checkers = []
+ self.checkers = [] # type: List[FileChecker]
self.statistics = {
"files": 0,
"logical lines": 0,
@@ -195,7 +193,7 @@ class Manager(object):
)
def make_checkers(self, paths=None):
- # type: (List[str]) -> None
+ # type: (Optional[List[str]]) -> None
"""Create checkers for each file."""
if paths is None:
paths = self.arguments
@@ -270,8 +268,10 @@ class Manager(object):
def run_parallel(self):
"""Run the checkers in parallel."""
- final_results = collections.defaultdict(list)
- final_statistics = collections.defaultdict(dict)
+ # fmt: off
+ final_results = collections.defaultdict(list) # type: Dict[str, List[Tuple[str, int, int, str, Optional[str]]]] # noqa: E501
+ final_statistics = collections.defaultdict(dict) # type: Dict[str, Dict[str, None]] # noqa: E501
+ # fmt: on
try:
pool = multiprocessing.Pool(self.jobs, _pool_init)
@@ -281,6 +281,7 @@ class Manager(object):
self.run_serial()
return
+ pool_closed = False
try:
pool_map = pool.imap_unordered(
_run_checks,
@@ -295,9 +296,9 @@ class Manager(object):
final_statistics[filename] = statistics
pool.close()
pool.join()
- pool = None
+ pool_closed = True
finally:
- if pool is not None:
+ if not pool_closed:
pool.terminate()
pool.join()
@@ -345,9 +346,6 @@ class Manager(object):
def stop(self):
"""Stop checking files."""
self._process_statistics()
- for proc in self.processes:
- LOG.info("Joining %s to the main process", proc.name)
- proc.join()
class FileChecker(object):
@@ -370,7 +368,9 @@ class FileChecker(object):
self.options = options
self.filename = filename
self.checks = checks
- self.results = []
+ # fmt: off
+ self.results = [] # type: List[Tuple[str, int, int, str, Optional[str]]] # noqa: E501
+ # fmt: on
self.statistics = {
"tokens": 0,
"logical lines": 0,
@@ -389,17 +389,17 @@ class FileChecker(object):
return "FileChecker for {}".format(self.filename)
def _make_processor(self):
+ # type: () -> Optional[processor.FileProcessor]
try:
return processor.FileProcessor(self.filename, self.options)
- except IOError:
+ except IOError as e:
# If we can not read the file due to an IOError (e.g., the file
# does not exist or we do not have the permissions to open it)
# then we need to format that exception for the user.
# NOTE(sigmavirus24): Historically, pep8 has always reported this
# as an E902. We probably *want* a better error code for this
# going forward.
- (exc_type, exception) = sys.exc_info()[:2]
- message = "{0}: {1}".format(exc_type.__name__, exception)
+ message = "{0}: {1}".format(type(e).__name__, e)
self.report("E902", 0, 0, message)
return None
@@ -446,7 +446,7 @@ class FileChecker(object):
token = ()
if len(exception.args) > 1:
token = exception.args[1]
- if len(token) > 2:
+ if token and len(token) > 2:
row, column = token[1:3]
else:
row, column = (1, 0)
@@ -482,14 +482,10 @@ class FileChecker(object):
"""Run all checks expecting an abstract syntax tree."""
try:
ast = self.processor.build_ast()
- except (ValueError, SyntaxError, TypeError):
- (exc_type, exception) = sys.exc_info()[:2]
- row, column = self._extract_syntax_information(exception)
+ except (ValueError, SyntaxError, TypeError) as e:
+ row, column = self._extract_syntax_information(e)
self.report(
- "E999",
- row,
- column,
- "%s: %s" % (exc_type.__name__, exception.args[0]),
+ "E999", row, column, "%s: %s" % (type(e).__name__, e.args[0])
)
return
diff --git a/src/flake8/exceptions.py b/src/flake8/exceptions.py
index bc44d8f..53ca4b7 100644
--- a/src/flake8/exceptions.py
+++ b/src/flake8/exceptions.py
@@ -1,5 +1,10 @@
"""Exception classes for all of Flake8."""
+from typing import Dict
+
+if False: # `typing.TYPE_CHECKING` was introduced in 3.5.2
+ from flake8.plugins.manager import Plugin
+
class Flake8Exception(Exception):
"""Plain Flake8 exception."""
@@ -19,13 +24,14 @@ class FailedToLoadPlugin(Flake8Exception):
FORMAT = 'Flake8 failed to load plugin "%(name)s" due to %(exc)s.'
def __init__(self, plugin, exception):
+ # type: (Plugin, Exception) -> None
"""Initialize our FailedToLoadPlugin exception."""
self.plugin = plugin
self.ep_name = self.plugin.name
self.original_exception = exception
super(FailedToLoadPlugin, self).__init__(plugin, exception)
- def __str__(self):
+ def __str__(self): # type: () -> str
"""Format our exception message."""
return self.FORMAT % {
"name": self.ep_name,
@@ -47,7 +53,7 @@ class InvalidSyntax(Flake8Exception):
self.column_number = 0
super(InvalidSyntax, self).__init__(exception)
- def __str__(self):
+ def __str__(self): # type: () -> str
"""Format our exception message."""
return self.error_message
@@ -58,6 +64,7 @@ class PluginRequestedUnknownParameters(Flake8Exception):
FORMAT = '"%(name)s" requested unknown parameters causing %(exc)s'
def __init__(self, plugin, exception):
+ # type: (Dict[str, str], Exception) -> None
"""Pop certain keyword arguments for initialization."""
self.plugin = plugin
self.original_exception = exception
@@ -65,7 +72,7 @@ class PluginRequestedUnknownParameters(Flake8Exception):
plugin, exception
)
- def __str__(self):
+ def __str__(self): # type: () -> str
"""Format our exception message."""
return self.FORMAT % {
"name": self.plugin["plugin_name"],
@@ -79,12 +86,13 @@ class PluginExecutionFailed(Flake8Exception):
FORMAT = '"%(name)s" failed during execution due to "%(exc)s"'
def __init__(self, plugin, exception):
+ # type: (Dict[str, str], Exception) -> None
"""Utilize keyword arguments for message generation."""
self.plugin = plugin
self.original_exception = exception
super(PluginExecutionFailed, self).__init__(plugin, exception)
- def __str__(self):
+ def __str__(self): # type: () -> str
"""Format our exception message."""
return self.FORMAT % {
"name": self.plugin["plugin_name"],
@@ -99,40 +107,33 @@ class HookInstallationError(Flake8Exception):
class GitHookAlreadyExists(HookInstallationError):
"""Exception raised when the git pre-commit hook file already exists."""
- def __init__(self, *args, **kwargs):
- """Initialize the path attribute."""
- self.path = kwargs.pop("path")
- super(GitHookAlreadyExists, self).__init__(*args, **kwargs)
-
- def __str__(self):
- """Provide a nice message regarding the exception."""
- msg = (
+ def __init__(self, path): # type: (str) -> None
+ """Initialize the exception message from the `path`."""
+ self.path = path
+ tmpl = (
"The Git pre-commit hook ({0}) already exists. To convince "
"Flake8 to install the hook, please remove the existing "
"hook."
)
- return msg.format(self.path)
+ super(GitHookAlreadyExists, self).__init__(tmpl.format(self.path))
class MercurialHookAlreadyExists(HookInstallationError):
"""Exception raised when a mercurial hook is already configured."""
- hook_name = None
+ hook_name = None # type: str
- def __init__(self, *args, **kwargs):
+ def __init__(self, path, value): # type: (str, str) -> None
"""Initialize the relevant attributes."""
- self.path = kwargs.pop("path")
- self.value = kwargs.pop("value")
- super(MercurialHookAlreadyExists, self).__init__(*args, **kwargs)
-
- def __str__(self):
- """Return a nicely formatted string for these errors."""
- msg = (
+ self.path = path
+ self.value = value
+ tmpl = (
'The Mercurial {0} hook already exists with "{1}" in {2}. '
"To convince Flake8 to install the hook, please remove the "
"{0} configuration from the [hooks] section of your hgrc."
)
- return msg.format(self.hook_name, self.value, self.path)
+ msg = tmpl.format(self.hook_name, self.value, self.path)
+ super(MercurialHookAlreadyExists, self).__init__(msg)
class MercurialCommitHookAlreadyExists(MercurialHookAlreadyExists):
diff --git a/src/flake8/formatting/base.py b/src/flake8/formatting/base.py
index 259d03f..388ab0e 100644
--- a/src/flake8/formatting/base.py
+++ b/src/flake8/formatting/base.py
@@ -1,6 +1,13 @@
"""The base class and interface for all formatting plugins."""
from __future__ import print_function
+import optparse
+from typing import IO, List, Optional, Tuple
+
+if False: # `typing.TYPE_CHECKING` was introduced in 3.5.2
+ from flake8.statistics import Statistics
+ from flake8.style_guide import Violation
+
class BaseFormatter(object):
"""Class defining the formatter interface.
@@ -25,6 +32,7 @@ class BaseFormatter(object):
"""
def __init__(self, options):
+ # type: (optparse.Values) -> None
"""Initialize with the options parsed from config and cli.
This also calls a hook, :meth:`after_init`, so subclasses do not need
@@ -36,33 +44,30 @@ class BaseFormatter(object):
"""
self.options = options
self.filename = options.output_file
- self.output_fd = None
+ self.output_fd = None # type: Optional[IO[str]]
self.newline = "\n"
self.after_init()
- def after_init(self):
+ def after_init(self): # type: () -> None
"""Initialize the formatter further."""
- pass
- def beginning(self, filename):
+ def beginning(self, filename): # type: (str) -> None
"""Notify the formatter that we're starting to process a file.
:param str filename:
The name of the file that Flake8 is beginning to report results
from.
"""
- pass
- def finished(self, filename):
+ def finished(self, filename): # type: (str) -> None
"""Notify the formatter that we've finished processing a file.
:param str filename:
The name of the file that Flake8 has finished reporting results
from.
"""
- pass
- def start(self):
+ def start(self): # type: () -> None
"""Prepare the formatter to receive input.
This defaults to initializing :attr:`output_fd` if :attr:`filename`
@@ -70,7 +75,7 @@ class BaseFormatter(object):
if self.filename:
self.output_fd = open(self.filename, "a")
- def handle(self, error):
+ def handle(self, error): # type: (Violation) -> None
"""Handle an error reported by Flake8.
This defaults to calling :meth:`format`, :meth:`show_source`, and
@@ -87,7 +92,7 @@ class BaseFormatter(object):
source = self.show_source(error)
self.write(line, source)
- def format(self, error):
+ def format(self, error): # type: (Violation) -> Optional[str]
"""Format an error reported by Flake8.
This method **must** be implemented by subclasses.
@@ -106,7 +111,7 @@ class BaseFormatter(object):
"Subclass of BaseFormatter did not implement" " format."
)
- def show_statistics(self, statistics):
+ def show_statistics(self, statistics): # type: (Statistics) -> None
"""Format and print the statistics."""
for error_code in statistics.error_codes():
stats_for_error_code = statistics.statistics_for(error_code)
@@ -122,6 +127,7 @@ class BaseFormatter(object):
)
def show_benchmarks(self, benchmarks):
+ # type: (List[Tuple[str, float]]) -> None
"""Format and print the benchmarks."""
# NOTE(sigmavirus24): The format strings are a little confusing, even
# to me, so here's a quick explanation:
@@ -142,7 +148,7 @@ class BaseFormatter(object):
benchmark = float_format(statistic=statistic, value=value)
self._write(benchmark)
- def show_source(self, error):
+ def show_source(self, error): # type: (Violation) -> Optional[str]
"""Show the physical line generating the error.
This also adds an indicator for the particular part of the line that
@@ -170,7 +176,7 @@ class BaseFormatter(object):
# one
return error.physical_line + pointer
- def _write(self, output):
+ def _write(self, output): # type: (str) -> None
"""Handle logic of whether to use an output file or print()."""
if self.output_fd is not None:
self.output_fd.write(output + self.newline)
@@ -178,6 +184,7 @@ class BaseFormatter(object):
print(output, end=self.newline)
def write(self, line, source):
+ # type: (Optional[str], Optional[str]) -> None
"""Write the line either to the output file or stdout.
This handles deciding whether to write to a file or print to standard
@@ -195,7 +202,7 @@ class BaseFormatter(object):
if source:
self._write(source)
- def stop(self):
+ def stop(self): # type: () -> None
"""Clean up after reporting is finished."""
if self.output_fd is not None:
self.output_fd.close()
diff --git a/src/flake8/formatting/default.py b/src/flake8/formatting/default.py
index e1061f3..55a5d01 100644
--- a/src/flake8/formatting/default.py
+++ b/src/flake8/formatting/default.py
@@ -1,6 +1,11 @@
"""Default formatting class for Flake8."""
+from typing import Optional, Set
+
from flake8.formatting import base
+if False: # `typing.TYPE_CHECKING` was introduced in 3.5.2
+ from flake8.style_guide import Violation
+
class SimpleFormatter(base.BaseFormatter):
"""Simple abstraction for Default and Pylint formatter commonality.
@@ -18,9 +23,9 @@ class SimpleFormatter(base.BaseFormatter):
"""
- error_format = None
+ error_format = None # type: str
- def format(self, error):
+ def format(self, error): # type: (Violation) -> Optional[str]
"""Format and write error out.
If an output filename is specified, write formatted errors to that
@@ -44,7 +49,7 @@ class Default(SimpleFormatter):
error_format = "%(path)s:%(row)d:%(col)d: %(code)s %(text)s"
- def after_init(self):
+ def after_init(self): # type: () -> None
"""Check for a custom format string."""
if self.options.format.lower() != "default":
self.error_format = self.options.format
@@ -61,28 +66,27 @@ class FilenameOnly(SimpleFormatter):
error_format = "%(path)s"
- def after_init(self):
+ def after_init(self): # type: () -> None
"""Initialize our set of filenames."""
- self.filenames_already_printed = set()
+ self.filenames_already_printed = set() # type: Set[str]
- def show_source(self, error):
+ def show_source(self, error): # type: (Violation) -> Optional[str]
"""Do not include the source code."""
- pass
- def format(self, error):
+ def format(self, error): # type: (Violation) -> Optional[str]
"""Ensure we only print each error once."""
if error.filename not in self.filenames_already_printed:
self.filenames_already_printed.add(error.filename)
return super(FilenameOnly, self).format(error)
+ else:
+ return None
class Nothing(base.BaseFormatter):
"""Print absolutely nothing."""
- def format(self, error):
+ def format(self, error): # type: (Violation) -> Optional[str]
"""Do nothing."""
- pass
- def show_source(self, error):
+ def show_source(self, error): # type: (Violation) -> Optional[str]
"""Do not print the source."""
- pass
diff --git a/src/flake8/main/application.py b/src/flake8/main/application.py
index c7859b9..f3c5382 100644
--- a/src/flake8/main/application.py
+++ b/src/flake8/main/application.py
@@ -2,9 +2,10 @@
from __future__ import print_function
import logging
+import optparse
import sys
import time
-from typing import List, Optional
+from typing import Dict, List, Optional, Set
import flake8
from flake8 import checker
@@ -18,11 +19,8 @@ from flake8.options import manager
from flake8.plugins import manager as plugin_manager
if False: # `typing.TYPE_CHECKING` was introduced in 3.5.2
- # fmt: off
- # `typing.Type` as introduced in 3.5.2
- from typing import Type # noqa: F401 (until flake8 3.7)
- from flake8.formatting.base import BaseFormatter # noqa: F401, E501 (until flake8 3.7)
- # fmt: on
+ from typing import Type # `typing.Type` was introduced in 3.5.2
+ from flake8.formatting.base import BaseFormatter
LOG = logging.getLogger(__name__)
@@ -32,7 +30,6 @@ class Application(object):
"""Abstract our application into a class."""
def __init__(self, program="flake8", version=flake8.__version__):
- # type: (str, str) -> None
"""Initialize our application.
:param str program:
@@ -43,7 +40,7 @@ class Application(object):
#: The timestamp when the Application instance was instantiated.
self.start_time = time.time()
#: The timestamp when the Application finished reported errors.
- self.end_time = None
+ self.end_time = None # type: float
#: The name of the program being run
self.program = program
#: The version of the program being run
@@ -56,33 +53,35 @@ class Application(object):
options.register_default_options(self.option_manager)
#: The preliminary options parsed from CLI before plugins are loaded,
#: into a :class:`optparse.Values` instance
- self.prelim_opts = None
+ self.prelim_opts = None # type: optparse.Values
#: The preliminary arguments parsed from CLI before plugins are loaded
- self.prelim_args = None
+ self.prelim_args = None # type: List[str]
#: The instance of :class:`flake8.options.config.ConfigFileFinder`
self.config_finder = None
#: The :class:`flake8.options.config.LocalPlugins` found in config
- self.local_plugins = None
+ self.local_plugins = None # type: config.LocalPlugins
#: The instance of :class:`flake8.plugins.manager.Checkers`
- self.check_plugins = None
+ self.check_plugins = None # type: plugin_manager.Checkers
+ # fmt: off
#: The instance of :class:`flake8.plugins.manager.ReportFormatters`
- self.formatting_plugins = None
+ self.formatting_plugins = None # type: plugin_manager.ReportFormatters
+ # fmt: on
#: The user-selected formatter from :attr:`formatting_plugins`
- self.formatter = None
+ self.formatter = None # type: BaseFormatter
#: The :class:`flake8.style_guide.StyleGuideManager` built from the
#: user's options
- self.guide = None
+ self.guide = None # type: style_guide.StyleGuideManager
#: The :class:`flake8.checker.Manager` that will handle running all of
#: the checks selected by the user.
- self.file_checker_manager = None
+ self.file_checker_manager = None # type: checker.Manager
#: The user-supplied options parsed into an instance of
#: :class:`optparse.Values`
- self.options = None
+ self.options = None # type: optparse.Values
#: The left over arguments that were not parsed by
#: :attr:`option_manager`
- self.args = None
+ self.args = None # type: List[str]
#: The number of errors, warnings, and other messages after running
#: flake8 and taking into account ignored errors and lines.
self.result_count = 0
@@ -96,7 +95,7 @@ class Application(object):
#: Whether the program is processing a diff or not
self.running_against_diff = False
#: The parsed diff information
- self.parsed_diff = {}
+ self.parsed_diff = {} # type: Dict[str, Set[int]]
def parse_preliminary_options_and_args(self, argv=None):
# type: (Optional[List[str]]) -> None
diff --git a/src/flake8/main/git.py b/src/flake8/main/git.py
index eed5c09..683396a 100644
--- a/src/flake8/main/git.py
+++ b/src/flake8/main/git.py
@@ -90,9 +90,7 @@ def install():
os.path.join(hooks_directory, "pre-commit")
)
if os.path.exists(pre_commit_file):
- raise exceptions.GitHookAlreadyExists(
- "File already exists", path=pre_commit_file
- )
+ raise exceptions.GitHookAlreadyExists(path=pre_commit_file)
executable = get_executable()
diff --git a/src/flake8/main/mercurial.py b/src/flake8/main/mercurial.py
index 65ef8ce..4ea410a 100644
--- a/src/flake8/main/mercurial.py
+++ b/src/flake8/main/mercurial.py
@@ -7,6 +7,7 @@
import configparser
import os
import subprocess
+from typing import Set
from flake8 import exceptions as exc
@@ -101,7 +102,7 @@ def install():
def get_filenames_from(repository, kwargs):
- seen_filenames = set()
+ seen_filenames = set() # type: Set[str]
node = kwargs["node"]
for revision in range(repository[node], len(repository)):
for filename in repository[revision].files():
diff --git a/src/flake8/main/setuptools_command.py b/src/flake8/main/setuptools_command.py
index 1b57c0d..55dc378 100644
--- a/src/flake8/main/setuptools_command.py
+++ b/src/flake8/main/setuptools_command.py
@@ -1,6 +1,6 @@
"""The logic for Flake8's integration with setuptools."""
import os
-from typing import List
+from typing import List, Tuple
import setuptools
@@ -48,7 +48,7 @@ class Flake8(setuptools.Command):
def package_files(self):
"""Collect the files/dirs included in the registered modules."""
- seen_package_directories = ()
+ seen_package_directories = () # type: Tuple[str, ...]
directories = self.distribution.package_dir or {}
empty_directory_exists = "" in directories
packages = self.distribution.packages or []
diff --git a/src/flake8/main/vcs.py b/src/flake8/main/vcs.py
index f7f139a..398643c 100644
--- a/src/flake8/main/vcs.py
+++ b/src/flake8/main/vcs.py
@@ -17,7 +17,7 @@ def install(option, option_string, value, parser):
For more information about the callback signature, see:
https://docs.python.org/3/library/optparse.html#optparse-option-callbacks
"""
- installer = _INSTALLERS.get(value)
+ installer = _INSTALLERS[value]
errored = False
successful = False
try:
diff --git a/src/flake8/options/config.py b/src/flake8/options/config.py
index 468341b..026618c 100644
--- a/src/flake8/options/config.py
+++ b/src/flake8/options/config.py
@@ -4,6 +4,7 @@ import configparser
import logging
import os.path
import sys
+from typing import Dict, List, Sequence, Tuple, Union
from flake8 import utils
@@ -54,12 +55,15 @@ class ConfigFileFinder(object):
# caches to avoid double-reading config files
self._local_configs = None
- self._local_found_files = []
+ self._local_found_files = [] # type: List[str]
self._user_config = None
- self._cli_configs = {}
+ # fmt: off
+ self._cli_configs = {} # type: Dict[str, configparser.RawConfigParser]
+ # fmt: on
@staticmethod
def _read_config(files):
+ # type: (Union[Sequence[str], str]) -> Tuple[configparser.RawConfigParser, List[str]] # noqa: E501
config = configparser.RawConfigParser()
if isinstance(files, (str, type(u""))):
files = [files]
@@ -83,6 +87,7 @@ class ConfigFileFinder(object):
return (config, found_files)
def cli_config(self, files):
+ # type: (str) -> configparser.RawConfigParser
"""Read and parse the config file specified on the command-line."""
if files not in self._cli_configs:
config, found_files = self._read_config(files)
@@ -379,7 +384,7 @@ def get_local_plugins(config_finder, cli_config=None, isolated=False):
raw_paths = utils.parse_comma_separated_list(
config.get(section, "paths").strip()
)
- norm_paths = []
+ norm_paths = [] # type: List[str]
for base_dir in base_dirs:
norm_paths.extend(
path
diff --git a/src/flake8/options/manager.py b/src/flake8/options/manager.py
index 0ded13a..5d21127 100644
--- a/src/flake8/options/manager.py
+++ b/src/flake8/options/manager.py
@@ -2,6 +2,7 @@
import collections
import logging
import optparse # pylint: disable=deprecated-module
+from typing import Any, Callable, Dict, List, Optional, Set
from flake8 import utils
@@ -108,7 +109,7 @@ class Option(object):
self.comma_separated_list = comma_separated_list
self.normalize_paths = normalize_paths
- self.config_name = None
+ self.config_name = None # type: Optional[str]
if parse_from_config:
if not long_option_name:
raise ValueError(
@@ -143,7 +144,7 @@ class Option(object):
"""Normalize the value based on the option configuration."""
if self.normalize_paths:
# Decide whether to parse a list of paths or a single path
- normalize = utils.normalize_path
+ normalize = utils.normalize_path # type: Callable[..., Any]
if self.comma_separated_list:
normalize = utils.normalize_paths
return normalize(value, *normalize_args)
@@ -200,13 +201,13 @@ class OptionManager(object):
self.parser = optparse.OptionParser(
prog=prog, version=version, usage=usage
)
- self.config_options_dict = {}
- self.options = []
+ self.config_options_dict = {} # type: Dict[str, Option]
+ self.options = [] # type: List[Option]
self.program_name = prog
self.version = version
- self.registered_plugins = set()
- self.extended_default_ignore = set()
- self.extended_default_select = set()
+ self.registered_plugins = set() # type: Set[PluginVersion]
+ self.extended_default_ignore = set() # type: Set[str]
+ self.extended_default_select = set() # type: Set[str]
@staticmethod
def format_plugin(plugin):
@@ -231,6 +232,7 @@ class OptionManager(object):
self.options.append(option)
if option.parse_from_config:
name = option.config_name
+ assert name is not None # nosec (for mypy)
self.config_options_dict[name] = option
self.config_options_dict[name.replace("_", "-")] = option
LOG.debug('Registered option "%s".', option)
@@ -326,7 +328,7 @@ class OptionManager(object):
values = self.parser.get_default_values()
self.parser.rargs = rargs
- self.parser.largs = largs = []
+ largs = [] # type: List[str]
self.parser.values = values
while rargs:
@@ -340,7 +342,8 @@ class OptionManager(object):
optparse.BadOptionError,
optparse.OptionValueError,
) as err:
- self.parser.largs.append(err.opt_str)
+ # TODO: https://gitlab.com/pycqa/flake8/issues/541
+ largs.append(err.opt_str) # type: ignore
args = largs + rargs
options, xargs = self.parser.check_values(values, args)
diff --git a/src/flake8/plugins/manager.py b/src/flake8/plugins/manager.py
index 303c0f9..1554aa2 100644
--- a/src/flake8/plugins/manager.py
+++ b/src/flake8/plugins/manager.py
@@ -1,17 +1,12 @@
"""Plugin loading and management logic and classes."""
import logging
-import sys
+from typing import Any, Dict, List, Set
import entrypoints
from flake8 import exceptions
from flake8 import utils
-if sys.version_info >= (3, 3):
- import collections.abc as collections_abc
-else:
- import collections as collections_abc
-
LOG = logging.getLogger(__name__)
__all__ = ("Checkers", "Plugin", "PluginManager", "ReportFormatters")
@@ -37,7 +32,7 @@ class Plugin(object):
self.name = name
self.entry_point = entry_point
self.local = local
- self._plugin = None
+ self._plugin = None # type: Any
self._parameters = None
self._parameter_names = None
self._group = None
@@ -236,8 +231,8 @@ class PluginManager(object): # pylint: disable=too-few-public-methods
Plugins from config (as "X = path.to:Plugin" strings).
"""
self.namespace = namespace
- self.plugins = {}
- self.names = []
+ self.plugins = {} # type: Dict[str, Plugin]
+ self.names = [] # type: List[str]
self._load_local_plugins(local_plugins or [])
self._load_entrypoint_plugins()
@@ -310,7 +305,7 @@ class PluginManager(object): # pylint: disable=too-few-public-methods
:rtype:
tuple
"""
- plugins_seen = set()
+ plugins_seen = set() # type: Set[str]
for entry_point_name in self.names:
plugin = self.plugins[entry_point_name]
plugin_name = plugin.plugin_name
@@ -345,7 +340,7 @@ def version_for(plugin):
class PluginTypeManager(object):
"""Parent class for most of the specific plugin types."""
- namespace = None
+ namespace = None # type: str
def __init__(self, local_plugins=None):
"""Initialize the plugin type's manager.
@@ -398,9 +393,7 @@ class PluginTypeManager(object):
def _generate_call_function(method_name, optmanager, *args, **kwargs):
def generated_function(plugin): # noqa: D105
method = getattr(plugin, method_name, None)
- if method is not None and isinstance(
- method, collections_abc.Callable
- ):
+ if method is not None and callable(method):
return method(optmanager, *args, **kwargs)
return generated_function
diff --git a/src/flake8/plugins/pyflakes.py b/src/flake8/plugins/pyflakes.py
index 2f233c4..9be0c81 100644
--- a/src/flake8/plugins/pyflakes.py
+++ b/src/flake8/plugins/pyflakes.py
@@ -10,6 +10,7 @@ except ImportError:
else:
demandimport.disable()
import os
+from typing import List
import pyflakes
import pyflakes.checker
@@ -59,8 +60,8 @@ class FlakesChecker(pyflakes.checker.Checker):
name = "pyflakes"
version = pyflakes.__version__
with_doctest = False
- include_in_doctest = []
- exclude_from_doctest = []
+ include_in_doctest = [] # type: List[str]
+ exclude_from_doctest = [] # type: List[str]
def __init__(self, tree, file_tokens, filename):
"""Initialize the PyFlakes plugin with an AST tree and filename."""
diff --git a/src/flake8/processor.py b/src/flake8/processor.py
index 09185e5..239f908 100644
--- a/src/flake8/processor.py
+++ b/src/flake8/processor.py
@@ -3,7 +3,7 @@ import contextlib
import logging
import sys
import tokenize
-from typing import List
+from typing import Any, Dict, List, Tuple
import flake8
from flake8 import defaults
@@ -66,9 +66,9 @@ class FileProcessor(object):
#: Number of blank lines
self.blank_lines = 0
#: Checker states for each plugin?
- self._checker_states = {}
+ self._checker_states = {} # type: Dict[str, Dict[Any, Any]]
#: Current checker state
- self.checker_state = None
+ self.checker_state = None # type: Dict[Any, Any]
#: User provided option for hang closing
self.hang_closing = options.hang_closing
#: Character used for indentation
@@ -93,8 +93,10 @@ class FileProcessor(object):
self.previous_logical = ""
#: Previous unindented (i.e. top-level) logical line
self.previous_unindented_logical_line = ""
+ # fmt: off
#: Current set of tokens
- self.tokens = []
+ self.tokens = [] # type: List[Tuple[int, str, Tuple[int, int], Tuple[int, int], str]] # noqa: E501
+ # fmt: on
#: Total number of lines in the file
self.total_lines = len(self.lines)
#: Verbosity level of Flake8
diff --git a/src/flake8/statistics.py b/src/flake8/statistics.py
index f2131b5..100227f 100644
--- a/src/flake8/statistics.py
+++ b/src/flake8/statistics.py
@@ -1,15 +1,19 @@
"""Statistic collection logic for Flake8."""
import collections
+from typing import Dict, Generator, List, Optional
+
+if False: # `typing.TYPE_CHECKING` was introduced in 3.5.2
+ from flake8.style_guide import Violation
class Statistics(object):
"""Manager of aggregated statistics for a run of Flake8."""
- def __init__(self):
+ def __init__(self): # type: () -> None
"""Initialize the underlying dictionary for our statistics."""
- self._store = {}
+ self._store = {} # type: Dict[Key, Statistic]
- def error_codes(self):
+ def error_codes(self): # type: () -> List[str]
"""Return all unique error codes stored.
:returns:
@@ -19,7 +23,7 @@ class Statistics(object):
"""
return sorted({key.code for key in self._store})
- def record(self, error):
+ def record(self, error): # type: (Violation) -> None
"""Add the fact that the error was seen in the file.
:param error:
@@ -34,6 +38,7 @@ class Statistics(object):
self._store[key].increment()
def statistics_for(self, prefix, filename=None):
+ # type: (str, Optional[str]) -> Generator[Statistic, None, None]
"""Generate statistics for the prefix and filename.
If you have a :class:`Statistics` object that has recorded errors,
@@ -74,11 +79,11 @@ class Key(collections.namedtuple("Key", ["filename", "code"])):
__slots__ = ()
@classmethod
- def create_from(cls, error):
+ def create_from(cls, error): # type: (Violation) -> Key
"""Create a Key from :class:`flake8.style_guide.Violation`."""
return cls(filename=error.filename, code=error.code)
- def matches(self, prefix, filename):
+ def matches(self, prefix, filename): # type: (str, Optional[str]) -> bool
"""Determine if this key matches some constraints.
:param str prefix:
@@ -106,6 +111,7 @@ class Statistic(object):
"""
def __init__(self, error_code, filename, message, count):
+ # type: (str, str, str, int) -> None
"""Initialize our Statistic."""
self.error_code = error_code
self.filename = filename
@@ -113,7 +119,7 @@ class Statistic(object):
self.count = count
@classmethod
- def create_from(cls, error):
+ def create_from(cls, error): # type: (Violation) -> Statistic
"""Create a Statistic from a :class:`flake8.style_guide.Violation`."""
return cls(
error_code=error.code,
@@ -122,6 +128,6 @@ class Statistic(object):
count=0,
)
- def increment(self):
+ def increment(self): # type: () -> None
"""Increment the number of times we've seen this error in this file."""
self.count += 1
diff --git a/src/flake8/style_guide.py b/src/flake8/style_guide.py
index c617f5a..b644f8b 100644
--- a/src/flake8/style_guide.py
+++ b/src/flake8/style_guide.py
@@ -7,7 +7,7 @@ import itertools
import linecache
import logging
import sys
-from typing import Optional, Union
+from typing import Dict, List, Optional, Set, Union
from flake8 import defaults
from flake8 import statistics
@@ -68,7 +68,7 @@ class Violation(_Violation):
"""Class representing a violation reported by Flake8."""
def is_inline_ignored(self, disable_noqa):
- # type: (Violation) -> bool
+ # type: (bool) -> bool
"""Determine if a comment has been added to ignore this line.
:param bool disable_noqa:
@@ -154,7 +154,7 @@ class DecisionEngine(object):
def __init__(self, options):
"""Initialize the engine."""
- self.cache = {}
+ self.cache = {} # type: Dict[str, Decision]
self.selected = tuple(options.select)
self.extended_selected = tuple(
sorted(options.extended_default_select, reverse=True)
@@ -332,7 +332,7 @@ class StyleGuideManager(object):
self.formatter = formatter
self.stats = statistics.Statistics()
self.decider = decider or DecisionEngine(options)
- self.style_guides = []
+ self.style_guides = [] # type: List[StyleGuide]
self.default_style_guide = StyleGuide(
options, formatter, self.stats, decider=decider
)
@@ -390,7 +390,7 @@ class StyleGuideManager(object):
text,
physical_line=None,
):
- # type: (str, str, int, int, str, Optional[str]) -> int
+ # type: (str, str, int, Optional[int], str, Optional[str]) -> int
"""Handle an error reported by a check.
:param str code:
@@ -419,6 +419,7 @@ class StyleGuideManager(object):
)
def add_diff_ranges(self, diffinfo):
+ # type: (Dict[str, Set[int]]) -> None
"""Update the StyleGuides to filter out information not in the diff.
This provides information to the underlying StyleGuides so that only
@@ -448,7 +449,7 @@ class StyleGuide(object):
self.filename = filename
if self.filename:
self.filename = utils.normalize_path(self.filename)
- self._parsed_diff = {}
+ self._parsed_diff = {} # type: Dict[str, Set[int]]
def __repr__(self):
"""Make it easier to debug which StyleGuide we're using."""
@@ -514,7 +515,7 @@ class StyleGuide(object):
text,
physical_line=None,
):
- # type: (str, str, int, int, str, Optional[str]) -> int
+ # type: (str, str, int, Optional[int], str, Optional[str]) -> int
"""Handle an error reported by a check.
:param str code:
@@ -567,6 +568,7 @@ class StyleGuide(object):
return 0
def add_diff_ranges(self, diffinfo):
+ # type: (Dict[str, Set[int]]) -> None
"""Update the StyleGuide to filter out information not in the diff.
This provides information to the StyleGuide so that only the errors
diff --git a/src/flake8/utils.py b/src/flake8/utils.py
index b95d4db..f822388 100644
--- a/src/flake8/utils.py
+++ b/src/flake8/utils.py
@@ -3,13 +3,14 @@ import collections
import fnmatch as _fnmatch
import inspect
import io
+import logging
import os
import platform
import re
import sys
import tokenize
-from typing import Callable, Dict, Generator, List, Pattern, Sequence, Set
-from typing import Tuple, Union
+from typing import Callable, Dict, Generator, List, Optional, Pattern
+from typing import Sequence, Set, Tuple, Union
from flake8 import exceptions
@@ -19,6 +20,7 @@ if False: # `typing.TYPE_CHECKING` was introduced in 3.5.2
DIFF_HUNK_REGEXP = re.compile(r"^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@.*$")
COMMA_SEPARATED_LIST_RE = re.compile(r"[,\s]")
LOCAL_PLUGIN_LIST_RE = re.compile(r"[,\t\n\r\f\v]")
+string_types = (str, type(u""))
def parse_comma_separated_list(value, regexp=COMMA_SEPARATED_LIST_RE):
@@ -40,7 +42,7 @@ def parse_comma_separated_list(value, regexp=COMMA_SEPARATED_LIST_RE):
if not value:
return []
- if not isinstance(value, (list, tuple)):
+ if isinstance(value, string_types):
value = regexp.split(value)
item_gen = (item.strip() for item in value)
@@ -77,8 +79,8 @@ def _tokenize_files_to_codes_mapping(value):
return tokens
-def parse_files_to_codes_mapping(value): # noqa: C901
- # type: (Union[Sequence[str], str]) -> List[Tuple[List[str], List[str]]]
+def parse_files_to_codes_mapping(value_): # noqa: C901
+ # type: (Union[Sequence[str], str]) -> List[Tuple[str, List[str]]]
"""Parse a files-to-codes maping.
A files-to-codes mapping a sequence of values specified as
@@ -88,20 +90,22 @@ def parse_files_to_codes_mapping(value): # noqa: C901
:param value: String to be parsed and normalized.
:type value: str
"""
- if isinstance(value, (list, tuple)):
- value = "\n".join(value)
+ if not isinstance(value_, string_types):
+ value = "\n".join(value_)
+ else:
+ value = value_
- ret = []
+ ret = [] # type: List[Tuple[str, List[str]]]
if not value.strip():
return ret
class State:
seen_sep = True
seen_colon = False
- filenames = []
- codes = []
+ filenames = [] # type: List[str]
+ codes = [] # type: List[str]
- def _reset():
+ def _reset(): # type: () -> None
if State.codes:
for filename in State.filenames:
ret.append((filename, State.codes))
@@ -110,11 +114,8 @@ def parse_files_to_codes_mapping(value): # noqa: C901
State.filenames = []
State.codes = []
- def _unexpected_token():
- # type: () -> exceptions.ExecutionError
-
- def _indent(s):
- # type: (str) -> str
+ def _unexpected_token(): # type: () -> exceptions.ExecutionError
+ def _indent(s): # type: (str) -> str
return " " + s.strip().replace("\n", "\n ")
return exceptions.ExecutionError(
@@ -192,7 +193,7 @@ def normalize_path(path, parent=os.curdir):
return path.rstrip(separator + alternate_separator)
-def _stdin_get_value_py3():
+def _stdin_get_value_py3(): # type: () -> io.StringIO
stdin_value = sys.stdin.buffer.read()
fd = io.BytesIO(stdin_value)
try:
@@ -211,13 +212,13 @@ def stdin_get_value():
stdin_value = io.BytesIO(sys.stdin.read())
else:
stdin_value = _stdin_get_value_py3()
- stdin_get_value.cached_stdin = stdin_value
- cached_value = stdin_get_value.cached_stdin
+ stdin_get_value.cached_stdin = stdin_value # type: ignore
+ cached_value = stdin_get_value.cached_stdin # type: ignore
return cached_value.getvalue()
def parse_unified_diff(diff=None):
- # type: (str) -> Dict[str, Set[int]]
+ # type: (Optional[str]) -> Dict[str, Set[int]]
"""Parse the unified diff passed on stdin.
:returns:
@@ -231,7 +232,7 @@ def parse_unified_diff(diff=None):
number_of_rows = None
current_path = None
- parsed_paths = collections.defaultdict(set)
+ parsed_paths = collections.defaultdict(set) # type: Dict[str, Set[int]]
for line in diff.splitlines():
if number_of_rows:
# NOTE(sigmavirus24): Below we use a slice because stdin may be
@@ -279,6 +280,7 @@ def parse_unified_diff(diff=None):
1 if not group else int(group)
for group in hunk_match.groups()
]
+ assert current_path is not None # nosec (for mypy)
parsed_paths[current_path].update(
range(row, row + number_of_rows)
)
@@ -338,12 +340,12 @@ def is_using_stdin(paths):
return "-" in paths
-def _default_predicate(*args):
+def _default_predicate(*args): # type: (*str) -> bool
return False
def filenames_from(arg, predicate=None):
- # type: (str, Callable[[str], bool]) -> Generator
+ # type: (str, Optional[Callable[[str], bool]]) -> Generator[str, None, None] # noqa: E501
"""Generate filenames from an argument.
:param str arg:
@@ -384,8 +386,8 @@ def filenames_from(arg, predicate=None):
yield arg
-def fnmatch(filename, patterns, default=True):
- # type: (str, List[str], bool) -> bool
+def fnmatch(filename, patterns):
+ # type: (str, List[str]) -> bool
"""Wrap :func:`fnmatch.fnmatch` to add some functionality.
:param str filename:
@@ -399,7 +401,7 @@ def fnmatch(filename, patterns, default=True):
``default`` if patterns is empty.
"""
if not patterns:
- return default
+ return True
return any(_fnmatch.fnmatch(filename, pattern) for pattern in patterns)
@@ -452,6 +454,7 @@ def parameters_for(plugin):
def matches_filename(path, patterns, log_message, logger):
+ # type: (str, List[str], str, logging.Logger) -> bool
"""Use fnmatch to discern if a path exists in patterns.
:param str path:
@@ -483,7 +486,7 @@ def matches_filename(path, patterns, log_message, logger):
return match
-def get_python_version():
+def get_python_version(): # type: () -> str
"""Find and format the python implementation and version.
:returns:
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..f7ac891
--- /dev/null
+++ b/tests/__init__.py
@@ -0,0 +1 @@
+"""This is here because mypy doesn't understand PEP 420."""
diff --git a/tests/integration/test_checker.py b/tests/integration/test_checker.py
index 0a3703c..a17e5cd 100644
--- a/tests/integration/test_checker.py
+++ b/tests/integration/test_checker.py
@@ -216,9 +216,7 @@ def test_report_order(results, expected_order):
# _handle_results is the first place which gets the sorted result
# Should something non-private be mocked instead?
- handler = mock.Mock()
- handler.side_effect = count_side_effect
- manager._handle_results = handler
-
- assert manager.report() == (len(results), len(results))
- handler.assert_called_once_with('placeholder', expected_results)
+ handler = mock.Mock(side_effect=count_side_effect)
+ with mock.patch.object(manager, '_handle_results', handler):
+ assert manager.report() == (len(results), len(results))
+ handler.assert_called_once_with('placeholder', expected_results)
diff --git a/tests/unit/test_base_formatter.py b/tests/unit/test_base_formatter.py
index f28ab80..ec051a8 100644
--- a/tests/unit/test_base_formatter.py
+++ b/tests/unit/test_base_formatter.py
@@ -44,7 +44,9 @@ def test_format_needs_to_be_implemented():
"""Ensure BaseFormatter#format raises a NotImplementedError."""
formatter = base.BaseFormatter(options())
with pytest.raises(NotImplementedError):
- formatter.format('foo')
+ formatter.format(
+ style_guide.Violation('A000', 'file.py', 1, 1, 'error text', None)
+ )
def test_show_source_returns_nothing_when_not_showing_source():
@@ -73,6 +75,7 @@ def test_show_source_updates_physical_line_appropriately(line, column):
formatter = base.BaseFormatter(options(show_source=True))
error = style_guide.Violation('A000', 'file.py', 1, column, 'error', line)
output = formatter.show_source(error)
+ assert output
_, pointer = output.rsplit('\n', 1)
assert pointer.count(' ') == (column - 1)
diff --git a/tests/unit/test_debug.py b/tests/unit/test_debug.py
index 0d1f903..2b7995d 100644
--- a/tests/unit/test_debug.py
+++ b/tests/unit/test_debug.py
@@ -72,8 +72,7 @@ def test_information(system, pyversion, pyimpl):
@mock.patch('json.dumps', return_value='{}')
def test_print_information_no_plugins(dumps, information, print_mock):
"""Verify we print and exit only when we have plugins."""
- plugins = []
- option_manager = mock.Mock(registered_plugins=set(plugins))
+ option_manager = mock.Mock(registered_plugins=set())
assert debug.print_information(
None, None, None, None, option_manager=option_manager,
) is None
diff --git a/tests/unit/test_decision_engine.py b/tests/unit/test_decision_engine.py
index fd87a45..635c9d8 100644
--- a/tests/unit/test_decision_engine.py
+++ b/tests/unit/test_decision_engine.py
@@ -74,11 +74,10 @@ def test_was_selected_selects_errors(select_list, enable_extensions,
def test_was_selected_implicitly_selects_errors():
"""Verify we detect users implicitly selecting an error."""
- select_list = []
error_code = 'E121'
decider = style_guide.DecisionEngine(
create_options(
- select=select_list,
+ select=[],
extended_default_select=['E'],
),
)
diff --git a/tests/unit/test_option.py b/tests/unit/test_option.py
index eb4f95d..e931d13 100644
--- a/tests/unit/test_option.py
+++ b/tests/unit/test_option.py
@@ -75,4 +75,4 @@ def test_config_name_needs_long_option_name():
def test_dest_is_not_overridden():
"""Show that we do not override custom destinations."""
opt = manager.Option('-s', '--short', dest='something_not_short')
- assert opt.dest == 'something_not_short'
+ assert opt.dest == 'something_not_short' # type: ignore
diff --git a/tests/unit/test_plugin_type_manager.py b/tests/unit/test_plugin_type_manager.py
index e1392a4..4174c58 100644
--- a/tests/unit/test_plugin_type_manager.py
+++ b/tests/unit/test_plugin_type_manager.py
@@ -1,17 +1,10 @@
"""Tests for flake8.plugins.manager.PluginTypeManager."""
-import sys
-
import mock
import pytest
from flake8 import exceptions
from flake8.plugins import manager
-if sys.version_info >= (3, 3):
- import collections.abc as collections_abc
-else:
- import collections as collections_abc
-
TEST_NAMESPACE = "testing.plugin-type-manager"
@@ -91,7 +84,7 @@ def test_generate_call_function():
'method_name', optmanager,
)
- assert isinstance(func, collections_abc.Callable)
+ assert callable(func)
assert func(plugin) is optmanager
@@ -168,15 +161,14 @@ def test_provide_options(PluginManager): # noqa: N803
PluginManager.return_value = create_mapping_manager_mock(plugins)
optmanager = object()
options = object()
- extra_args = []
type_mgr = FakeTestType()
- type_mgr.provide_options(optmanager, options, extra_args)
+ type_mgr.provide_options(optmanager, options, [])
for plugin in plugins:
plugin.provide_options.assert_called_with(optmanager,
options,
- extra_args)
+ [])
@mock.patch('flake8.plugins.manager.PluginManager')
diff --git a/tests/unit/test_statistics.py b/tests/unit/test_statistics.py
index 31d6276..16896f0 100644
--- a/tests/unit/test_statistics.py
+++ b/tests/unit/test_statistics.py
@@ -82,7 +82,7 @@ def test_recording_statistics():
assert isinstance(key, stats.Key)
assert isinstance(value, stats.Statistic)
- assert storage[(DEFAULT_FILENAME, DEFAULT_ERROR_CODE)].count == 1
+ assert storage[stats.Key(DEFAULT_FILENAME, DEFAULT_ERROR_CODE)].count == 1
def test_statistics_for_single_record():
diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py
index dcbf8b8..9f6925e 100644
--- a/tests/unit/test_utils.py
+++ b/tests/unit/test_utils.py
@@ -163,12 +163,6 @@ def test_fnmatch(filename, patterns, expected):
assert utils.fnmatch(filename, patterns) is expected
-def test_fnmatch_returns_the_default_with_empty_default():
- """The default parameter should be returned when no patterns are given."""
- sentinel = object()
- assert utils.fnmatch('file.py', [], default=sentinel) is sentinel
-
-
def test_filenames_from_a_directory():
"""Verify that filenames_from walks a directory."""
filenames = list(utils.filenames_from('src/flake8/'))
diff --git a/tox.ini b/tox.ini
index f39c687..464f3a4 100644
--- a/tox.ini
+++ b/tox.ini
@@ -70,13 +70,12 @@ deps =
commands =
doc8 docs/source/
-[testenv:mypy]
+[testenv:pre-commit]
basepython = python3
skip_install = true
-deps =
- mypy
+deps = pre-commit
commands =
- mypy src
+ pre-commit run --all-files --show-diff-on-failure
[testenv:bandit]
basepython = python3