Skip to content

[WIP] Extract vec without specialization #1

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

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

### Added

* `FromPyObject` is now automatically implemented for `T: Clone` pyclasses. [#730](https://github.com/PyO3/pyo3/pull/730)
* Implemented `IntoIterator` for `PySet` and `PyFrozenSet`. [#716](https://github.com/PyO3/pyo3/pull/716)
* `PyClass`, `PyClassShell`, `PyObjectLayout`, `PyClassInitializer` [#683](https://github.com/PyO3/pyo3/pull/683)

Expand Down
12 changes: 12 additions & 0 deletions pyo3-derive-backend/src/pyclass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,18 @@ fn impl_class(
#weakref
}

impl pyo3::FromPyObjectImpl for #cls {
type Impl = pyo3::extract_impl::Cloned;
}

impl pyo3::FromPyObjectImpl for &'_ #cls {
type Impl = pyo3::extract_impl::Reference;
}

impl pyo3::FromPyObjectImpl for &'_ mut #cls {
type Impl = pyo3::extract_impl::MutReference;
}

#into_pyobject

#inventory_impl
Expand Down
19 changes: 19 additions & 0 deletions src/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use crate::ffi;
use crate::types::PyAny;
use crate::AsPyPointer;
use crate::Python;
use crate::instance::PyNativeType;
use libc;
use std::ffi::CStr;
use std::os::raw;
Expand Down Expand Up @@ -651,6 +652,24 @@ impl_element!(isize, SignedInteger);
impl_element!(f32, Float);
impl_element!(f64, Float);

pub(crate) fn extract_buffer<'a, T>(obj: &'a PyAny) -> PyResult<Vec<T>>
where
T: Copy + Element + 'a
{

let buf = PyBuffer::get(obj.py(), obj)?;
let result;

if buf.dimensions() == 1 {
result = buf.to_vec::<T>(obj.py());
} else {
result = Err(err::PyDowncastError.into());
}

buf.release(obj.py());
result
}

#[cfg(test)]
mod test {
use super::PyBuffer;
Expand Down
126 changes: 110 additions & 16 deletions src/conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@
use crate::err::{self, PyDowncastError, PyResult};
use crate::object::PyObject;
use crate::type_object::{PyObjectLayout, PyTypeInfo};
use crate::types::PyAny;
use crate::types::PyTuple;
use crate::{ffi, gil, Py, Python};
use crate::types::{PyAny, PySequence, PyTuple};
use crate::{ffi, gil, Py, Python, ObjectProtocol};
use std::ptr::NonNull;

/// This trait represents that, **we can do zero-cost conversion from the object to FFI pointer**.
Expand Down Expand Up @@ -181,6 +180,26 @@ impl<T> FromPy<T> for T {
pub trait FromPyObject<'source>: Sized {
/// Extracts `Self` from the source `PyObject`.
fn extract(ob: &'source PyAny) -> PyResult<Self>;

/// Extracts a `Vec<Self>` for the source `PyObject`.
///
/// This method is provided here with a default implementation to offer a crude form of
/// specialization. Types may override this to add optimizations (e.g. for types which
/// can be stored in PyBuffer objects.)
///
/// Users typically should not call this method directly; instead use it via the
/// implementation of FromPyObject for `Vec<T>`, i.e.
///
/// let any: &PyAny = ...;
/// let vec = Vec::<i32>::extract(any)?;
///
/// This method will probably be removed once specialization is stabilised.
fn extract_vec(obj: &'source PyAny) -> PyResult<Vec<Self>>
where
Self: Sized,
{
extract_sequence(obj)
}
}

/// Identity conversion: allows using existing `PyObject` instances where
Expand Down Expand Up @@ -244,25 +263,79 @@ where
}
}

/// Extract reference to instance from `PyObject`
impl<'a, T> FromPyObject<'a> for &'a T
where
T: PyTryFrom<'a>,
{
#[inline]
fn extract(ob: &'a PyAny) -> PyResult<&'a T> {
Ok(T::try_from(ob)?)
pub mod extract_impl {
use super::*;

pub trait ExtractImpl<'a, Target>: Sized {
fn extract(source: &'a PyAny) -> PyResult<Target>;
}

pub struct Cloned;
pub struct Reference;
pub struct MutReference;

impl<'a, T: 'a> ExtractImpl<'a, T> for Cloned
where
T: Clone,
Reference: ExtractImpl<'a, &'a T>,
{
fn extract(source: &'a PyAny) -> PyResult<T> {
Ok(Reference::extract(source)?.clone())
}
}

impl<'a, T> ExtractImpl<'a, &'a T> for Reference
where
T: PyTryFrom<'a>,
{
fn extract(source: &'a PyAny) -> PyResult<&'a T> {
Ok(T::try_from(source)?)
}
}

impl<'a, T> ExtractImpl<'a, &'a mut T> for MutReference
where
T: PyTryFrom<'a>,
{
fn extract(source: &'a PyAny) -> PyResult<&'a mut T> {
Ok(T::try_from_mut(source)?)
}
}
}

use extract_impl::ExtractImpl;

/// Implement this trait with to specify the implementor of ExtractImpl to use for extracting
/// this type from Python objects.
///
/// Example valid implementations are `Cloned`, `Reference`, and `MutReference`, which are for
/// extracting `T`, `&T` and `&mut T` respectively via PyTryFrom
///
/// This is an internal trait mostly for re-using FromPyObject implementations for many pyo3
/// types.
///
/// Most users should implement `FromPyObject` directly instead of via this trait..
pub trait FromPyObjectImpl {
// We deliberately don't require Impl: ExtractImpl here because we allow #[pyclass]
// to specify an Impl which doesn't satisfy the ExtractImpl constraints.
//
// e.g. non-clone #[pyclass] can still have Impl: Cloned.
//
// We catch invalid Impls in the blanket impl for FromPyObject, which only
// complains when .extract() is actually used.

/// The type which implements `ExtractImpl`.
type Impl;
}

/// Extract mutable reference to instance from `PyObject`
impl<'a, T> FromPyObject<'a> for &'a mut T
impl<'a, T> FromPyObject<'a> for T
where
T: PyTryFrom<'a>,
T: FromPyObjectImpl,
<T as FromPyObjectImpl>::Impl: ExtractImpl<'a, Self>,
{
#[inline]
fn extract(ob: &'a PyAny) -> PyResult<&'a mut T> {
Ok(T::try_from_mut(ob)?)
fn extract(ob: &'a PyAny) -> PyResult<T> {
<T as FromPyObjectImpl>::Impl::extract(ob)
}
}

Expand All @@ -282,6 +355,27 @@ where
}
}

pub(crate) fn extract_sequence<'s, T>(obj: &'s PyAny) -> PyResult<Vec<T>>
where
T: FromPyObject<'s>,
{
let seq = <PySequence as PyTryFrom>::try_from(obj)?;
let mut v = Vec::with_capacity(seq.len().unwrap_or(0) as usize);
for item in seq.iter()? {
v.push(item?.extract::<T>()?);
}
Ok(v)
}

impl<'a, T> FromPyObject<'a> for Vec<T>
where
T: FromPyObject<'a>
{
fn extract(obj: &'a PyAny) -> PyResult<Self> {
T::extract_vec(obj)
}
}

/// Trait implemented by Python object types that allow a checked downcast.
/// This trait is similar to `std::convert::TryInto`
pub trait PyTryInto<T>: Sized {
Expand Down
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,8 @@

pub use crate::class::*;
pub use crate::conversion::{
AsPyPointer, FromPy, FromPyObject, FromPyPointer, IntoPy, IntoPyPointer, PyTryFrom, PyTryInto,
ToBorrowedObject, ToPyObject,
extract_impl, AsPyPointer, FromPy, FromPyObject, FromPyObjectImpl, FromPyPointer, IntoPy,
IntoPyPointer, PyTryFrom, PyTryInto, ToBorrowedObject, ToPyObject,
};
pub use crate::err::{PyDowncastError, PyErr, PyErrArguments, PyErrValue, PyResult};
pub use crate::gil::{init_once, GILGuard, GILPool};
Expand Down
14 changes: 11 additions & 3 deletions src/types/floatob.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
// Copyright (c) 2017-present PyO3 Project and Contributors
//
// based on Daniel Grunwald's https://github.com/dgrunwald/rust-cpython

use crate::buffer::extract_buffer;
use crate::conversion::extract_sequence;
use crate::err::PyErr;
use crate::ffi;
use crate::instance::PyNativeType;
use crate::internal_tricks::Unsendable;
use crate::object::PyObject;
use crate::objectprotocol::ObjectProtocol;
use crate::types::PyAny;
use crate::FromPyObject;
use crate::PyResult;
use crate::Python;
use crate::ToPyObject;
use crate::{FromPyObject, ToPyObject};
use crate::{AsPyPointer, FromPy};
use std::os::raw::c_double;

Expand Down Expand Up @@ -68,6 +68,10 @@ impl<'source> FromPyObject<'source> for f64 {
Ok(v)
}
}

fn extract_vec(obj: &'source PyAny) -> PyResult<Vec<Self>> {
extract_buffer(obj).or_else(|_| extract_sequence(obj))
}
}

impl ToPyObject for f32 {
Expand All @@ -86,6 +90,10 @@ impl<'source> FromPyObject<'source> for f32 {
fn extract(obj: &'source PyAny) -> PyResult<Self> {
Ok(obj.extract::<f64>()? as f32)
}

fn extract_vec(obj: &'source PyAny) -> PyResult<Vec<Self>> {
extract_buffer(obj).or_else(|_| extract_sequence(obj))
}
}

#[cfg(test)]
Expand Down
4 changes: 4 additions & 0 deletions src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,10 @@ macro_rules! pyobject_native_type_convert(
}
}

impl<$($type_param,)*> $crate::conversion::FromPyObjectImpl for &'_ $name {
type Impl = $crate::extract_impl::Reference;
}

impl<$($type_param,)*> ::std::fmt::Debug for $name {
fn fmt(&self, f: &mut ::std::fmt::Formatter)
-> Result<(), ::std::fmt::Error>
Expand Down
16 changes: 15 additions & 1 deletion src/types/num.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
//
// based on Daniel Grunwald's https://github.com/dgrunwald/rust-cpython

use crate::buffer::extract_buffer;
use crate::conversion::extract_sequence;
use crate::err::{PyErr, PyResult};
use crate::exceptions;
use crate::ffi;
Expand Down Expand Up @@ -52,6 +54,10 @@ macro_rules! int_fits_larger_int {
None => Err(exceptions::OverflowError.into()),
}
}

fn extract_vec(obj: &'source PyAny) -> PyResult<Vec<Self>> {
extract_buffer(obj).or_else(|_| extract_sequence(obj))
}
}
};
}
Expand Down Expand Up @@ -159,6 +165,10 @@ macro_rules! int_fits_c_long {
None => Err(exceptions::OverflowError.into()),
}
}

fn extract_vec(obj: &'source PyAny) -> PyResult<Vec<Self>> {
extract_buffer(obj).or_else(|_| extract_sequence(obj))
}
}
};
}
Expand All @@ -178,7 +188,7 @@ macro_rules! int_convert_u64_or_i64 {
}
}
impl<'source> FromPyObject<'source> for $rust_type {
fn extract(ob: &'source PyAny) -> PyResult<$rust_type> {
fn extract(ob: &'source PyAny) -> PyResult<Self> {
let ptr = ob.as_ptr();
unsafe {
let num = ffi::PyNumber_Index(ptr);
Expand All @@ -191,6 +201,10 @@ macro_rules! int_convert_u64_or_i64 {
}
}
}

fn extract_vec(obj: &'source PyAny) -> PyResult<Vec<Self>> {
extract_buffer(obj).or_else(|_| extract_sequence(obj))
}
}
};
}
Expand Down
Loading