Skip to content

Commit 958b8ee

Browse files
committed
improve coverage of PyAnyMethods
1 parent a6a8ab0 commit 958b8ee

File tree

4 files changed

+110
-65
lines changed

4 files changed

+110
-65
lines changed

src/instance.rs

+49-43
Original file line numberDiff line numberDiff line change
@@ -67,54 +67,42 @@ impl<'py> Py2<'py, PyAny> {
6767
}
6868

6969
impl<'py, T> Py2<'py, T> {
70-
/// Helper to cast to Py2<'py, T>
70+
/// Helper to cast to Py2<'py, PyAny>
7171
pub(crate) fn as_any(&self) -> &Py2<'py, PyAny> {
72+
// Safety: all Py2<T> have the same memory layout, and all Py2<T> are valid Py2<PyAny>
7273
unsafe { std::mem::transmute(self) }
7374
}
7475
}
7576

76-
impl<'py, T> std::fmt::Debug for Py2<'py, T>
77-
where
78-
Self: Deref<Target = Py2<'py, PyAny>>,
79-
{
77+
impl<'py, T> std::fmt::Debug for Py2<'py, T> {
8078
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
81-
let s = self.repr().or(Err(std::fmt::Error))?;
82-
f.write_str(&s.as_gil_ref().to_string_lossy())
79+
let any = self.as_any();
80+
python_format(any, any.repr(), f)
8381
}
8482
}
8583

86-
impl<'py, T> std::fmt::Display for Py2<'py, T>
87-
where
88-
Self: Deref<Target = Py2<'py, PyAny>>,
89-
{
84+
impl<'py, T> std::fmt::Display for Py2<'py, T> {
9085
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
91-
match self.str() {
92-
Result::Ok(s) => return f.write_str(&s.as_gil_ref().to_string_lossy()),
93-
Result::Err(err) => {
94-
err.write_unraisable(self.py(), std::option::Option::Some(self.as_gil_ref()))
95-
}
96-
}
97-
98-
match self.get_type().name() {
99-
Result::Ok(name) => std::write!(f, "<unprintable {} object>", name),
100-
Result::Err(_err) => f.write_str("<unprintable object>"),
101-
}
86+
let any = self.as_any();
87+
python_format(any, any.str(), f)
10288
}
10389
}
10490

105-
impl<'py> std::fmt::Display for Py2<'py, PyAny> {
106-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
107-
match self.str() {
108-
Result::Ok(s) => return f.write_str(&s.as_gil_ref().to_string_lossy()),
109-
Result::Err(err) => {
110-
err.write_unraisable(self.py(), std::option::Option::Some(self.as_gil_ref()))
111-
}
91+
fn python_format(
92+
any: &Py2<'_, PyAny>,
93+
format_result: PyResult<Py2<'_, PyString>>,
94+
f: &mut std::fmt::Formatter<'_>,
95+
) -> Result<(), std::fmt::Error> {
96+
match format_result {
97+
Result::Ok(s) => return f.write_str(&s.as_gil_ref().to_string_lossy()),
98+
Result::Err(err) => {
99+
err.write_unraisable(any.py(), std::option::Option::Some(any.as_gil_ref()))
112100
}
101+
}
113102

114-
match self.get_type().name() {
115-
Result::Ok(name) => std::write!(f, "<unprintable {} object>", name),
116-
Result::Err(_err) => f.write_str("<unprintable object>"),
117-
}
103+
match any.get_type().name() {
104+
Result::Ok(name) => std::write!(f, "<unprintable {} object>", name),
105+
Result::Err(_err) => f.write_str("<unprintable object>"),
118106
}
119107
}
120108

@@ -126,14 +114,16 @@ where
126114

127115
#[inline]
128116
fn deref(&self) -> &Py2<'py, PyAny> {
129-
::std::convert::AsRef::as_ref(self)
117+
self.as_any()
130118
}
131119
}
132120

133-
impl<'py, T> AsRef<Py2<'py, PyAny>> for Py2<'py, T> {
121+
impl<'py, T> AsRef<Py2<'py, PyAny>> for Py2<'py, T>
122+
where
123+
T: AsRef<PyAny>,
124+
{
134125
fn as_ref(&self) -> &Py2<'py, PyAny> {
135-
// Safety: &Py2<T> has the same layout as &Py2<PyAny>
136-
unsafe { std::mem::transmute(self) }
126+
self.as_any()
137127
}
138128
}
139129

@@ -176,9 +166,7 @@ impl<'py, T> Py2<'py, T> {
176166
/// of the pointer or decrease the reference count (e.g. with [`pyo3::ffi::Py_DecRef`](crate::ffi::Py_DecRef)).
177167
#[inline]
178168
pub fn into_ptr(self) -> *mut ffi::PyObject {
179-
let ptr = (self.1).0.as_ptr();
180-
std::mem::forget(self);
181-
ptr
169+
self.into_non_null().as_ptr()
182170
}
183171

184172
/// Internal helper to convert e.g. &'a &'py PyDict to &'a Py2<'py, PyDict> for
@@ -211,10 +199,12 @@ impl<'py, T> Py2<'py, T> {
211199
unsafe { self.py().from_owned_ptr(self.into_ptr()) }
212200
}
213201

202+
// Internal helper to convert `self` into a `NonNull` which owns the
203+
// Python reference.
214204
pub(crate) fn into_non_null(self) -> NonNull<ffi::PyObject> {
215-
let ptr = (self.1).0;
216-
std::mem::forget(self);
217-
ptr
205+
// wrap in ManuallyDrop to avoid running Drop for self and decreasing
206+
// the reference count
207+
ManuallyDrop::new(self).1 .0
218208
}
219209
}
220210

@@ -1477,6 +1467,22 @@ a = A()
14771467
});
14781468
}
14791469

1470+
#[test]
1471+
fn test_debug_fmt() {
1472+
Python::with_gil(|py| {
1473+
let obj = "hello world".to_object(py).attach_into(py);
1474+
assert_eq!(format!("{:?}", obj), "'hello world'");
1475+
});
1476+
}
1477+
1478+
#[test]
1479+
fn test_display_fmt() {
1480+
Python::with_gil(|py| {
1481+
let obj = "hello world".to_object(py).attach_into(py);
1482+
assert_eq!(format!("{}", obj), "hello world");
1483+
});
1484+
}
1485+
14801486
#[cfg(feature = "macros")]
14811487
mod using_macros {
14821488
use super::*;

src/types/any.rs

+44-8
Original file line numberDiff line numberDiff line change
@@ -702,7 +702,13 @@ impl PyAny {
702702
/// This is typically a new iterator but if the argument is an iterator,
703703
/// this returns itself.
704704
pub fn iter(&self) -> PyResult<&PyIterator> {
705-
PyIterator::from_object(self)
705+
Py2::<PyAny>::borrowed_from_gil_ref(&self)
706+
.iter()
707+
.map(|py2| {
708+
// Can't use into_gil_ref here because T: PyTypeInfo bound is not satisfied
709+
// Safety: into_ptr produces a valid pointer to PyIterator object
710+
unsafe { self.py().from_owned_ptr(py2.into_ptr()) }
711+
})
706712
}
707713

708714
/// Returns the Python type object for this object's type.
@@ -1804,42 +1810,48 @@ impl<'py> PyAnyMethods<'py> for Py2<'py, PyAny> {
18041810
where
18051811
O: ToPyObject,
18061812
{
1807-
self.rich_compare(other, CompareOp::Lt)?.is_true()
1813+
self.rich_compare(other, CompareOp::Lt)
1814+
.and_then(|any| any.is_true())
18081815
}
18091816

18101817
fn le<O>(&self, other: O) -> PyResult<bool>
18111818
where
18121819
O: ToPyObject,
18131820
{
1814-
self.rich_compare(other, CompareOp::Le)?.is_true()
1821+
self.rich_compare(other, CompareOp::Le)
1822+
.and_then(|any| any.is_true())
18151823
}
18161824

18171825
fn eq<O>(&self, other: O) -> PyResult<bool>
18181826
where
18191827
O: ToPyObject,
18201828
{
1821-
self.rich_compare(other, CompareOp::Eq)?.is_true()
1829+
self.rich_compare(other, CompareOp::Eq)
1830+
.and_then(|any| any.is_true())
18221831
}
18231832

18241833
fn ne<O>(&self, other: O) -> PyResult<bool>
18251834
where
18261835
O: ToPyObject,
18271836
{
1828-
self.rich_compare(other, CompareOp::Ne)?.is_true()
1837+
self.rich_compare(other, CompareOp::Ne)
1838+
.and_then(|any| any.is_true())
18291839
}
18301840

18311841
fn gt<O>(&self, other: O) -> PyResult<bool>
18321842
where
18331843
O: ToPyObject,
18341844
{
1835-
self.rich_compare(other, CompareOp::Gt)?.is_true()
1845+
self.rich_compare(other, CompareOp::Gt)
1846+
.and_then(|any| any.is_true())
18361847
}
18371848

18381849
fn ge<O>(&self, other: O) -> PyResult<bool>
18391850
where
18401851
O: ToPyObject,
18411852
{
1842-
self.rich_compare(other, CompareOp::Ge)?.is_true()
1853+
self.rich_compare(other, CompareOp::Ge)
1854+
.and_then(|any| any.is_true())
18431855
}
18441856

18451857
fn is_callable(&self) -> bool {
@@ -2169,7 +2181,7 @@ impl<'py> PyAnyMethods<'py> for Py2<'py, PyAny> {
21692181
mod tests {
21702182
use crate::{
21712183
types::{IntoPyDict, PyAny, PyBool, PyList, PyLong, PyModule},
2172-
Python, ToPyObject,
2184+
PyTypeInfo, Python, ToPyObject,
21732185
};
21742186

21752187
#[test]
@@ -2542,4 +2554,28 @@ class SimpleClass:
25422554
assert!(!not_ellipsis.is_ellipsis());
25432555
});
25442556
}
2557+
2558+
#[test]
2559+
fn test_is_callable() {
2560+
Python::with_gil(|py| {
2561+
assert!(PyList::type_object(py).is_callable());
2562+
2563+
let not_callable = 5.to_object(py).into_ref(py);
2564+
assert!(!not_callable.is_callable());
2565+
});
2566+
}
2567+
2568+
#[test]
2569+
fn test_is_empty() {
2570+
Python::with_gil(|py| {
2571+
let empty_list: &PyAny = PyList::empty(py);
2572+
assert!(empty_list.is_empty().unwrap());
2573+
2574+
let list: &PyAny = PyList::new(py, vec![1, 2, 3]);
2575+
assert!(!list.is_empty().unwrap());
2576+
2577+
let not_container = 5.to_object(py).into_ref(py);
2578+
assert!(not_container.is_empty().is_err());
2579+
});
2580+
}
25452581
}

src/types/iterator.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,11 @@ impl PyIterator {
3333
///
3434
/// Equivalent to Python's built-in `iter` function.
3535
pub fn from_object(obj: &PyAny) -> PyResult<&PyIterator> {
36-
unsafe {
37-
obj.py()
38-
.from_owned_ptr_or_err(ffi::PyObject_GetIter(obj.as_ptr()))
39-
}
36+
Self::from_object2(Py2::borrowed_from_gil_ref(&obj)).map(|py2| {
37+
// Can't use into_gil_ref here because T: PyTypeInfo bound is not satisfied
38+
// Safety: into_ptr produces a valid pointer to PyIterator object
39+
unsafe { obj.py().from_owned_ptr(py2.into_ptr()) }
40+
})
4041
}
4142

4243
pub(crate) fn from_object2<'py>(obj: &Py2<'py, PyAny>) -> PyResult<Py2<'py, PyIterator>> {

src/types/pysuper.rs

+12-10
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
use crate::ffi;
21
use crate::instance::Py2;
32
use crate::types::any::PyAnyMethods;
43
use crate::types::PyType;
4+
use crate::{ffi, PyTypeInfo};
55
use crate::{PyAny, PyResult};
66

77
/// Represents a Python `super` object.
@@ -57,20 +57,22 @@ impl PySuper {
5757
/// }
5858
/// ```
5959
pub fn new<'py>(ty: &'py PyType, obj: &'py PyAny) -> PyResult<&'py PySuper> {
60-
let py = ty.py();
61-
let super_ = py.get_type::<PySuper>().call1((ty, obj))?;
62-
let super_ = super_.downcast::<PySuper>()?;
63-
Ok(super_)
60+
Self::new2(
61+
Py2::borrowed_from_gil_ref(&ty),
62+
Py2::borrowed_from_gil_ref(&obj),
63+
)
64+
.map(Py2::into_gil_ref)
6465
}
6566

6667
pub(crate) fn new2<'py>(
6768
ty: &Py2<'py, PyType>,
6869
obj: &Py2<'py, PyAny>,
6970
) -> PyResult<Py2<'py, PySuper>> {
70-
let py = ty.py();
71-
let super_ =
72-
Py2::<PyType>::borrowed_from_gil_ref(&py.get_type::<PySuper>()).call1((ty, obj))?;
73-
let super_ = super_.downcast_into::<PySuper>()?;
74-
Ok(super_)
71+
Py2::<PyType>::borrowed_from_gil_ref(&PySuper::type_object(ty.py()))
72+
.call1((ty, obj))
73+
.map(|any| {
74+
// Safety: super() always returns instance of super
75+
unsafe { any.downcast_into_unchecked() }
76+
})
7577
}
7678
}

0 commit comments

Comments
 (0)