Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove ToPyPointer and so on from pyclass #335

Merged
merged 10 commits into from
Feb 12, 2019
66 changes: 66 additions & 0 deletions guide/src/class.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,72 @@ struct MyClass {

The above example generates implementations for `PyTypeInfo` and `PyTypeObject` for `MyClass`.

## Get Python objects from `pyclass`
You can use `pyclass`es like normal rust structs.

However, if instantiate noramlly, you can't treat `pyclass`es as Python objects.

To get a Python object which includes `pyclass`, we have to use some special methods.

### `PyRef`
`PyRef` is a special reference, which ensures that the reffrered struct is a part of
a Python object, and you are also holding the GIL.

You can get an instance of `PyRef` by `PyRef::new`, which does 3 things:
1. Allocate a Python object in the Python heap
2. Copies the rust struct into the Python object
3. Returns a reference of it

You can use `PyRef` just like `&T`, because it implements `Deref<Target=T>`.
```rust
# use pyo3::prelude::*;
#[pyclass]
struct MyClass {
num: i32,
debug: bool,
}
let gil = Python::acquire_gil();
let py = gil.python();
let obj = PyRef::new(py, || MyClass { num: 3, debug: true }).unwrap();
assert_eq!(obj.num, 3);
let dict = PyDict::new();
// You can treat a `PyRef` as a Python object
dict.set_item("obj", obj)).unwrap();
```

### `PyRefMut`
`PyRefMut` is a mutable version of `PyRef`.
```rust
# use pyo3::prelude::*;
#[pyclass]
struct MyClass {
num: i32,
debug: bool,
}
let gil = Python::acquire_gil();
let py = gil.python();
let mut obj = PyRefMut::new(py, || MyClass { num: 3, debug: true }).unwrap();
obj.num = 5;
```

### `Py`
`Py` is a object wrapper which stores an object longer than the GIL lifetime.

You can use it to avoid lifetime problems.
```rust
# use pyo3::prelude::*;
#[pyclass]
struct MyClass {
num: i32,
}
fn return_myclass() -> Py<MyClass> {
let gil = Python::acquire_gil();
Py::new(|| MyClass { num: 1 })
}
let gil = Python::acquire_gil();
let obj = return_myclass();
assert_eq!(obj.as_ref(gil.python()).num, 1);
```

## Customizing the class

Expand Down
10 changes: 6 additions & 4 deletions pyo3-derive-backend/src/func.rs
Original file line number Diff line number Diff line change
Expand Up @@ -492,10 +492,12 @@ fn modify_arg_ty(sig: &mut syn::MethodSig, idx: usize, decl1: &syn::FnDecl, decl
}

fn modify_self_ty(sig: &mut syn::MethodSig) {
if let syn::FnArg::SelfRef(ref mut r) = sig.decl.inputs[0] {
r.lifetime = Some(syn::parse_quote! {'p});
} else {
panic!("not supported")
match sig.decl.inputs[0] {
syn::FnArg::SelfRef(ref mut slf) => {
slf.lifetime = Some(syn::parse_quote! {'p});
}
syn::FnArg::Captured(_) => {}
_ => panic!("not supported"),
}
}

Expand Down
23 changes: 0 additions & 23 deletions pyo3-derive-backend/src/py_class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,29 +210,6 @@ fn impl_class(
}
}

impl ::pyo3::ToPyObject for #cls {
fn to_object(&self, py: ::pyo3::Python) -> ::pyo3::PyObject {
use ::pyo3::python::ToPyPointer;
unsafe { ::pyo3::PyObject::from_borrowed_ptr(py, self.as_ptr()) }
}
}

impl ::pyo3::ToPyPointer for #cls {
fn as_ptr(&self) -> *mut ::pyo3::ffi::PyObject {
unsafe {
{self as *const _ as *mut u8}
.offset(-<#cls as ::pyo3::typeob::PyTypeInfo>::OFFSET) as *mut ::pyo3::ffi::PyObject
}
}
}

impl<'a> ::pyo3::ToPyObject for &'a mut #cls {
fn to_object(&self, py: ::pyo3::Python) -> ::pyo3::PyObject {
use ::pyo3::python::ToPyPointer;
unsafe { ::pyo3::PyObject::from_borrowed_ptr(py, self.as_ptr()) }
}
}

#inventory_impl

#extra
Expand Down
11 changes: 6 additions & 5 deletions src/class/iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::callback::{CallbackConverter, PyObjectCallbackConverter};
use crate::conversion::IntoPyObject;
use crate::err::PyResult;
use crate::ffi;
use crate::instance::PyRefMut;
use crate::python::{IntoPyPointer, Python};
use crate::typeob::PyTypeInfo;

Expand All @@ -16,15 +17,15 @@ use crate::typeob::PyTypeInfo;
/// more information
/// `https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_iter`
#[allow(unused_variables)]
pub trait PyIterProtocol<'p>: PyTypeInfo {
fn __iter__(&'p mut self) -> Self::Result
pub trait PyIterProtocol<'p>: PyTypeInfo + Sized {
fn __iter__(slf: PyRefMut<'p, Self>) -> Self::Result
where
Self: PyIterIterProtocol<'p>,
{
unimplemented!()
}

fn __next__(&'p mut self) -> Self::Result
fn __next__(slf: PyRefMut<'p, Self>) -> Self::Result
where
Self: PyIterNextProtocol<'p>,
{
Expand Down Expand Up @@ -74,7 +75,7 @@ where
{
#[inline]
fn tp_iter() -> Option<ffi::getiterfunc> {
py_unary_func!(
py_unary_pyref_func!(
PyIterIterProtocol,
T::__iter__,
T::Success,
Expand All @@ -97,7 +98,7 @@ where
{
#[inline]
fn tp_iternext() -> Option<ffi::iternextfunc> {
py_unary_func!(
py_unary_pyref_func!(
PyIterNextProtocol,
T::__next__,
Option<T::Success>,
Expand Down
8 changes: 4 additions & 4 deletions src/class/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,17 @@ macro_rules! py_unary_func {

#[macro_export]
#[doc(hidden)]
macro_rules! py_unary_func_self {
($trait:ident, $class:ident :: $f:ident, $res_type:ty, $conv:ty) => {{
macro_rules! py_unary_pyref_func {
($trait:ident, $class:ident :: $f:ident, $res_type:ty, $conv:expr) => {{
unsafe extern "C" fn wrap<T>(slf: *mut $crate::ffi::PyObject) -> *mut $crate::ffi::PyObject
where
T: for<'p> $trait<'p>,
{
use $crate::ObjectProtocol;
use $crate::instance::PyRefMut;
let _pool = $crate::GILPool::new();
let py = $crate::Python::assume_gil_acquired();
let slf = py.mut_from_borrowed_ptr::<T>(slf);
let res = slf.$f().into();
let res = $class::$f(PyRefMut::from_mut(slf)).into();
$crate::callback::cb_convert($conv, py, res)
}
Some(wrap::<$class>)
Expand Down
44 changes: 27 additions & 17 deletions src/conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ where
/// Extract reference to instance from `PyObject`
impl<'a, T> FromPyObject<'a> for &'a T
where
T: PyTryFrom,
T: PyTryFrom<'a>,
{
#[inline]
default fn extract(ob: &'a PyObjectRef) -> PyResult<&'a T> {
Expand All @@ -176,7 +176,7 @@ where
/// Extract mutable reference to instance from `PyObject`
impl<'a, T> FromPyObject<'a> for &'a mut T
where
T: PyTryFrom,
T: PyTryFrom<'a>,
{
#[inline]
default fn extract(ob: &'a PyObjectRef) -> PyResult<&'a mut T> {
Expand Down Expand Up @@ -221,33 +221,35 @@ pub trait PyTryInto<T>: Sized {

/// Trait implemented by Python object types that allow a checked downcast.
/// This trait is similar to `std::convert::TryFrom`
pub trait PyTryFrom: Sized {
pub trait PyTryFrom<'v>: Sized {
/// Cast from a concrete Python object type to PyObject.
fn try_from(value: &PyObjectRef) -> Result<&Self, PyDowncastError>;
fn try_from<V: Into<&'v PyObjectRef>>(value: V) -> Result<&'v Self, PyDowncastError>;

/// Cast from a concrete Python object type to PyObject. With exact type check.
fn try_from_exact(value: &PyObjectRef) -> Result<&Self, PyDowncastError>;
fn try_from_exact<V: Into<&'v PyObjectRef>>(value: V) -> Result<&'v Self, PyDowncastError>;

/// Cast from a concrete Python object type to PyObject.
fn try_from_mut(value: &PyObjectRef) -> Result<&mut Self, PyDowncastError>;
fn try_from_mut<V: Into<&'v PyObjectRef>>(value: V) -> Result<&'v mut Self, PyDowncastError>;

/// Cast from a concrete Python object type to PyObject. With exact type check.
fn try_from_mut_exact(value: &PyObjectRef) -> Result<&mut Self, PyDowncastError>;
fn try_from_mut_exact<V: Into<&'v PyObjectRef>>(
value: V,
) -> Result<&'v mut Self, PyDowncastError>;

/// Cast a PyObjectRef to a specific type of PyObject. The caller must
/// have already verified the reference is for this type.
unsafe fn try_from_unchecked(value: &PyObjectRef) -> &Self;
unsafe fn try_from_unchecked<V: Into<&'v PyObjectRef>>(value: V) -> &'v Self;

/// Cast a PyObjectRef to a specific type of PyObject. The caller must
/// have already verified the reference is for this type.
#[allow(clippy::mut_from_ref)]
unsafe fn try_from_mut_unchecked(value: &PyObjectRef) -> &mut Self;
unsafe fn try_from_mut_unchecked<V: Into<&'v PyObjectRef>>(value: V) -> &'v mut Self;
}

// TryFrom implies TryInto
impl<U> PyTryInto<U> for PyObjectRef
where
U: PyTryFrom,
U: for<'v> PyTryFrom<'v>,
{
type Error = PyDowncastError;

Expand All @@ -265,11 +267,12 @@ where
}
}

impl<T> PyTryFrom for T
impl<'v, T> PyTryFrom<'v> for T
where
T: PyTypeInfo,
{
fn try_from(value: &PyObjectRef) -> Result<&T, PyDowncastError> {
fn try_from<V: Into<&'v PyObjectRef>>(value: V) -> Result<&'v T, PyDowncastError> {
let value = value.into();
unsafe {
if T::is_instance(value) {
Ok(PyTryFrom::try_from_unchecked(value))
Expand All @@ -279,7 +282,8 @@ where
}
}

fn try_from_exact(value: &PyObjectRef) -> Result<&T, PyDowncastError> {
fn try_from_exact<V: Into<&'v PyObjectRef>>(value: V) -> Result<&'v T, PyDowncastError> {
let value = value.into();
unsafe {
if T::is_exact_instance(value) {
Ok(PyTryFrom::try_from_unchecked(value))
Expand All @@ -289,7 +293,8 @@ where
}
}

fn try_from_mut(value: &PyObjectRef) -> Result<&mut T, PyDowncastError> {
fn try_from_mut<V: Into<&'v PyObjectRef>>(value: V) -> Result<&'v mut T, PyDowncastError> {
let value = value.into();
unsafe {
if T::is_instance(value) {
Ok(PyTryFrom::try_from_mut_unchecked(value))
Expand All @@ -299,7 +304,10 @@ where
}
}

fn try_from_mut_exact(value: &PyObjectRef) -> Result<&mut T, PyDowncastError> {
fn try_from_mut_exact<V: Into<&'v PyObjectRef>>(
value: V,
) -> Result<&'v mut T, PyDowncastError> {
let value = value.into();
unsafe {
if T::is_exact_instance(value) {
Ok(PyTryFrom::try_from_mut_unchecked(value))
Expand All @@ -310,7 +318,8 @@ where
}

#[inline]
unsafe fn try_from_unchecked(value: &PyObjectRef) -> &T {
unsafe fn try_from_unchecked<V: Into<&'v PyObjectRef>>(value: V) -> &'v T {
let value = value.into();
let ptr = if T::OFFSET == 0 {
value as *const _ as *const u8 as *const T
} else {
Expand All @@ -320,7 +329,8 @@ where
}

#[inline]
unsafe fn try_from_mut_unchecked(value: &PyObjectRef) -> &mut T {
unsafe fn try_from_mut_unchecked<V: Into<&'v PyObjectRef>>(value: V) -> &'v mut T {
let value = value.into();
let ptr = if T::OFFSET == 0 {
value as *const _ as *mut u8 as *mut T
} else {
Expand Down
Loading