Skip to main content

pyo3_ffi/impl_/
macros.rs

1// On x86 Windows, `raw-dylib` with `import_name_type = "undecorated"` removes the
2// leading cdecl underscore from function names. This is expected behavior for
3// `import_name_type = "undecorated"` (not a rustc bug): it strips the cdecl `_`
4// prefix, which collides with symbols whose real names start with `_Py`.
5// See https://doc.rust-lang.org/reference/items/external-blocks.html#the-import_name_type-key
6//
7// That matches ordinary `Py_*` exports, but it breaks CPython's internal `_Py*`
8// function exports whose real DLL names already start with an underscore. For
9// those functions, ask rustc for one extra underscore so that x86 undecoration
10// lands back on CPython's export.
11//
12// Variables are intentionally excluded here: `import_name_type` does not affect
13// variable imports, so `_Py_*` statics continue to work without any rewriting.
14#[allow(unused_macros, reason = "used indirectly by extern_libpython_item!")]
15macro_rules! extern_libpython_cpython_private_fn {
16    ($(#[$attrs:meta])* $vis:vis $name:ident($($args:tt)*) $(-> $ret:ty)?) => {
17        #[cfg_attr(
18            all(windows, target_arch = "x86", not(any(PyPy, GraalPy))),
19            link_name = concat!("_", stringify!($name))
20        )]
21        $(#[$attrs])*
22        $vis fn $name($($args)*) $(-> $ret)?;
23    };
24}
25
26// Keep this list in sync with `_Py*` function imports declared through
27// `extern_libpython!`. The x86 workaround only needs to apply to functions:
28// statics keep their original names even when `import_name_type` is set. Match
29// by name only here so the function signature stays in a single generic arm.
30//
31// TODO: reduce the number of `_Py*` exports from pyo3-ffi over time — the fewer
32// CPython-private functions we expose, the smaller this workaround list becomes.
33#[allow(unused_macros, reason = "used indirectly by extern_libpython_item!")]
34macro_rules! extern_libpython_maybe_private_fn {
35    (
36        [_PyObject_CallFunction_SizeT]
37        $(#[$attrs:meta])* $vis:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)?
38    ) => {
39        extern_libpython_cpython_private_fn! { $(#[$attrs])* $vis $name($($args)*) $(-> $ret)? }
40    };
41    (
42        [_PyObject_MakeTpCall]
43        $(#[$attrs:meta])* $vis:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)?
44    ) => {
45        extern_libpython_cpython_private_fn! { $(#[$attrs])* $vis $name($($args)*) $(-> $ret)? }
46    };
47    (
48        [_Py_CheckFunctionResult]
49        $(#[$attrs:meta])* $vis:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)?
50    ) => {
51        extern_libpython_cpython_private_fn! { $(#[$attrs])* $vis $name($($args)*) $(-> $ret)? }
52    };
53    (
54        [_PyBytes_Resize]
55        $(#[$attrs:meta])* $vis:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)?
56    ) => {
57        extern_libpython_cpython_private_fn! { $(#[$attrs])* $vis $name($($args)*) $(-> $ret)? }
58    };
59    (
60        [_PyLong_AsByteArray]
61        $(#[$attrs:meta])* $vis:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)?
62    ) => {
63        extern_libpython_cpython_private_fn! { $(#[$attrs])* $vis $name($($args)*) $(-> $ret)? }
64    };
65    (
66        [_PyLong_FromByteArray]
67        $(#[$attrs:meta])* $vis:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)?
68    ) => {
69        extern_libpython_cpython_private_fn! { $(#[$attrs])* $vis $name($($args)*) $(-> $ret)? }
70    };
71    (
72        [_PyUnicode_Ready]
73        $(#[$attrs:meta])* $vis:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)?
74    ) => {
75        extern_libpython_cpython_private_fn! { $(#[$attrs])* $vis $name($($args)*) $(-> $ret)? }
76    };
77    (
78        [_PyUnicode_ToDecimalDigit]
79        $(#[$attrs:meta])* $vis:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)?
80    ) => {
81        extern_libpython_cpython_private_fn! { $(#[$attrs])* $vis $name($($args)*) $(-> $ret)? }
82    };
83    (
84        [_PyThreadState_UncheckedGet]
85        $(#[$attrs:meta])* $vis:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)?
86    ) => {
87        extern_libpython_cpython_private_fn! { $(#[$attrs])* $vis $name($($args)*) $(-> $ret)? }
88    };
89    (
90        [_PyObject_GC_New]
91        $(#[$attrs:meta])* $vis:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)?
92    ) => {
93        extern_libpython_cpython_private_fn! { $(#[$attrs])* $vis $name($($args)*) $(-> $ret)? }
94    };
95    (
96        [_PyObject_GC_NewVar]
97        $(#[$attrs:meta])* $vis:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)?
98    ) => {
99        extern_libpython_cpython_private_fn! { $(#[$attrs])* $vis $name($($args)*) $(-> $ret)? }
100    };
101    (
102        [_PyObject_GC_Resize]
103        $(#[$attrs:meta])* $vis:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)?
104    ) => {
105        extern_libpython_cpython_private_fn! { $(#[$attrs])* $vis $name($($args)*) $(-> $ret)? }
106    };
107    (
108        [_PyObject_New]
109        $(#[$attrs:meta])* $vis:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)?
110    ) => {
111        extern_libpython_cpython_private_fn! { $(#[$attrs])* $vis $name($($args)*) $(-> $ret)? }
112    };
113    (
114        [_PyObject_NewVar]
115        $(#[$attrs:meta])* $vis:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)?
116    ) => {
117        extern_libpython_cpython_private_fn! { $(#[$attrs])* $vis $name($($args)*) $(-> $ret)? }
118    };
119    (
120        [_Py_HashBytes]
121        $(#[$attrs:meta])* $vis:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)?
122    ) => {
123        extern_libpython_cpython_private_fn! { $(#[$attrs])* $vis $name($($args)*) $(-> $ret)? }
124    };
125    (
126        [_Py_DECREF_DecRefTotal]
127        $(#[$attrs:meta])* $vis:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)?
128    ) => {
129        extern_libpython_cpython_private_fn! { $(#[$attrs])* $vis $name($($args)*) $(-> $ret)? }
130    };
131    (
132        [_Py_Dealloc]
133        $(#[$attrs:meta])* $vis:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)?
134    ) => {
135        extern_libpython_cpython_private_fn! { $(#[$attrs])* $vis $name($($args)*) $(-> $ret)? }
136    };
137    (
138        [_Py_DecRef]
139        $(#[$attrs:meta])* $vis:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)?
140    ) => {
141        extern_libpython_cpython_private_fn! { $(#[$attrs])* $vis $name($($args)*) $(-> $ret)? }
142    };
143    (
144        [_Py_INCREF_IncRefTotal]
145        $(#[$attrs:meta])* $vis:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)?
146    ) => {
147        extern_libpython_cpython_private_fn! { $(#[$attrs])* $vis $name($($args)*) $(-> $ret)? }
148    };
149    (
150        [_Py_IncRef]
151        $(#[$attrs:meta])* $vis:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)?
152    ) => {
153        extern_libpython_cpython_private_fn! { $(#[$attrs])* $vis $name($($args)*) $(-> $ret)? }
154    };
155    (
156        [_Py_NegativeRefcount]
157        $(#[$attrs:meta])* $vis:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)?
158    ) => {
159        extern_libpython_cpython_private_fn! { $(#[$attrs])* $vis $name($($args)*) $(-> $ret)? }
160    };
161    (
162        [_PyErr_BadInternalCall]
163        $(#[$attrs:meta])* $vis:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)?
164    ) => {
165        extern_libpython_cpython_private_fn! { $(#[$attrs])* $vis $name($($args)*) $(-> $ret)? }
166    };
167    (
168        [$name:ident]
169        $(#[$attrs:meta])* $vis:vis fn $fn_name:ident($($args:tt)*) $(-> $ret:ty)?
170    ) => {
171        $(#[$attrs])*
172        $vis fn $fn_name($($args)*) $(-> $ret)?;
173    };
174}
175
176macro_rules! extern_libpython_item {
177    ($(#[$attrs:meta])* $vis:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)?) => {
178        extern_libpython_maybe_private_fn! {
179            [$name]
180            $(#[$attrs])*
181            $vis fn $name($($args)*) $(-> $ret)?
182        }
183    };
184    ($(#[$attrs:meta])* $vis:vis static mut $name:ident: $ty:ty) => {
185        $(#[$attrs])*
186        $vis static mut $name: $ty;
187    };
188    ($(#[$attrs:meta])* $vis:vis static $name:ident: $ty:ty) => {
189        $(#[$attrs])*
190        $vis static $name: $ty;
191    };
192}
193
194macro_rules! extern_libpython_items {
195    () => {};
196    (
197        $(#[$attrs:meta])*
198        $vis:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)?;
199        $($rest:tt)*
200    ) => {
201        extern_libpython_item! {
202            $(#[$attrs])*
203            $vis fn $name($($args)*) $(-> $ret)?
204        }
205        extern_libpython_items! { $($rest)* }
206    };
207    (
208        $(#[$attrs:meta])*
209        $vis:vis static mut $name:ident: $ty:ty;
210        $($rest:tt)*
211    ) => {
212        extern_libpython_item! {
213            $(#[$attrs])*
214            $vis static mut $name: $ty
215        }
216        extern_libpython_items! { $($rest)* }
217    };
218    (
219        $(#[$attrs:meta])*
220        $vis:vis static $name:ident: $ty:ty;
221        $($rest:tt)*
222    ) => {
223        extern_libpython_item! {
224            $(#[$attrs])*
225            $vis static $name: $ty
226        }
227        extern_libpython_items! { $($rest)* }
228    };
229}
230
231/// Helper macro to declare `extern` blocks that link against libpython on Windows
232/// using `raw-dylib`, eliminating the need for import libraries.
233///
234/// The build script sets a `pyo3_dll` cfg value to the target DLL name (e.g. `python312`),
235/// and this macro expands to the appropriate `#[link(name = "...", kind = "raw-dylib")]`
236/// attribute for that DLL.
237///
238/// # Usage
239///
240/// ```rust,ignore
241/// // Default ABI "C" (most common):
242/// extern_libpython! {
243///     pub fn PyObject_Call(
244///         callable: *mut PyObject,
245///         args: *mut PyObject,
246///         kwargs: *mut PyObject,
247///     ) -> *mut PyObject;
248/// }
249///
250/// // Explicit ABI:
251/// extern_libpython! { "C-unwind" {
252///     pub fn PyGILState_Ensure() -> PyGILState_STATE;
253/// }}
254/// ```
255macro_rules! extern_libpython {
256    // Explicit ABI
257    ($abi:literal { $($body:tt)* }) => {
258        extern_libpython!(@impl $abi { $($body)* }
259            // abi3
260            "python3", "python3_d",
261            // abi3t
262            "python3t", "python3t_d",
263            // Python 3.9 - 3.15
264            "python39", "python39_d",
265            "python310", "python310_d",
266            "python311", "python311_d",
267            "python312", "python312_d",
268            "python313", "python313_d",
269            "python314", "python314_d",
270            "python315", "python315_d",
271            "python316", "python316_d",
272            // free-threaded builds (3.13+)
273            "python313t", "python313t_d",
274            "python314t", "python314t_d",
275            "python315t", "python315t_d",
276            "python316t", "python316t_d",
277            // PyPy (DLL is libpypy3.X-c.dll, not pythonXY.dll)
278            "libpypy3.11-c",
279        );
280    };
281    // Internal: generate cfg_attr for each DLL name. One of these will be selected
282    // by `pyo3-ffi`'s `build.rs`.
283    //
284    // On x86 Windows, Python DLLs export undecorated symbol names (no leading
285    // underscore), but the default for raw-dylib on x86 is fully-decorated
286    // (cdecl adds a `_` prefix). We use `import_name_type = "undecorated"` to
287    // match. The `import_name_type` key is only valid on x86, so we need
288    // separate cfg_attr arms per architecture.
289    (@impl $abi:literal { $($body:tt)* } $($dll:literal),* $(,)?) => {
290        $(
291            #[cfg_attr(all(windows, pyo3_use_raw_dylib, target_arch = "x86", pyo3_dll = $dll),
292                link(name = $dll, kind = "raw-dylib", import_name_type = "undecorated"))]
293            #[cfg_attr(all(windows, pyo3_use_raw_dylib, not(target_arch = "x86"), pyo3_dll = $dll),
294                link(name = $dll, kind = "raw-dylib"))]
295        )*
296        #[cfg_attr(all(windows, not(pyo3_use_raw_dylib)), link(name = "pythonXY"))]
297        extern $abi {
298            extern_libpython_items! { $($body)* }
299        }
300    };
301    // Default ABI: "C"
302    ($($body:tt)*) => {
303        extern_libpython!("C" { $($body)* });
304    };
305}
⚠️ Internal Docs ⚠️ Not Public API 👉 Official Docs Here