use std::cmp::Ordering;
use std::fmt;
use crate::conversion::ToPyObject;
use crate::err::{self, PyErr, PyResult};
use crate::ffi;
use crate::objects::{PyDict, PyObject, PyString, PyTuple};
use crate::python::{Python, PythonObject, ToPythonPointer};
pub trait ObjectProtocol: PythonObject {
#[inline]
fn hasattr<N>(&self, py: Python, attr_name: N) -> PyResult<bool>
where
N: ToPyObject,
{
attr_name.with_borrowed_ptr(py, |attr_name| unsafe {
Ok(ffi::PyObject_HasAttr(self.as_ptr(), attr_name) != 0)
})
}
#[inline]
fn getattr<N>(&self, py: Python, attr_name: N) -> PyResult<PyObject>
where
N: ToPyObject,
{
attr_name.with_borrowed_ptr(py, |attr_name| unsafe {
err::result_from_owned_ptr(py, ffi::PyObject_GetAttr(self.as_ptr(), attr_name))
})
}
#[inline]
fn setattr<N, V>(&self, py: Python, attr_name: N, value: V) -> PyResult<()>
where
N: ToPyObject,
V: ToPyObject,
{
attr_name.with_borrowed_ptr(py, move |attr_name| {
value.with_borrowed_ptr(py, |value| unsafe {
err::error_on_minusone(py, ffi::PyObject_SetAttr(self.as_ptr(), attr_name, value))
})
})
}
#[inline]
fn delattr<N>(&self, py: Python, attr_name: N) -> PyResult<()>
where
N: ToPyObject,
{
attr_name.with_borrowed_ptr(py, |attr_name| unsafe {
err::error_on_minusone(py, ffi::PyObject_DelAttr(self.as_ptr(), attr_name))
})
}
fn compare<O>(&self, py: Python, other: O) -> PyResult<Ordering>
where
O: ToPyObject,
{
#[cfg(feature = "python27-sys")]
unsafe fn do_compare(
py: Python,
a: *mut ffi::PyObject,
b: *mut ffi::PyObject,
) -> PyResult<Ordering> {
let mut result = -1;
err::error_on_minusone(py, ffi::PyObject_Cmp(a, b, &mut result))?;
Ok(result.cmp(&0))
}
#[cfg(feature = "python3-sys")]
unsafe fn do_compare(
py: Python,
a: *mut ffi::PyObject,
b: *mut ffi::PyObject,
) -> PyResult<Ordering> {
let result = ffi::PyObject_RichCompareBool(a, b, ffi::Py_EQ);
if result == 1 {
return Ok(Ordering::Equal);
} else if result < 0 {
return Err(PyErr::fetch(py));
}
let result = ffi::PyObject_RichCompareBool(a, b, ffi::Py_LT);
if result == 1 {
return Ok(Ordering::Less);
} else if result < 0 {
return Err(PyErr::fetch(py));
}
let result = ffi::PyObject_RichCompareBool(a, b, ffi::Py_GT);
if result == 1 {
return Ok(Ordering::Greater);
} else if result < 0 {
return Err(PyErr::fetch(py));
}
Err(PyErr::new::<crate::exc::TypeError, _>(
py,
"ObjectProtocol::compare(): All comparisons returned false",
))
}
other.with_borrowed_ptr(py, |other| unsafe { do_compare(py, self.as_ptr(), other) })
}
fn rich_compare<O>(
&self,
py: Python,
other: O,
compare_op: crate::CompareOp,
) -> PyResult<PyObject>
where
O: ToPyObject,
{
other.with_borrowed_ptr(py, |other| unsafe {
err::result_cast_from_owned_ptr(
py,
ffi::PyObject_RichCompare(self.as_ptr(), other, compare_op as libc::c_int),
)
})
}
#[inline]
fn repr(&self, py: Python) -> PyResult<PyString> {
unsafe { err::result_cast_from_owned_ptr(py, ffi::PyObject_Repr(self.as_ptr())) }
}
#[inline]
fn str(&self, py: Python) -> PyResult<PyString> {
unsafe { err::result_cast_from_owned_ptr(py, ffi::PyObject_Str(self.as_ptr())) }
}
#[inline]
#[cfg(feature = "python27-sys")]
fn unistr(&self, py: Python) -> PyResult<crate::objects::PyUnicode> {
unsafe { err::result_cast_from_owned_ptr(py, ffi::PyObject_Unicode(self.as_ptr())) }
}
#[inline]
fn is_callable(&self, _py: Python) -> bool {
unsafe { ffi::PyCallable_Check(self.as_ptr()) != 0 }
}
#[inline]
fn call<A>(&self, py: Python, args: A, kwargs: Option<&PyDict>) -> PyResult<PyObject>
where
A: ToPyObject<ObjectType = PyTuple>,
{
args.with_borrowed_ptr(py, |args| unsafe {
err::result_from_owned_ptr(py, ffi::PyObject_Call(self.as_ptr(), args, kwargs.as_ptr()))
})
}
#[inline]
fn call_method<A>(
&self,
py: Python,
name: &str,
args: A,
kwargs: Option<&PyDict>,
) -> PyResult<PyObject>
where
A: ToPyObject<ObjectType = PyTuple>,
{
self.getattr(py, name)?.call(py, args, kwargs)
}
#[inline]
fn hash(&self, py: Python) -> PyResult<crate::Py_hash_t> {
let v = unsafe { ffi::PyObject_Hash(self.as_ptr()) };
if v == -1 {
Err(PyErr::fetch(py))
} else {
Ok(v)
}
}
#[inline]
fn is_true(&self, py: Python) -> PyResult<bool> {
let v = unsafe { ffi::PyObject_IsTrue(self.as_ptr()) };
if v == -1 {
Err(PyErr::fetch(py))
} else {
Ok(v != 0)
}
}
#[inline]
fn len(&self, py: Python) -> PyResult<usize> {
let v = unsafe { ffi::PyObject_Size(self.as_ptr()) };
if v == -1 {
Err(PyErr::fetch(py))
} else {
Ok(v as usize)
}
}
#[inline]
fn get_item<K>(&self, py: Python, key: K) -> PyResult<PyObject>
where
K: ToPyObject,
{
key.with_borrowed_ptr(py, |key| unsafe {
err::result_from_owned_ptr(py, ffi::PyObject_GetItem(self.as_ptr(), key))
})
}
#[inline]
fn set_item<K, V>(&self, py: Python, key: K, value: V) -> PyResult<()>
where
K: ToPyObject,
V: ToPyObject,
{
key.with_borrowed_ptr(py, move |key| {
value.with_borrowed_ptr(py, |value| unsafe {
err::error_on_minusone(py, ffi::PyObject_SetItem(self.as_ptr(), key, value))
})
})
}
#[inline]
fn del_item<K>(&self, py: Python, key: K) -> PyResult<()>
where
K: ToPyObject,
{
key.with_borrowed_ptr(py, |key| unsafe {
err::error_on_minusone(py, ffi::PyObject_DelItem(self.as_ptr(), key))
})
}
#[inline]
fn iter<'p>(&self, py: Python<'p>) -> PyResult<crate::objects::PyIterator<'p>> {
let obj = unsafe { err::result_from_owned_ptr(py, ffi::PyObject_GetIter(self.as_ptr())) }?;
Ok(crate::objects::PyIterator::from_object(py, obj)?)
}
}
impl ObjectProtocol for PyObject {}
impl fmt::Debug for PyObject {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
let gil_guard = Python::acquire_gil();
let py = gil_guard.python();
let repr_obj = self.repr(py).map_err(|_| fmt::Error)?;
f.write_str(&repr_obj.to_string_lossy(py))
}
}
impl fmt::Display for PyObject {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
let gil_guard = Python::acquire_gil();
let py = gil_guard.python();
let str_obj = self.str(py).map_err(|_| fmt::Error)?;
f.write_str(&str_obj.to_string_lossy(py))
}
}
#[cfg(test)]
mod test {
use super::ObjectProtocol;
use crate::conversion::ToPyObject;
use crate::objects::{PyList, PyTuple};
use crate::python::{Python, PythonObject};
#[test]
fn test_debug_string() {
let gil = Python::acquire_gil();
let py = gil.python();
let v = "Hello\n".to_py_object(py).into_object();
assert_eq!(format!("{:?}", v), "'Hello\\n'");
}
#[test]
fn test_display_string() {
let gil = Python::acquire_gil();
let py = gil.python();
let v = "Hello\n".to_py_object(py).into_object();
assert_eq!(format!("{}", v), "Hello\n");
}
#[test]
fn test_compare() {
use std::cmp::Ordering;
let gil = Python::acquire_gil();
let py = gil.python();
let one = 1i32.to_py_object(py).into_object();
assert_eq!(one.compare(py, 1).unwrap(), Ordering::Equal);
assert_eq!(one.compare(py, 2).unwrap(), Ordering::Less);
assert_eq!(one.compare(py, 0).unwrap(), Ordering::Greater);
}
}