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__ is None. With 0.27.2 it is the module, as it is for CPython's own builtin functions such as math.sqrt.
- On CPython 3.11 and newer, a call of a function that has parameters and no
**kwargs is about 2 to 4 ns slower. For a function with an empty body this is 10 to 21%. Functions without parameters and functions with **kwargs are 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 cb456da; 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: overcat@b3d1fd3. If you agree with the approach, I can turn it into a PR.
Bug Description
Since PyO3 0.28, every module-level
#[pyfunction]is created with theMETH_STATICflag. A function with one parameter hasml_flags0xa2(METH_FASTCALL | METH_KEYWORDS | METH_STATIC). With PyO3 0.27.2 it has0x82.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.**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 usespass_module, which does not get the flag, and is the control.Output with CPython 3.14.6 and PyO3 0.29.2:
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 cb456da; 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: overcat@b3d1fd3. If you agree with the approach, I can turn it into a PR.