diff --git a/.flake8 b/.flake8 index 58152bb95..5c527abb5 100644 --- a/.flake8 +++ b/.flake8 @@ -1,44 +1,19 @@ -# The following rules come from plugins that are not used by typeshed. -# Since typeshed stubs them, they can still be expected to be found in a -# developer's venv for intellisense and reference reasons -# A flake8-builtins -# B flake8-bugbear -# D flake8-docstrings -# N8 pep8-naming -# SIM flake8-simplify -# RST flake8-rst-docstrings -# TYP flake8-typing-imports - -# The following rules are incompatible with or enforced by Black: -# E203 whitespace before ':' -- scripts only -# E301 expected 1 blank line -# E302 expected 2 blank lines -# E305 expected 2 blank lines -# E501 line too long -# E701 Multiple statements on one line (colon) -- disallows "..." on the same line - -# Some rules are considered irrelevant to stub files: -# F401 imported but unused -- does not recognize re-exports -# https://github.com/PyCQA/pyflakes/issues/474 - -# Rules that are out of the control of stub authors: -# E741 ambiguous variable name -# F403 import *' used; unable to detect undefined names -# F405 defined from star imports - [flake8] -extend-ignore = A, B, D, N8, SIM, RST, TYP, E301, E302, E305, E501, E701 +# NQA: Ruff won't warn about redundant `# noqa: Y` +# Y: Flake8 is only used to run flake8-pyi, everything else is in Ruff +# F821: Until https://github.com/astral-sh/ruff/issues/3011 is fixed, we need flake8-pyi's monkeypatching +select = NQA, Y, F821 +# Ignore rules normally excluded by default +extend-ignore = Y090 per-file-ignores = - *.py: E203 - *.pyi: E741, F401, F403, F405 - # Since typing.pyi defines "overload" this is not recognized by Flake8 as typing.overload. - # Unfortunately, Flake8 does not allow to "noqa" just a specific error inside the file itself. - # https://github.com/PyCQA/flake8/issues/1079 - # F811 redefinition of unused '...' - stdlib/typing.pyi: E741, F401, F403, F405, F811 - # Generated protobuf files include docstrings, - # and import some things from typing_extensions that could be imported from typing - *_pb2.pyi: E741, F401, F403, F405, Y021, Y023, Y026, Y053, Y054 + # We should only need to noqa Y and F821 codes in .pyi files + *.py: NQA + # Generated protobuf files: + # Y021: Include docstrings + # Y023: Alias typing as typing_extensions + # Y026: Have implicit type aliases + # Y053: have literals >50 characters long + *_pb2.pyi: Y021, Y023, Y026, Y053 exclude = .venv*,.git noqa_require_code = true diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6736544d1..198ba6eb6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -25,7 +25,7 @@ repos: # Very few rules are useful to run on our test cases; # we explicitly enumerate them here: name: Run ruff on the test cases - args: ["--exit-non-zero-on-fix", "--select=FA,I", "--no-force-exclude", "--unsafe-fixes"] + args: ["--exit-non-zero-on-fix", "--select=FA,I,RUF100", "--no-force-exclude", "--unsafe-fixes"] files: '.*test_cases/.+\.py$' - repo: https://github.com/psf/black-pre-commit-mirror rev: 24.1.1 # must match requirements-tests.txt diff --git a/pyproject.toml b/pyproject.toml index 9239e77a5..fa3befac8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,13 +32,21 @@ exclude = [ # are invoked via separate runs of ruff in pre-commit: # see our .pre-commit-config.yaml file for details exclude = ["**/test_cases/**/*.py"] +# We still use flake8-pyi and flake8-noqa to check these (see .flake8 config file); +# tell ruff not to flag these as e.g. "unused noqa comments" +external = ["F821", "NQA", "Y"] select = [ "B", # flake8-bugbear "FA", # flake8-future-annotations "I", # isort + "RUF", # Ruff-specific and unused-noqa "UP", # pyupgrade - # Only enable rules that have safe autofixes: - "F401", # Remove unused imports + # Flake8 base rules + "E", # pycodestyle Error + "F", # Pyflakes + "W", # pycodestyle Warning + # PYI: only enable rules that always autofix, avoids duplicate # noqa with flake8-pyi + # See https://github.com/plinss/flake8-noqa/issues/22 "PYI009", # use `...`, not `pass`, in empty class bodies "PYI010", # function bodies must be empty "PYI012", # class bodies must not contain `pass` @@ -51,8 +59,27 @@ select = [ "PYI058", # use `Iterator` as the return type for `__iter__` methods ] ignore = [ + ### + # Rules that can conflict with the formatter (Black) + # https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules + ### + "E111", # indentation-with-invalid-multiple + "E114", # indentation-with-invalid-multiple-comment + "E117", # over-indented + "W191", # tab-indentation + ### + # Rules we don't want or don't agree with + ### # Slower and more verbose https://github.com/astral-sh/ruff/issues/7871 "UP038", # Use `X | Y` in `isinstance` call instead of `(X, Y)` + # Used for direct, non-subclass type comparison, for example: `type(val) is str` + # see https://github.com/astral-sh/ruff/issues/6465 + "E721", # Do not compare types, use `isinstance()` + ### + # False-positives, but already checked by type-checkers + ### + # Ruff doesn't support multi-file analysis yet: https://github.com/astral-sh/ruff/issues/5295 + "RUF013", # PEP 484 prohibits implicit `Optional` ] [tool.ruff.lint.per-file-ignores] @@ -60,6 +87,15 @@ ignore = [ # Most flake8-bugbear rules don't apply for third-party stubs like typeshed. # B033 could be slightly useful but Ruff doesn't have per-file select "B", # flake8-bugbear + # Rules that are out of the control of stub authors: + "E501", # Line too long + "E741", # ambiguous variable name + "F403", # `from . import *` used; unable to detect undefined names + # False positives in stubs + "F821", # Undefined name: https://github.com/astral-sh/ruff/issues/3011 + # Stubs can sometimes re-export entire modules. + # Issues with using a star-imported name will be caught by type-checkers. + "F405", # may be undefined, or defined from star imports ] [tool.ruff.lint.isort] diff --git a/stdlib/builtins.pyi b/stdlib/builtins.pyi index 99aac7382..b4765b26c 100644 --- a/stdlib/builtins.pyi +++ b/stdlib/builtins.pyi @@ -99,7 +99,7 @@ class object: @property def __class__(self) -> type[Self]: ... @__class__.setter - def __class__(self, type: type[object], /) -> None: ... # noqa: F811 + def __class__(self, type: type[object], /) -> None: ... def __init__(self) -> None: ... def __new__(cls) -> Self: ... # N.B. `object.__setattr__` and `object.__delattr__` are heavily special-cased by type checkers. diff --git a/stdlib/socket.pyi b/stdlib/socket.pyi index 12d5b4187..cdbd70533 100644 --- a/stdlib/socket.pyi +++ b/stdlib/socket.pyi @@ -803,7 +803,7 @@ def getfqdn(name: str = "") -> str: ... if sys.version_info >= (3, 11): def create_connection( address: tuple[str | None, int], - timeout: float | None = ..., # noqa: F811 + timeout: float | None = ..., source_address: _Address | None = None, *, all_errors: bool = False, @@ -811,7 +811,7 @@ if sys.version_info >= (3, 11): else: def create_connection( - address: tuple[str | None, int], timeout: float | None = ..., source_address: _Address | None = None # noqa: F811 + address: tuple[str | None, int], timeout: float | None = ..., source_address: _Address | None = None ) -> socket: ... def has_dualstack_ipv6() -> bool: ... diff --git a/stdlib/typing.pyi b/stdlib/typing.pyi index ecf1ed739..c42acd4bf 100644 --- a/stdlib/typing.pyi +++ b/stdlib/typing.pyi @@ -1,3 +1,5 @@ +# Since this module defines "overload" it is not recognized by Ruff as typing.overload +# ruff: noqa: F811 # TODO: The collections import is required, otherwise mypy crashes. # https://github.com/python/mypy/issues/16744 import collections # noqa: F401 # pyright: ignore diff --git a/stubs/WebOb/@tests/test_cases/check_wsgify.py b/stubs/WebOb/@tests/test_cases/check_wsgify.py index ec4b01dd0..bc9f2a9de 100644 --- a/stubs/WebOb/@tests/test_cases/check_wsgify.py +++ b/stubs/WebOb/@tests/test_cases/check_wsgify.py @@ -1,7 +1,7 @@ from __future__ import annotations from _typeshed.wsgi import StartResponse, WSGIApplication, WSGIEnvironment -from collections.abc import Iterable # noqa: F401 +from collections.abc import Iterable from typing_extensions import assert_type from webob.dec import _AnyResponse, wsgify diff --git a/test_cases/stdlib/typing/check_all.py b/test_cases/stdlib/typing/check_all.py index 3c3f56813..44eb548e0 100644 --- a/test_cases/stdlib/typing/check_all.py +++ b/test_cases/stdlib/typing/check_all.py @@ -5,10 +5,10 @@ This tests that star imports work when using "all += " syntax. from __future__ import annotations import sys -from typing import * # noqa: F403 -from zipfile import * # noqa: F403 +from typing import * +from zipfile import * if sys.version_info >= (3, 9): - x: Annotated[int, 42] # noqa: F405 + x: Annotated[int, 42] -p: Path # noqa: F405 +p: Path