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
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?
(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.
Yep, let's keep this one simpler.
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?
Then install pre-commit into the venv (this is the current behaviour).
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
Or simpler:
system prek
venv pre-commit
In practice, the only way a tool ends up in the venv is if the Makefile installs it when not found.
Then install pre-commit into the venv (this is the current behaviour).
Or simpler:
system prek
venv pre-commit
In practice, the only way a tool ends up in the venv is if the Makefile installs it when not found.
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.
Also, what about the developer guide? Should it mention prek at all? Because that would put additional strain on the developer, first to decide which tool to use and then potentially manually installing something.
My understanding is that most of performance improvements from prek for a cold cache comes from using uv to install hooks. Perhaps instead of switching to an entirely different tool, we could introduce pre-commit-uv which sees significant speedups for the cold cache case without needing to change anything else:
Cold cache:
Benchmark 1: prek run --all-files
Time (mean ± σ): 6.054 s ± 0.083 s [User: 9.335 s, System: 3.220 s]
Range (min … max): 5.954 s … 6.160 s 5 runs
Benchmark 2: pre-commit run --all-files
Time (mean ± σ): 20.475 s ± 0.273 s [User: 25.388 s, System: 4.260 s]
Range (min … max): 20.194 s … 20.922 s 5 runs
Benchmark 3: pre-commit run --all-files (with pre-commit-uv)
Time (mean ± σ): 13.364 s ± 0.254 s [User: 20.372 s, System: 3.699 s]
Range (min … max): 13.070 s … 13.661 s 5 runs
Warm cache:
Benchmark 1: prek run --all-files
Time (mean ± σ): 2.646 s ± 0.007 s [User: 3.873 s, System: 1.666 s]
Range (min … max): 2.635 s … 2.652 s 5 runs
Benchmark 2: pre-commit run --all-files
Time (mean ± σ): 3.126 s ± 0.002 s [User: 11.106 s, System: 1.821 s]
Range (min … max): 3.124 s … 3.130 s 5 runs
Benchmark 3: pre-commit run --all-files (with pre-commit-uv)
Time (mean ± σ): 3.105 s ± 0.011 s [User: 10.940 s, System: 1.833 s]
Range (min … max): 3.092 s … 3.117 s 5 runs
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
hugovk commentedon Dec 29, 2025
Yep, let's keep this one simpler.
Then install pre-commit into the venv (this is the current behaviour).
Or simpler:
In practice, the only way a tool ends up in the venv is if the Makefile installs it when not found.
juhannc commentedon Dec 29, 2025
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.
Also, what about the developer guide? Should it mention prek at all? Because that would put additional strain on the developer, first to decide which tool to use and then potentially manually installing something.
emmatyping commentedon Dec 30, 2025
My understanding is that most of performance improvements from prek for a cold cache comes from using
uvto install hooks. Perhaps instead of switching to an entirely different tool, we could introduce pre-commit-uv which sees significant speedups for the cold cache case without needing to change anything else:Cold cache:
Warm cache:
hugovk commentedon Dec 30, 2025
A bigger improvement comes from prek cloning/installing repos in parallel.
prek's 6.1s is a lot better than pre-commit-uv's 13.4s (and pre-commit's 20.5s).
prek also reimplements some hooks in Rust, which helps with warm cache speeds.
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.