1use std::cmp::Ordering;
20use std::fmt;
21
22use crate::conversion::ToPyObject;
23use crate::err::{self, PyErr, PyResult};
24use crate::ffi;
25use crate::objects::{PyDict, PyObject, PyString, PyTuple};
26use crate::python::{Python, PythonObject, ToPythonPointer};
27
28mod number;
29
30pub use self::number::NumberProtocol;
31
32pub trait ObjectProtocol: PythonObject {
34 #[inline]
37 fn hasattr<N>(&self, py: Python, attr_name: N) -> PyResult<bool>
38 where
39 N: ToPyObject,
40 {
41 attr_name.with_borrowed_ptr(py, |attr_name| unsafe {
42 Ok(ffi::PyObject_HasAttr(self.as_ptr(), attr_name) != 0)
43 })
44 }
45
46 #[inline]
49 fn getattr<N>(&self, py: Python, attr_name: N) -> PyResult<PyObject>
50 where
51 N: ToPyObject,
52 {
53 attr_name.with_borrowed_ptr(py, |attr_name| unsafe {
54 err::result_from_owned_ptr(py, ffi::PyObject_GetAttr(self.as_ptr(), attr_name))
55 })
56 }
57
58 #[inline]
61 fn setattr<N, V>(&self, py: Python, attr_name: N, value: V) -> PyResult<()>
62 where
63 N: ToPyObject,
64 V: ToPyObject,
65 {
66 attr_name.with_borrowed_ptr(py, move |attr_name| {
67 value.with_borrowed_ptr(py, |value| unsafe {
68 err::error_on_minusone(py, ffi::PyObject_SetAttr(self.as_ptr(), attr_name, value))
69 })
70 })
71 }
72
73 #[inline]
76 fn delattr<N>(&self, py: Python, attr_name: N) -> PyResult<()>
77 where
78 N: ToPyObject,
79 {
80 attr_name.with_borrowed_ptr(py, |attr_name| unsafe {
81 err::error_on_minusone(py, ffi::PyObject_DelAttr(self.as_ptr(), attr_name))
82 })
83 }
84
85 fn compare<O>(&self, py: Python, other: O) -> PyResult<Ordering>
101 where
102 O: ToPyObject,
103 {
104 #[cfg(feature = "python27-sys")]
105 unsafe fn do_compare(
106 py: Python,
107 a: *mut ffi::PyObject,
108 b: *mut ffi::PyObject,
109 ) -> PyResult<Ordering> {
110 let mut result = -1;
111 err::error_on_minusone(py, ffi::PyObject_Cmp(a, b, &mut result))?;
112 Ok(result.cmp(&0))
113 }
114
115 #[cfg(feature = "python3-sys")]
116 unsafe fn do_compare(
117 py: Python,
118 a: *mut ffi::PyObject,
119 b: *mut ffi::PyObject,
120 ) -> PyResult<Ordering> {
121 let result = ffi::PyObject_RichCompareBool(a, b, ffi::Py_EQ);
122 if result == 1 {
123 return Ok(Ordering::Equal);
124 } else if result < 0 {
125 return Err(PyErr::fetch(py));
126 }
127 let result = ffi::PyObject_RichCompareBool(a, b, ffi::Py_LT);
128 if result == 1 {
129 return Ok(Ordering::Less);
130 } else if result < 0 {
131 return Err(PyErr::fetch(py));
132 }
133 let result = ffi::PyObject_RichCompareBool(a, b, ffi::Py_GT);
134 if result == 1 {
135 return Ok(Ordering::Greater);
136 } else if result < 0 {
137 return Err(PyErr::fetch(py));
138 }
139 Err(PyErr::new::<crate::exc::TypeError, _>(
140 py,
141 "ObjectProtocol::compare(): All comparisons returned false",
142 ))
143 }
144
145 other.with_borrowed_ptr(py, |other| unsafe { do_compare(py, self.as_ptr(), other) })
146 }
147
148 fn rich_compare<O>(
158 &self,
159 py: Python,
160 other: O,
161 compare_op: crate::CompareOp,
162 ) -> PyResult<PyObject>
163 where
164 O: ToPyObject,
165 {
166 other.with_borrowed_ptr(py, |other| unsafe {
167 err::result_cast_from_owned_ptr(
168 py,
169 ffi::PyObject_RichCompare(self.as_ptr(), other, compare_op as libc::c_int),
170 )
171 })
172 }
173
174 #[inline]
177 fn repr(&self, py: Python) -> PyResult<PyString> {
178 unsafe { err::result_cast_from_owned_ptr(py, ffi::PyObject_Repr(self.as_ptr())) }
179 }
180
181 #[inline]
184 fn str(&self, py: Python) -> PyResult<PyString> {
185 unsafe { err::result_cast_from_owned_ptr(py, ffi::PyObject_Str(self.as_ptr())) }
186 }
187
188 #[inline]
191 #[cfg(feature = "python27-sys")]
192 fn unistr(&self, py: Python) -> PyResult<crate::objects::PyUnicode> {
193 unsafe { err::result_cast_from_owned_ptr(py, ffi::PyObject_Unicode(self.as_ptr())) }
194 }
195
196 #[inline]
198 fn is_callable(&self, _py: Python) -> bool {
199 unsafe { ffi::PyCallable_Check(self.as_ptr()) != 0 }
200 }
201
202 #[inline]
210 fn call<A>(&self, py: Python, args: A, kwargs: Option<&PyDict>) -> PyResult<PyObject>
211 where
212 A: ToPyObject<ObjectType = PyTuple>,
213 {
214 args.with_borrowed_ptr(py, |args| unsafe {
215 err::result_from_owned_ptr(py, ffi::PyObject_Call(self.as_ptr(), args, kwargs.as_ptr()))
216 })
217 }
218
219 #[inline]
240 fn call_method<A>(
241 &self,
242 py: Python,
243 name: &str,
244 args: A,
245 kwargs: Option<&PyDict>,
246 ) -> PyResult<PyObject>
247 where
248 A: ToPyObject<ObjectType = PyTuple>,
249 {
250 self.getattr(py, name)?.call(py, args, kwargs)
251 }
252
253 #[inline]
256 fn hash(&self, py: Python) -> PyResult<crate::Py_hash_t> {
257 let v = unsafe { ffi::PyObject_Hash(self.as_ptr()) };
258 if v == -1 {
259 Err(PyErr::fetch(py))
260 } else {
261 Ok(v)
262 }
263 }
264
265 #[inline]
268 fn is_true(&self, py: Python) -> PyResult<bool> {
269 let v = unsafe { ffi::PyObject_IsTrue(self.as_ptr()) };
270 if v == -1 {
271 Err(PyErr::fetch(py))
272 } else {
273 Ok(v != 0)
274 }
275 }
276
277 #[inline]
280 fn len(&self, py: Python) -> PyResult<usize> {
281 let v = unsafe { ffi::PyObject_Size(self.as_ptr()) };
282 if v == -1 {
283 Err(PyErr::fetch(py))
284 } else {
285 Ok(v as usize)
286 }
287 }
288
289 #[inline]
291 fn get_item<K>(&self, py: Python, key: K) -> PyResult<PyObject>
292 where
293 K: ToPyObject,
294 {
295 key.with_borrowed_ptr(py, |key| unsafe {
296 err::result_from_owned_ptr(py, ffi::PyObject_GetItem(self.as_ptr(), key))
297 })
298 }
299
300 #[inline]
303 fn set_item<K, V>(&self, py: Python, key: K, value: V) -> PyResult<()>
304 where
305 K: ToPyObject,
306 V: ToPyObject,
307 {
308 key.with_borrowed_ptr(py, move |key| {
309 value.with_borrowed_ptr(py, |value| unsafe {
310 err::error_on_minusone(py, ffi::PyObject_SetItem(self.as_ptr(), key, value))
311 })
312 })
313 }
314
315 #[inline]
318 fn del_item<K>(&self, py: Python, key: K) -> PyResult<()>
319 where
320 K: ToPyObject,
321 {
322 key.with_borrowed_ptr(py, |key| unsafe {
323 err::error_on_minusone(py, ffi::PyObject_DelItem(self.as_ptr(), key))
324 })
325 }
326
327 #[inline]
331 fn iter<'p>(&self, py: Python<'p>) -> PyResult<crate::objects::PyIterator<'p>> {
332 let obj = unsafe { err::result_from_owned_ptr(py, ffi::PyObject_GetIter(self.as_ptr())) }?;
333 Ok(crate::objects::PyIterator::from_object(py, obj)?)
334 }
335}
336
337impl ObjectProtocol for PyObject {}
338
339impl fmt::Debug for PyObject {
340 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
341 let gil_guard = Python::acquire_gil();
343 let py = gil_guard.python();
344 let repr_obj = self.repr(py).map_err(|_| fmt::Error)?;
345 f.write_str(&repr_obj.to_string_lossy(py))
346 }
347}
348
349impl fmt::Display for PyObject {
350 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
351 let gil_guard = Python::acquire_gil();
353 let py = gil_guard.python();
354 let str_obj = self.str(py).map_err(|_| fmt::Error)?;
355 f.write_str(&str_obj.to_string_lossy(py))
356 }
357}
358
359#[cfg(test)]
360mod test {
361 use super::ObjectProtocol;
362 use crate::conversion::ToPyObject;
363 use crate::objects::{PyList, PyTuple};
364 use crate::python::{Python, PythonObject};
365
366 #[test]
367 fn test_debug_string() {
368 let gil = Python::acquire_gil();
369 let py = gil.python();
370 let v = "Hello\n".to_py_object(py).into_object();
371 assert_eq!(format!("{:?}", v), "'Hello\\n'");
372 }
373
374 #[test]
375 fn test_display_string() {
376 let gil = Python::acquire_gil();
377 let py = gil.python();
378 let v = "Hello\n".to_py_object(py).into_object();
379 assert_eq!(format!("{}", v), "Hello\n");
380 }
381
382 #[test]
383 fn test_compare() {
384 use std::cmp::Ordering;
385 let gil = Python::acquire_gil();
386 let py = gil.python();
387 let one = 1i32.to_py_object(py).into_object();
388 assert_eq!(one.compare(py, 1).unwrap(), Ordering::Equal);
389 assert_eq!(one.compare(py, 2).unwrap(), Ordering::Less);
390 assert_eq!(one.compare(py, 0).unwrap(), Ordering::Greater);
391 }
392}