From d76aca9528c5965521e46ea9a0d70fae0d939be1 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 24 Nov 2023 15:23:13 +0200 Subject: [PATCH 1/7] Format with Black via pre-commit --- .github/workflows/lint.yml | 20 ++++++++++++++++++++ .pre-commit-config.yaml | 5 +++++ pyproject.toml | 19 +++++++++++++++++++ sphinxlint/checkers.py | 8 ++++++-- sphinxlint/utils.py | 5 +---- tests/test_sphinxlint.py | 4 +++- tox.ini | 10 ++++++++++ 7 files changed, 64 insertions(+), 7 deletions(-) create mode 100644 .github/workflows/lint.yml create mode 100644 .pre-commit-config.yaml diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 000000000..850976317 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,20 @@ +name: Lint + +on: [push, pull_request, workflow_dispatch] + +env: + FORCE_COLOR: 1 + +permissions: + contents: read + +jobs: + lint: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 + with: + python-version: "3.x" + - uses: pre-commit/action@v3.0.0 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000..89a47f392 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,5 @@ +repos: + - repo: https://github.com/psf/black-pre-commit-mirror + rev: 23.11.0 + hooks: + - id: black diff --git a/pyproject.toml b/pyproject.toml index 298c32435..a0cf7db96 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,5 +50,24 @@ local_scheme = "no-local-version" [tool.black] +[tool.ruff] +select = [ + "E", # pycodestyle errors + "F", # pyflakes errors + "I", # isort + "ISC", # flake8-implicit-str-concat + "PGH", # pygrep-hooks + "RUF100", # unused noqa (yesqa) + "UP", # pyupgrade + "W", # pycodestyle warnings + "YTT", # flake8-2020 +] +extend-ignore = [ + "E203", # Whitespace before ':' + "E221", # Multiple spaces before operator + "E226", # Missing whitespace around arithmetic operator + "E241", # Multiple spaces after ',' +] + [tool.pylint.variables] callbacks = ["check_"] diff --git a/sphinxlint/checkers.py b/sphinxlint/checkers.py index 791cac6ca..0d084f749 100644 --- a/sphinxlint/checkers.py +++ b/sphinxlint/checkers.py @@ -129,8 +129,12 @@ def check_default_role(file, lines, options=None): before_match = line[: match.start()] after_match = line[match.end() :] stripped_line = line.strip() - if (stripped_line.startswith("|") and stripped_line.endswith("|") and - stripped_line.count("|") >= 4 and "|" in match.group(0)): + if ( + stripped_line.startswith("|") + and stripped_line.endswith("|") + and stripped_line.count("|") >= 4 + and "|" in match.group(0) + ): return # we don't handle tables yet. if _ends_with_role_tag(before_match): # It's not a default role: it ends with a tag. diff --git a/sphinxlint/utils.py b/sphinxlint/utils.py index 1dfb3ffec..e158e9ef8 100644 --- a/sphinxlint/utils.py +++ b/sphinxlint/utils.py @@ -187,10 +187,7 @@ def hide_non_rst_blocks(lines, hidden_block_cb=None): in_literal = len(_ZERO_OR_MORE_SPACES_RE.match(line)[0]) block_line_start = lineno assert not excluded_lines - if ( - type_of_explicit_markup(line) == "comment" - and _COMMENT_RE.search(line) - ): + if type_of_explicit_markup(line) == "comment" and _COMMENT_RE.search(line): line = "\n" output.append(line) if excluded_lines and hidden_block_cb: diff --git a/tests/test_sphinxlint.py b/tests/test_sphinxlint.py index 85b08afaa..753f18e3c 100644 --- a/tests/test_sphinxlint.py +++ b/tests/test_sphinxlint.py @@ -64,7 +64,9 @@ def test_sphinxlint_shall_not_pass(file, expected_errors, capsys): assert expected_error in err number_of_expected_errors = len(expected_errors) number_of_reported_errors = len(err.splitlines()) - assert number_of_expected_errors == number_of_reported_errors, f"{number_of_reported_errors=}, {err=}" + assert ( + number_of_expected_errors == number_of_reported_errors + ), f"{number_of_reported_errors=}, {err=}" @pytest.mark.parametrize("file", [str(FIXTURE_DIR / "paragraphs.rst")]) diff --git a/tox.ini b/tox.ini index fe5261b02..e616fc791 100644 --- a/tox.ini +++ b/tox.ini @@ -2,6 +2,7 @@ requires = tox>=4.2 env_list = + lint py{py3, 313, 312, 311, 310, 39, 38} [testenv] @@ -17,3 +18,12 @@ commands = --cov-report term \ --cov-report xml \ {posargs} + +[testenv:lint] +skip_install = true +deps = + pre-commit +pass_env = + PRE_COMMIT_COLOR +commands = + pre-commit run --all-files --show-diff-on-failure From 6f3b39b205bcad8fc01d961d4075e03c4db66015 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 24 Nov 2023 15:27:55 +0200 Subject: [PATCH 2/7] Lint with Ruff --- .pre-commit-config.yaml | 6 ++++++ sphinxlint/checkers.py | 28 +++++++++++++++++++++------- sphinxlint/cli.py | 6 ++++-- sphinxlint/utils.py | 1 - tests/test_default_role_re.py | 2 +- tests/test_enable_disable.py | 2 +- tests/test_filter_out_literal.py | 1 - tests/test_sphinxlint.py | 3 +-- tests/test_xpass_friends.py | 3 +-- 9 files changed, 35 insertions(+), 17 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 89a47f392..9f808c4ed 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,4 +1,10 @@ repos: + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.1.6 + hooks: + - id: ruff + args: [--fix, --exit-non-zero-on-fix] + - repo: https://github.com/psf/black-pre-commit-mirror rev: 23.11.0 hooks: diff --git a/sphinxlint/checkers.py b/sphinxlint/checkers.py index 0d084f749..141e3a73d 100644 --- a/sphinxlint/checkers.py +++ b/sphinxlint/checkers.py @@ -12,7 +12,6 @@ paragraphs, ) - all_checkers = {} @@ -60,7 +59,10 @@ def check_missing_backtick_after_role(file, lines, options=None): error = rst.ROLE_MISSING_CLOSING_BACKTICK_RE.search(paragraph) if error: error_offset = paragraph[: error.start()].count("\n") - yield paragraph_lno + error_offset, f"role missing closing backtick: {error.group(0)!r}" + yield ( + paragraph_lno + error_offset, + f"role missing closing backtick: {error.group(0)!r}", + ) _RST_ROLE_RE = re.compile("``.+?``(?!`).", flags=re.DOTALL) @@ -145,7 +147,10 @@ def check_default_role(file, lines, options=None): if match.group(0).startswith("``") and match.group(0).endswith("``"): # It's not a default role: it's an inline literal. continue - yield lno, "default role used (hint: for inline literals, use double backticks)" + yield ( + lno, + "default role used (hint: for inline literals, use double backticks)", + ) @checker(".rst", ".po") @@ -291,7 +296,10 @@ def check_role_with_double_backticks(file, lines, options=None): before = paragraph[: inline_literal.start()] if _ends_with_role_tag(before): error_offset = paragraph[: inline_literal.start()].count("\n") - yield paragraph_lno + error_offset, "role use a single backtick, double backtick found." + yield ( + paragraph_lno + error_offset, + "role use a single backtick, double backtick found.", + ) paragraph = ( paragraph[: inline_literal.start()] + paragraph[inline_literal.end() :] ) @@ -312,9 +320,15 @@ def check_missing_space_before_role(file, lines, options=None): if match: error_offset = paragraph[: match.start()].count("\n") if looks_like_glued(match): - yield paragraph_lno + error_offset, f"missing space before role ({match.group(0)})." + yield ( + paragraph_lno + error_offset, + f"missing space before role ({match.group(0)}).", + ) else: - yield paragraph_lno + error_offset, f"role missing opening tag colon ({match.group(0)})." + yield ( + paragraph_lno + error_offset, + f"role missing opening tag colon ({match.group(0)}).", + ) @checker(".rst", ".po") @@ -498,4 +512,4 @@ def check_dangling_hyphen(file, lines, options): for lno, line in enumerate(lines): stripped_line = line.rstrip("\n") if _has_dangling_hyphen(stripped_line): - yield lno + 1, f"Line ends with dangling hyphen" + yield lno + 1, "Line ends with dangling hyphen" diff --git a/sphinxlint/cli.py b/sphinxlint/cli.py index 55d2bdaf5..c0a5638bf 100644 --- a/sphinxlint/cli.py +++ b/sphinxlint/cli.py @@ -54,7 +54,8 @@ def __call__(self, parser, namespace, values, option_string=None): sort_fields.append(SortField[field_name.upper()]) except KeyError: raise ValueError( - f"Unsupported sort field: {field_name}, supported values are {SortField.as_supported_options()}" + f"Unsupported sort field: {field_name}, " + f"supported values are {SortField.as_supported_options()}" ) from None setattr(namespace, self.dest, sort_fields) @@ -85,7 +86,8 @@ def job_count(values): "-d", "--disable", action=DisableAction, - help='comma-separated list of checks to disable. Give "all" to disable them all. ' + help="comma-separated list of checks to disable. " + 'Give "all" to disable them all. ' "Can be used in conjunction with --enable (it's evaluated left-to-right). " '"--disable all --enable trailing-whitespace" can be used to enable a ' "single check.", diff --git a/sphinxlint/utils.py b/sphinxlint/utils.py index e158e9ef8..6aee9d51a 100644 --- a/sphinxlint/utils.py +++ b/sphinxlint/utils.py @@ -6,7 +6,6 @@ from sphinxlint import rst - PER_FILE_CACHES = [] diff --git a/tests/test_default_role_re.py b/tests/test_default_role_re.py index 86dd2d23a..5e68eaa4b 100644 --- a/tests/test_default_role_re.py +++ b/tests/test_default_role_re.py @@ -12,5 +12,5 @@ def test_shall_not_pass(): assert not rst.INTERPRETED_TEXT_RE.search("``") assert not rst.INTERPRETED_TEXT_RE.search("2 * x a ** b (* BOM32_* ` `` _ __ |") assert not rst.INTERPRETED_TEXT_RE.search( - """"`" '|' (`) [`] {`} <`> ‘`’ ‚`‘ ‘`‚ ’`’ ‚`’ “`” „`“ “`„ ”`” „`” »`« ›`‹ «`» »`» ›`›""" + """"`" '|' (`) [`] {`} <`> ‘`’ ‚`‘ ‘`‚ ’`’ ‚`’ “`” „`“ “`„ ”`” „`” »`« ›`‹ «`» »`» ›`›""" # noqa: E501 ) diff --git a/tests/test_enable_disable.py b/tests/test_enable_disable.py index 1d0c1fd45..1fd506da9 100644 --- a/tests/test_enable_disable.py +++ b/tests/test_enable_disable.py @@ -1,5 +1,5 @@ -from random import choice import re +from random import choice from sphinxlint.cli import main diff --git a/tests/test_filter_out_literal.py b/tests/test_filter_out_literal.py index 4ea7f54a5..34a7792dd 100644 --- a/tests/test_filter_out_literal.py +++ b/tests/test_filter_out_literal.py @@ -1,6 +1,5 @@ from sphinxlint.utils import hide_non_rst_blocks - LITERAL = r""" Hide non-RST Blocks =================== diff --git a/tests/test_sphinxlint.py b/tests/test_sphinxlint.py index 753f18e3c..4e68ed5a1 100644 --- a/tests/test_sphinxlint.py +++ b/tests/test_sphinxlint.py @@ -1,10 +1,9 @@ from pathlib import Path -from sphinxlint.utils import paragraphs - import pytest from sphinxlint.cli import main +from sphinxlint.utils import paragraphs FIXTURE_DIR = Path(__file__).resolve().parent / "fixtures" diff --git a/tests/test_xpass_friends.py b/tests/test_xpass_friends.py index 206b35068..2bb42aa0a 100644 --- a/tests/test_xpass_friends.py +++ b/tests/test_xpass_friends.py @@ -3,14 +3,13 @@ This is useful to avoid a sphinx-lint release to break many CIs. """ -from pathlib import Path import shlex +from pathlib import Path import pytest from sphinxlint.cli import main - FIXTURE_DIR = Path(__file__).resolve().parent / "fixtures" From 6a4866defba1d47cd3b9129c6fe8850ced4c7fb7 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 24 Nov 2023 15:31:27 +0200 Subject: [PATCH 3/7] Add some handy pre-commit hooks --- .github/workflows/tests.yml | 2 +- .pre-commit-config.yaml | 18 ++++++++++++++++++ README.md | 2 +- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ff94f3e2b..4592194cc 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,7 +13,7 @@ jobs: strategy: fail-fast: false matrix: - # when adding new versions, update the one used to test + # when adding new versions, update the one used to test # friend projects below to the latest one python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] os: [ubuntu-latest, macos-latest, windows-latest] diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9f808c4ed..95bec58d5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,3 +9,21 @@ repos: rev: 23.11.0 hooks: - id: black + + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: check-case-conflict + - id: check-merge-conflict + - id: check-toml + - id: check-yaml + - id: debug-statements + - id: end-of-file-fixer + exclude: tests/fixtures/xfail/missing-newline-at-end-of-file.rst + - id: trailing-whitespace + exclude: tests/fixtures/xfail/trailing-whitespaces.rst + + - repo: meta + hooks: + - id: check-hooks-apply + - id: check-useless-excludes diff --git a/README.md b/README.md index afb5200ee..39541db9c 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ $ docutils --writer=pseudoxml tests/fixtures/xpass/role-in-code-sample.rst 1. Make sure that the [CI tests pass](https://github.com/sphinx-contrib/sphinx-lint/actions) and optionally double-check locally with "friends projects" by running: - + sh download-more-tests.sh python -m pytest 2. Go on the [Releases page](https://github.com/sphinx-contrib/sphinx-lint/releases) From 8b0379add8ee78ac766fd2310c17332ba9f270f5 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 24 Nov 2023 15:32:37 +0200 Subject: [PATCH 4/7] Apply common format to pyproject.toml and validate --- .pre-commit-config.yaml | 10 ++++++++++ pyproject.toml | 33 +++++++++++++++++++-------------- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 95bec58d5..4dd8c58ff 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -23,6 +23,16 @@ repos: - id: trailing-whitespace exclude: tests/fixtures/xfail/trailing-whitespaces.rst + - repo: https://github.com/tox-dev/pyproject-fmt + rev: 1.5.1 + hooks: + - id: pyproject-fmt + additional_dependencies: [tox] + + - repo: https://github.com/abravalheri/validate-pyproject + rev: v0.15 + hooks: + - id: validate-pyproject - repo: meta hooks: - id: check-hooks-apply diff --git a/pyproject.toml b/pyproject.toml index a0cf7db96..d97348ea4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,31 +14,36 @@ authors = [ {name = "Georg Brandl", email = "georg@python.org"}, {name = "Julien Palard", email = "julien@palard.fr"}, ] +requires-python = ">= 3.8" classifiers = [ - "Development Status :: 5 - Production/Stable", - "Topic :: Documentation :: Sphinx", - "Intended Audience :: Developers", - "License :: OSI Approved :: Python Software Foundation License", - "Natural Language :: English", - "Programming Language :: Python :: 3", + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "License :: OSI Approved :: Python Software Foundation License", + "Natural Language :: English", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Topic :: Documentation :: Sphinx", +] +dynamic = [ + "version", ] -requires-python = ">= 3.8" dependencies = [ - "regex", - "polib", + "polib", + "regex", ] -dynamic = ["version"] - - [project.optional-dependencies] tests = [ "pytest", "pytest-cov", ] [project.urls] -Repository = "https://github.com/sphinx-contrib/sphinx-lint" Changelog = "https://github.com/sphinx-contrib/sphinx-lint/releases" - +Repository = "https://github.com/sphinx-contrib/sphinx-lint" [project.scripts] sphinx-lint = "sphinxlint.cli:main" From 3d2f0f7dbc371a25e5897d1925c8180d98cb55cb Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 24 Nov 2023 15:33:03 +0200 Subject: [PATCH 5/7] Format tox.ini --- .pre-commit-config.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4dd8c58ff..8dde961c5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -33,6 +33,12 @@ repos: rev: v0.15 hooks: - id: validate-pyproject + + - repo: https://github.com/tox-dev/tox-ini-fmt + rev: 1.3.1 + hooks: + - id: tox-ini-fmt + - repo: meta hooks: - id: check-hooks-apply From 5b0381b348bb1b9ad831f085e11efc3bd282c347 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 24 Nov 2023 15:33:55 +0200 Subject: [PATCH 6/7] autoupdate_schedule: quarterly --- .pre-commit-config.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8dde961c5..7447b5ce2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -43,3 +43,6 @@ repos: hooks: - id: check-hooks-apply - id: check-useless-excludes + +ci: + autoupdate_schedule: quarterly From 3eb6753b93d4e3c3b31e3c0b389f765b4bbecfd6 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 24 Nov 2023 11:10:23 -0700 Subject: [PATCH 7/7] Ignore UP038: it makes code slower and more verbose Co-authored-by: Alex Waygood --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index d97348ea4..69bb7c8cf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -72,6 +72,7 @@ extend-ignore = [ "E221", # Multiple spaces before operator "E226", # Missing whitespace around arithmetic operator "E241", # Multiple spaces after ',' + "UP038", # makes code slower and more verbose ] [tool.pylint.variables]