Skip to content

Commit 35b74bc

Browse files
AlexWaygoodAvasam
andauthored
Rework our linting setup (#11522)
Co-authored-by: Avasam <[email protected]>
1 parent 1c40e64 commit 35b74bc

File tree

9 files changed

+75
-87
lines changed

9 files changed

+75
-87
lines changed

.github/workflows/tests.yml

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -44,27 +44,6 @@ jobs:
4444
python-version: "3.11"
4545
- run: ./tests/check_new_syntax.py
4646

47-
ruff:
48-
name: Lint with Ruff
49-
runs-on: ubuntu-latest
50-
steps:
51-
- uses: actions/checkout@v4
52-
- uses: chartboost/ruff-action@v1
53-
with:
54-
version: "0.2.1" # must match .pre-commit-config.yaml and requirements-test.txt
55-
56-
flake8:
57-
name: Lint with Flake8
58-
runs-on: ubuntu-latest
59-
steps:
60-
- uses: actions/checkout@v4
61-
- uses: actions/setup-python@v4
62-
with:
63-
python-version: "3.11"
64-
- run: curl -LsSf https://astral.sh/uv/install.sh | sh
65-
- run: uv pip install -r requirements-tests.txt --system
66-
- run: flake8 --color always
67-
6847
pytype:
6948
name: Run pytype against the stubs
7049
runs-on: ubuntu-latest

.pre-commit-config.yaml

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,30 @@ repos:
88
- id: check-toml
99
- id: check-merge-conflict
1010
- id: mixed-line-ending
11+
args: [--fix=lf]
1112
- id: check-case-conflict
13+
- repo: https://github.com/astral-sh/ruff-pre-commit
14+
rev: v0.3.0 # must match requirements-tests.txt
15+
hooks:
16+
- id: ruff
17+
name: Run ruff on stubs, tests and scripts
18+
args: ["--exit-non-zero-on-fix"]
19+
- id: ruff
20+
# Run this separately because we don't really want
21+
# to use --unsafe-fixes for all rules
22+
name: Remove unnecessary `sys.version_info` blocks
23+
args: ["--exit-non-zero-on-fix", "--select=UP036", "--unsafe-fixes"]
24+
- id: ruff
25+
# Very few rules are useful to run on our test cases;
26+
# we explicitly enumerate them here:
27+
name: Run ruff on the test cases
28+
args: ["--exit-non-zero-on-fix", "--select=FA,I", "--no-force-exclude", "--unsafe-fixes"]
29+
files: '.*test_cases/.+\.py$'
1230
- repo: https://github.com/psf/black-pre-commit-mirror
1331
rev: 24.1.1 # must match requirements-tests.txt
1432
hooks:
1533
- id: black
1634
language_version: python3.10
17-
- repo: https://github.com/astral-sh/ruff-pre-commit
18-
rev: v0.2.1 # must match requirements-tests.txt and tests.yml
19-
hooks:
20-
- id: ruff
21-
args: [--exit-non-zero-on-fix, --fix-only]
2235
- repo: https://github.com/pycqa/flake8
2336
rev: 7.0.0 # must match requirements-tests.txt
2437
hooks:
@@ -28,11 +41,13 @@ repos:
2841
- "flake8-pyi==24.1.0" # must match requirements-tests.txt
2942
types: [file]
3043
types_or: [python, pyi]
44+
- repo: meta
45+
hooks:
46+
- id: check-hooks-apply
3147

3248
ci:
3349
autofix_commit_msg: "[pre-commit.ci] auto fixes from pre-commit.com hooks"
3450
autofix_prs: true
3551
autoupdate_commit_msg: "[pre-commit.ci] pre-commit autoupdate"
3652
autoupdate_schedule: quarterly
37-
skip: [flake8]
3853
submodules: false

CONTRIBUTING.md

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -87,34 +87,25 @@ terminal to install all non-pytype requirements:
8787
## Code formatting
8888

8989
The code is formatted using [`Black`](https://github.com/psf/black).
90-
Various other autofixes are
91-
also performed by [`Ruff`](https://github.com/astral-sh/ruff).
90+
Various other autofixes and lint rules are
91+
also performed by [`Ruff`](https://github.com/astral-sh/ruff) and
92+
[`Flake8`](https://github.com/pycqa/flake8),
93+
with plugins [`flake8-pyi`](https://github.com/pycqa/flake8-pyi),
94+
and [`flake8-noqa`](https://github.com/plinss/flake8-noqa).
9295

9396
The repository is equipped with a [`pre-commit.ci`](https://pre-commit.ci/)
9497
configuration file. This means that you don't *need* to do anything yourself to
95-
run the code formatters. When you push a commit, a bot will run those for you
96-
right away and add a commit to your PR.
97-
98-
That being said, if you *want* to run the checks locally when you commit,
99-
you're free to do so. Either run the following manually...
98+
run the code formatters or linters. When you push a commit, a bot will run
99+
those for you right away and add any autofixes to your PR. Anything
100+
that can't be autofixed will show up as a CI failure, hopefully with an error
101+
message that will make it clear what's gone wrong.
100102

101-
```bash
102-
(.venv)$ ruff check .
103-
(.venv)$ black .
104-
```
105-
106-
...Or install the pre-commit hooks: please refer to the
107-
[pre-commit](https://pre-commit.com/) documentation.
108-
109-
Our code is also linted using [`Flake8`](https://github.com/pycqa/flake8),
110-
with plugins [`flake8-pyi`](https://github.com/pycqa/flake8-pyi),
111-
and [`flake8-noqa`](https://github.com/plinss/flake8-noqa).
112-
As with our other checks, running
113-
Flake8 before filing a PR is not required. However, if you wish to run Flake8
114-
locally, install the test dependencies as outlined above, and then run:
103+
That being said, if you *want* to run the formatters and linters locally
104+
when you commit, you're free to do so. To use the same configuration as we use
105+
in CI, we recommend doing this via pre-commit:
115106

116107
```bash
117-
(.venv)$ flake8 .
108+
(.venv)$ pre-commit run --all-files
118109
```
119110

120111
## Where to make changes

pyproject.toml

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@ exclude = [
2424
]
2525

2626
[tool.ruff.lint]
27+
# Disable all rules on test cases by default:
28+
# test cases often deliberately contain code
29+
# that might not be considered idiomatic or modern.
30+
#
31+
# Note: some rules that are specifically useful to the test cases
32+
# are invoked via separate runs of ruff in pre-commit:
33+
# see our .pre-commit-config.yaml file for details
34+
exclude = ["**/test_cases/**/*.py"]
2735
select = [
2836
"B", # flake8-bugbear
2937
"FA", # flake8-future-annotations
@@ -48,12 +56,8 @@ ignore = [
4856
]
4957

5058
[tool.ruff.lint.per-file-ignores]
51-
# Disable "modernization" rules with autofixes from test cases as they often
52-
# deliberately contain code that might not be considered idiomatic or modern
53-
# These can be run manually once in a while
54-
"**/test_cases/**/*.py" = ["UP"]
5559
"*.pyi" = [
56-
# Most flake8-bugbear rules don't apply for third-party stubs like typeshed,
60+
# Most flake8-bugbear rules don't apply for third-party stubs like typeshed.
5761
# B033 could be slightly useful but Ruff doesn't have per-file select
5862
"B", # flake8-bugbear
5963
]

requirements-tests.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@ flake8-pyi==24.1.0 # must match .pre-commit-config.yaml
88
mypy==1.8.0
99
pre-commit-hooks==4.5.0 # must match .pre-commit-config.yaml
1010
pytype==2024.2.27; platform_system != "Windows" and python_version < "3.12"
11-
ruff==0.2.1 # must match .pre-commit-config.yaml and tests.yml
11+
ruff==0.3.0 # must match .pre-commit-config.yaml
1212

1313
# Libraries used by our various scripts.
1414
aiohttp==3.9.3
1515
packaging==23.2
1616
pathspec>=0.11.1
17+
pre-commit
1718
pyyaml==6.0.1
1819
stubdefaulter==0.1.0
1920
termcolor>=2.3

scripts/generate_proto_stubs.sh

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,7 @@ PYTHON_PROTOBUF_DIR="protobuf-$PYTHON_PROTOBUF_VERSION"
4545
VENV=venv
4646
python3 -m venv "$VENV"
4747
source "$VENV/bin/activate"
48-
pip install -r "$REPO_ROOT/requirements-tests.txt" # for Black and Ruff
49-
50-
# Install mypy-protobuf
51-
pip install mypy-protobuf=="$MYPY_PROTOBUF_VERSION"
48+
pip install pre-commit mypy-protobuf=="$MYPY_PROTOBUF_VERSION"
5249

5350
# Remove existing pyi
5451
find "$REPO_ROOT/stubs/protobuf/" -name '*_pb2.pyi' -delete
@@ -73,9 +70,9 @@ PROTO_FILES=$(grep "GenProto.*google" $PYTHON_PROTOBUF_DIR/python/setup.py | \
7370
# shellcheck disable=SC2086
7471
protoc_install/bin/protoc --proto_path="$PYTHON_PROTOBUF_DIR/src" --mypy_out="relax_strict_optional_primitives:$REPO_ROOT/stubs/protobuf" $PROTO_FILES
7572

76-
ruff check "$REPO_ROOT/stubs/protobuf" --fix-only
77-
ruff check "$REPO_ROOT/stubs/protobuf" --fix-only --select=UP036 --unsafe-fixes
78-
black "$REPO_ROOT/stubs/protobuf"
73+
# use `|| true` so the script still continues even if a pre-commit hook
74+
# applies autofixes (which will result in a nonzero exit code)
75+
pre-commit run --files "$REPO_ROOT/stubs/protobuf" || true
7976

8077
sed --in-place="" \
8178
"s/extra_description = .*$/extra_description = \"Generated using [mypy-protobuf==$MYPY_PROTOBUF_VERSION](https:\/\/github.com\/nipunn1313\/mypy-protobuf\/tree\/v$MYPY_PROTOBUF_VERSION) on protobuf==$PYTHON_PROTOBUF_VERSION\"/" \

scripts/runtests.py

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -81,17 +81,8 @@ def main() -> None:
8181
stubtest_result: subprocess.CompletedProcess[bytes] | None = None
8282
pytype_result: subprocess.CompletedProcess[bytes] | None = None
8383

84-
# Run formatters first. Order matters.
85-
print("\nRunning Ruff...")
86-
subprocess.run([sys.executable, "-m", "ruff", "check", path])
87-
print("\nRunning Black...")
88-
black_result = subprocess.run([sys.executable, "-m", "black", path])
89-
if black_result.returncode == 123:
90-
print("Could not run tests due to an internal error with Black. See above for details.", file=sys.stderr)
91-
sys.exit(black_result.returncode)
92-
93-
print("\nRunning Flake8...")
94-
flake8_result = subprocess.run([sys.executable, "-m", "flake8", path])
84+
print("\nRunning pre-commit...")
85+
pre_commit_result = subprocess.run(["pre-commit", "run", "--all-files"])
9586

9687
print("\nRunning check_consistent.py...")
9788
check_consistent_result = subprocess.run([sys.executable, "tests/check_consistent.py"])
@@ -187,7 +178,7 @@ def main() -> None:
187178

188179
any_failure = any(
189180
[
190-
flake8_result.returncode,
181+
pre_commit_result.returncode,
191182
check_consistent_result.returncode,
192183
check_new_syntax_result.returncode,
193184
pyright_returncode,
@@ -203,7 +194,18 @@ def main() -> None:
203194
print(colored("\n\n--- TEST SUMMARY: One or more tests failed. See above for details. ---\n", "red"))
204195
else:
205196
print(colored("\n\n--- TEST SUMMARY: All tests passed! ---\n", "green"))
206-
print("Flake8:", _SUCCESS if flake8_result.returncode == 0 else _FAILED)
197+
if pre_commit_result.returncode == 0:
198+
print("pre-commit", _SUCCESS)
199+
else:
200+
print("pre-commit", _FAILED)
201+
print(
202+
"""\
203+
Check the output of pre-commit for more details.
204+
This could mean that there's a lint failure on your code,
205+
but could also just mean that one of the pre-commit tools
206+
applied some autofixes. If the latter, you may want to check
207+
that the autofixes did sensible things."""
208+
)
207209
print("Check consistent:", _SUCCESS if check_consistent_result.returncode == 0 else _FAILED)
208210
print("Check new syntax:", _SUCCESS if check_new_syntax_result.returncode == 0 else _FAILED)
209211
if pyright_skipped:

scripts/sync_tensorflow_protobuf_stubs.sh

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ TENSORFLOW_VERSION=2.12.1
1515
# protobuf<4.
1616
MYPY_PROTOBUF_VERSION=3.5.0
1717

18-
pip install mypy-protobuf=="$MYPY_PROTOBUF_VERSION"
18+
pip install pre-commit mypy-protobuf=="$MYPY_PROTOBUF_VERSION"
1919

2020
cd "$(dirname "$0")" > /dev/null
2121
cd ../stubs/tensorflow
@@ -61,9 +61,9 @@ rm tensorflow/compiler/xla/service/hlo_execution_profile_data_pb2.pyi \
6161
tensorflow/core/protobuf/worker_service_pb2.pyi \
6262
tensorflow/core/util/example_proto_fast_parsing_test_pb2.pyi
6363

64-
ruff check "$REPO_ROOT/stubs/tensorflow/tensorflow" --fix-only
65-
ruff check "$REPO_ROOT/stubs/protobuf" --fix-only --select=UP036 --unsafe-fixes
66-
black "$REPO_ROOT/stubs/tensorflow/tensorflow"
64+
# use `|| true` so the script still continues even if a pre-commit hook
65+
# applies autofixes (which will result in a nonzero exit code)
66+
pre-commit run --files "$REPO_ROOT/stubs/tensorflow/tensorflow" || true
6767

6868
sed --in-place="" \
6969
"s/extra_description = .*$/extra_description = \"Partially generated using [mypy-protobuf==$MYPY_PROTOBUF_VERSION](https:\/\/github.com\/nipunn1313\/mypy-protobuf\/tree\/v$MYPY_PROTOBUF_VERSION) on tensorflow==$TENSORFLOW_VERSION\"/" \

tests/check_consistent.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,6 @@ def get_txt_requirements() -> dict[str, SpecifierSet]:
145145
class PreCommitConfigRepos(TypedDict):
146146
hooks: list[dict[str, str]]
147147
repo: str
148-
rev: str
149148

150149

151150
class PreCommitConfig(TypedDict):
@@ -158,16 +157,16 @@ def get_precommit_requirements() -> dict[str, SpecifierSet]:
158157
yam: PreCommitConfig = yaml.load(precommit, Loader=yaml.Loader)
159158
precommit_requirements: dict[str, SpecifierSet] = {}
160159
for repo in yam["repos"]:
161-
if not repo.get("python_requirement", True):
160+
package_rev = repo.get("rev")
161+
if not isinstance(package_rev, str):
162162
continue
163-
hook = repo["hooks"][0]
164163
package_name = Path(urllib.parse.urlparse(repo["repo"]).path).name
165-
package_rev = repo["rev"].removeprefix("v")
166-
package_specifier = SpecifierSet(f"=={package_rev}")
164+
package_specifier = SpecifierSet(f"=={package_rev.removeprefix('v')}")
167165
precommit_requirements[package_name] = package_specifier
168-
for additional_req in hook.get("additional_dependencies", ()):
169-
req = Requirement(additional_req)
170-
precommit_requirements[req.name] = req.specifier
166+
for hook in repo["hooks"]:
167+
for additional_req in hook.get("additional_dependencies", ()):
168+
req = Requirement(additional_req)
169+
precommit_requirements[req.name] = req.specifier
171170
return precommit_requirements
172171

173172

0 commit comments

Comments
 (0)