diff --git a/.flake8 b/.flake8 index 814db9c3..e840e08d 100644 --- a/.flake8 +++ b/.flake8 @@ -21,5 +21,6 @@ max-line-length = 80 max-complexity = 12 select = B,C,E,F,W,Y,B9 per-file-ignores = - *.py: E203, E501, W503, W291, W293 - *.pyi: E301, E302, E305, E501, E701, E704, W503 + # TODO: reenable some of the bugbear checks + *.py: B950, E203, E501, W503, W291, W293 + *.pyi: B902, B903, B950, E301, E302, E305, E501, E701, E704, W503 diff --git a/CHANGELOG.md b/CHANGELOG.md index 46fcae1d..0e5a0a46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## Unreleased + +* Add support for flake8 5.0.0. + ## 22.8.0 New error codes: diff --git a/pyi.py b/pyi.py index 2c07b2b7..cccf244f 100644 --- a/pyi.py +++ b/pyi.py @@ -18,17 +18,11 @@ from pathlib import Path from typing import TYPE_CHECKING, Any, ClassVar, NamedTuple -from flake8 import checker # type: ignore[import] +import flake8 # type: ignore[import] +from flake8 import checker from flake8.options.manager import OptionManager # type: ignore[import] -from flake8.plugins.manager import Plugin # type: ignore[import] from flake8.plugins.pyflakes import FlakesChecker # type: ignore[import] -from pyflakes.checker import ( - PY2, - ClassDefinition, - ClassScope, - FunctionScope, - ModuleScope, -) +from pyflakes.checker import ClassDefinition, ClassScope, FunctionScope, ModuleScope if sys.version_info >= (3, 9): from ast import unparse @@ -48,6 +42,19 @@ def unparse(node: ast.AST) -> str: __version__ = "22.8.0" LOG = logging.getLogger("flake8.pyi") +FLAKE8_MAJOR_VERSION = flake8.__version_info__[0] + +if FLAKE8_MAJOR_VERSION < 5: + import warnings + + warnings.warn( + ( + "flake8-pyi will drop support for running with flake8 < 5.0.0 " + "in a future version. This will not happen until November 2022 " + "at the earliest." + ), + category=FutureWarning, + ) class Error(NamedTuple): @@ -223,9 +230,8 @@ def CLASSDEF(self, node: ast.ClassDef) -> None: self.handleNode(decorator, node) for baseNode in node.bases: self.deferHandleNode(baseNode, node) - if not PY2: - for keywordNode in node.keywords: - self.deferHandleNode(keywordNode, node) + for keywordNode in node.keywords: + self.deferHandleNode(keywordNode, node) self.pushScope(ClassScope) # doctest does not process doctest within a doctest # classes within classes are processed. @@ -249,20 +255,26 @@ def handleNodeDelete(self, node: ast.AST) -> None: class PyiAwareFileChecker(checker.FileChecker): - def run_check(self, plugin: Plugin, **kwargs: Any) -> Any: + def run_check(self, plugin, **kwargs: Any) -> Any: if self.filename == "-": filename = self.options.stdin_display_name else: filename = self.filename - if filename.endswith(".pyi") and plugin["plugin"] == FlakesChecker: - LOG.info( - "Replacing FlakesChecker with PyiAwareFlakesChecker while " - "checking %r", - filename, + if filename.endswith(".pyi"): + log_msg = ( + f"Replacing FlakesChecker with PyiAwareFlakesChecker while " + f"checking {filename!r}" ) - plugin = dict(plugin) - plugin["plugin"] = PyiAwareFlakesChecker + if FLAKE8_MAJOR_VERSION < 5: + if plugin["plugin"] is FlakesChecker: + LOG.info(log_msg) + plugin = dict(plugin) + plugin["plugin"] = PyiAwareFlakesChecker + else: + if plugin.obj is FlakesChecker: + LOG.info(log_msg) + plugin = plugin._replace(obj=PyiAwareFlakesChecker) return super().run_check(plugin, **kwargs) diff --git a/setup.py b/setup.py index 800308fc..36221b32 100644 --- a/setup.py +++ b/setup.py @@ -38,7 +38,7 @@ zip_safe=False, python_requires=">=3.7", install_requires=[ - "flake8 >= 3.2.1, < 5.0.0", + "flake8 >= 3.2.1, < 6.0.0", "pyflakes >= 2.1.1", 'ast-decompiler <1.0; python_version < "3.9"', ], diff --git a/tests/typevar.pyi b/tests/typevar.pyi index 9a3313d8..de643327 100644 --- a/tests/typevar.pyi +++ b/tests/typevar.pyi @@ -37,7 +37,7 @@ class BadClass: def bad_class_method(cls: type[_S], arg: int) -> _S: ... # Y019 Use "_typeshed.Self" instead of "_S", e.g. "def bad_class_method(cls: type[Self], arg: int) -> Self: ..." class GoodClass: - def __new__(cls: type[Self], *args: list[int], **kwargs: set[str]) -> Self: ... + def __new__(cls: type[Self], *args: list[int], **kwargs: set[str]) -> Self: ... def good_instance_method_1(self: Self, arg: bytes) -> Self: ... def good_instance_method_2(self, arg1: _S, arg2: _S) -> _S: ... @classmethod