Skip to content

Commit 012a10c

Browse files
authored
chore: more ruff checks enabled (#285)
2 parents 2e8e273 + 7aab5b0 commit 012a10c

23 files changed

Lines changed: 208 additions & 152 deletions

.ruff.toml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,28 +5,48 @@ exclude = ["tools/to_schemastore.py", "tests/invalid-examples"]
55
# --- Linting config ---
66
[lint]
77
extend-select = [
8+
"ARG", # flake8-unused-arguments
89
"B", # flake8-bugbear
10+
"BLE", # flake8-blind-except
911
"C4", # flake8-comprehensions
1012
"C90", # McCabe cyclomatic complexity
1113
"DTZ", # flake8-datetimez
14+
"EM", # flake8-errmsg
1215
"EXE", # flake8-executable
16+
"FA", # flake8-future-annotations
1317
"FBT", # flake8-boolean-trap
18+
"FLY", # flynt
19+
"FURB", # refurb
1420
"I", # isort
1521
"ICN", # flake8-import-conventions
1622
"INT", # flake8-gettext
23+
"ISC", # flake8-implicit-str-concat
24+
"LOG", # flake8-logging
25+
"PERF", # Perflint
26+
"PGH", # pygrep-hooks
27+
"PIE", # flake8-pie
1728
"PL", # Pylint
29+
"PT", # flake8-pytest-style
1830
"PYI", # flake8-pyi
31+
"Q", # flake8-quotes
1932
"RET", # flake8-return
33+
"RSE", # flake8-raise
2034
"RUF", # Ruff-specific rules
2135
"S", # flake8-bandit
2236
"SIM", # flake8-simplify
37+
"SLOT", # flake8-slots
2338
"T10", # flake8-debugger
39+
"TC", # flake8-type-checking
2440
"TCH", # flake8-type-checking
41+
"TRY", # tryceratops
2542
"UP", # pyupgrade
2643
"YTT", # flake8-2020
2744
]
2845
ignore = [
2946
"PLC0415", # import at top of file
47+
"RSE102", # parens on exception raise
48+
"S101", # assert is used by mypy and pytest
49+
"TRY401", # redundant logging message, TODO check
3050
]
3151

3252
[lint.per-file-ignores]

docs/_gendocs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
__location__ = Path(__file__).parent
2020

2121

22-
def gen_stubs(module_dir: str, output_dir: str):
22+
def gen_stubs(module_dir: str, output_dir: str): # noqa: ARG001
2323
shutil.rmtree(output_dir, ignore_errors=True) # Always start fresh
2424
out = Path(output_dir)
2525
out.mkdir(parents=True, exist_ok=True)

src/validate_pyproject/_tomllib.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
)
1313
from toml import loads # type: ignore[no-redef]
1414
except ImportError as ex:
15-
raise ImportError("Please install `tomli` (TOML parser)") from ex
15+
msg = "Please install `tomli` (TOML parser)"
16+
raise ImportError(msg) from ex
1617

1718

1819
__all__ = [

src/validate_pyproject/api.py

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
Retrieve JSON schemas for validating dicts representing a ``pyproject.toml`` file.
33
"""
44

5+
from __future__ import annotations
6+
57
import json
68
import logging
79
import sys
@@ -11,14 +13,10 @@
1113
from types import MappingProxyType, ModuleType
1214
from typing import (
1315
Callable,
14-
Dict,
1516
Iterator,
1617
Mapping,
17-
Optional,
1818
Sequence,
19-
Tuple,
2019
TypeVar,
21-
Union,
2220
)
2321

2422
import fastjsonschema as FJS
@@ -37,7 +35,7 @@
3735
if sys.version_info >= (3, 9): # pragma: no cover
3836
from importlib.resources import files
3937

40-
def read_text(package: Union[str, ModuleType], resource: str) -> str:
38+
def read_text(package: str | ModuleType, resource: str) -> str:
4139
""":meta private:"""
4240
return files(package).joinpath(resource).read_text(encoding="utf-8")
4341

@@ -94,8 +92,8 @@ class SchemaRegistry(Mapping[str, Schema]):
9492
:meta private: (low level detail)
9593
"""
9694

97-
def __init__(self, plugins: Sequence["PluginProtocol"] = ()):
98-
self._schemas: Dict[str, Tuple[str, str, Schema]] = {}
95+
def __init__(self, plugins: Sequence[PluginProtocol] = ()):
96+
self._schemas: dict[str, tuple[str, str, Schema]] = {}
9997
# (which part of the TOML, who defines, schema)
10098

10199
top_level = typing.cast("dict", load(TOP_LEVEL_SCHEMA)) # Make it mutable
@@ -114,7 +112,7 @@ def __init__(self, plugins: Sequence["PluginProtocol"] = ()):
114112
# Add tools using Plugins
115113
for plugin in plugins:
116114
if plugin.tool:
117-
allow_overwrite: Optional[str] = None
115+
allow_overwrite: str | None = None
118116
if plugin.tool in tool_properties:
119117
_logger.warning(f"{plugin} overwrites `tool.{plugin.tool}` schema")
120118
allow_overwrite = plugin.schema.get("$id")
@@ -150,7 +148,7 @@ def _ensure_compatibility(
150148
self,
151149
reference: str,
152150
schema: Schema,
153-
allow_overwrite: Optional[str] = None,
151+
allow_overwrite: str | None = None,
154152
) -> Schema:
155153
if "$id" not in schema or not schema["$id"]:
156154
raise errors.SchemaMissingId(reference or "<extra>")
@@ -208,19 +206,19 @@ def __getitem__(self, key: str) -> Callable[[str], Schema]:
208206

209207

210208
class Validator:
211-
_plugins: Sequence["PluginProtocol"]
209+
_plugins: Sequence[PluginProtocol]
212210

213211
def __init__(
214212
self,
215-
plugins: Union[Sequence["PluginProtocol"], AllPlugins] = ALL_PLUGINS,
213+
plugins: Sequence[PluginProtocol] | AllPlugins = ALL_PLUGINS,
216214
format_validators: Mapping[str, FormatValidationFn] = FORMAT_FUNCTIONS,
217215
extra_validations: Sequence[ValidationFn] = EXTRA_VALIDATIONS,
218216
*,
219-
extra_plugins: Sequence["PluginProtocol"] = (),
217+
extra_plugins: Sequence[PluginProtocol] = (),
220218
):
221-
self._code_cache: Optional[str] = None
222-
self._cache: Optional[ValidationFn] = None
223-
self._schema: Optional[Schema] = None
219+
self._code_cache: str | None = None
220+
self._cache: ValidationFn | None = None
221+
self._schema: Schema | None = None
224222

225223
# Let's make the following options readonly
226224
self._format_validators = MappingProxyType(format_validators)

src/validate_pyproject/caching.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
# This module is intentionally kept minimal,
22
# so that it can be imported without triggering imports outside stdlib.
3+
from __future__ import annotations
4+
35
import hashlib
4-
import io
56
import logging
67
import os
78
from pathlib import Path
8-
from typing import Callable, Optional, Union
9+
from typing import TYPE_CHECKING, Callable, Union
10+
11+
if TYPE_CHECKING:
12+
import io
913

1014
PathLike = Union[str, "os.PathLike[str]"]
1115
_logger = logging.getLogger(__name__)
@@ -14,8 +18,8 @@
1418
def as_file(
1519
fn: Callable[[str], io.StringIO],
1620
arg: str,
17-
cache_dir: Optional[PathLike] = None,
18-
) -> Union[io.StringIO, io.BufferedReader]:
21+
cache_dir: PathLike | None = None,
22+
) -> io.StringIO | io.BufferedReader:
1923
"""
2024
Cache the result of calling ``fn(arg)`` into a file inside ``cache_dir``.
2125
The file name is derived from ``arg``.
@@ -36,7 +40,7 @@ def as_file(
3640
return open(cache_path, "rb")
3741

3842

39-
def path_for(arbitrary_id: str, cache: Optional[PathLike] = None) -> Optional[Path]:
43+
def path_for(arbitrary_id: str, cache: PathLike | None = None) -> Path | None:
4044
cache_dir = cache or os.getenv("VALIDATE_PYPROJECT_CACHE_REMOTE")
4145
if not cache_dir:
4246
return None

src/validate_pyproject/cli.py

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,22 @@
44

55
# ruff: noqa: C408
66
# Unnecessary `dict` call (rewrite as a literal)
7+
from __future__ import annotations
78

89
import argparse
9-
import io
1010
import json
1111
import logging
1212
import sys
1313
from contextlib import contextmanager
1414
from itertools import chain
1515
from textwrap import dedent, wrap
1616
from typing import (
17+
TYPE_CHECKING,
1718
Callable,
18-
Dict,
1919
Generator,
2020
Iterator,
21-
List,
2221
NamedTuple,
2322
Sequence,
24-
Tuple,
25-
Type,
2623
TypeVar,
2724
)
2825

@@ -34,6 +31,9 @@
3431
from .plugins import list_from_entry_points as list_plugins_from_entry_points
3532
from .remote import RemotePlugin, load_store
3633

34+
if TYPE_CHECKING:
35+
import io
36+
3737
_logger = logging.getLogger(__package__)
3838
T = TypeVar("T", bound=NamedTuple)
3939

@@ -53,7 +53,7 @@ def critical_logging() -> Generator[None, None, None]:
5353

5454
_STDIN = argparse.FileType("r")("-")
5555

56-
META: Dict[str, dict] = {
56+
META: dict[str, dict] = {
5757
"version": dict(
5858
flags=("-V", "--version"),
5959
action="version",
@@ -116,15 +116,15 @@ def critical_logging() -> Generator[None, None, None]:
116116

117117

118118
class CliParams(NamedTuple):
119-
input_file: List[io.TextIOBase]
120-
plugins: List[PluginWrapper]
121-
tool: List[str]
119+
input_file: list[io.TextIOBase]
120+
plugins: list[PluginWrapper]
121+
tool: list[str]
122122
store: str
123123
loglevel: int = logging.WARNING
124124
dump_json: bool = False
125125

126126

127-
def __meta__(plugins: Sequence[PluginProtocol]) -> Dict[str, dict]:
127+
def __meta__(plugins: Sequence[PluginProtocol]) -> dict[str, dict]:
128128
"""'Hyper parameters' to instruct :mod:`argparse` how to create the CLI"""
129129
meta = {k: v.copy() for k, v in META.items()}
130130
meta["enable"]["choices"] = {p.tool for p in plugins}
@@ -137,8 +137,8 @@ def parse_args(
137137
args: Sequence[str],
138138
plugins: Sequence[PluginProtocol],
139139
description: str = "Validate a given TOML file",
140-
get_parser_spec: Callable[[Sequence[PluginProtocol]], Dict[str, dict]] = __meta__,
141-
params_class: Type[T] = CliParams, # type: ignore[assignment]
140+
get_parser_spec: Callable[[Sequence[PluginProtocol]], dict[str, dict]] = __meta__,
141+
params_class: type[T] = CliParams, # type: ignore[assignment]
142142
) -> T:
143143
"""Parse command line parameters
144144
@@ -174,7 +174,7 @@ def select_plugins(
174174
plugins: Sequence[Plugins],
175175
enabled: Sequence[str] = (),
176176
disabled: Sequence[str] = (),
177-
) -> List[Plugins]:
177+
) -> list[Plugins]:
178178
available = list(plugins)
179179
if enabled:
180180
available = [p for p in available if p.tool in enabled]
@@ -200,13 +200,13 @@ def exceptions2exit() -> Generator[None, None, None]:
200200
except _ExceptionGroup as group:
201201
for prefix, ex in group:
202202
print(prefix)
203-
_logger.error(str(ex) + "\n")
203+
_logger.exception(str(ex) + "\n")
204204
raise SystemExit(1) from None
205205
except _REGULAR_EXCEPTIONS as ex:
206-
_logger.error(str(ex))
206+
_logger.exception(str(ex))
207207
raise SystemExit(1) from None
208208
except Exception as ex: # pragma: no cover
209-
_logger.error(f"{ex.__class__.__name__}: {ex}\n")
209+
_logger.exception(f"{ex.__class__.__name__}: {ex}\n")
210210
_logger.debug("Please check the following information:", exc_info=True)
211211
raise SystemExit(1) from None
212212

@@ -234,7 +234,7 @@ def run(args: Sequence[str] = ()) -> int:
234234
for file in params.input_file:
235235
try:
236236
_run_on_file(validator, params, file)
237-
except _REGULAR_EXCEPTIONS as ex:
237+
except _REGULAR_EXCEPTIONS as ex: # noqa: PERF203
238238
exceptions.add(f"Invalid {_format_file(file)}", ex)
239239

240240
exceptions.raise_if_any()
@@ -262,7 +262,7 @@ class Formatter(argparse.RawTextHelpFormatter):
262262
# order to create our own formatter, we are left no choice other then overwrite a
263263
# "private" method considered to be an implementation detail.
264264

265-
def _split_lines(self, text: str, width: int) -> List[str]:
265+
def _split_lines(self, text: str, width: int) -> list[str]:
266266
return list(chain.from_iterable(wrap(x, width) for x in text.splitlines()))
267267

268268

@@ -289,7 +289,7 @@ def _format_file(file: io.TextIOBase) -> str:
289289

290290

291291
class _ExceptionGroup(Exception):
292-
_members: List[Tuple[str, Exception]]
292+
_members: list[tuple[str, Exception]]
293293

294294
def __init__(self) -> None:
295295
self._members = []
@@ -298,7 +298,7 @@ def __init__(self) -> None:
298298
def add(self, prefix: str, ex: Exception) -> None:
299299
self._members.append((prefix, ex))
300300

301-
def __iter__(self) -> Iterator[Tuple[str, Exception]]:
301+
def __iter__(self) -> Iterator[tuple[str, Exception]]:
302302
return iter(self._members)
303303

304304
def raise_if_any(self) -> None:

0 commit comments

Comments
 (0)