Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

replace flake8-eradicate with ruff ... and enable a ton of more lint rules, fixing a couple of them. #196

Merged
merged 1 commit into from
May 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.ruff_cache
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
Expand Down
22 changes: 8 additions & 14 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,16 @@ repos:
- id: trailing-whitespace
args: ['--markdown-linebreak-ext=md,markdown']

- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.0.269
hooks:
- id: ruff

- repo: https://github.com/PyCQA/flake8
rev: 6.0.0
hooks:
- id: flake8
# this doesn't seem to work, pyi files don't get checked with --all-files
types_or: [python, pyi]
language_version: python3
additional_dependencies:
Expand All @@ -86,26 +92,14 @@ repos:
- flake8-comprehensions
- flake8-datetimez
- flake8-docstrings
- flake8-mutable
- flake8-noqa
- flake8-mutable # not official supported by ruff
- flake8-pie
- flake8-pyi
- flake8-pytest-style
- flake8-return
- flake8-simplify
- flake8-type-checking

# Pinned to flake8 5.0.4 since flake8-eradicate is not updated to work with flake8 6
# https://github.com/wemake-services/flake8-eradicate/pull/271
- repo: https://github.com/PyCQA/flake8
rev: 5.0.4
hooks:
- id: flake8
name: flake8-eradicate
args: [--select=E800]
language_version: python3
additional_dependencies:
- flake8-eradicate
# all other are

- repo: https://github.com/jumanjihouse/pre-commit-hook-yamlfmt
rev: 0.2.2
Expand Down
4 changes: 2 additions & 2 deletions flake8_trio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ def get_matching_codes(
all_codes: set[str] = {
err_code
for err_class in (*ERROR_CLASSES, *ERROR_CLASSES_CST)
for err_code in err_class.error_codes.keys() # type: ignore[attr-defined]
for err_code in err_class.error_codes # type: ignore[attr-defined]
if len(err_code) == 7 # exclude e.g. TRIO103_anyio_trio
}

Expand Down Expand Up @@ -367,7 +367,7 @@ def parse_trio200_dict(raw_value: str) -> dict[str, str]:
# if we raise it as ValueError
raise ArgumentTypeError(
f"Invalid number ({len(split_values)-1}) of splitter "
+ f"tokens {splitter!r} in {value!r}"
f"tokens {splitter!r} in {value!r}"
)
res[split_values[0]] = split_values[1]
return res
16 changes: 9 additions & 7 deletions flake8_trio/visitors/flake8triovisitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ def library_str(self) -> str:

def add_library(self, name: str) -> None:
if name not in self.__state.library:
self.__state.library = self.__state.library + (name,)
self.__state.library = (*self.__state.library, name)


class Flake8TrioVisitor_cst(cst.CSTTransformer, ABC):
Expand Down Expand Up @@ -200,8 +200,9 @@ def restore_state(self, node: cst.CSTNode):
def is_noqa(self, node: cst.CSTNode, code: str):
if self.options.disable_noqa:
return False
pos = self.get_metadata(PositionProvider, node).start
noqas = self.noqas.get(pos.line)
# pyright + get_metadata is acting up
pos = self.get_metadata(PositionProvider, node).start # type: ignore
noqas = self.noqas.get(pos.line) # type: ignore
return noqas is not None and (noqas == set() or code in noqas)

def error(
Expand All @@ -223,13 +224,14 @@ def error(
if self.is_noqa(node, error_code):
return False

pos = self.get_metadata(PositionProvider, node).start
# pyright + get_metadata is acting up
pos = self.get_metadata(PositionProvider, node).start # type: ignore
self.__state.problems.append(
Error(
# 7 == len('TRIO...'), so alt messages raise the original code
error_code[:7],
pos.line,
pos.column,
pos.line, # type: ignore
pos.column, # type: ignore
self.error_codes[error_code],
*args,
)
Expand All @@ -253,4 +255,4 @@ def library(self) -> tuple[str, ...]:

def add_library(self, name: str) -> None:
if name not in self.__state.library:
self.__state.library = self.__state.library + (name,)
self.__state.library = (*self.__state.library, name)
2 changes: 1 addition & 1 deletion flake8_trio/visitors/visitor100.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from typing import Any

import libcst as cst
import libcst as cst # noqa: TCH002
import libcst.matchers as m

from .flake8triovisitor import Flake8TrioVisitor_cst
Expand Down
16 changes: 9 additions & 7 deletions flake8_trio/visitors/visitor91x.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,9 +284,9 @@ def visit_FunctionDef(self, node: cst.FunctionDef) -> bool:
# this should improve performance on codebases with many sync functions
return any(m.findall(node, m.FunctionDef(asynchronous=m.Asynchronous())))

pos = self.get_metadata(PositionProvider, node).start
pos = self.get_metadata(PositionProvider, node).start # type: ignore
self.uncheckpointed_statements = {
Statement("function definition", pos.line, pos.column)
Statement("function definition", pos.line, pos.column) # type: ignore
}
return True

Expand Down Expand Up @@ -419,8 +419,10 @@ def leave_Yield(
self.add_statement = self.checkpoint_statement()

# mark as requiring checkpoint after
pos = self.get_metadata(PositionProvider, original_node).start
self.uncheckpointed_statements = {Statement("yield", pos.line, pos.column)}
pos = self.get_metadata(PositionProvider, original_node).start # type: ignore
self.uncheckpointed_statements = {
Statement("yield", pos.line, pos.column) # type: ignore
}
# return original to avoid problems with identity equality
assert original_node.deep_equals(updated_node)
return original_node
Expand All @@ -442,9 +444,9 @@ def visit_Try(self, node: cst.Try):
)
# yields inside `try` can always be uncheckpointed
for inner_node in m.findall(node.body, m.Yield()):
pos = self.get_metadata(PositionProvider, inner_node).start
pos = self.get_metadata(PositionProvider, inner_node).start # type: ignore
self.try_state.body_uncheckpointed_statements.add(
Statement("yield", pos.line, pos.column)
Statement("yield", pos.line, pos.column) # type: ignore
)

def leave_Try_body(self, node: cst.Try):
Expand Down Expand Up @@ -657,7 +659,7 @@ def leave_While_orelse(self, node: cst.For | cst.While):
else:
# We may exit from:
# orelse (covering: no body, body until continue, and all body)
# break
# `break`
self.uncheckpointed_statements.update(
self.loop_state.uncheckpointed_before_break
)
Expand Down
3 changes: 2 additions & 1 deletion flake8_trio/visitors/visitor_utility.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import re
from typing import TYPE_CHECKING, Any

import libcst as cst
import libcst.matchers as m
from libcst.metadata import PositionProvider

Expand All @@ -17,6 +16,8 @@
if TYPE_CHECKING:
from re import Match

import libcst as cst


@utility_visitor
class VisitorTypeTracker(Flake8TrioVisitor):
Expand Down
72 changes: 72 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,75 @@ reportShadowedImports = true
reportUninitializedInstanceVariable = true
reportUnusedCallResult = false
strict = ["*.py", "tests/*.py", "flake8_trio/**/*.py"]

[tool.ruff]
extend-exclude = [
".*",
"tests/eval_files/*",
"tests/autofix_files/*"
]
ignore = [
"COM", # flake8-comma, handled by black
"ANN", # annotations, handled by pyright/mypy
"T20", # flake8-print
"TID252", # relative imports from parent modules https://github.com/Zac-HD/flake8-trio/pull/196#discussion_r1200413372
"D101",
"D102",
"D103",
"D105",
"D106",
"D107",
"S101",
"D203", # one-blank-line-before-class
"D213", # multi-line-summary-second-line
"EM101", # exception must not use a string literal
"EM102", # exception must not use an f-string literal
'PGH001', # No builtin `eval()` allowed
'N802', # function name should be lowercase - not an option with inheritance
'PTH123', # `open()` should be replaced by `Path.open()`
'PYI021', # docstring in stub
'S603', # `subprocess` call: check for execution of untrusted input
'N815', # Variable `visit_AsyncWith` in class scope should not be mixedCase
'PLR091', # Too many return statements/branches/arguments
'C901', # function is too complex
# maybe should be ignored in-place
'N806', # Variable `MyDict` in function should be lowercase
# --- maybe should be fixed / ignored in-place ---
'ARG001', # Unused function argument
'ARG002', # Unused method argument
'B904', # Within an `except` clause, raise exceptions with `raise ... from err` or `raise ... from None` to distinguish them from errors in exception handling
'B905', # zip without explicit strict parameter
'BLE001', # Do not catch blind exception: `Exception`
'FBT001', # Boolean positional arg in function definition
'FBT002', # Boolean default value in function definition
'N801', # Class name {} should use CapWords convention
'PGH003', # Use specific rule codes when ignoring type issues
'PLR2004', # Magic value used in comparison
'PLW2901', # Outer `for` loop variable `err` overwritten by inner `for` loop target
'PTH118', # `os.path.join()` should be replaced by `Path` with `/` operator
'S607', # Starting a process with a partial executable path
'SLF001', # Private member accessed: `_tree`
'TD002', # missing author in TODO
'TD003', # missing issue link in TODO
'TRY003', # Avoid specifying long messages outside the exception class
'TRY200', # Use `raise from` to specify exception cause
'TRY201', # Use `raise` without specifying exception name
# enable if flake8/plugins are removed
'PGH004', # Use specific rule codes when using `noqa`
'RUF100' # Unused `noqa` - enable only if flake8 is no longer used
]
line-length = 90
select = ["ALL"]
target-version = "py39"

[tool.ruff.per-file-ignores]
# docstrings, and arguments we can't modify
"*.pyi" = ["D", 'FBT001', 'PLR0913']
# imports
"flake8_trio/visitors/__init__.py" = [
"F401",
"E402"
]
# visitor_utility contains comments specifying how it parses noqa comments, which get
# parsed as noqa comments
"flake8_trio/visitors/visitor_utility.py" = ["RUF100", "PGH004"]
2 changes: 1 addition & 1 deletion tests/test_changelog_and_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ def ensure_tagged() -> None:
def update_version() -> None:
# If we've added a new version to the changelog, update __version__ to match
last_version = next(iter(get_releases()))
if VERSION != last_version:
if last_version != VERSION:
INIT_FILE = ROOT_PATH / "flake8_trio" / "__init__.py"
subs = (f'__version__ = "{VERSION}"', f'__version__ = "{last_version}"')
INIT_FILE.write_text(INIT_FILE.read_text().replace(*subs))
Expand Down
4 changes: 2 additions & 2 deletions tests/test_decorator.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ def test_pep614():


def test_command_line_1(capfd: pytest.CaptureFixture[str]):
Application().run(common_flags + ["--no-checkpoint-warning-decorators=app.route"])
Application().run([*common_flags, "--no-checkpoint-warning-decorators=app.route"])
assert capfd.readouterr() == ("", "")


Expand All @@ -115,7 +115,7 @@ def test_command_line_1(capfd: pytest.CaptureFixture[str]):


def test_command_line_2(capfd: pytest.CaptureFixture[str]):
Application().run(common_flags + ["--no-checkpoint-warning-decorators=app"])
Application().run([*common_flags, "--no-checkpoint-warning-decorators=app"])
assert capfd.readouterr() == (expected_out, "")


Expand Down
11 changes: 5 additions & 6 deletions tests/test_flake8_trio.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@
import tokenize
import unittest
from argparse import ArgumentParser
from collections import deque
from collections import defaultdict, deque
from dataclasses import dataclass, fields
from pathlib import Path
from typing import TYPE_CHECKING, Any, DefaultDict
from typing import TYPE_CHECKING, Any

import libcst as cst
import pytest
Expand Down Expand Up @@ -66,7 +66,7 @@ def check_version(test: str):
ERROR_CODES: dict[str, Flake8TrioVisitor] = {
err_code: err_class # type: ignore[misc]
for err_class in (*ERROR_CLASSES, *ERROR_CLASSES_CST)
for err_code in err_class.error_codes.keys() # type: ignore[attr-defined]
for err_code in err_class.error_codes # type: ignore[attr-defined]
}


Expand Down Expand Up @@ -500,11 +500,11 @@ def print_first_diff(errors: Sequence[Error], expected: Sequence[Error]):

def assert_correct_lines_and_codes(errors: Iterable[Error], expected: Iterable[Error]):
"""Check that errors are on correct lines."""
MyDict = DefaultDict[int, DefaultDict[str, int]] # TypeAlias
MyDict = defaultdict[int, defaultdict[str, int]] # TypeAlias

all_lines = sorted({e.line for e in (*errors, *expected)})

error_dict: MyDict = DefaultDict(lambda: DefaultDict(int))
error_dict: MyDict = defaultdict(lambda: defaultdict(int))
expected_dict = copy.deepcopy(error_dict)

# populate dicts with number of errors per line
Expand Down Expand Up @@ -622,7 +622,6 @@ def test_line_numbers_match_end_result():
plugin = Plugin.from_source(text)
initialize_options(plugin, args=["--enable=TRIO100,TRIO115", "--autofix=TRIO100"])
errors = tuple(plugin.run())
plugin.module.code
assert errors[1].line != plugin.module.code.split("\n").index("trio.sleep(0)") + 1


Expand Down
Empty file modified tests/test_messages_documented.py
100644 → 100755
Empty file.
2 changes: 1 addition & 1 deletion typings/flake8/options/manager.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Generated for flake8 5, so OptionManager signature is incorrect for flake8 6
import argparse
import enum
from collections.abc import Callable, Mapping, Sequence
from typing import Any, TypeAlias
from typing import Any

from flake8.plugins.finder import Plugins

Expand Down