Skip to content

Commit

Permalink
even more conversions
Browse files Browse the repository at this point in the history
  • Loading branch information
Icxolu committed Jul 6, 2024
1 parent c1b6076 commit 815deb3
Show file tree
Hide file tree
Showing 14 changed files with 476 additions and 28 deletions.
47 changes: 40 additions & 7 deletions src/conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,15 +193,48 @@ pub trait IntoPyObject<'py>: Sized {
fn into_pyobject(self, py: Python<'py>) -> Result<Bound<'py, Self::Target>, Self::Error>;
}

impl<'py, T> IntoPyObject<'py> for &'_ T
where
T: Copy + IntoPyObject<'py>,
{
type Target = T::Target;
type Error = T::Error;
impl<'py, T> IntoPyObject<'py> for Bound<'py, T> {
type Target = T;
type Error = Infallible;

fn into_pyobject(self, _py: Python<'py>) -> Result<Bound<'py, Self::Target>, Self::Error> {
Ok(self)
}
}

impl<'py, T> IntoPyObject<'py> for &Bound<'py, T> {
type Target = T;
type Error = Infallible;

fn into_pyobject(self, _py: Python<'py>) -> Result<Bound<'py, Self::Target>, Self::Error> {
Ok(self.clone())
}
}

impl<'py, T> IntoPyObject<'py> for Borrowed<'_, 'py, T> {
type Target = T;
type Error = Infallible;

fn into_pyobject(self, _py: Python<'py>) -> Result<Bound<'py, Self::Target>, Self::Error> {
Ok(self.to_owned())
}
}

impl<'py, T> IntoPyObject<'py> for Py<T> {
type Target = T;
type Error = Infallible;

fn into_pyobject(self, py: Python<'py>) -> Result<Bound<'py, Self::Target>, Self::Error> {
Ok(self.into_bound(py))
}
}

impl<'py, T> IntoPyObject<'py> for &Py<T> {
type Target = T;
type Error = Infallible;

fn into_pyobject(self, py: Python<'py>) -> Result<Bound<'py, Self::Target>, Self::Error> {
(*self).into_pyobject(py)
Ok(self.bind(py).clone())
}
}

Expand Down
21 changes: 19 additions & 2 deletions src/conversions/either.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@
#[cfg(feature = "experimental-inspect")]
use crate::inspect::types::TypeInfo;
use crate::{
exceptions::PyTypeError, types::any::PyAnyMethods, Bound, FromPyObject, IntoPy, PyAny,
PyObject, PyResult, Python, ToPyObject,
conversion::IntoPyObject, exceptions::PyTypeError, types::any::PyAnyMethods, Bound,
FromPyObject, IntoPy, PyAny, PyObject, PyResult, Python, ToPyObject,
};
use either::Either;

Expand All @@ -66,6 +66,23 @@ where
}
}

#[cfg_attr(docsrs, doc(cfg(feature = "either")))]
impl<'py, L, R, T, E> IntoPyObject<'py> for Either<L, R>
where
L: IntoPyObject<'py, Target = T, Error = E>,
R: IntoPyObject<'py, Target = T, Error = E>,
{
type Target = T;
type Error = E;

fn into_pyobject(self, py: Python<'py>) -> Result<Bound<'py, Self::Target>, Self::Error> {
match self {
Either::Left(l) => l.into_pyobject(py),
Either::Right(r) => r.into_pyobject(py),
}
}
}

#[cfg_attr(docsrs, doc(cfg(feature = "either")))]
impl<L, R> ToPyObject for Either<L, R>
where
Expand Down
54 changes: 49 additions & 5 deletions src/conversions/hashbrown.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,14 @@
//! Note that you must use compatible versions of hashbrown and PyO3.
//! The required hashbrown version may vary based on the version of PyO3.
use crate::{
types::any::PyAnyMethods,
types::dict::PyDictMethods,
types::frozenset::PyFrozenSetMethods,
types::set::{new_from_iter, PySetMethods},
types::{IntoPyDict, PyDict, PyFrozenSet, PySet},
conversion::IntoPyObject,
types::{
any::PyAnyMethods,
dict::PyDictMethods,
frozenset::PyFrozenSetMethods,
set::{new_from_iter, try_new_from_iter, PySetMethods},
IntoPyDict, PyDict, PyFrozenSet, PySet,
},
Bound, FromPyObject, IntoPy, PyAny, PyErr, PyObject, PyResult, Python, ToPyObject,
};
use std::{cmp, hash};
Expand Down Expand Up @@ -51,6 +54,25 @@ where
}
}

impl<'py, K, V, H> IntoPyObject<'py> for hashbrown::HashMap<K, V, H>
where
K: hash::Hash + cmp::Eq + IntoPyObject<'py>,
V: IntoPyObject<'py>,
H: hash::BuildHasher,
PyErr: From<K::Error> + From<V::Error>,
{
type Target = PyDict;
type Error = PyErr;

fn into_pyobject(self, py: Python<'py>) -> Result<Bound<'py, Self::Target>, Self::Error> {
let dict = PyDict::new_bound(py);
for (k, v) in self {
dict.set_item(k.into_pyobject(py)?, v.into_pyobject(py)?)?;
}
Ok(dict)
}
}

impl<'py, K, V, S> FromPyObject<'py> for hashbrown::HashMap<K, V, S>
where
K: FromPyObject<'py> + cmp::Eq + hash::Hash,
Expand Down Expand Up @@ -90,6 +112,28 @@ where
}
}

impl<'py, K, H> IntoPyObject<'py> for hashbrown::HashSet<K, H>
where
K: hash::Hash + cmp::Eq + IntoPyObject<'py>,
H: hash::BuildHasher,
PyErr: From<K::Error>,
{
type Target = PySet;
type Error = PyErr;

fn into_pyobject(self, py: Python<'py>) -> Result<Bound<'py, Self::Target>, Self::Error> {
try_new_from_iter(
py,
self.into_iter().map(|item| {
item.into_pyobject(py)
.map(Bound::into_any)
.map(Bound::unbind)
.map_err(Into::into)
}),
)
}
}

impl<'py, K, S> FromPyObject<'py> for hashbrown::HashSet<K, S>
where
K: FromPyObject<'py> + cmp::Eq + hash::Hash,
Expand Down
20 changes: 20 additions & 0 deletions src/conversions/indexmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
//! # if another hash table was used, the order could be random
//! ```
use crate::conversion::IntoPyObject;
use crate::types::*;
use crate::{Bound, FromPyObject, IntoPy, PyErr, PyObject, Python, ToPyObject};
use std::{cmp, hash};
Expand Down Expand Up @@ -116,6 +117,25 @@ where
}
}

impl<'py, K, V, H> IntoPyObject<'py> for indexmap::IndexMap<K, V, H>
where
K: hash::Hash + cmp::Eq + IntoPyObject<'py>,
V: IntoPyObject<'py>,
H: hash::BuildHasher,
PyErr: From<K::Error> + From<V::Error>,
{
type Target = PyDict;
type Error = PyErr;

fn into_pyobject(self, py: Python<'py>) -> Result<Bound<'py, Self::Target>, Self::Error> {
let dict = PyDict::new_bound(py);
for (k, v) in self {
dict.set_item(k.into_pyobject(py)?, v.into_pyobject(py)?)?;
}
Ok(dict)
}
}

impl<'py, K, V, S> FromPyObject<'py> for indexmap::IndexMap<K, V, S>
where
K: FromPyObject<'py> + cmp::Eq + hash::Hash,
Expand Down
50 changes: 49 additions & 1 deletion src/conversions/num_bigint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,11 @@ use crate::ffi_ptr_ext::FfiPtrExt;
#[cfg(Py_LIMITED_API)]
use crate::types::{bytes::PyBytesMethods, PyBytes};
use crate::{
conversion::IntoPyObject,
ffi,
instance::Bound,
types::{any::PyAnyMethods, PyLong},
FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject,
FromPyObject, IntoPy, Py, PyAny, PyErr, PyObject, PyResult, Python, ToPyObject,
};

use num_bigint::{BigInt, BigUint};
Expand Down Expand Up @@ -133,6 +134,53 @@ macro_rules! bigint_conversion {
self.to_object(py)
}
}

#[cfg_attr(docsrs, doc(cfg(feature = "num-bigint")))]
impl<'py> IntoPyObject<'py> for $rust_ty {
type Target = PyLong;
type Error = PyErr;

#[cfg(not(Py_LIMITED_API))]
fn into_pyobject(
self,
py: Python<'py>,
) -> Result<Bound<'py, Self::Target>, Self::Error> {
use crate::ffi_ptr_ext::FfiPtrExt;
let bytes = $to_bytes(&self);
unsafe {
Ok(ffi::_PyLong_FromByteArray(
bytes.as_ptr().cast(),
bytes.len(),
1,
$is_signed.into(),
)
.assume_owned(py)
.downcast_into_unchecked())
}
}

#[cfg(Py_LIMITED_API)]
fn into_pyobject(
self,
py: Python<'py>,
) -> Result<Bound<'py, Self::Target>, Self::Error> {
use $crate::py_result_ext::PyResultExt;
let bytes = $to_bytes(&self);
let bytes_obj = PyBytes::new_bound(py, &bytes);
let kwargs = if $is_signed {
let kwargs = crate::types::PyDict::new_bound(py);
kwargs.set_item(crate::intern!(py, "signed"), true)?;
Some(kwargs)
} else {
None
};
unsafe {
py.get_type_bound::<PyLong>()
.call_method("from_bytes", (bytes_obj, "little"), kwargs.as_ref())
.downcast_into_unchecked()
}
}
}
};
}

Expand Down
19 changes: 19 additions & 0 deletions src/conversions/num_complex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,25 @@ macro_rules! complex_conversion {
}
}

#[cfg_attr(docsrs, doc(cfg(feature = "num-complex")))]
impl<'py> crate::conversion::IntoPyObject<'py> for Complex<$float> {
type Target = PyComplex;
type Error = std::convert::Infallible;

fn into_pyobject(
self,
py: Python<'py>,
) -> Result<Bound<'py, Self::Target>, Self::Error> {
unsafe {
Ok(
ffi::PyComplex_FromDoubles(self.re as c_double, self.im as c_double)
.assume_owned(py)
.downcast_into_unchecked(),
)
}
}
}

#[cfg_attr(docsrs, doc(cfg(feature = "num-complex")))]
impl FromPyObject<'_> for Complex<$float> {
fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult<Complex<$float>> {
Expand Down
17 changes: 16 additions & 1 deletion src/conversions/rust_decimal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,15 @@
//! assert d + 1 == value
//! ```
use crate::conversion::IntoPyObject;
use crate::exceptions::PyValueError;
use crate::sync::GILOnceCell;
use crate::types::any::PyAnyMethods;
use crate::types::string::PyStringMethods;
use crate::types::PyType;
use crate::{Bound, FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject};
use crate::{
Bound, FromPyObject, IntoPy, Py, PyAny, PyErr, PyObject, PyResult, Python, ToPyObject,
};
use rust_decimal::Decimal;
use std::str::FromStr;

Expand Down Expand Up @@ -99,6 +102,18 @@ impl IntoPy<PyObject> for Decimal {
}
}

impl<'py> IntoPyObject<'py> for Decimal {
type Target = PyAny;
type Error = PyErr;

fn into_pyobject(self, py: Python<'py>) -> Result<Bound<'py, Self::Target>, Self::Error> {
let dec_cls = get_decimal_cls(py)?;
// now call the constructor with the Rust Decimal string-ified
// to not be lossy
dec_cls.call1((self.to_string(),))
}
}

#[cfg(test)]
mod test_rust_decimal {
use super::*;
Expand Down
26 changes: 24 additions & 2 deletions src/conversions/smallvec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@
//!
//! Note that you must use compatible versions of smallvec and PyO3.
//! The required smallvec version may vary based on the version of PyO3.
use crate::conversion::IntoPyObject;
use crate::exceptions::PyTypeError;
#[cfg(feature = "experimental-inspect")]
use crate::inspect::types::TypeInfo;
use crate::types::any::PyAnyMethods;
use crate::types::list::new_from_iter;
use crate::types::{PySequence, PyString};
use crate::types::list::{new_from_iter, try_new_from_iter};
use crate::types::{PyList, PySequence, PyString};
use crate::PyErr;
use crate::{
err::DowncastError, ffi, Bound, FromPyObject, IntoPy, PyAny, PyObject, PyResult, Python,
ToPyObject,
Expand Down Expand Up @@ -54,6 +56,26 @@ where
}
}

impl<'py, A> IntoPyObject<'py> for SmallVec<A>
where
A: Array,
A::Item: IntoPyObject<'py>,
PyErr: From<<A::Item as IntoPyObject<'py>>::Error>,
{
type Target = PyList;
type Error = PyErr;

fn into_pyobject(self, py: Python<'py>) -> Result<Bound<'py, Self::Target>, Self::Error> {
let mut iter = self.into_iter().map(|e| {
e.into_pyobject(py)
.map(Bound::into_any)
.map(Bound::unbind)
.map_err(Into::into)
});
try_new_from_iter(py, &mut iter)
}
}

impl<'py, A> FromPyObject<'py> for SmallVec<A>
where
A: Array,
Expand Down
Loading

0 comments on commit 815deb3

Please sign in to comment.