Skip to content

Commit

Permalink
Rework Py methods and documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
davidhewitt committed Aug 8, 2020
1 parent 916900c commit 48bd720
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 128 deletions.
2 changes: 1 addition & 1 deletion pyo3-derive-backend/src/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ pub fn add_fn_to_module(
};

let function = unsafe {
pyo3::PyObject::from_owned_ptr_or_panic(
pyo3::PyObject::from_owned_ptr(
py,
pyo3::ffi::PyCFunction_New(
Box::into_raw(Box::new(_def.as_method_def())),
Expand Down
231 changes: 121 additions & 110 deletions src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,64 +115,6 @@ where
}

impl<T> Py<T> {
/// For internal conversions
pub(crate) unsafe fn from_not_null(ptr: NonNull<ffi::PyObject>) -> Self {
Self(ptr, PhantomData)
}

/// Creates a `Py<T>` instance for the given FFI pointer.
///
/// This moves ownership over the pointer into the `Py<T>`.
/// Undefined behavior if the pointer is NULL or invalid.
#[inline]
pub unsafe fn from_owned_ptr(_py: Python, ptr: *mut ffi::PyObject) -> Py<T> {
debug_assert!(
!ptr.is_null() && ffi::Py_REFCNT(ptr) > 0,
format!("REFCNT: {:?} - {:?}", ptr, ffi::Py_REFCNT(ptr))
);
Py(NonNull::new_unchecked(ptr), PhantomData)
}

/// Creates a `Py<T>` instance for the given FFI pointer.
///
/// Panics if the pointer is NULL.
/// Undefined behavior if the pointer is invalid.
#[inline]
pub unsafe fn from_owned_ptr_or_panic(_py: Python, ptr: *mut ffi::PyObject) -> Py<T> {
match NonNull::new(ptr) {
Some(nonnull_ptr) => Py(nonnull_ptr, PhantomData),
None => {
crate::err::panic_after_error(_py);
}
}
}

/// Construct `Py<T>` from the result of a Python FFI call that
///
/// Returns a new reference (owned pointer).
/// Returns `Err(PyErr)` if the pointer is NULL.
/// Unsafe because the pointer might be invalid.
pub unsafe fn from_owned_ptr_or_err(py: Python, ptr: *mut ffi::PyObject) -> PyResult<Py<T>> {
match NonNull::new(ptr) {
Some(nonnull_ptr) => Ok(Py(nonnull_ptr, PhantomData)),
None => Err(PyErr::fetch(py)),
}
}

/// Creates a `Py<T>` instance for the given Python FFI pointer.
///
/// Calls `Py_INCREF()` on the ptr.
/// Undefined behavior if the pointer is NULL or invalid.
#[inline]
pub unsafe fn from_borrowed_ptr(_py: Python, ptr: *mut ffi::PyObject) -> Py<T> {
debug_assert!(
!ptr.is_null() && ffi::Py_REFCNT(ptr) > 0,
format!("REFCNT: {:?} - {:?}", ptr, ffi::Py_REFCNT(ptr))
);
ffi::Py_INCREF(ptr);
Py(NonNull::new_unchecked(ptr), PhantomData)
}

/// Gets the reference count of the `ffi::PyObject` pointer.
#[inline]
pub fn get_refcnt(&self, _py: Python) -> isize {
Expand All @@ -185,47 +127,6 @@ impl<T> Py<T> {
unsafe { Py::from_borrowed_ptr(py, self.0.as_ptr()) }
}

/// Returns the inner pointer without decreasing the refcount.
///
/// This will eventually move into its own trait.
pub(crate) fn into_non_null(self) -> NonNull<ffi::PyObject> {
let pointer = self.0;
mem::forget(self);
pointer
}

/// Constructs a `PyObject` from the result of a Python FFI call that
/// returns a new reference (owned pointer).
/// Returns `None` if the pointer is NULL.
pub unsafe fn from_owned_ptr_or_opt(_py: Python, ptr: *mut ffi::PyObject) -> Option<Self> {
match NonNull::new(ptr) {
Some(nonnull_ptr) => Some(Py(nonnull_ptr, PhantomData)),
None => None,
}
}

/// Creates a `PyObject` instance for the given Python FFI pointer.
/// Calls `Py_INCREF()` on the ptr.
/// Returns `Err(PyErr)` if the pointer is NULL.
pub unsafe fn from_borrowed_ptr_or_err(py: Python, ptr: *mut ffi::PyObject) -> PyResult<Self> {
if ptr.is_null() {
Err(PyErr::fetch(py))
} else {
Ok(Self::from_borrowed_ptr(py, ptr))
}
}

/// Creates a `PyObject` instance for the given Python FFI pointer.
/// Calls `Py_INCREF()` on the ptr.
/// Returns `None` if the pointer is NULL.
pub unsafe fn from_borrowed_ptr_or_opt(py: Python, ptr: *mut ffi::PyObject) -> Option<Self> {
if ptr.is_null() {
None
} else {
Some(Self::from_borrowed_ptr(py, ptr))
}
}

/// Returns whether the object is considered to be None.
///
/// This is equivalent to the Python expression `self is None`.
Expand All @@ -245,16 +146,6 @@ impl<T> Py<T> {
}
}

/// Casts the PyObject to a concrete Python object type.
///
/// This can cast only to native Python types, not types implemented in Rust.
pub fn cast_as<'p, D>(&'p self, py: Python<'p>) -> Result<&'p D, PyDowncastError>
where
D: PyTryFrom<'p>,
{
D::try_from(unsafe { py.from_borrowed_ptr::<PyAny>(self.as_ptr()) })
}

/// Extracts some type from the Python object.
///
/// This is a wrapper function around `FromPyObject::extract()`.
Expand Down Expand Up @@ -355,6 +246,113 @@ impl<T> Py<T> {
pub fn call_method0(&self, py: Python, name: &str) -> PyResult<PyObject> {
self.call_method(py, name, (), None)
}

/// Create a `Py<T>` instance by taking ownership of the given FFI pointer.
///
/// # Safety
/// `ptr` must be a pointer to a Python object of type T.
///
/// # Panics
/// Panics if `ptr` is null.
#[inline]
pub unsafe fn from_owned_ptr(py: Python, ptr: *mut ffi::PyObject) -> Py<T> {
match NonNull::new(ptr) {
Some(nonnull_ptr) => Py(nonnull_ptr, PhantomData),
None => crate::err::panic_after_error(py),
}
}

/// Deprecated alias for [`from_owned_ptr`](#method.from_owned_ptr).
#[inline]
#[deprecated = "this is a deprecated alias for Py::from_owned_ptr"]
pub unsafe fn from_owned_ptr_or_panic(py: Python, ptr: *mut ffi::PyObject) -> Py<T> {
Py::from_owned_ptr(py, ptr)
}

/// Create a `Py<T>` instance by taking ownership of the given FFI pointer.
///
/// If `ptr` is null then the current Python exception is fetched as a `PyErr`.
///
/// # Safety
/// If non-null, `ptr` must be a pointer to a Python object of type T.
#[inline]
pub unsafe fn from_owned_ptr_or_err(py: Python, ptr: *mut ffi::PyObject) -> PyResult<Py<T>> {
match NonNull::new(ptr) {
Some(nonnull_ptr) => Ok(Py(nonnull_ptr, PhantomData)),
None => Err(PyErr::fetch(py)),
}
}

/// Create a `Py<T>` instance by taking ownership of the given FFI pointer.
///
/// If `ptr` is null then `None` is returned.
///
/// # Safety
/// If non-null, `ptr` must be a pointer to a Python object of type T.
#[inline]
pub unsafe fn from_owned_ptr_or_opt(_py: Python, ptr: *mut ffi::PyObject) -> Option<Self> {
match NonNull::new(ptr) {
Some(nonnull_ptr) => Some(Py(nonnull_ptr, PhantomData)),
None => None,
}
}

/// Create a `Py<T>` instance by creating a new reference from the given FFI pointer.
///
/// # Safety
/// `ptr` must be a pointer to a Python object of type T.
///
/// # Panics
/// Panics if `ptr` is null.
#[inline]
pub unsafe fn from_borrowed_ptr(py: Python, ptr: *mut ffi::PyObject) -> Py<T> {
match Self::from_borrowed_ptr_or_opt(py, ptr) {
Some(slf) => slf,
None => crate::err::panic_after_error(py),
}
}

/// Create a `Py<T>` instance by creating a new reference from the given FFI pointer.
///
/// If `ptr` is null then the current Python exception is fetched as a `PyErr`.
///
/// # Safety
/// `ptr` must be a pointer to a Python object of type T.
#[inline]
pub unsafe fn from_borrowed_ptr_or_err(py: Python, ptr: *mut ffi::PyObject) -> PyResult<Self> {
Self::from_borrowed_ptr_or_opt(py, ptr).ok_or_else(|| PyErr::fetch(py))
}

/// Create a `Py<T>` instance by creating a new reference from the given FFI pointer.
///
/// If `ptr` is null then `None` is returned.
///
/// # Safety
/// `ptr` must be a pointer to a Python object of type T.
#[inline]
pub unsafe fn from_borrowed_ptr_or_opt(_py: Python, ptr: *mut ffi::PyObject) -> Option<Self> {
NonNull::new(ptr).map(|nonnull_ptr| {
ffi::Py_INCREF(ptr);
Py(nonnull_ptr, PhantomData)
})
}

/// For internal conversions.
///
/// # Safety
/// `ptr` must point to a Python object of type T.
#[inline]
unsafe fn from_non_null(ptr: NonNull<ffi::PyObject>) -> Self {
Self(ptr, PhantomData)
}

/// Returns the inner pointer without decreasing the refcount.
#[inline]
fn into_non_null(self) -> NonNull<ffi::PyObject> {
let pointer = self.0;
mem::forget(self);
pointer
}
}

/// Retrieves `&'py` types from `Py<T>` or `PyObject`.
Expand Down Expand Up @@ -421,7 +419,7 @@ impl<T> IntoPy<PyObject> for Py<T> {
/// Consumes `self` without calling `Py_DECREF()`.
#[inline]
fn into_py(self, _py: Python) -> PyObject {
unsafe { PyObject::from_not_null(self.into_non_null()) }
unsafe { PyObject::from_non_null(self.into_non_null()) }
}
}

Expand Down Expand Up @@ -566,6 +564,19 @@ impl<T> std::fmt::Debug for Py<T> {
/// See the documentation for [`Py`](struct.Py.html).
pub type PyObject = Py<PyAny>;

impl PyObject {
/// Casts the PyObject to a concrete Python object type.
///
/// This can cast only to native Python types, not types implemented in Rust. For a more
/// flexible alternative, see [`Py::extract`](struct.Py.html#method.extract).
pub fn cast_as<'p, D>(&'p self, py: Python<'p>) -> Result<&'p D, PyDowncastError>
where
D: PyTryFrom<'p>,
{
D::try_from(unsafe { py.from_borrowed_ptr::<PyAny>(self.as_ptr()) })
}
}

#[cfg(test)]
mod test {
use super::{Py, PyObject};
Expand Down
2 changes: 1 addition & 1 deletion src/types/complex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ mod complex_conversion {
unsafe {
let raw_obj =
ffi::PyComplex_FromDoubles(self.re as c_double, self.im as c_double);
PyObject::from_owned_ptr_or_panic(py, raw_obj)
PyObject::from_owned_ptr(py, raw_obj)
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/types/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ where
let obj = e.to_object(py).into_ptr();
ffi::PyList_SetItem(ptr, i as Py_ssize_t, obj);
}
PyObject::from_owned_ptr_or_panic(py, ptr)
PyObject::from_owned_ptr(py, ptr)
}
}
}
Expand Down Expand Up @@ -218,7 +218,7 @@ where
let obj = e.into_py(py).into_ptr();
ffi::PyList_SetItem(ptr, i as Py_ssize_t, obj);
}
PyObject::from_owned_ptr_or_panic(py, ptr)
PyObject::from_owned_ptr(py, ptr)
}
}
}
Expand Down
18 changes: 7 additions & 11 deletions src/types/num.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,17 +62,13 @@ macro_rules! int_fits_c_long {
impl ToPyObject for $rust_type {
#![cfg_attr(feature = "cargo-clippy", allow(clippy::cast_lossless))]
fn to_object(&self, py: Python) -> PyObject {
unsafe {
PyObject::from_owned_ptr_or_panic(py, ffi::PyLong_FromLong(*self as c_long))
}
unsafe { PyObject::from_owned_ptr(py, ffi::PyLong_FromLong(*self as c_long)) }
}
}
impl IntoPy<PyObject> for $rust_type {
#![cfg_attr(feature = "cargo-clippy", allow(clippy::cast_lossless))]
fn into_py(self, py: Python) -> PyObject {
unsafe {
PyObject::from_owned_ptr_or_panic(py, ffi::PyLong_FromLong(self as c_long))
}
unsafe { PyObject::from_owned_ptr(py, ffi::PyLong_FromLong(self as c_long)) }
}
}

Expand Down Expand Up @@ -101,13 +97,13 @@ macro_rules! int_convert_u64_or_i64 {
impl ToPyObject for $rust_type {
#[inline]
fn to_object(&self, py: Python) -> PyObject {
unsafe { PyObject::from_owned_ptr_or_panic(py, $pylong_from_ll_or_ull(*self)) }
unsafe { PyObject::from_owned_ptr(py, $pylong_from_ll_or_ull(*self)) }
}
}
impl IntoPy<PyObject> for $rust_type {
#[inline]
fn into_py(self, py: Python) -> PyObject {
unsafe { PyObject::from_owned_ptr_or_panic(py, $pylong_from_ll_or_ull(self)) }
unsafe { PyObject::from_owned_ptr(py, $pylong_from_ll_or_ull(self)) }
}
}
impl<'source> FromPyObject<'source> for $rust_type {
Expand Down Expand Up @@ -194,7 +190,7 @@ mod int128_conversion {
IS_LITTLE_ENDIAN,
$is_signed,
);
PyObject::from_owned_ptr_or_panic(py, obj)
PyObject::from_owned_ptr(py, obj)
}
}
}
Expand Down Expand Up @@ -278,7 +274,7 @@ mod int128_conversion {
super::IS_LITTLE_ENDIAN,
0,
);
let obj = PyObject::from_owned_ptr_or_panic(py, obj);
let obj = PyObject::from_owned_ptr(py, obj);
let err = obj.extract::<u128>(py).unwrap_err();
assert!(err.is_instance::<exceptions::PyOverflowError>(py));
}
Expand Down Expand Up @@ -319,7 +315,7 @@ mod bigint_conversion {
1,
$is_signed,
);
PyObject::from_owned_ptr_or_panic(py, obj)
PyObject::from_owned_ptr(py, obj)
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/types/tuple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+
unsafe {
let ptr = ffi::PyTuple_New($length);
$(ffi::PyTuple_SetItem(ptr, $n, self.$n.to_object(py).into_ptr());)+
PyObject::from_owned_ptr_or_panic(py, ptr)
PyObject::from_owned_ptr(py, ptr)
}
}
}
Expand All @@ -153,7 +153,7 @@ macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+
unsafe {
let ptr = ffi::PyTuple_New($length);
$(ffi::PyTuple_SetItem(ptr, $n, self.$n.into_py(py).into_ptr());)+
PyObject::from_owned_ptr_or_panic(py, ptr)
PyObject::from_owned_ptr(py, ptr)
}
}
}
Expand All @@ -163,7 +163,7 @@ macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+
unsafe {
let ptr = ffi::PyTuple_New($length);
$(ffi::PyTuple_SetItem(ptr, $n, self.$n.into_py(py).into_ptr());)+
Py::from_owned_ptr_or_panic(py, ptr)
Py::from_owned_ptr(py, ptr)
}
}
}
Expand Down

0 comments on commit 48bd720

Please sign in to comment.