From 11cd1497b852fd07be140013f3325ffe907723f9 Mon Sep 17 00:00:00 2001 From: Johannes Maron Date: Sun, 11 Oct 2026 21:49:14 +0200 Subject: [PATCH 1/3] Update the Ruff configuration - Use the shared extend-select rule set, including D213 and S - Align the ignore list with the other projects --- pyproject.toml | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 66b712b..7383c13 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,18 +57,27 @@ name = "relint" [tool.ruff] src = [ "relint", "tests" ] [tool.ruff.lint] -select = [ - "B", # flake8-bugbear - "C", # flake8-comprehensions - "D", # pydocstyle - "E", # pycodestyle errors - "F", # pyflakes - "I", # isort - "S", # flake8-bandit - "UP", # pyupgrade - "W", # pycodestyle warnings +extend-select = [ + "C90", + "D", + "D213", + "DJ", + "E", + "G", + "N", + "PERF", + "PT", + "PTH", + "RET", + "S", + "SIM", + "SLOT", + "T20", + "TRY", + "UP", + "W", ] -ignore = [ "B904", "D1", "E501", "S101" ] +ignore = [ "D1", "D203", "D212", "E501", "RUF01" ] isort.combine-as-imports = true isort.force-wrap-aliases = true isort.section-order = [ "future", "standard-library", "third-party", "first-party", "local-folder" ] From afc4cfd6ccf145871fa84066c138ba12e2df9c43 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 11 Oct 2026 19:56:20 +0000 Subject: [PATCH 2/3] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_main.py | 50 ++++++++++++++++++--------------------------- tests/test_parse.py | 32 ++++++++++++----------------- 2 files changed, 33 insertions(+), 49 deletions(-) diff --git a/tests/test_main.py b/tests/test_main.py index 8b1b129..22e9930 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -8,9 +8,8 @@ def test_version(tmpdir, capsys): """Test that the version is correct.""" - with tmpdir.as_cwd(): - with pytest.raises(SystemExit) as exc_info: - main(["--version"]) + with tmpdir.as_cwd(), pytest.raises(SystemExit) as exc_info: + main(["--version"]) assert "0" in str(exc_info.value) assert f"relint: {relint.__version__}" in capsys.readouterr().out @@ -21,9 +20,8 @@ def test_main_execution(self, tmpdir, fixture_dir): config = fs.read() tmpdir.join(".relint.yml").write(config) tmpdir.join("dummy.py").write("# TODO do something") - with tmpdir.as_cwd(): - with pytest.raises(SystemExit) as exc_info: - main(["dummy.py"]) + with tmpdir.as_cwd(), pytest.raises(SystemExit) as exc_info: + main(["dummy.py"]) assert exc_info.value.code == 0 @@ -32,9 +30,8 @@ def test_main_execution_with_error(self, capsys, tmpdir, fixture_dir): config = fs.read() tmpdir.join(".relint.yml").write(config) tmpdir.join("dummy.py").write("# FIXME do something") - with tmpdir.as_cwd(): - with pytest.raises(SystemExit) as exc_info: - main(["dummy.py"]) + with tmpdir.as_cwd(), pytest.raises(SystemExit) as exc_info: + main(["dummy.py"]) out, _ = capsys.readouterr() assert "dummy.py:1" in out @@ -51,9 +48,8 @@ def test_main_execution_with_error__github_workflow_output( config = fs.read() tmpdir.join(".relint.yml").write(config) tmpdir.join("dummy.py").write("# FIXME do something") - with tmpdir.as_cwd(): - with pytest.raises(SystemExit) as exc_info: - main(["dummy.py"]) + with tmpdir.as_cwd(), pytest.raises(SystemExit) as exc_info: + main(["dummy.py"]) out, _ = capsys.readouterr() assert ( @@ -68,9 +64,8 @@ def test_main_execution_without_hint(self, args, capsys, tmpdir, fixture_dir): config = fs.read() tmpdir.join(".relint.yml").write(config) tmpdir.join("dummy.py").write("# hint: 🤐") - with tmpdir.as_cwd(): - with pytest.raises(SystemExit): - main(["dummy.py", *args]) + with tmpdir.as_cwd(), pytest.raises(SystemExit): + main(["dummy.py", *args]) out, _ = capsys.readouterr() assert "dummy.py:1" in out @@ -81,9 +76,8 @@ def test_raise_for_warnings(self, tmpdir, fixture_dir): config = fs.read() tmpdir.join(".relint.yml").write(config) tmpdir.join("dummy.py").write("# TODO do something") - with tmpdir.as_cwd(): - with pytest.raises(SystemExit) as exc_info: - main(["dummy.py", "-W"]) + with tmpdir.as_cwd(), pytest.raises(SystemExit) as exc_info: + main(["dummy.py", "-W"]) assert exc_info.value.code == 1 @@ -92,9 +86,8 @@ def test_ignore_warnings(self, tmpdir, fixture_dir): config = fs.read() tmpdir.join(".relint.yml").write(config) tmpdir.join("dummy.py").write("# TODO do something") - with tmpdir.as_cwd(): - with pytest.raises(SystemExit) as exc_info: - main(["dummy.py", "--ignore-warnings"]) + with tmpdir.as_cwd(), pytest.raises(SystemExit) as exc_info: + main(["dummy.py", "--ignore-warnings"]) assert exc_info.value.code == 0 @@ -103,9 +96,8 @@ def test_summarize(self, tmpdir, fixture_dir, capsys): config = fs.read() tmpdir.join(".relint.yml").write(config) tmpdir.join("dummy.py").write("# FIXME do something") - with tmpdir.as_cwd(): - with pytest.raises(SystemExit) as exc_info: - main(["dummy.py", "--summarize"]) + with tmpdir.as_cwd(), pytest.raises(SystemExit) as exc_info: + main(["dummy.py", "--summarize"]) out, _ = capsys.readouterr() assert "dummy.py:1" in out @@ -119,9 +111,8 @@ def test_code_padding_disabled(self, tmpdir, fixture_dir, capsys): config = fs.read() tmpdir.join(".relint.yml").write(config) tmpdir.join("dummy.py").write("# FIXME do something") - with tmpdir.as_cwd(): - with pytest.raises(SystemExit) as exc_info: - main(["dummy.py", "--code-padding=-1"]) + with tmpdir.as_cwd(), pytest.raises(SystemExit) as exc_info: + main(["dummy.py", "--code-padding=-1"]) out, _ = capsys.readouterr() assert "dummy.py:1" in out @@ -140,9 +131,8 @@ def test_main_execution_with_diff(self, capsys, mocker, tmpdir, fixture_dir): mocker.patch.object(sys, "stdin", diff) - with tmpdir.as_cwd(): - with pytest.raises(SystemExit) as exc_info: - main(["dummy.py", "--diff"]) + with tmpdir.as_cwd(), pytest.raises(SystemExit) as exc_info: + main(["dummy.py", "--diff"]) out, _ = capsys.readouterr() assert "Get it done right away!" in out diff --git a/tests/test_parse.py b/tests/test_parse.py index aaf1a76..4ebb989 100644 --- a/tests/test_parse.py +++ b/tests/test_parse.py @@ -141,10 +141,9 @@ def test_empty_config_file(self, tmpdir): tmpdir.join(".relint.yml").write("") tmpdir.join("dummy.py").write("") - with tmpdir.as_cwd(): - with warnings.catch_warnings(record=True) as w: - with pytest.raises(SystemExit) as exc_info: - main(["dummy.py"]) + with tmpdir.as_cwd(), warnings.catch_warnings(record=True) as w: + with pytest.raises(SystemExit) as exc_info: + main(["dummy.py"]) assert exc_info.value.code == 0 assert issubclass(w[-1].category, UserWarning) @@ -155,9 +154,8 @@ def test_empty_config_file(self, tmpdir): def test_malformed_config_file(self, tmpdir): tmpdir.join(".relint.yml").write("test:") - with tmpdir.as_cwd(): - with pytest.raises(ConfigError) as exc_info: - main(["**"]) + with tmpdir.as_cwd(), pytest.raises(ConfigError) as exc_info: + main(["**"]) assert "Your relint config is not a valid YAML list of relint tests." in str( exc_info.value @@ -166,9 +164,8 @@ def test_malformed_config_file(self, tmpdir): def test_corrupt_config_file(self, tmpdir): tmpdir.join(".relint.yml").write(b"\x00") - with tmpdir.as_cwd(): - with pytest.raises(ConfigError) as exc_info: - main(["**"]) + with tmpdir.as_cwd(), pytest.raises(ConfigError) as exc_info: + main(["**"]) assert "Error parsing your relint config file." in str(exc_info.value) @@ -179,9 +176,8 @@ def test_git_diff(self, capsys, tmpdir, fixture_dir): tmpdir.join("dummy.py").write("# TODO do something") subprocess.check_call(["git", "init"], cwd=tmpdir.strpath) # noqa: S607 - with tmpdir.as_cwd(): - with pytest.raises(SystemExit) as exc_info: - main(["dummy.py", "--git-diff"]) + with tmpdir.as_cwd(), pytest.raises(SystemExit) as exc_info: + main(["dummy.py", "--git-diff"]) assert "0" in str(exc_info.value) @@ -197,9 +193,8 @@ def test_git_diff_with_mnemonic_prefixes(self, capsys, tmpdir, fixture_dir): ) subprocess.check_call(["git", "add", "dummy.py"], cwd=tmpdir.strpath) # noqa: S607 - with tmpdir.as_cwd(): - with pytest.raises(SystemExit) as exc_info: - main(["dummy.py", "--git-diff"]) + with tmpdir.as_cwd(), pytest.raises(SystemExit) as exc_info: + main(["dummy.py", "--git-diff"]) out, _ = capsys.readouterr() assert "Get it done right away!" in out @@ -213,9 +208,8 @@ def test_no_unicode(capsys, tmpdir, fixture_dir): with (fixture_dir / "test.png").open("rb") as fs: png = fs.read() tmpdir.join("test.png").write(png, mode="wb") - with tmpdir.as_cwd(): - with pytest.raises(SystemExit) as exc_info: - main(["test.png"]) + with tmpdir.as_cwd(), pytest.raises(SystemExit) as exc_info: + main(["test.png"]) assert "0" in str(exc_info.value) From fb28bd07cc1ca4fcf54fa8268405804f707de8c5 Mon Sep 17 00:00:00 2001 From: Johannes Maron Date: Mon, 12 Oct 2026 00:19:57 +0200 Subject: [PATCH 3/3] Fix the remaining Ruff violations - Scope the new rules to tests with per-file-ignores - Modernise the code the new rules flag, without changing behaviour --- pyproject.toml | 1 + relint/__main__.py | 10 +++++----- relint/config.py | 11 ++++++----- relint/parse.py | 19 +++++++++++-------- tests/test_parse.py | 19 +++++++++++-------- 5 files changed, 34 insertions(+), 26 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 7383c13..506069a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -78,6 +78,7 @@ extend-select = [ "W", ] ignore = [ "D1", "D203", "D212", "E501", "RUF01" ] +per-file-ignores."tests/**" = [ "DJ006", "DJ007", "DJ008", "PLR2004", "S101", "S105", "S106", "S311", "S603", "S607" ] isort.combine-as-imports = true isort.force-wrap-aliases = true isort.section-order = [ "future", "standard-library", "third-party", "first-party", "local-folder" ] diff --git a/relint/__main__.py b/relint/__main__.py index 21ead42..0b93857 100644 --- a/relint/__main__.py +++ b/relint/__main__.py @@ -78,8 +78,8 @@ def main(args=None): if args.version: from . import __version__ - print(f"relint: {__version__}") - exit(0) + sys.stdout.write(f"relint: {__version__}\n") + sys.exit(0) tests = list(load_config(args.config, args.fail_warnings, args.ignore_warnings)) @@ -99,12 +99,12 @@ def main(args=None): changed_content = parse_diff(output) matches = match_with_diff_changes(changed_content, matches) - GITHUB_ACTIONS = os.getenv("GITHUB_ACTIONS") == "true" - if GITHUB_ACTIONS: + github_actions = os.getenv("GITHUB_ACTIONS") == "true" + if github_actions: exit_code = print_github_actions_output(matches, args) else: exit_code = print_culprits(matches, args) - exit(exit_code) + sys.exit(exit_code) if not sys.warnoptions: diff --git a/relint/config.py b/relint/config.py index aefc301..4678e83 100644 --- a/relint/config.py +++ b/relint/config.py @@ -5,6 +5,7 @@ import regex as re except ImportError: import re +from pathlib import Path import yaml @@ -23,7 +24,7 @@ def load_config(path, fail_warnings, ignore_warnings): - with open(path) as fs: + with Path(path).open() as fs: try: for test in yaml.safe_load(fs): if ignore_warnings and not test.get("error", True): @@ -39,7 +40,8 @@ def load_config(path, fail_warnings, ignore_warnings): error=test.get("error", True) or fail_warnings, ) except yaml.YAMLError as e: - raise ConfigError("Error parsing your relint config file.") from e + msg = "Error parsing your relint config file." + raise ConfigError(msg) from e except TypeError: warnings.warn( "Your relint config is empty, no tests were executed.", @@ -47,6 +49,5 @@ def load_config(path, fail_warnings, ignore_warnings): stacklevel=2, ) except (AttributeError, ValueError) as e: - raise ConfigError( - "Your relint config is not a valid YAML list of relint tests." - ) from e + msg = "Your relint config is not a valid YAML list of relint tests." + raise ConfigError(msg) from e diff --git a/relint/parse.py b/relint/parse.py index 1e5fb76..0a43568 100644 --- a/relint/parse.py +++ b/relint/parse.py @@ -1,10 +1,13 @@ import collections +import sys try: import regex as re except ImportError: import re +from pathlib import Path + from rich import print as rprint from rich.console import Group from rich.markdown import Markdown @@ -23,7 +26,7 @@ def lint_file(filename, tests): try: - with open(filename) as fs: + with Path(filename).open() as fs: content = fs.read() except IsADirectoryError, UnicodeDecodeError: pass @@ -86,14 +89,14 @@ def split_diff_content_by_filename(output: str) -> {str: str}: dict: Filename and its content. """ - content_by_filename = {} filenames = parse_filenames(output) split_content = GIT_DIFF_SPLIT_PATTERN.split(output) split_content = filter(lambda x: x != "", split_content) - for filename, content in zip(filenames, split_content, strict=True): - content_by_filename[filename] = content - return content_by_filename + return { + filename: content + for filename, content in zip(filenames, split_content, strict=True) + } def print_github_actions_output(matches, args): @@ -112,10 +115,10 @@ def print_github_actions_output(matches, args): f"title={test.name}::{test.hint}".replace("\n", "%0A") ) for filename, messages in groups.items(): - print(f"::group::{filename}") + sys.stdout.write(f"::group::{filename}\n") for annotation in messages: - print(annotation) - print("::endgroup::") + sys.stdout.write(f"{annotation}\n") + sys.stdout.write("::endgroup::\n") return exit_code diff --git a/tests/test_parse.py b/tests/test_parse.py index 4ebb989..c7bee89 100644 --- a/tests/test_parse.py +++ b/tests/test_parse.py @@ -27,7 +27,7 @@ def test_line_numbers_when_only_one_line_was_changed(self): assert parse_line_numbers(output) == [54] @pytest.mark.parametrize( - "output,expected_filename", + ("output", "expected_filename"), [ ("diff --git a/relint.py b/relint.py", "relint.py"), ( @@ -141,9 +141,12 @@ def test_empty_config_file(self, tmpdir): tmpdir.join(".relint.yml").write("") tmpdir.join("dummy.py").write("") - with tmpdir.as_cwd(), warnings.catch_warnings(record=True) as w: - with pytest.raises(SystemExit) as exc_info: - main(["dummy.py"]) + with ( + tmpdir.as_cwd(), + warnings.catch_warnings(record=True) as w, + pytest.raises(SystemExit) as exc_info, + ): + main(["dummy.py"]) assert exc_info.value.code == 0 assert issubclass(w[-1].category, UserWarning) @@ -174,7 +177,7 @@ def test_git_diff(self, capsys, tmpdir, fixture_dir): config = fs.read() tmpdir.join(".relint.yml").write(config) tmpdir.join("dummy.py").write("# TODO do something") - subprocess.check_call(["git", "init"], cwd=tmpdir.strpath) # noqa: S607 + subprocess.check_call(["git", "init"], cwd=tmpdir.strpath) with tmpdir.as_cwd(), pytest.raises(SystemExit) as exc_info: main(["dummy.py", "--git-diff"]) @@ -186,12 +189,12 @@ def test_git_diff_with_mnemonic_prefixes(self, capsys, tmpdir, fixture_dir): config = fs.read() tmpdir.join(".relint.yml").write(config) tmpdir.join("dummy.py").write("# TODO do something") - subprocess.check_call(["git", "init"], cwd=tmpdir.strpath) # noqa: S607 + subprocess.check_call(["git", "init"], cwd=tmpdir.strpath) subprocess.check_call( - ["git", "config", "diff.mnemonicPrefix", "true"], # noqa: S607 + ["git", "config", "diff.mnemonicPrefix", "true"], cwd=tmpdir.strpath, ) - subprocess.check_call(["git", "add", "dummy.py"], cwd=tmpdir.strpath) # noqa: S607 + subprocess.check_call(["git", "add", "dummy.py"], cwd=tmpdir.strpath) with tmpdir.as_cwd(), pytest.raises(SystemExit) as exc_info: main(["dummy.py", "--git-diff"])