Skip to content

Commit 33f55d3

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 d89056f commit 33f55d3

File tree

8 files changed

+44
-33
lines changed

8 files changed

+44
-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: 37 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,10 @@ mod pythonrun;
214214
pub mod py_class;
215215
mod sharedref;
216216

217+
// Re-export paste so it can be used in macros.
218+
#[doc(hidden)]
219+
pub use paste;
220+
217221
/// Private re-exports for macros. Do not use.
218222
#[doc(hidden)]
219223
pub mod _detail {
@@ -233,20 +237,22 @@ pub mod _detail {
233237
/// Expands to an `extern "C"` function that allows Python to load
234238
/// the rust code as a Python extension module.
235239
///
236-
/// Macro syntax: `py_module_initializer!($name, $py2_init, $py3_init, |$py, $m| $body)`
240+
/// Macro syntax: `py_module_initializer!($name, |$py, $m| $body)`
237241
///
238242
/// 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<()>`.
243+
/// 2. A lambda of type `Fn(Python, &PyModule) -> PyResult<()>`.
242244
/// This function will be called when the module is imported, and is responsible
243245
/// for adding the module's members.
244246
///
247+
/// For backwards compatibilty with older versions of rust-cpython,
248+
/// two additional name identifiers (for py2 and py3 initializer names)
249+
/// can be provided, but they will be ignored.
250+
///
245251
/// # Example
246252
/// ```
247253
/// use cpython::{Python, PyResult, PyObject, py_module_initializer, py_fn};
248254
///
249-
/// py_module_initializer!(hello, inithello, PyInit_hello, |py, m| {
255+
/// py_module_initializer!(hello, |py, m| {
250256
/// m.add(py, "__doc__", "Module documentation string")?;
251257
/// m.add(py, "run", py_fn!(py, run()))?;
252258
/// Ok(())
@@ -287,16 +293,18 @@ pub mod _detail {
287293
#[macro_export]
288294
#[cfg(feature = "python27-sys")]
289295
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
296+
($name: ident, $( $_py2: ident, $_py3: ident, )? |$py_id: ident, $m_id: ident| $body: tt) => {
297+
$crate::paste::item! {
298+
#[no_mangle]
299+
#[allow(non_snake_case)]
300+
pub unsafe extern "C" fn [< init $name >]() {
301+
// Nest init function so that $body isn't in unsafe context
302+
fn init($py_id: $crate::Python, $m_id: &$crate::PyModule) -> $crate::PyResult<()> {
303+
$body
304+
}
305+
let name = concat!(stringify!($name), "\0").as_ptr() as *const _;
306+
$crate::py_module_initializer_impl(name, init)
297307
}
298-
let name = concat!(stringify!($name), "\0").as_ptr() as *const _;
299-
$crate::py_module_initializer_impl(name, init)
300308
}
301309
};
302310
}
@@ -335,20 +343,22 @@ pub unsafe fn py_module_initializer_impl(
335343
#[macro_export]
336344
#[cfg(feature = "python3-sys")]
337345
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
346+
($name: ident, $( $_py2: ident, $_py3: ident, )? |$py_id: ident, $m_id: ident| $body: tt) => {
347+
$crate::paste::item! {
348+
#[no_mangle]
349+
#[allow(non_snake_case)]
350+
pub unsafe extern "C" fn [< PyInit_ $name >]() -> *mut $crate::_detail::ffi::PyObject {
351+
// Nest init function so that $body isn't in unsafe context
352+
fn init($py_id: $crate::Python, $m_id: &$crate::PyModule) -> $crate::PyResult<()> {
353+
$body
354+
}
355+
static mut MODULE_DEF: $crate::_detail::ffi::PyModuleDef =
356+
$crate::_detail::ffi::PyModuleDef_INIT;
357+
// We can't convert &'static str to *const c_char within a static initializer,
358+
// so we'll do it here in the module initialization:
359+
MODULE_DEF.m_name = concat!(stringify!($name), "\0").as_ptr() as *const _;
360+
$crate::py_module_initializer_impl(&mut MODULE_DEF, init)
345361
}
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)
352362
}
353363
};
354364
}

src/objects/capsule.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ 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(())

0 commit comments

Comments
 (0)