`#[pyfunction]` gets `METH_STATIC` since 0.28, which makes calls slower on CPython 3.11+
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 16.2k
- Forks
- 1k
- Avg merge
- 2d 6h
- Merged PRs (30d)
- 66
Description
Bug Description
Since PyO3 0.28, every module-level #[pyfunction] is created with the METH_STATIC flag. A function with one parameter has ml_flags 0xa2 (METH_FASTCALL | METH_KEYWORDS | METH_STATIC). With PyO3 0.27.2 it has 0x82.
The flag has two effects:
function.__self__isNone. With 0.27.2 it is the module, as it is for CPython's own builtin functions such asmath.sqrt.- On CPython 3.11 and newer, a call of a function that has parameters and no
**kwargsis about 2 to 4 ns slower. For a function with an empty body this is 10 to 21%. Functions without parameters and functions with**kwargsare not slower. CPython 3.9 and 3.10 are not affected.
I found this while benchmarking fastcrc after upgrading PyO3 from 0.26 to 0.29.2.
Steps to Reproduce
A reproduction is at https://github.com/overcat/pyo3-meth-static-repro. It defines pairs of functions with empty bodies. One function of each pair is a plain #[pyfunction]. The other one uses pass_module, which does not get the flag, and is the control.
git clone https://github.com/overcat/pyo3-meth-static-repro
cd pyo3-meth-static-repro
python -m venv .venv
.venv/bin/pip install .
.venv/bin/python repro.py
Output with CPython 3.14.6 and PyO3 0.29.2:
plain #[pyfunction] #[pyfunction(pass_module)]
ml_flags __self__ ns/call ml_flags __self__ ns/call difference
no parameters 0x0024 None 13.1 0x0004 module 13.0 +0.1 ns +1%
one parameter 0x00a2 None 19.6 0x0082 module 16.4 +3.3 ns +20%
**kwargs 0x0023 None 16.8 0x0003 module 16.7 +0.1 ns +1%
0x0020 = METH_STATIC
With PyO3 0.27.2, both columns show the same flags, __self__ is the module in both, and the differences are 0.0 ns.
Your operating system and version
Linux Mint 22.3 (kernel 7.0.0-31-generic, x86-64)
Your Python version (python --version)
Python 3.14.6
Your Rust version (rustc --version)
rustc 1.98.0 (88d9e12ae 2026-08-18)
Your PyO3 version
0.29.2 (also 0.28.2 and main at cb456da1; 0.27.2 is not affected)
How did you install python? Did you use a virtualenv?
mise (a precompiled python-build-standalone build), in a virtualenv created with "python -m venv"
Additional Info
This was introduced by #5581. The README of the reproduction explains the cause in PyO3 and in CPython. It also has the results for several PyO3 and CPython versions, the instruction counts and a workaround.
There is a candidate fix with a test: https://github.com/overcat/pyo3/commit/b3d1fd3277ee15d25943593aaab2cf17d20e7198. If you agree with the approach, I can turn it into a PR.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Review the change introduced by #5581 and the candidate fix commit b3d1fd3277ee15d25943593aaab2cf17d20e7198. Run the pyo3-meth-static-repro benchmark across the affected PyO3 and CPython versions, then verify the candidate test preserves module binding and avoids the regression for parameterized functions.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python, rust
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 48/100