Last active
January 20, 2018 07:58
-
-
Save atheriel/3ede430057f9dd25da36 to your computer and use it in GitHub Desktop.
Can one write a Python extension in Rust?
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
PyObject * RustPy_InitModule(const char *name, PyMethodDef *methods, const char *doc) { | |
// return Py_InitModule4(name, methods, doc, (PyObject *) NULL, PYTHON_API_VERSION); | |
return Py_InitModule3(name, methods, doc); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![feature(macro_rules)] | |
#![crate_type = "dylib"] | |
extern crate libc; | |
use std::ptr; | |
use libc::{c_char, c_int}; | |
pub struct PyObjectRaw; | |
type PyCFunction = extern "C" fn(slf: *mut PyObjectRaw, args: *mut PyObjectRaw) -> *mut PyObjectRaw; | |
type PyCFunctionWithKeywords = extern "C" fn(slf: *mut PyObjectRaw, args: *mut PyObjectRaw, kwargs: *mut PyObjectRaw) -> *mut PyObjectRaw; | |
#[repr(C)] | |
pub struct PyMethodDef { | |
ml_name: &'static [u8], | |
ml_meth: Option<PyCFunction>, | |
ml_flags: c_int, | |
ml_doc: &'static [u8], | |
} | |
#[link(name = "python2.7")] | |
#[link(name = "macroexpand", kind = "static")] | |
extern "C" { | |
fn RustPy_InitModule(name: *const c_char, methods: *const PyMethodDef, | |
doc: *const c_char) -> *mut PyObjectRaw; | |
fn PyModule_AddObject(module: *mut PyObjectRaw, name: *const c_char, | |
value: *mut PyObjectRaw) -> c_int; | |
fn PyString_FromString(string: *const c_char) -> *mut PyObjectRaw; | |
} | |
fn py_str(string: &str) -> Option<*mut PyObjectRaw> { | |
let cstring = string.to_c_str(); | |
unsafe { | |
let obj = PyString_FromString(cstring.as_ptr()); | |
if obj.is_not_null() { Some(obj) } else { None } | |
} | |
} | |
pub trait RustPyModule { | |
fn init(name: &'static str, methods: &'static [PyMethodDef]) -> Option<Self>; | |
fn add_object(&mut self, name: &str, | |
object: *mut PyObjectRaw) -> Result<(), ()>; | |
} | |
impl RustPyModule for PyObjectRaw { | |
fn init(name: &'static str, methods: &'static [PyMethodDef]) -> Option<PyObjectRaw> { | |
let c_name = name.to_c_str(); | |
unsafe { | |
let m = RustPy_InitModule(c_name.as_ptr(), methods.as_ptr(), ptr::null()); | |
if m.is_not_null() { Some(*m) } else { None } | |
} | |
} | |
fn add_object(&mut self, name: &str, | |
object: *mut PyObjectRaw) -> Result<(), ()> { | |
let c_name = name.to_c_str(); | |
unsafe { | |
if PyModule_AddObject(self, c_name.as_ptr(), object) == 0 { | |
Ok(()) | |
} else { Err(()) } | |
} | |
} | |
} | |
#[allow(unused_variable)] | |
extern "C" fn helloworld(slf: *mut PyObjectRaw, | |
args: *mut PyObjectRaw) -> *mut PyObjectRaw { | |
println!("Hello, world!"); | |
ptr::mut_null() | |
} | |
static RUSTTEST_METHODS: &'static [PyMethodDef] = &[ | |
PyMethodDef { | |
ml_name: b"helloworld\x00", | |
ml_meth: Some(helloworld), ml_flags: 1, | |
ml_doc: b"No documentation provided.\x00" | |
} | |
]; | |
#[no_mangle] | |
pub extern "C" fn initrusttest() { | |
let mut m: PyObjectRaw = match RustPyModule::init("rusttest", RUSTTEST_METHODS) { | |
Some(val) => val, | |
None => return | |
}; | |
let _ = m.add_object("__author__", py_str("The Pumpkin King").unwrap()); | |
let _ = m.add_object("__version__", py_str("0.0.1").unwrap()); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment