use crate::conversion::PyTryFrom;
use crate::err::{PyDowncastError, PyErr, PyResult};
use crate::ffi;
use crate::instance::{AsPyRef, Py, PyToken};
use crate::object::PyObject;
use crate::pythonrun::{self, GILGuard};
use crate::typeob::PyTypeCreate;
use crate::typeob::{PyTypeInfo, PyTypeObject};
use crate::types::{PyDict, PyModule, PyObjectRef, PyType};
use std;
use std::ffi::CString;
use std::marker::PhantomData;
use std::os::raw::c_int;
use std::ptr::NonNull;
pub type NonNullPyObject = NonNull<ffi::PyObject>;
#[derive(Copy, Clone)]
pub struct Python<'p>(PhantomData<&'p GILGuard>);
pub trait ToPyPointer {
fn as_ptr(&self) -> *mut ffi::PyObject;
}
pub trait IntoPyPointer {
fn into_ptr(self) -> *mut ffi::PyObject;
}
impl<T> ToPyPointer for Option<T>
where
T: ToPyPointer,
{
#[inline]
fn as_ptr(&self) -> *mut ffi::PyObject {
match *self {
Some(ref t) => t.as_ptr(),
None => std::ptr::null_mut(),
}
}
}
impl<T> IntoPyPointer for Option<T>
where
T: IntoPyPointer,
{
#[inline]
fn into_ptr(self) -> *mut ffi::PyObject {
match self {
Some(t) => t.into_ptr(),
None => std::ptr::null_mut(),
}
}
}
impl<'a, T> IntoPyPointer for &'a T
where
T: ToPyPointer,
{
fn into_ptr(self) -> *mut ffi::PyObject {
let ptr = self.as_ptr();
if !ptr.is_null() {
unsafe {
ffi::Py_INCREF(ptr);
}
}
ptr
}
}
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<&'p PyObjectRef> {
self.run_code(code, ffi::Py_eval_input, globals, locals)
}
pub fn run(
self,
code: &str,
globals: Option<&PyDict>,
locals: Option<&PyDict>,
) -> PyResult<&'p PyObjectRef> {
self.run_code(code, ffi::Py_file_input, globals, locals)
}
fn run_code(
self,
code: &str,
start: c_int,
globals: Option<&PyDict>,
locals: Option<&PyDict>,
) -> PyResult<&'p PyObjectRef> {
let code = CString::new(code)?;
unsafe {
let mptr = ffi::PyImport_AddModule("__main__\0".as_ptr() as *const _);
if mptr.is_null() {
return Err(PyErr::fetch(self));
}
let globals = globals
.map(|g| g.as_ptr())
.unwrap_or_else(|| ffi::PyModule_GetDict(mptr));
let locals = locals.map(|l| l.as_ptr()).unwrap_or(globals);
let res_ptr = ffi::PyRun_StringFlags(
code.as_ptr(),
start,
globals,
locals,
::std::ptr::null_mut(),
);
self.from_owned_ptr_or_err(res_ptr)
}
}
pub fn get_type<T>(self) -> &'p PyType
where
T: PyTypeObject,
{
unsafe { self.from_borrowed_ptr(T::type_object().into_ptr()) }
}
pub fn import(self, name: &str) -> PyResult<&'p PyModule> {
PyModule::import(self, name)
}
pub fn is_instance<T: PyTypeObject, V: ToPyPointer>(self, obj: &V) -> PyResult<bool> {
T::type_object().as_ref(self).is_instance(obj)
}
pub fn is_subclass<T, U>(self) -> PyResult<bool>
where
T: PyTypeObject,
U: PyTypeObject,
{
T::type_object().as_ref(self).is_subclass::<U>()
}
#[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 NotImplemented(self) -> PyObject {
unsafe { PyObject::from_borrowed_ptr(self, ffi::Py_NotImplemented()) }
}
}
impl<'p> Python<'p> {
#[inline]
pub fn init<T, F>(self, f: F) -> PyResult<Py<T>>
where
F: FnOnce(PyToken) -> T,
T: PyTypeCreate,
{
Py::new(self, f)
}
#[inline]
pub fn init_ref<T, F>(self, f: F) -> PyResult<&'p T>
where
F: FnOnce(PyToken) -> T,
T: PyTypeCreate,
{
Py::new_ref(self, f)
}
#[inline]
pub fn init_mut<T, F>(self, f: F) -> PyResult<&'p mut T>
where
F: FnOnce(PyToken) -> T,
T: PyTypeCreate,
{
Py::new_mut(self, f)
}
}
impl<'p> Python<'p> {
unsafe fn unchecked_downcast<T: PyTypeInfo>(self, ob: &PyObjectRef) -> &'p T {
if T::OFFSET == 0 {
&*(ob as *const _ as *const T)
} else {
let ptr = (ob.as_ptr() as *mut u8).offset(T::OFFSET) as *mut T;
&*ptr
}
}
unsafe fn unchecked_mut_downcast<T: PyTypeInfo>(self, ob: &PyObjectRef) -> &'p mut T {
if T::OFFSET == 0 {
&mut *(ob as *const _ as *mut T)
} else {
let ptr = (ob.as_ptr() as *mut u8).offset(T::OFFSET) as *mut T;
&mut *ptr
}
}
pub fn checked_cast_as<T>(self, obj: PyObject) -> Result<&'p T, PyDowncastError>
where
T: PyTypeInfo,
{
let p;
unsafe {
p = pythonrun::register_owned(self, obj.into_ptr());
}
<T as PyTryFrom>::try_from(p)
}
pub unsafe fn cast_as<T>(self, obj: PyObject) -> &'p T
where
T: PyTypeInfo,
{
let p = pythonrun::register_owned(self, obj.into_ptr());
self.unchecked_downcast(p)
}
pub unsafe fn from_borrowed_ptr_to_obj(self, ptr: *mut ffi::PyObject) -> &'p PyObjectRef {
if ptr.is_null() {
crate::err::panic_after_error();
} else {
pythonrun::register_borrowed(self, ptr)
}
}
pub unsafe fn from_owned_ptr<T>(self, ptr: *mut ffi::PyObject) -> &'p T
where
T: PyTypeInfo,
{
if ptr.is_null() {
crate::err::panic_after_error();
} else {
let p = pythonrun::register_owned(self, ptr);
self.unchecked_downcast(p)
}
}
pub unsafe fn mut_from_owned_ptr<T>(self, ptr: *mut ffi::PyObject) -> &'p mut T
where
T: PyTypeInfo,
{
if ptr.is_null() {
crate::err::panic_after_error();
} else {
let p = pythonrun::register_owned(self, ptr);
self.unchecked_mut_downcast(p)
}
}
pub unsafe fn from_owned_ptr_or_err<T>(self, ptr: *mut ffi::PyObject) -> PyResult<&'p T>
where
T: PyTypeInfo,
{
if ptr.is_null() {
Err(PyErr::fetch(self))
} else {
let p = pythonrun::register_owned(self, ptr);
Ok(self.unchecked_downcast(p))
}
}
pub unsafe fn from_owned_ptr_or_opt<T>(self, ptr: *mut ffi::PyObject) -> Option<&'p T>
where
T: PyTypeInfo,
{
if ptr.is_null() {
None
} else {
let p = pythonrun::register_owned(self, ptr);
Some(self.unchecked_downcast(p))
}
}
pub unsafe fn from_borrowed_ptr<T>(self, ptr: *mut ffi::PyObject) -> &'p T
where
T: PyTypeInfo,
{
let p = pythonrun::register_borrowed(self, ptr);
self.unchecked_downcast(p)
}
pub unsafe fn mut_from_borrowed_ptr<T>(self, ptr: *mut ffi::PyObject) -> &'p mut T
where
T: PyTypeInfo,
{
if ptr.is_null() {
crate::err::panic_after_error();
} else {
let p = pythonrun::register_borrowed(self, ptr);
self.unchecked_mut_downcast(p)
}
}
pub unsafe fn from_borrowed_ptr_or_err<T>(self, ptr: *mut ffi::PyObject) -> PyResult<&'p T>
where
T: PyTypeInfo,
{
if ptr.is_null() {
Err(PyErr::fetch(self))
} else {
let p = pythonrun::register_borrowed(self, ptr);
Ok(self.unchecked_downcast(p))
}
}
pub unsafe fn from_borrowed_ptr_or_opt<T>(self, ptr: *mut ffi::PyObject) -> Option<&'p T>
where
T: PyTypeInfo,
{
if ptr.is_null() {
None
} else {
let p = pythonrun::register_borrowed(self, ptr);
Some(self.unchecked_downcast(p))
}
}
#[doc(hidden)]
pub fn register_any<T: 'static>(self, ob: T) -> &'p T {
unsafe { pythonrun::register_any(ob) }
}
#[inline]
pub fn release<T>(self, ob: T)
where
T: IntoPyPointer,
{
unsafe {
let ptr = ob.into_ptr();
if !ptr.is_null() {
ffi::Py_DECREF(ptr);
}
}
}
#[inline]
pub fn xdecref<T: IntoPyPointer>(self, ptr: T) {
unsafe { ffi::Py_XDECREF(ptr.into_ptr()) };
}
}
#[cfg(test)]
mod test {
use crate::objectprotocol::ObjectProtocol;
use crate::types::{PyBool, PyDict, PyInt, PyList, PyObjectRef};
use crate::Python;
#[test]
fn test_eval() {
let gil = Python::acquire_gil();
let py = gil.python();
let v: i32 = py
.eval("min(1, 2)", None, None)
.map_err(|e| e.print(py))
.unwrap()
.extract()
.unwrap();
assert_eq!(v, 1);
let d = PyDict::new(py);
d.set_item("foo", 13).unwrap();
let v: i32 = py
.eval("foo + 29", Some(d), None)
.unwrap()
.extract()
.unwrap();
assert_eq!(v, 42);
let v: i32 = py
.eval("foo + 29", None, Some(d))
.unwrap()
.extract()
.unwrap();
assert_eq!(v, 42);
let v: i32 = py
.eval("min(foo, 2)", None, Some(d))
.unwrap()
.extract()
.unwrap();
assert_eq!(v, 2);
}
#[test]
fn test_is_instance() {
let gil = Python::acquire_gil();
let py = gil.python();
assert!(py
.is_instance::<PyBool, PyObjectRef>(PyBool::new(py, true).into())
.unwrap());
let list = PyList::new(py, &[1, 2, 3, 4]);
assert!(!py.is_instance::<PyBool, _>(list.as_ref()).unwrap());
assert!(py.is_instance::<PyList, _>(list.as_ref()).unwrap());
}
#[test]
fn test_is_subclass() {
let gil = Python::acquire_gil();
let py = gil.python();
assert!(py.is_subclass::<PyBool, PyInt>().unwrap());
assert!(!py.is_subclass::<PyBool, PyList>().unwrap());
}
}