use std;
use std::ffi::CString;
use std::marker::PhantomData;
use libc::c_int;
use ffi;
use objects::{PyObject, PyType, PyBool, PyDict, PyModule};
use err::{self, PyErr, PyResult};
use pythonrun::GILGuard;
#[derive(Copy, Clone)]
pub struct Python<'p>(PhantomData<&'p GILGuard>);
pub trait PythonObject : ::conversion::ToPyObject + Send + Sized + 'static {
fn as_object(&self) -> &PyObject;
fn into_object(self) -> PyObject;
unsafe fn unchecked_downcast_from(PyObject) -> Self;
unsafe fn unchecked_downcast_borrow_from(&PyObject) -> &Self;
}
pub struct PythonObjectDowncastError<'p>(pub Python<'p>);
pub trait PythonObjectWithCheckedDowncast : PythonObject {
fn downcast_from<'p>(Python<'p>, PyObject) -> Result<Self, PythonObjectDowncastError<'p>>;
fn downcast_borrow_from<'a, 'p>(Python<'p>, &'a PyObject) -> Result<&'a Self, PythonObjectDowncastError<'p>>;
}
pub trait PythonObjectWithTypeObject : PythonObjectWithCheckedDowncast {
fn type_object(Python) -> PyType;
}
pub trait PyClone : Sized {
fn clone_ref(&self, Python) -> Self;
}
impl <T> PyClone for T where T: PythonObject {
#[inline]
fn clone_ref(&self, py: Python) -> T {
let ptr = self.as_object().as_ptr();
unsafe {
T::unchecked_downcast_from(PyObject::from_borrowed_ptr(py, ptr))
}
}
}
impl <T> PyClone for Option<T> where T: PyClone {
#[inline]
fn clone_ref(&self, py: Python) -> Option<T> {
match *self {
Some(ref v) => Some(v.clone_ref(py)),
None => None
}
}
}
pub trait PyDrop : Sized {
fn release_ref(self, Python);
}
impl <T> PyDrop for T where T: PythonObject {
#[inline]
fn release_ref(self, _py: Python) {
let ptr = self.into_object().steal_ptr();
unsafe {
ffi::Py_DECREF(ptr);
}
}
}
impl <T> PyDrop for Option<T> where T: PyDrop {
#[inline]
fn release_ref(self, py: Python) {
match self {
Some(v) => v.release_ref(py),
None => {}
}
}
}
pub trait ToPythonPointer {
fn as_ptr(&self) -> *mut ffi::PyObject;
fn steal_ptr(self, py: Python) -> *mut ffi::PyObject;
}
impl ToPythonPointer for PyObject {
#[inline]
fn as_ptr(&self) -> *mut ffi::PyObject {
self.as_ptr()
}
#[inline]
fn steal_ptr(self, _py: Python) -> *mut ffi::PyObject {
self.steal_ptr()
}
}
impl <'a, T> ToPythonPointer for &'a T where T: PythonObject {
#[inline]
fn as_ptr(&self) -> *mut ffi::PyObject {
self.as_object().as_ptr()
}
#[inline]
fn steal_ptr(self, py: Python) -> *mut ffi::PyObject {
self.as_object().clone_ref(py).steal_ptr()
}
}
impl <T> ToPythonPointer for Option<T> where T: ToPythonPointer {
#[inline]
fn as_ptr(&self) -> *mut ffi::PyObject {
match *self {
Some(ref t) => t.as_ptr(),
None => std::ptr::null_mut()
}
}
#[inline]
fn steal_ptr(self, py: Python) -> *mut ffi::PyObject {
match self {
Some(t) => t.steal_ptr(py),
None => std::ptr::null_mut()
}
}
}
impl<'p> Python<'p> {
#[inline]
pub unsafe fn assume_gil_acquired() -> Python<'p> {
Python(PhantomData)
}
#[inline]
pub fn acquire_gil() -> GILGuard {
GILGuard::acquire()
}
pub fn allow_threads<T, F>(self, f: F) -> T where F : Send + FnOnce() -> T {
unsafe {
let save = ffi::PyEval_SaveThread();
let result = f();
ffi::PyEval_RestoreThread(save);
result
}
}
pub fn eval(self, code: &str, globals: Option<&PyDict>,
locals: Option<&PyDict>) -> PyResult<PyObject> {
self.run_code(code, ffi::Py_eval_input, globals, locals)
}
pub fn run(self, code: &str, globals: Option<&PyDict>,
locals: Option<&PyDict>) -> PyResult<()> {
self.run_code(code, ffi::Py_file_input, globals, locals)?;
Ok(())
}
fn run_code(self, code: &str, start: c_int,
globals: Option<&PyDict>, locals: Option<&PyDict>)
-> PyResult<PyObject> {
let code = CString::new(code).unwrap();
unsafe {
let mptr = ffi::PyImport_AddModule("__main__\0".as_ptr() as *const _);
if mptr.is_null() {
return Err(PyErr::fetch(self));
}
let mdict = ffi::PyModule_GetDict(mptr);
let globals = match globals {
Some(g) => g.as_ptr(),
None => mdict,
};
let locals = match locals {
Some(l) => l.as_ptr(),
None => globals
};
let res_ptr = ffi::PyRun_StringFlags(code.as_ptr(),
start, globals, locals, 0 as *mut _);
err::result_from_owned_ptr(self, res_ptr)
}
}
#[allow(non_snake_case)] #[inline]
pub fn None(self) -> PyObject {
unsafe { PyObject::from_borrowed_ptr(self, ffi::Py_None()) }
}
#[allow(non_snake_case)] #[inline]
pub fn True(self) -> PyBool {
unsafe { PyObject::from_borrowed_ptr(self, ffi::Py_True()).unchecked_cast_into::<PyBool>() }
}
#[allow(non_snake_case)] #[inline]
pub fn False(self) -> PyBool {
unsafe { PyObject::from_borrowed_ptr(self, ffi::Py_False()).unchecked_cast_into::<PyBool>() }
}
#[allow(non_snake_case)] #[inline]
pub fn NotImplemented(self) -> PyObject {
unsafe { PyObject::from_borrowed_ptr(self, ffi::Py_NotImplemented()) }
}
pub fn get_type<T>(self) -> PyType where T: PythonObjectWithTypeObject {
T::type_object(self)
}
pub fn import(self, name : &str) -> PyResult<PyModule> {
PyModule::import(self, name)
}
}
impl <'p> std::fmt::Debug for PythonObjectDowncastError<'p> {
fn fmt(&self, f : &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
f.write_str("PythonObjectDowncastError")
}
}
#[cfg(test)]
mod test {
use {Python, PyDict};
#[test]
fn test_eval() {
let gil = Python::acquire_gil();
let py = gil.python();
let v: i32 = py.eval("min(1, 2)", None, None).unwrap().extract(py).unwrap();
assert_eq!(v, 1);
let d = PyDict::new(py);
d.set_item(py, "foo", 13).unwrap();
let v: i32 = py.eval("foo + 29", None, Some(&d)).unwrap().extract(py).unwrap();
assert_eq!(v, 42);
let v: i32 = py.eval("min(foo, 2)", None, Some(&d)).unwrap().extract(py).unwrap();
assert_eq!(v, 2);
}
}