diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index af98d7a5f..e8de18be9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -22,7 +22,7 @@ repos: exclude: "^tests" - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.3 + rev: v0.1.4 hooks: - id: ruff args: ["--fix", "--show-fixes"] diff --git a/docs/ext/conftabs.py b/docs/ext/conftabs.py index 26ebd8b89..eb3083d16 100644 --- a/docs/ext/conftabs.py +++ b/docs/ext/conftabs.py @@ -2,6 +2,7 @@ import ast import textwrap +from typing import Any from docutils import nodes from docutils.statemachine import StringList @@ -12,7 +13,7 @@ class ConfTabs(SphinxDirective): required_arguments = 2 final_argument_whitespace = True - def run(self): + def run(self) -> list[nodes.Node]: name, result = self.arguments env_name = f"SKBUILD_{name.replace('.', '_').replace('-', '_').upper()}" value_result = ast.literal_eval(result) @@ -82,7 +83,7 @@ def run(self): return [content] -def setup(app): +def setup(app: Any) -> dict[str, Any]: app.add_directive("conftabs", ConfTabs) return { diff --git a/pyproject.toml b/pyproject.toml index cc0dbc8d6..f45991f11 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -203,48 +203,59 @@ ignore = ["W002"] # Triggers on __init__.py's [tool.ruff] src = ["src"] -exclude = [] +exclude = [] # Required due to "build" module [tool.ruff.lint] -select = [ - "E", "F", "W", # flake8 - "B", # flake8-bugbear - "I", # isort +extend-select = [ + "ANN", # flake8-annotations "ARG", # flake8-unused-arguments + "B", # flake8-bugbear "C4", # flake8-comprehensions "EM", # flake8-errmsg + "FBT", # flake8-boolean-trap + "FLY", # flynt + "I", # isort "ICN", # flake8-import-conventions "ISC", # flake8-implicit-str-concat + "N", # flake8-naming + "PERF", # perflint "PGH", # pygrep-hooks "PIE", # flake8-pie "PL", # pylint "PT", # flake8-pytest-style "PTH", # flake8-use-pathlib + "PYI", # flake8-pyi "RET", # flake8-return "RUF", # Ruff-specific + "S", # eval -> literal_eval "SIM", # flake8-simplify - "TID251", # flake8-tidy-imports.banned-api "T20", # flake8-print - "UP", # pyupgrade - "YTT", # flake8-2020 - "ANN204", # Add -> None to __init__ - "S307", # eval -> literal_eval "TCH", # flake8-type-checking - "PERF", # perflint - "FLY", # flynt + "TID251", # flake8-tidy-imports.banned-api "TRY", # tryceratops - "FBT", # flake8-boolean-trap - "PYI", # flake8-pyi + "UP", # pyupgrade + "YTT", # flake8-2020 + # (in preview till 0.2.0) "FURB", # refurb ] ignore = [ - "PLR", # Design rules for pylint "PLE1205", # Format check doesn't work with our custom logger - "E501", # Line too long "PT004", # Incorrect, just usefixtures instead. "RUF009", # Too easy to get a false positive "PYI025", # Wants Set to be renamed AbstractSet + "ISC001", # Conflicts with formatter + "PLR0911", # Too many return statements + "PLR0912", # Too many branches + "PLR0913", # Too many arguments + "PLR0915", # Too many local statements + "PLR2004", # Magic value used in comparison + "PLC0415", # Import should be at top of file + "ANN101", # Missing type annotation for `self` in method + "ANN102", # Missing type annotation for `cls` in classmethod + "ANN401", # Disallow dynamically typed expressions + "S101", # Use of assert detected + "S603", # subprocess untrusted input + "S607", # subprocess call ] -unfixable = ["T20", "F841"] typing-modules = ["scikit_build_core._compat.typing"] [tool.ruff.lint.flake8-tidy-imports.banned-api] @@ -272,13 +283,15 @@ typing-modules = ["scikit_build_core._compat.typing"] [tool.ruff.lint.per-file-ignores] -"tests/**" = ["T20"] +"tests/**" = ["T20", "ANN"] "noxfile.py" = ["T20", "TID251"] "src/scikit_build_core/resources/*.py" = ["PTH", "ARG002", "FBT", "TID251"] "src/scikit_build_core/_compat/**.py" = ["TID251"] "tests/conftest.py" = ["TID251"] "tests/packages/**.py" = ["TID251"] "docs/conf.py" = ["TID251"] +"docs/examples/**" = ["ANN"] +"src/scikit_build_core/file_api/model/*.py" = ["N"] [tool.check-sdist] diff --git a/src/scikit_build_core/__init__.py b/src/scikit_build_core/__init__.py index 7b3d6eb16..8f728efec 100644 --- a/src/scikit_build_core/__init__.py +++ b/src/scikit_build_core/__init__.py @@ -4,7 +4,6 @@ scikit-build-core: PEP 517 builder for Scikit-Build """ - from __future__ import annotations from ._version import version as __version__ diff --git a/src/scikit_build_core/_compat/typing.py b/src/scikit_build_core/_compat/typing.py index 239477525..171dea7af 100644 --- a/src/scikit_build_core/_compat/typing.py +++ b/src/scikit_build_core/_compat/typing.py @@ -19,7 +19,7 @@ else: Self = object - def assert_never(_: typing.Any) -> None: + def assert_never(_: object) -> None: msg = "Expected code to be unreachable" raise AssertionError(msg) diff --git a/src/scikit_build_core/_logging.py b/src/scikit_build_core/_logging.py index 0cf84cb5a..d8894186f 100644 --- a/src/scikit_build_core/_logging.py +++ b/src/scikit_build_core/_logging.py @@ -69,10 +69,10 @@ def exception(self, msg: str, *args: object, **kwargs: object) -> None: def log(self, level: int, msg: str, *args: object, **kwargs: object) -> None: self.logger.log(level, FStringMessage(msg, *args, **kwargs), **opts) - def setLevel(self, level: int) -> None: + def setLevel(self, level: int) -> None: # noqa: N802 self.logger.setLevel(level) - def addHandler(self, handler: logging.Handler) -> None: + def addHandler(self, handler: logging.Handler) -> None: # noqa: N802 self.logger.addHandler(handler) diff --git a/src/scikit_build_core/build/__init__.py b/src/scikit_build_core/build/__init__.py index 70c68a8a6..af5620762 100644 --- a/src/scikit_build_core/build/__init__.py +++ b/src/scikit_build_core/build/__init__.py @@ -2,7 +2,6 @@ This is the entry point for the build backend. Items in this module are designed for the build backend API. """ - from __future__ import annotations import sys diff --git a/src/scikit_build_core/builder/wheel_tag.py b/src/scikit_build_core/builder/wheel_tag.py index b86d81b30..9f28381c5 100644 --- a/src/scikit_build_core/builder/wheel_tag.py +++ b/src/scikit_build_core/builder/wheel_tag.py @@ -116,7 +116,7 @@ def tags_dict(self) -> dict[str, list[str]]: def as_tags_set(self) -> frozenset[packaging.tags.Tag]: vals = itertools.product(self.pyvers, self.abis, self.archs) - return frozenset(packaging.tags.Tag(*v) for v in vals) + return frozenset(itertools.starmap(packaging.tags.Tag, vals)) if __name__ == "__main__": diff --git a/src/scikit_build_core/file_api/reply.py b/src/scikit_build_core/file_api/reply.py index 153bf9baf..3e061b817 100644 --- a/src/scikit_build_core/file_api/reply.py +++ b/src/scikit_build_core/file_api/reply.py @@ -50,7 +50,7 @@ def make_class(self, data: InputDict, target: Type[T]) -> T: Convert a dict to a dataclass. Automatically load a few nested jsonFile classes. """ if ( - target in (CodeModel, Target, Cache, CMakeFiles, Directory) + target in {CodeModel, Target, Cache, CMakeFiles, Directory} and "jsonFile" in data and data["jsonFile"] is not None ): @@ -91,9 +91,9 @@ def _convert_any(self, item: Any, target: Type[T]) -> T: return self.make_class(item, target) # type: ignore[return-value] origin = get_origin(target) if origin is not None: - if origin == list: + if origin is list: return [self._convert_any(i, get_args(target)[0]) for i in item] # type: ignore[return-value] - if origin == Union: + if origin is Union: return self._convert_any(item, get_args(target)[0]) # type: ignore[no-any-return] return target(item) # type: ignore[call-arg] diff --git a/src/scikit_build_core/resources/_editable_redirect.py b/src/scikit_build_core/resources/_editable_redirect.py index 9f0ddace5..a2e5fb690 100644 --- a/src/scikit_build_core/resources/_editable_redirect.py +++ b/src/scikit_build_core/resources/_editable_redirect.py @@ -15,7 +15,7 @@ else: from typing import TypedDict - class KWDict_1(TypedDict, total=False): + class KWDict_1(TypedDict, total=False): # noqa: N801 submodule_search_locations: list[str] else: diff --git a/src/scikit_build_core/settings/json_schema.py b/src/scikit_build_core/settings/json_schema.py index 637e5f87d..23d6548c5 100644 --- a/src/scikit_build_core/settings/json_schema.py +++ b/src/scikit_build_core/settings/json_schema.py @@ -11,14 +11,14 @@ from .._compat.typing import Literal, get_args, get_origin from .documentation import pull_docs -__all__ = ["to_json_schema", "convert_type", "FailedConversion"] +__all__ = ["to_json_schema", "convert_type", "FailedConversionError"] def __dir__() -> list[str]: return __all__ -class FailedConversion(TypeError): +class FailedConversionError(TypeError): pass @@ -36,7 +36,7 @@ def to_json_schema(dclass: type[Any], *, normalize_keys: bool) -> dict[str, Any] try: props[field.name] = convert_type(field.type, normalize_keys=normalize_keys) - except FailedConversion as err: + except FailedConversionError as err: if sys.version_info < (3, 11): notes = "__notes__" # set so linter's won't try to be clever setattr(err, notes, [*getattr(err, notes, []), f"Field: {field.name}"]) @@ -119,4 +119,4 @@ def convert_type(t: Any, *, normalize_keys: bool) -> dict[str, Any]: return {"enum": list(args)} msg = f"Cannot convert type {t} to JSON Schema" - raise FailedConversion(msg) + raise FailedConversionError(msg) diff --git a/src/scikit_build_core/settings/sources.py b/src/scikit_build_core/settings/sources.py index 19e9856a4..395fde4e5 100644 --- a/src/scikit_build_core/settings/sources.py +++ b/src/scikit_build_core/settings/sources.py @@ -43,7 +43,6 @@ Integers/floats would be easy to add, but haven't been needed yet. """ - from __future__ import annotations import dataclasses @@ -125,9 +124,9 @@ def _get_inner_type(__target: type[Any]) -> type[Any]: raw_target = _get_target_raw_type(__target) target = _process_union(__target) - if raw_target == list: + if raw_target is list: return get_args(target)[0] # type: ignore[no-any-return] - if raw_target == dict: + if raw_target is dict: return get_args(target)[1] # type: ignore[no-any-return] msg = f"Expected a list or dict, got {target!r}" raise AssertionError(msg) @@ -219,11 +218,11 @@ def convert(cls, item: str, target: type[Any]) -> object: if dataclasses.is_dataclass(raw_target): msg = f"Array of dataclasses are not supported in configuration settings ({raw_target})" raise TypeError(msg) - if raw_target == list: + if raw_target is list: return [ cls.convert(i.strip(), _get_inner_type(target)) for i in item.split(";") ] - if raw_target == dict: + if raw_target is dict: items = (i.strip().split("=") for i in item.split(";")) return {k: cls.convert(v, _get_inner_type(target)) for k, v in items} @@ -334,7 +333,7 @@ def convert( if dataclasses.is_dataclass(raw_target): msg = f"Array of dataclasses are not supported in configuration settings ({raw_target})" raise TypeError(msg) - if raw_target == list: + if raw_target is list: if isinstance(item, list): return [cls.convert(i, _get_inner_type(target)) for i in item] if isinstance(item, dict): @@ -343,7 +342,7 @@ def convert( return [ cls.convert(i.strip(), _get_inner_type(target)) for i in item.split(";") ] - if raw_target == dict: + if raw_target is dict: assert not isinstance(item, (str, list)) return {k: cls.convert(v, _get_inner_type(target)) for k, v in item.items()} if isinstance(item, (list, dict)): @@ -379,7 +378,7 @@ def unrecognized_options(self, options: object) -> Generator[str, None, None]: except KeyError: yield keystr continue - if _get_target_raw_type(outer_option) == dict: + if _get_target_raw_type(outer_option) is dict: continue def all_option_names(self, target: type[Any]) -> Iterator[str]: @@ -393,7 +392,8 @@ def __init__(self, *prefixes: str, settings: Mapping[str, Any]) -> None: self.prefixes = prefixes self.settings = _dig_not_strict(settings, *prefixes) - def _get_name(self, *fields: str) -> list[str]: + @staticmethod + def _get_name(*fields: str) -> list[str]: return [field.replace("_", "-") for field in fields] def has_item(self, *fields: str, is_dict: bool) -> bool: # noqa: ARG002 @@ -500,7 +500,7 @@ def convert_target(self, target: type[T], *prefixes: str) -> T: errors.append(e) continue - is_dict = _get_target_raw_type(field.type) == dict + is_dict = _get_target_raw_type(field.type) is dict for source in self.sources: if source.has_item(*prefixes, field.name, is_dict=is_dict): diff --git a/tests/test_fileapi.py b/tests/test_fileapi.py index fbd307eef..658f8b811 100644 --- a/tests/test_fileapi.py +++ b/tests/test_fileapi.py @@ -106,7 +106,7 @@ def test_included_dir(): assert codemodel.kind == "codemodel" assert codemodel.version.major == 2 assert codemodel.version.minor == 4 - assert codemodel.configurations[0].name == "" + assert not codemodel.configurations[0].name cache = index.reply.cache_v2 assert cache is not None diff --git a/tests/test_get_requires.py b/tests/test_get_requires.py index 60ab0e691..a3001ac8c 100644 --- a/tests/test_get_requires.py +++ b/tests/test_get_requires.py @@ -20,7 +20,7 @@ def which_mock(name: str) -> str | None: - if name in ("ninja", "ninja-build", "cmake3", "samu", "gmake", "make"): + if name in {"ninja", "ninja-build", "cmake3", "samu", "gmake", "make"}: return None if name == "cmake": return "cmake/path" diff --git a/tests/test_json_schema.py b/tests/test_json_schema.py index 154116159..d1b9f96ff 100644 --- a/tests/test_json_schema.py +++ b/tests/test_json_schema.py @@ -4,7 +4,7 @@ import pytest from packaging.version import Version -from scikit_build_core.settings.json_schema import FailedConversion, convert_type +from scikit_build_core.settings.json_schema import FailedConversionError, convert_type def test_convert_str(): @@ -57,5 +57,5 @@ def test_convert_dict(): def test_convert_invalid(): - with pytest.raises(FailedConversion): + with pytest.raises(FailedConversionError): convert_type(object, normalize_keys=False)