Skip to content

Commit a9e7e49

Browse files
committed
use paste to generate module init symbol names
Use the `paste` crate to generate the module init symbol names (`initfoo` and `PyInit_foo`), rather than requiring the user to pass them in. This makes creating modules less error-prone.
1 parent 45fdacc commit a9e7e49

File tree

8 files changed

+42
-33
lines changed

8 files changed

+42
-33
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ appveyor = { repository = "dgrunwald/rust-cpython" }
3434
[dependencies]
3535
libc = "0.2"
3636
num-traits = "0.2"
37+
paste = "0.1"
3738

3839
# These features are both optional, but you must pick one to
3940
# indicate which python ffi you are trying to bind to.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ use cpython::{PyResult, Python, py_module_initializer, py_fn};
8080

8181
// add bindings to the generated python module
8282
// N.B: names: "rust2py" must be the name of the `.so` or `.pyd` file
83-
py_module_initializer!(rust2py, initrust2py, PyInit_rust2py, |py, m| {
83+
py_module_initializer!(rust2py, |py, m| {
8484
m.add(py, "__doc__", "This module is implemented in Rust.")?;
8585
m.add(py, "sum_as_string", py_fn!(py, sum_as_string_py(a: i64, b:i64)))?;
8686
Ok(())

extensions/hello/src/hello.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use cpython::{py_fn, py_module_initializer, PyDict, PyObject, PyResult, PyTuple,
44
// This requires that the output binary file is named `hello.so` (or Windows: `hello.pyd`).
55
// As the output name cannot be configured in cargo (https://github.com/rust-lang/cargo/issues/1970),
66
// you'll have to rename the output file.
7-
py_module_initializer!(hello, inithello, PyInit_hello, |py, m| {
7+
py_module_initializer!(hello, |py, m| {
88
m.add(py, "__doc__", "Module documentation string")?;
99
m.add(py, "func", py_fn!(py, func(a: &str, b: i32)))?;
1010
m.add(py, "run", py_fn!(py, run(*args, **kwargs)))?;

extensions/tests/btree.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use cpython::{PyObject, PyResult, py_module_initializer, py_class};
44
use std::{cell, cmp, collections};
55

6-
py_module_initializer!(btree, initbtree, PyInit_btree, |py, m| {
6+
py_module_initializer!(btree, |py, m| {
77
m.add(py, "__doc__", "Rust BTreeSet for Python.")?;
88
m.add_class::<BTreeSet>(py)?;
99
Ok(())

extensions/tests/custom_class.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
use cpython::{PyObject, PyResult, py_module_initializer, py_class};
44

5-
py_module_initializer!(custom_class, initcustom_class, PyInit_custom_class, |py, m| {
5+
py_module_initializer!(custom_class, |py, m| {
66
m.add(py, "__doc__", "Module documentation string")?;
77
m.add_class::<MyType>(py)?;
88
Ok(())

extensions/tests/hello.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
use cpython::{PyObject, PyResult, Python, PyTuple, PyDict, py_module_initializer, py_fn};
44

5-
py_module_initializer!(hello, inithello, PyInit_hello, |py, m| {
5+
py_module_initializer!(hello, |py, m| {
66
m.add(py, "__doc__", "Module documentation string")?;
77
m.add(py, "run", py_fn!(py, run(*args, **kwargs)))?;
88
m.add(py, "val", py_fn!(py, val()))?;

src/lib.rs

Lines changed: 34 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -228,25 +228,28 @@ pub mod _detail {
228228
handle_callback, py_fn_impl, AbortOnDrop, PyObjectCallbackConverter,
229229
PythonObjectCallbackConverter,
230230
};
231+
pub use paste;
231232
}
232233

233234
/// Expands to an `extern "C"` function that allows Python to load
234235
/// the rust code as a Python extension module.
235236
///
236-
/// Macro syntax: `py_module_initializer!($name, $py2_init, $py3_init, |$py, $m| $body)`
237+
/// Macro syntax: `py_module_initializer!($name, |$py, $m| $body)`
237238
///
238239
/// 1. `name`: The module name as a Rust identifier.
239-
/// 2. `py2_init`: "init" + $name. Necessary because macros can't use concat_idents!().
240-
/// 3. `py3_init`: "PyInit_" + $name. Necessary because macros can't use concat_idents!().
241-
/// 4. A lambda of type `Fn(Python, &PyModule) -> PyResult<()>`.
240+
/// 2. A lambda of type `Fn(Python, &PyModule) -> PyResult<()>`.
242241
/// This function will be called when the module is imported, and is responsible
243242
/// for adding the module's members.
244243
///
244+
/// For backwards compatibilty with older versions of rust-cpython,
245+
/// two additional name identifiers (for py2 and py3 initializer names)
246+
/// can be provided, but they will be ignored.
247+
///
245248
/// # Example
246249
/// ```
247250
/// use cpython::{Python, PyResult, PyObject, py_module_initializer, py_fn};
248251
///
249-
/// py_module_initializer!(hello, inithello, PyInit_hello, |py, m| {
252+
/// py_module_initializer!(hello, |py, m| {
250253
/// m.add(py, "__doc__", "Module documentation string")?;
251254
/// m.add(py, "run", py_fn!(py, run()))?;
252255
/// Ok(())
@@ -287,16 +290,18 @@ pub mod _detail {
287290
#[macro_export]
288291
#[cfg(feature = "python27-sys")]
289292
macro_rules! py_module_initializer {
290-
($name: ident, $py2: ident, $py3: ident, |$py_id: ident, $m_id: ident| $body: expr) => {
291-
#[no_mangle]
292-
#[allow(non_snake_case)]
293-
pub unsafe extern "C" fn $py2() {
294-
// Nest init function so that $body isn't in unsafe context
295-
fn init($py_id: $crate::Python, $m_id: &$crate::PyModule) -> $crate::PyResult<()> {
296-
$body
293+
($name: ident, $( $_py2: ident, $_py3: ident, )? |$py_id: ident, $m_id: ident| $body: tt) => {
294+
$crate::_detail::paste::item! {
295+
#[no_mangle]
296+
#[allow(non_snake_case)]
297+
pub unsafe extern "C" fn [< init $name >]() {
298+
// Nest init function so that $body isn't in unsafe context
299+
fn init($py_id: $crate::Python, $m_id: &$crate::PyModule) -> $crate::PyResult<()> {
300+
$body
301+
}
302+
let name = concat!(stringify!($name), "\0").as_ptr() as *const _;
303+
$crate::py_module_initializer_impl(name, init)
297304
}
298-
let name = concat!(stringify!($name), "\0").as_ptr() as *const _;
299-
$crate::py_module_initializer_impl(name, init)
300305
}
301306
};
302307
}
@@ -335,20 +340,22 @@ pub unsafe fn py_module_initializer_impl(
335340
#[macro_export]
336341
#[cfg(feature = "python3-sys")]
337342
macro_rules! py_module_initializer {
338-
($name: ident, $py2: ident, $py3: ident, |$py_id: ident, $m_id: ident| $body: expr) => {
339-
#[no_mangle]
340-
#[allow(non_snake_case)]
341-
pub unsafe extern "C" fn $py3() -> *mut $crate::_detail::ffi::PyObject {
342-
// Nest init function so that $body isn't in unsafe context
343-
fn init($py_id: $crate::Python, $m_id: &$crate::PyModule) -> $crate::PyResult<()> {
344-
$body
343+
($name: ident, $( $_py2: ident, $_py3: ident, )? |$py_id: ident, $m_id: ident| $body: tt) => {
344+
$crate::_detail::paste::item! {
345+
#[no_mangle]
346+
#[allow(non_snake_case)]
347+
pub unsafe extern "C" fn [< PyInit_ $name >]() -> *mut $crate::_detail::ffi::PyObject {
348+
// Nest init function so that $body isn't in unsafe context
349+
fn init($py_id: $crate::Python, $m_id: &$crate::PyModule) -> $crate::PyResult<()> {
350+
$body
351+
}
352+
static mut MODULE_DEF: $crate::_detail::ffi::PyModuleDef =
353+
$crate::_detail::ffi::PyModuleDef_INIT;
354+
// We can't convert &'static str to *const c_char within a static initializer,
355+
// so we'll do it here in the module initialization:
356+
MODULE_DEF.m_name = concat!(stringify!($name), "\0").as_ptr() as *const _;
357+
$crate::py_module_initializer_impl(&mut MODULE_DEF, init)
345358
}
346-
static mut MODULE_DEF: $crate::_detail::ffi::PyModuleDef =
347-
$crate::_detail::ffi::PyModuleDef_INIT;
348-
// We can't convert &'static str to *const c_char within a static initializer,
349-
// so we'll do it here in the module initialization:
350-
MODULE_DEF.m_name = concat!(stringify!($name), "\0").as_ptr() as *const _;
351-
$crate::py_module_initializer_impl(&mut MODULE_DEF, init)
352359
}
353360
};
354361
}

src/objects/capsule.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,11 +174,12 @@ use crate::python::{Python, ToPythonPointer};
174174
/// # a + b
175175
/// # }
176176
/// # static DATA: CapsData = CapsData{value: 1, fun: add};
177-
/// py_module_initializer!(somemod, initsomemod, PyInit_somemod, |py, m| {
177+
/// py_module_initializer!(somemod, |py, m| {
178178
/// m.add(py, "__doc__", "A module holding a capsule")?;
179179
/// m.add(py, "capsdata", PyCapsule::new_data(py, &DATA, "somemod.capsdata").unwrap())?;
180180
/// Ok(())
181181
/// });
182+
/// # fn main() {}
182183
/// ```
183184
/// Another Rust extension could then declare `CapsData` and use `PyCapsule::import_data` to
184185
/// fetch it back.

0 commit comments

Comments
 (0)