diff --git a/.travis.yml b/.travis.yml index 919c616b..6cf32f7d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,8 +4,8 @@ env: global: - secure: g4kCg8twONwKPquuJmYrvGjo2n0lNtWTbyzFOITNn8FgCxNK2j38Qc9/UhErTR3g3rDjVzsTHZ8FTH7TJZrOK1Nzz90tJG6JHqUv77ufkcBlxgwwjilOz84uQhkDTMpLitMEeQDLEynKeWbxrjtc5LIpjEkxOPk5eiqwzKRN14c= script: - - cargo build --verbose - - cargo test --verbose + - make test extensions PY=2 + - make test extensions PY=3 # 'cargo doc' is broken due to https://github.com/rust-lang/cargo/issues/1622 # - cargo doc --verbose diff --git a/Cargo.toml b/Cargo.toml index 89c251b0..af8cd98f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,8 +23,9 @@ exclude = [ build = "build.rs" [dependencies] -libc="*" -num="*" +libc = "*" +num = "*" +interpolate_idents = "*" # These features are both optional, but you must pick one to # indicate which python ffi you are trying to bind to. diff --git a/Makefile b/Makefile index 7245a1f3..36e5bf36 100644 --- a/Makefile +++ b/Makefile @@ -1,18 +1,29 @@ .PHONY: default build test doc extensions clean +ifndef PY +PY=3 +endif + +ifeq ($(PY),2) +FEATURES=--features python27-sys --no-default-features +endif +ifeq ($(PY),3) +FEATURES=--features python3-sys --no-default-features +endif + default: test extensions build: - cargo build + cargo build $(FEATURES) test: build - cargo test + cargo test $(FEATURES) doc: build - cargo doc --no-deps + cargo doc --no-deps $(FEATURES) extensions: build - make -C extensions/ + make -C extensions/ PY=$(PY) clean: rm -r target diff --git a/extensions/Makefile b/extensions/Makefile index 9df3507e..0eeab4a3 100644 --- a/extensions/Makefile +++ b/extensions/Makefile @@ -3,6 +3,10 @@ rwildcard=$(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst .PHONY: all clean +ifndef PY +PY=3 +endif + all: clean: @@ -13,15 +17,16 @@ clean: stamps: mkdir stamps -stamps/rust-cpython: $(call rwildcard,../src,*.rs) Makefile | stamps - cd .. && cargo build +stamps/rust-cpython-$(PY): $(call rwildcard,../src,*.rs) Makefile | stamps + -rm stamps/rust-cpython-* + cd .. && make build PY=$(PY) touch $@ -%.so: %.rs stamps/rust-cpython +%.so: %.rs stamps/rust-cpython-$(PY) rustc $< -L $(TARGETDIR) -L $(TARGETDIR)/deps -o $@ hello.out: hello.so - python -c "import hello; hello.run(hello.val())" 2>&1 | tee $@ + python$(PY) -c "import hello; hello.run(hello.val())" 2>&1 | tee $@ all: stamps/test-hello stamps/test-hello: hello.out diff --git a/extensions/hello.rs b/extensions/hello.rs index f415eec8..4bf2b358 100644 --- a/extensions/hello.rs +++ b/extensions/hello.rs @@ -1,10 +1,12 @@ #![crate_type = "dylib"] +#![feature(plugin)] +#![plugin(interpolate_idents)] #[macro_use] extern crate cpython; use cpython::{PyObject, PyResult, PyModule, Python, PyTuple}; -py_module_initializer!("hello", inithello, |py, m| { +py_module_initializer!(hello, |py, m| { try!(m.add("__doc__", "Module documentation string")); try!(m.add("run", py_func!(py, run))); try!(add_val(py, &m)); diff --git a/python3-sys/src/moduleobject.rs b/python3-sys/src/moduleobject.rs index b0080272..adc0141c 100644 --- a/python3-sys/src/moduleobject.rs +++ b/python3-sys/src/moduleobject.rs @@ -40,11 +40,13 @@ pub struct PyModuleDef_Base { impl ::std::clone::Clone for PyModuleDef_Base { fn clone(&self) -> PyModuleDef_Base { *self } } -impl ::std::default::Default for PyModuleDef_Base { - fn default() -> PyModuleDef_Base { - unsafe { ::std::mem::zeroed() } - } -} + +pub const PyModuleDef_HEAD_INIT: PyModuleDef_Base = PyModuleDef_Base { + ob_base: PyObject_HEAD_INIT, + m_init: None, + m_index: 0, + m_copy: 0 as *mut PyObject +}; #[repr(C)] #[derive(Copy)] @@ -62,7 +64,5 @@ pub struct PyModuleDef { impl ::std::clone::Clone for PyModuleDef { fn clone(&self) -> PyModuleDef { *self } } -impl ::std::default::Default for PyModuleDef { - fn default() -> PyModuleDef { unsafe { ::std::mem::zeroed() } } -} + diff --git a/python3-sys/src/object.rs b/python3-sys/src/object.rs index 4d8a6119..f8af5117 100644 --- a/python3-sys/src/object.rs +++ b/python3-sys/src/object.rs @@ -13,6 +13,20 @@ pub struct PyObject { pub ob_type: *mut PyTypeObject, } +#[cfg(py_sys_config="Py_TRACE_REFS")] +pub const PyObject_HEAD_INIT: PyObject = PyObject { + _ob_next: 0 as *mut PyObject, + _ob_prev: 0 as *mut PyObject, + ob_refcnt: 1, + ob_type: 0 as *mut PyTypeObject +}; + +#[cfg(not(py_sys_config="Py_TRACE_REFS"))] +pub const PyObject_HEAD_INIT: PyObject = PyObject { + ob_refcnt: 1, + ob_type: 0 as *mut PyTypeObject +}; + #[repr(C)] #[derive(Copy, Clone)] pub struct PyVarObject { diff --git a/src/lib.rs b/src/lib.rs index 3bfab9da..21cb33b8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,6 +22,8 @@ #![feature(optin_builtin_traits)] // for opting out of Sync/Send #![feature(slice_patterns)] // for tuple_conversion macros #![feature(utf8_error)] // for translating Utf8Error to python exception +#![feature(plugin)] +#![plugin(interpolate_idents)] #![allow(unused_imports, unused_variables)] //! Rust bindings to the python interpreter. @@ -129,10 +131,12 @@ pub mod _detail { /// # Example /// ``` /// #![crate_type = "dylib"] +/// #![feature(plugin)] +/// #![plugin(interpolate_idents)] /// #[macro_use] extern crate cpython; /// use cpython::{Python, PyResult, PyObject, PyTuple}; /// -/// py_module_initializer!("example", initexample, |py, m| { +/// py_module_initializer!(example, |py, m| { /// try!(m.add("__doc__", "Module documentation string")); /// try!(m.add("run", py_func!(py, run))); /// Ok(()) @@ -158,18 +162,55 @@ pub mod _detail { /// ``` /// #[macro_export] +#[cfg(feature="python27-sys")] macro_rules! py_module_initializer { - ($name: tt, $init_funcname: ident, $init: expr) => { - #[no_mangle] - pub extern "C" fn $init_funcname() { + ($name: ident, $init: expr) => ( interpolate_idents! { + #[[no_mangle]] + #[allow(non_snake_case)] + pub extern "C" fn [ init $name ]() { let py = unsafe { $crate::Python::assume_gil_acquired() }; - let name = unsafe { ::std::ffi::CStr::from_ptr(concat!($name, "\0").as_ptr() as *const _) }; + let name = unsafe { ::std::ffi::CStr::from_ptr(concat!(stringify!($name), "\0").as_ptr() as *const _) }; match $crate::PyModule::_init(py, name, $init) { Ok(()) => (), Err(e) => e.restore() } } - } + }) +} + +#[macro_export] +#[cfg(feature="python3-sys")] +macro_rules! py_module_initializer { + ($name: ident, $init: expr) => ( interpolate_idents! { + #[[no_mangle]] + #[allow(non_snake_case)] + pub extern "C" fn [ PyInit_ $name ]() -> *mut $crate::_detail::ffi::PyObject { + let py = unsafe { $crate::Python::assume_gil_acquired() }; + static mut module_def: $crate::_detail::ffi::PyModuleDef = $crate::_detail::ffi::PyModuleDef { + m_base: $crate::_detail::ffi::PyModuleDef_HEAD_INIT, + m_name: 0 as *const _, + m_doc: 0 as *const _, + m_size: 0, // we don't use per-module state + m_methods: 0 as *mut _, + m_reload: None, + m_traverse: None, + m_clear: None, + m_free: None + }; + // We can't convert &'static str to *const c_char within a static initializer, + // so we'll do it here in the module initialization: + unsafe { + module_def.m_name = concat!(stringify!($name), "\0").as_ptr() as *const _; + } + match $crate::PyModule::_init(py, unsafe { &mut module_def }, $init) { + Ok(m) => $crate::ToPythonPointer::steal_ptr(m), + Err(e) => { + e.restore(); + return ::std::ptr::null_mut(); + } + } + } + }) } /// Creates a python callable object that invokes a Rust function. diff --git a/src/objects/module.rs b/src/objects/module.rs index f6d1adc1..7346ba0b 100644 --- a/src/objects/module.rs +++ b/src/objects/module.rs @@ -47,13 +47,24 @@ impl <'p> PyModule<'p> { // Helper method for module_initializer!() macro, do not use directly! #[doc(hidden)] #[cfg(feature="python27-sys")] - pub fn _init(py: Python<'p>, name: &CStr, init: F) -> PyResult<'p, R> - where F: FnOnce(Python<'p>, PyModule<'p>) -> PyResult<'p, R> { + pub fn _init(py: Python<'p>, name: &CStr, init: F) -> PyResult<'p, ()> + where F: FnOnce(Python<'p>, &PyModule<'p>) -> PyResult<'p, ()> { let module = try!(unsafe { err::result_from_borrowed_ptr(py, ffi::Py_InitModule(name.as_ptr(), std::ptr::null_mut())) }); let module = try!(module.cast_into::()); - init(py, module) + init(py, &module) + } + + #[doc(hidden)] + #[cfg(feature="python3-sys")] + pub fn _init(py: Python<'p>, def: *mut ffi::PyModuleDef, init: F) -> PyResult<'p, PyModule<'p>> + where F: FnOnce(Python<'p>, &PyModule<'p>) -> PyResult<'p, ()> { + let module: PyModule = try!(unsafe { + err::result_cast_from_owned_ptr(py, ffi::PyModule_Create(def)) + }); + try!(init(py, &module)); + Ok(module) } /// Return the dictionary object that implements moduleā€˜s namespace;