You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Replace pre-commit with prek for faster linting #143148
First, I hope this is the correct issue template. If not, please let me know, and I can resubmit the proposal using the proper template.
prek is a "reimagined version of pre-commit, [...] designed to be a faster, dependency-free and drop-in alternative".
It has already been adopted by major projects from Apache, FastAPI, and Django (see prek/Who is using prek?).
Running prek against the entire CPython codebase yields a speedup of almost 2.5x without a cache and 1.25x with a warm cache.
hyperfine --warmup 2 --runs 5 \
--prepare "prek cache clean""prek run --all-files" \
--prepare "pre-commit clean""pre-commit run --all-files"
Benchmark 1: prek run --all-files
Time (mean ± σ): 19.835 s ± 0.290 s [User: 50.857 s, System: 6.248 s]
Range (min … max): 19.527 s … 20.201 s 5 runs
Benchmark 2: pre-commit run --all-files
Time (mean ± σ): 48.944 s ± 0.448 s [User: 76.063 s, System: 6.096 s]
Range (min … max): 48.522 s … 49.681 s 5 runs
Summary
prek run --all-files ran
2.47 ± 0.04 times faster than pre-commit run --all-files
hyperfine --warmup 2 --runs 5 \
"prek run --all-files" \
"pre-commit run --all-files"
Benchmark 1: prek run --all-files
Time (mean ± σ): 6.763 s ± 0.124 s [User: 35.990 s, System: 2.134 s]
Range (min … max): 6.589 s … 6.900 s 5 runs
Benchmark 2: pre-commit run --all-files
Time (mean ± σ): 8.537 s ± 0.048 s [User: 43.797 s, System: 1.903 s]
Range (min … max): 8.484 s … 8.608 s 5 runs
Summary
prek run --all-files ran
1.26 ± 0.02 times faster than pre-commit run --all-files
Has this already been discussed elsewhere?
This is a minor feature, which does not need previous discussion elsewhere
I don't think we should yet wholesale replace, prek is still on 0.x, and explicitly notes in the README that it is not at parity etc.
What's blocking parity with pre-commit is the lack of the gc command to clean only unused objects vs. cleaning the entire cache and some missing languages for hooks that pre-commit supports (cf. prek/TODO). Given preks speed in installing new hooks, I feel like the missing gc command is not critical. And regarding the missing language support, none of those are required for the hooks in CPython. But I understand if you are hesitant to switch to prek. In that case, we can leave the issue and PR open until prek evolves further.
Personally, I don't want to switch to yet another tool. I'm not against changing the CI itself but I'm against changing something that works with my local configuration (that is, I'm ok with the Makefile picking either prek or pre-commit, but I don't want to be forced to install it).
In that case, we can leave the issue and PR open until prek evolves further.
Usually, we prefer closing issues & PRs instead of keeping them open as we never know when it will be "ready" (it's not a problem to reopen the issue and PR afterwards).
I'm against changing something that works with my local configuration (that is, I'm ok with the Makefile picking either prek or pre-commit, but I don't want to be forced to install it).
I totally understand your hesitation to change a running system. What about making prek the new default for new developers (as per the devguide), but changing the Makefile to fallback to pre-commit? As of right now, this would surely be possible, although I see prek developing features pre-commit never implemented (and probably never will), such as priority, which allows for a custom order of execution and running hooks in parallel, potentially increasing the speed gain even further. This is obviously stuff for a separate issue, but would require everyone to switch to prek. But yes, in the meantime, a two-pronged approach would work and would be transparent to the developer. Both tools would work just fine.
I've been using prek for months (https://hugovk.dev/blog/2025/ready-prek-go/) and have switched most of my projects over. I'm using it locally with Git for CPython (not via make check) and all the other python/ repos too.
I don't think we should yet wholesale replace, prek is still on 0.x, and explicitly notes in the README that it is not at parity etc.
Ruff and uv are also still on 0.x :) I think all the hooks I need across all my projects are supported by prek. Everything we use here is already supported.
Personally, I don't want to switch to yet another tool. I'm not against changing the CI itself but I'm against changing something that works with my local configuration (that is, I'm ok with the Makefile picking either prek or pre-commit, but I don't want to be forced to install it).
Fair enough, although the Makefile uses a venv and does everything for you -- when you run make check, if pre-commit isn't in the venv, it installs and uses it from the venv. So if we used prek in the venv, it would take care of that for you too, and you wouldn't notice, except for much faster runs.
Looking at the numbers again. ~8.5s to ~6.5s isn't massively compelling for the extra complexity here.
Two seconds is a good improvement for pre-commit, when run locally, this is a worthwhile improvement. And ~20s vs ~49s is huge for uncached! (With extra config, prek also allows running hooks in parallel, which would be even quicker.)
However, using prek if pre-installed feels reasonable, we already do this with uv.
👍
I suggest:
CI: Use j178/prek-action
Makefile: If others don't want to use prek in the venv, then we can have it check if a system prek exists, and use that; otherwise fallback to the current pre-commit in the venv.
I also want to mention that some core devs were already skeptical about pre-commit (IIRC, @encukou) as it would install something that one does not necessarily want (even if isolated in a venv). Likewise, django (framework, not the website project) rejected the change.
(With extra config, prek also allows running hooks in parallel, which would be even quicker.)
Exactly, although I guess that should be a different pull request.
I suggest:
CI: Use j178/prek-action
Makefile: If others don't want to use prek in the venv, then we can have it check if a system prek exists, and use that; otherwise fallback to the current pre-commit in the venv.
So, just to get it straight, you propose to first look for a system prek. If that doesn't exist, look for pre-commit from the venv. And what if that also doesn't exist? Should pre-commit be installed or prek?
I would propose the following to ease the way for a future with full prek adoption, especially with new developers coming in:
system prek
system pre-commit
venv pre-commit
venv prek
Where prek will be installed in the venv if all checks fail.
That way, developers who are already accustomed to pre-commit will stay with pre-commit (and have an easy way to do so by installing pre-commit system-wide). But new developers will start with prek and won't have to switch in the future
Likewise, django (framework, not the website project) rejected the change.
That was a different change: django/django#20449 replaces Flake8+isort+Black in the CI with prek. They don't already run pre-commit in CI, so it's not a drop-in replacement.
Indeed, the Django website did accept the simpler pre-commit -> prek replacement in django/djangoproject.com#2252, which is similar to this PR.
IMHO the more important feature prek 0.2.23+ has is priority - hooks that modify file content (ruff format, etc) can be run sequentially, and read-only hooks (ruff check, mypy, github workflow linters) can be run in parallel. For large number of read-only hooks the speed up can be huge.
Hook installation speed with cold cache is a nice improvement, but this is important only in CI workflows, not on local machine.
But then there is no clear way to unite the tooling again in the future. I don't think a discrepancy between the tools used locally and in the CI is a clean approach.
And I don't want tools that are not widely used as others. I'm ok with ruff, etc because those are tools I can trust and others can trust. It's more about how widespread prek is. If it were used as much as pre-commit I'd probably have no issue with it, but for now, I definitely want to see whether the project will be as wide spread as pre-commit. To be clear, I'm willing to cast my core dev vote against the change if I'm forced to install prek. What I eventually want is one tool, not multiple ones used multiple projects that then pollute my global ~/.cache folder. FTR, I'm using uvx pre-commit because I have too many Python versions installed and my default Python version (thanks to my OS) is Python 3.6 (though I may need to update it at some point, I was waiting for OpenSUSE Leap 16.0 to come out) and I prefer having very small system-wide installations.
And what if that also doesn't exist? Should pre-commit be installed or prek?
In this case, I would install nothing and leave it to the developer. It's their choice to install additional software and their choice whether they want to trust it.
IMHO the more important feature prek 0.2.23+ has is priority
There is a huge warning about it:
The following configuration keys are prek-specific and are not supported by the original pre-commit (at least at the time of writing). If you run the same config with pre-commit, it may warn about unexpected/unknown keys.
In this case, using priority but ending up with warnings is definitely something I don't want either.
OK, CI is updated to use prek, we can revisit later about using it in the Makefile when adoption is higher. Right now, pre-commit has 81m/month downloads, and prek is 14m/month.
If you're not using the Makefile, you can already use prek instead of pre-commit if you prefer.
Regarding priority, I have doubts as to the correctness of running hooks in parallel, that seems likely to have unexpected failure modes.
I think the trick is to keep the existing order for hooks that can write (that is, don't define a priority, and they'll be run in sequential order), but the read-only hooks can then run in parallel afterwards (so give them all something like priority: 100).
I'm running with this in a .prek.yaml (via an alias) and haven't seen any problems (yet). It's also 0.8 seconds faster, 2.6s compared to 3.4s without (and 2.1s faster than pre-commit's 4.7s).
❯ hyperfine --warmup 1 --runs 3 \
"prek run --all-files -c .prek.yaml" \
"prek run --all-files" \
"pre-commit run --all-files"
Benchmark 1: prek run --all-files -c .prek.yaml
Time (mean ± σ): 2.606 s ± 0.003 s [User: 13.918 s, System: 4.674 s]
Range (min … max): 2.602 s … 2.608 s 3 runs
Benchmark 2: prek run --all-files
Time (mean ± σ): 3.396 s ± 0.144 s [User: 13.855 s, System: 5.581 s]
Range (min … max): 3.231 s … 3.500 s 3 runs
Benchmark 3: pre-commit run --all-files
Time (mean ± σ): 4.728 s ± 0.330 s [User: 18.064 s, System: 4.610 s]
Range (min … max): 4.409 s … 5.067 s 3 runs
Summary
prek run --all-files -c .prek.yaml ran
1.30 ± 0.06 times faster than prek run --all-files
1.81 ± 0.13 times faster than pre-commit run --all-files
Anyway, we can also reconsider adding priority in the future when switching to prek in the Makefile.
I recommend migrating directly to the pre-commit.ci GitHub app instead of the approach in this issue.
Running prek within GitHub Actions lacks persistent caching, forcing CI to re-download and reinstall all hooks from scratch on every run. In contrast, the pre-commit.ci GitHub app caches environments efficiently, ensuring time is spent only on executing hooks rather than setup. This would be significantly faster than both existing options.
From what I can see, prek-actiondid reuse the cached hooks and didn’t reinstall them.
Cache hit for: prek-v1|Linux|X64||00ab2e29f83edd65b54f623a840eb7eaac02f3410a1103a298c9f5e0cfd9fb1c
Received 87066013 of 87066013 (100.0%), 113.1 MBs/sec
Cache Size: ~83 MB (87066013 B)
/usr/bin/tar -xf /home/runner/work/_temp/44d88d24-8d1d-4b2e-9a1a-d22fd1a7d932/cache.tzst -P -C /home/runner/work/cpython/cpython --use-compress-program unzstd
Cache restored successfully
Cache restored from key: prek-v1|Linux|X64||00ab2e29f83edd65b54f623a840eb7eaac02f3410a1103a298c9f5e0cfd9fb1c
2026-03-23T05:12:52.889535Z DEBUG Found installed environment for hook `ruff-check` at `/home/runner/.cache/prek/hooks/python-lxWWQdrVxM97y9gmhlYn`
2026-03-23T05:12:52.889547Z DEBUG Found installed environment for hook `ruff-check` at `/home/runner/.cache/prek/hooks/python-lxWWQdrVxM97y9gmhlYn`
2026-03-23T05:12:52.889550Z DEBUG Found installed environment for hook `ruff-check` at `/home/runner/.cache/prek/hooks/python-lxWWQdrVxM97y9gmhlYn`
2026-03-23T05:12:52.889554Z DEBUG Found installed environment for hook `ruff-check` at `/home/runner/.cache/prek/hooks/python-lxWWQdrVxM97y9gmhlYn`
2026-03-23T05:12:52.889557Z DEBUG Found installed environment for hook `ruff-check` at `/home/runner/.cache/prek/hooks/python-lxWWQdrVxM97y9gmhlYn`
2026-03-23T05:12:52.889561Z DEBUG Found installed environment for hook `ruff-check` at `/home/runner/.cache/prek/hooks/python-lxWWQdrVxM97y9gmhlYn`
2026-03-23T05:12:52.889564Z DEBUG Found installed environment for hook `ruff-check` at `/home/runner/.cache/prek/hooks/python-lxWWQdrVxM97y9gmhlYn`
2026-03-23T05:12:52.889568Z DEBUG Found installed environment for hook `ruff-check` at `/home/runner/.cache/prek/hooks/python-lxWWQdrVxM97y9gmhlYn`
2026-03-23T05:12:52.889571Z DEBUG Found installed environment for hook `ruff-check` at `/home/runner/.cache/prek/hooks/python-lxWWQdrVxM97y9gmhlYn`
2026-03-23T05:12:52.889574Z DEBUG Found installed environment for hook `ruff-format` at `/home/runner/.cache/prek/hooks/python-lxWWQdrVxM97y9gmhlYn`
2026-03-23T05:12:52.889578Z DEBUG Found installed environment for hook `ruff-format` at `/home/runner/.cache/prek/hooks/python-lxWWQdrVxM97y9gmhlYn`
2026-03-23T05:12:52.889581Z DEBUG Found installed environment for hook `ruff-format` at `/home/runner/.cache/prek/hooks/python-lxWWQdrVxM97y9gmhlYn`
2026-03-23T05:12:52.889585Z DEBUG Found installed environment for hook `ruff-format` at `/home/runner/.cache/prek/hooks/python-lxWWQdrVxM97y9gmhlYn`
2026-03-23T05:12:52.889588Z DEBUG Found installed environment for hook `ruff-format` at `/home/runner/.cache/prek/hooks/python-lxWWQdrVxM97y9gmhlYn`
2026-03-23T05:12:52.889593Z DEBUG Found installed environment for hook `black` at `/home/runner/.cache/prek/hooks/python-mSN0r7l4SMxCSpFUuEhx`
2026-03-23T05:12:52.889597Z DEBUG Found installed environment for hook `remove-tabs` at `/home/runner/.cache/prek/hooks/python-RBUuRr1uTN0ATKQuH0N6`
2026-03-23T05:12:52.889607Z DEBUG Found installed environment for hook `check-case-conflict` at `/home/runner/.cache/prek/hooks/python-aZBFktYSFEwszQ3BaFhr`
2026-03-23T05:12:52.889611Z DEBUG Found installed environment for hook `check-merge-conflict` at `/home/runner/.cache/prek/hooks/python-aZBFktYSFEwszQ3BaFhr`
2026-03-23T05:12:52.889615Z DEBUG Found installed environment for hook `check-toml` at `/home/runner/.cache/prek/hooks/python-aZBFktYSFEwszQ3BaFhr`
2026-03-23T05:12:52.889618Z DEBUG Found installed environment for hook `check-yaml` at `/home/runner/.cache/prek/hooks/python-aZBFktYSFEwszQ3BaFhr`
2026-03-23T05:12:52.889621Z DEBUG Found installed environment for hook `end-of-file-fixer` at `/home/runner/.cache/prek/hooks/python-aZBFktYSFEwszQ3BaFhr`
2026-03-23T05:12:52.889625Z DEBUG Found installed environment for hook `end-of-file-fixer` at `/home/runner/.cache/prek/hooks/python-aZBFktYSFEwszQ3BaFhr`
2026-03-23T05:12:52.889628Z DEBUG Found installed environment for hook `trailing-whitespace` at `/home/runner/.cache/prek/hooks/python-aZBFktYSFEwszQ3BaFhr`
2026-03-23T05:12:52.889632Z DEBUG Found installed environment for hook `trailing-whitespace` at `/home/runner/.cache/prek/hooks/python-aZBFktYSFEwszQ3BaFhr`
2026-03-23T05:12:52.889636Z DEBUG Found installed environment for hook `check-dependabot` at `/home/runner/.cache/prek/hooks/python-IjRmuA7wrzrJpNj8HAx1`
2026-03-23T05:12:52.889640Z DEBUG Found installed environment for hook `check-github-workflows` at `/home/runner/.cache/prek/hooks/python-IjRmuA7wrzrJpNj8HAx1`
2026-03-23T05:12:52.889643Z DEBUG Found installed environment for hook `check-readthedocs` at `/home/runner/.cache/prek/hooks/python-IjRmuA7wrzrJpNj8HAx1`
2026-03-23T05:12:52.889647Z DEBUG Found installed environment for hook `zizmor` at `/home/runner/.cache/prek/hooks/python-ufsx3xz1oK0OYZO5eNzi`
2026-03-23T05:12:52.889651Z DEBUG Found installed environment for hook `sphinx-lint` at `/home/runner/.cache/prek/hooks/python-cRoa1LI13h9MPgizrWrl
Reacted by Johann Christensen, vivodi, Hugo van Kemenade and Metin Bektas
Activity
StanFromIreland commentedon Dec 24, 2025
CC @hugovk
AA-Turner commentedon Dec 24, 2025
I don't think we should yet wholesale replace, prek is still on 0.x, and explicitly notes in the README that it is not at parity etc.
However, using prek if pre-installed feels reasonable, we already do this with uv.
A
juhannc commentedon Dec 25, 2025
What's blocking parity with pre-commit is the lack of the
gccommand to clean only unused objects vs. cleaning the entire cache and some missing languages for hooks that pre-commit supports (cf. prek/TODO). Given preks speed in installing new hooks, I feel like the missinggccommand is not critical. And regarding the missing language support, none of those are required for the hooks in CPython. But I understand if you are hesitant to switch to prek. In that case, we can leave the issue and PR open until prek evolves further.picnixz commentedon Dec 25, 2025
Personally, I don't want to switch to yet another tool. I'm not against changing the CI itself but I'm against changing something that works with my local configuration (that is, I'm ok with the Makefile picking either prek or pre-commit, but I don't want to be forced to install it).
Usually, we prefer closing issues & PRs instead of keeping them open as we never know when it will be "ready" (it's not a problem to reopen the issue and PR afterwards).
juhannc commentedon Dec 25, 2025
I totally understand your hesitation to change a running system. What about making prek the new default for new developers (as per the devguide), but changing the Makefile to fallback to pre-commit? As of right now, this would surely be possible, although I see prek developing features pre-commit never implemented (and probably never will), such as priority, which allows for a custom order of execution and running hooks in parallel, potentially increasing the speed gain even further. This is obviously stuff for a separate issue, but would require everyone to switch to prek. But yes, in the meantime, a two-pronged approach would work and would be transparent to the developer. Both tools would work just fine.
AA-Turner commentedon Dec 25, 2025
Looking at the numbers again. ~8.5s to ~6.5s isn't massively compelling for the extra complexity here.
In terms of CI, linting is already one of the fastest jobs, and hence not on the critical path.
I think for now we should hold off on making this change. I'll close in a few days unless there's opinions to the contrary from other core devs.
A
hugovk commentedon Dec 26, 2025
I support this.
I've been using prek for months (https://hugovk.dev/blog/2025/ready-prek-go/) and have switched most of my projects over. I'm using it locally with Git for CPython (not via
make check) and all the otherpython/repos too.Ruff and uv are also still on 0.x :) I think all the hooks I need across all my projects are supported by prek. Everything we use here is already supported.
Fair enough, although the Makefile uses a venv and does everything for you -- when you run
make check, if pre-commit isn't in the venv, it installs and uses it from the venv. So if we used prek in the venv, it would take care of that for you too, and you wouldn't notice, except for much faster runs.Two seconds is a good improvement for pre-commit, when run locally, this is a worthwhile improvement. And ~20s vs ~49s is huge for uncached! (With extra config, prek also allows running hooks in parallel, which would be even quicker.)
👍
I suggest:
picnixz commentedon Dec 29, 2025
I also want to mention that some core devs were already skeptical about pre-commit (IIRC, @encukou) as it would install something that one does not necessarily want (even if isolated in a venv). Likewise, django (framework, not the website project) rejected the change.
juhannc commentedon Dec 29, 2025
Exactly, although I guess that should be a different pull request.
So, just to get it straight, you propose to first look for a system prek. If that doesn't exist, look for pre-commit from the venv. And what if that also doesn't exist? Should pre-commit be installed or prek?
I would propose the following to ease the way for a future with full prek adoption, especially with new developers coming in:
Where prek will be installed in the venv if all checks fail.
That way, developers who are already accustomed to pre-commit will stay with pre-commit (and have an easy way to do so by installing pre-commit system-wide). But new developers will start with prek and won't have to switch in the future
hugovk commentedon Dec 29, 2025
That was a different change: django/django#20449 replaces Flake8+isort+Black in the CI with prek. They don't already run pre-commit in CI, so it's not a drop-in replacement.
Indeed, the Django website did accept the simpler pre-commit -> prek replacement in django/djangoproject.com#2252, which is similar to this PR.
4 remaining items
dolfinus commentedon Dec 30, 2025
IMHO the more important feature prek 0.2.23+ has is priority - hooks that modify file content (ruff format, etc) can be run sequentially, and read-only hooks (ruff check, mypy, github workflow linters) can be run in parallel. For large number of read-only hooks the speed up can be huge.
Hook installation speed with cold cache is a nice improvement, but this is important only in CI workflows, not on local machine.
picnixz commentedon Dec 30, 2025
And I don't want tools that are not widely used as others. I'm ok with ruff, etc because those are tools I can trust and others can trust. It's more about how widespread
prekis. If it were used as much aspre-commitI'd probably have no issue with it, but for now, I definitely want to see whether the project will be as wide spread aspre-commit. To be clear, I'm willing to cast my core dev vote against the change if I'm forced to installprek. What I eventually want is one tool, not multiple ones used multiple projects that then pollute my global~/.cachefolder. FTR, I'm usinguvx pre-commitbecause I have too many Python versions installed and my default Python version (thanks to my OS) is Python 3.6 (though I may need to update it at some point, I was waiting for OpenSUSE Leap 16.0 to come out) and I prefer having very small system-wide installations.In this case, I would install nothing and leave it to the developer. It's their choice to install additional software and their choice whether they want to trust it.
There is a huge warning about it:
In this case, using priority but ending up with warnings is definitely something I don't want either.
emmatyping commentedon Dec 30, 2025
Regarding priority, I have doubts as to the correctness of running hooks in parallel, that seems likely to have unexpected failure modes.
I also agree with all of the points @picnixz made, especially that I don't think prek is widely used enough to adopt in CPython yet.
gh-143148: Replace pre-commit with prek in CI (#143149)
pythongh-143148: Replace pre-commit with prek in CI (pythonGH-143149)
pythongh-143148: Replace pre-commit with prek in CI (pythonGH-143149)
[3.13] gh-143148: Replace pre-commit with prek in CI (GH-143149) (#14…
[3.14] gh-143148: Replace pre-commit with prek in CI (GH-143149) (#14…
hugovk commentedon Jan 8, 2026
OK, CI is updated to use prek, we can revisit later about using it in the
Makefilewhen adoption is higher. Right now, pre-commit has 81m/month downloads, and prek is 14m/month.If you're not using the
Makefile, you can already use prek instead of pre-commit if you prefer.I think the trick is to keep the existing order for hooks that can write (that is, don't define a priority, and they'll be run in sequential order), but the read-only hooks can then run in parallel afterwards (so give them all something like
priority: 100).I'm running with this in a
.prek.yaml(via an alias) and haven't seen any problems (yet). It's also 0.8 seconds faster, 2.6s compared to 3.4s without (and 2.1s faster than pre-commit's 4.7s).Anyway, we can also reconsider adding
priorityin the future when switching to prek in theMakefile.pythongh-143148: Replace pre-commit with prek in CI (python#143149)
vivodi commentedon Mar 23, 2026
I recommend migrating directly to the pre-commit.ci GitHub app instead of the approach in this issue.
Running prek within GitHub Actions lacks persistent caching, forcing CI to re-download and reinstall all hooks from scratch on every run. In contrast, the pre-commit.ci GitHub app caches environments efficiently, ensuring time is spent only on executing hooks rather than setup. This would be significantly faster than both existing options.
I opened the issue #146317 Replace prek GitHub Action with pre-commit.ci GitHub app for better performance for this.
j178 commentedon Mar 23, 2026
@vivodi Did you check that? Where’s that claim coming from?
I just looked at the logs from the latest
lintjob:https://github.com/python/cpython/actions/runs/23422492043/job/68130457779
From what I can see,
prek-actiondid reuse the cached hooks and didn’t reinstall them.vivodi commentedon Mar 23, 2026
I just tested it—running via the pre-commit.ci GitHub app takes about 38 seconds, which is roughly the same as running
prekin GitHub Actions.Sorry for the confusion earlier, and apologies for the noise.