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        [_PyEval_EvalFrameDefault]
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        [_PyEval_RequestCodeExtraIndex]
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        [_PyCode_GetExtra]
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        [_PyCode_SetExtra]
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        [_PyLong_AsByteArray]
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        [_PyLong_FromByteArray]
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_Calloc]
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_Malloc]
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        [_Py_GetAllocatedBlocks]
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        [_PyRun_AnyFileObject]
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        [_PyRun_InteractiveLoopObject]
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        [_PyRun_SimpleFileObject]
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        [_PyUnicode_CheckConsistency]
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        [_PyUnicode_Ready]
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        [_PyLong_NumBits]
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        [_PyThreadState_UncheckedGet]
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        [_PyObject_GC_New]
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        [_PyObject_GC_NewVar]
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        [_PyObject_GC_Resize]
169        $(#[$attrs:meta])* $vis:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)?
170    ) => {
171        extern_libpython_cpython_private_fn! { $(#[$attrs])* $vis $name($($args)*) $(-> $ret)? }
172    };
173    (
174        [_PyObject_New]
175        $(#[$attrs:meta])* $vis:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)?
176    ) => {
177        extern_libpython_cpython_private_fn! { $(#[$attrs])* $vis $name($($args)*) $(-> $ret)? }
178    };
179    (
180        [_PyObject_NewVar]
181        $(#[$attrs:meta])* $vis:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)?
182    ) => {
183        extern_libpython_cpython_private_fn! { $(#[$attrs])* $vis $name($($args)*) $(-> $ret)? }
184    };
185    (
186        [_PyErr_BadInternalCall]
187        $(#[$attrs:meta])* $vis:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)?
188    ) => {
189        extern_libpython_cpython_private_fn! { $(#[$attrs])* $vis $name($($args)*) $(-> $ret)? }
190    };
191    (
192        [_Py_HashBytes]
193        $(#[$attrs:meta])* $vis:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)?
194    ) => {
195        extern_libpython_cpython_private_fn! { $(#[$attrs])* $vis $name($($args)*) $(-> $ret)? }
196    };
197    (
198        [_Py_DECREF_DecRefTotal]
199        $(#[$attrs:meta])* $vis:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)?
200    ) => {
201        extern_libpython_cpython_private_fn! { $(#[$attrs])* $vis $name($($args)*) $(-> $ret)? }
202    };
203    (
204        [_Py_Dealloc]
205        $(#[$attrs:meta])* $vis:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)?
206    ) => {
207        extern_libpython_cpython_private_fn! { $(#[$attrs])* $vis $name($($args)*) $(-> $ret)? }
208    };
209    (
210        [_Py_DecRef]
211        $(#[$attrs:meta])* $vis:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)?
212    ) => {
213        extern_libpython_cpython_private_fn! { $(#[$attrs])* $vis $name($($args)*) $(-> $ret)? }
214    };
215    (
216        [_Py_INCREF_IncRefTotal]
217        $(#[$attrs:meta])* $vis:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)?
218    ) => {
219        extern_libpython_cpython_private_fn! { $(#[$attrs])* $vis $name($($args)*) $(-> $ret)? }
220    };
221    (
222        [_Py_IncRef]
223        $(#[$attrs:meta])* $vis:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)?
224    ) => {
225        extern_libpython_cpython_private_fn! { $(#[$attrs])* $vis $name($($args)*) $(-> $ret)? }
226    };
227    (
228        [_Py_NegativeRefcount]
229        $(#[$attrs:meta])* $vis:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)?
230    ) => {
231        extern_libpython_cpython_private_fn! { $(#[$attrs])* $vis $name($($args)*) $(-> $ret)? }
232    };
233    (
234        [$name:ident]
235        $(#[$attrs:meta])* $vis:vis fn $fn_name:ident($($args:tt)*) $(-> $ret:ty)?
236    ) => {
237        $(#[$attrs])*
238        $vis fn $fn_name($($args)*) $(-> $ret)?;
239    };
240}
241
242macro_rules! extern_libpython_item {
243    ($(#[$attrs:meta])* $vis:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)?) => {
244        extern_libpython_maybe_private_fn! {
245            [$name]
246            $(#[$attrs])*
247            $vis fn $name($($args)*) $(-> $ret)?
248        }
249    };
250    ($(#[$attrs:meta])* $vis:vis static mut $name:ident: $ty:ty) => {
251        $(#[$attrs])*
252        $vis static mut $name: $ty;
253    };
254    ($(#[$attrs:meta])* $vis:vis static $name:ident: $ty:ty) => {
255        $(#[$attrs])*
256        $vis static $name: $ty;
257    };
258}
259
260macro_rules! extern_libpython_items {
261    () => {};
262    (
263        $(#[$attrs:meta])*
264        $vis:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)?;
265        $($rest:tt)*
266    ) => {
267        extern_libpython_item! {
268            $(#[$attrs])*
269            $vis fn $name($($args)*) $(-> $ret)?
270        }
271        extern_libpython_items! { $($rest)* }
272    };
273    (
274        $(#[$attrs:meta])*
275        $vis:vis static mut $name:ident: $ty:ty;
276        $($rest:tt)*
277    ) => {
278        extern_libpython_item! {
279            $(#[$attrs])*
280            $vis static mut $name: $ty
281        }
282        extern_libpython_items! { $($rest)* }
283    };
284    (
285        $(#[$attrs:meta])*
286        $vis:vis static $name:ident: $ty:ty;
287        $($rest:tt)*
288    ) => {
289        extern_libpython_item! {
290            $(#[$attrs])*
291            $vis static $name: $ty
292        }
293        extern_libpython_items! { $($rest)* }
294    };
295}
296
297/// Helper macro to declare `extern` blocks that link against libpython on Windows
298/// using `raw-dylib`, eliminating the need for import libraries.
299///
300/// The build script sets a `pyo3_dll` cfg value to the target DLL name (e.g. `python312`),
301/// and this macro expands to the appropriate `#[link(name = "...", kind = "raw-dylib")]`
302/// attribute for that DLL.
303///
304/// # Usage
305///
306/// ```rust,ignore
307/// // Default ABI "C" (most common):
308/// extern_libpython! {
309///     pub fn PyObject_Call(
310///         callable: *mut PyObject,
311///         args: *mut PyObject,
312///         kwargs: *mut PyObject,
313///     ) -> *mut PyObject;
314/// }
315///
316/// // Explicit ABI:
317/// extern_libpython! { "C-unwind" {
318///     pub fn PyGILState_Ensure() -> PyGILState_STATE;
319/// }}
320/// ```
321macro_rules! extern_libpython {
322    // Explicit ABI
323    ($abi:literal { $($body:tt)* }) => {
324        extern_libpython!(@impl $abi { $($body)* }
325            // abi3
326            "python3", "python3_d",
327            // Python 3.8 - 3.15
328            "python38", "python38_d",
329            "python39", "python39_d",
330            "python310", "python310_d",
331            "python311", "python311_d",
332            "python312", "python312_d",
333            "python313", "python313_d",
334            "python314", "python314_d",
335            "python315", "python315_d",
336            // free-threaded builds (3.13+)
337            "python313t", "python313t_d",
338            "python314t", "python314t_d",
339            "python315t", "python315t_d",
340            // PyPy (DLL is libpypy3.X-c.dll, not pythonXY.dll)
341            "libpypy3.11-c",
342        );
343    };
344    // Internal: generate cfg_attr for each DLL name. One of these will be selected
345    // by `pyo3-ffi`'s `build.rs`.
346    //
347    // On x86 Windows, Python DLLs export undecorated symbol names (no leading
348    // underscore), but the default for raw-dylib on x86 is fully-decorated
349    // (cdecl adds a `_` prefix). We use `import_name_type = "undecorated"` to
350    // match. The `import_name_type` key is only valid on x86, so we need
351    // separate cfg_attr arms per architecture.
352    (@impl $abi:literal { $($body:tt)* } $($dll:literal),* $(,)?) => {
353        $(
354            #[cfg_attr(all(windows, target_arch = "x86", pyo3_dll = $dll),
355                link(name = $dll, kind = "raw-dylib", import_name_type = "undecorated"))]
356            #[cfg_attr(all(windows, not(target_arch = "x86"), pyo3_dll = $dll),
357                link(name = $dll, kind = "raw-dylib"))]
358        )*
359        extern $abi {
360            extern_libpython_items! { $($body)* }
361        }
362    };
363    // Default ABI: "C"
364    ($($body:tt)*) => {
365        extern_libpython!("C" { $($body)* });
366    };
367}
⚠️ Internal Docs ⚠️ Not Public API 👉 Official Docs Here