Merge pull request #1863 from davidhewitt/more-conversions
conversions: move more features inside the module
This commit is contained in:
commit
0c5ac220de
|
@ -0,0 +1,178 @@
|
|||
#![cfg(feature = "hashbrown")]
|
||||
#![cfg_attr(docsrs, doc(cfg(feature = "hashbrown")))]
|
||||
|
||||
//! Conversions to and from [hashbrown](https://docs.rs/hashbrown/)’s
|
||||
//! `HashMap` and `HashSet`.
|
||||
//!
|
||||
//! # Setup
|
||||
//!
|
||||
//! To use this feature, add this to your **`Cargo.toml`**:
|
||||
//!
|
||||
//! ```toml
|
||||
//! [dependencies]
|
||||
//! # change * to the latest versions
|
||||
//! hashbrown = "*"
|
||||
// workaround for `extended_key_value_attributes`: https://github.com/rust-lang/rust/issues/82768#issuecomment-803935643
|
||||
#![cfg_attr(docsrs, cfg_attr(docsrs, doc = concat!("pyo3 = { version = \"", env!("CARGO_PKG_VERSION"), "\", features = [\"hashbrown\"] }")))]
|
||||
#![cfg_attr(
|
||||
not(docsrs),
|
||||
doc = "pyo3 = { version = \"*\", features = [\"hashbrown\"] }"
|
||||
)]
|
||||
//! ```
|
||||
//!
|
||||
//! 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::{IntoPyDict, PyDict, PySet},
|
||||
FromPyObject, IntoPy, PyAny, PyErr, PyObject, PyResult, PyTryFrom, Python, ToPyObject,
|
||||
};
|
||||
use std::{cmp, hash};
|
||||
|
||||
impl<K, V, H> ToPyObject for hashbrown::HashMap<K, V, H>
|
||||
where
|
||||
K: hash::Hash + cmp::Eq + ToPyObject,
|
||||
V: ToPyObject,
|
||||
H: hash::BuildHasher,
|
||||
{
|
||||
fn to_object(&self, py: Python) -> PyObject {
|
||||
IntoPyDict::into_py_dict(self, py).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V, H> IntoPy<PyObject> for hashbrown::HashMap<K, V, H>
|
||||
where
|
||||
K: hash::Hash + cmp::Eq + IntoPy<PyObject>,
|
||||
V: IntoPy<PyObject>,
|
||||
H: hash::BuildHasher,
|
||||
{
|
||||
fn into_py(self, py: Python) -> PyObject {
|
||||
let iter = self
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k.into_py(py), v.into_py(py)));
|
||||
IntoPyDict::into_py_dict(iter, py).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'source, K, V, S> FromPyObject<'source> for hashbrown::HashMap<K, V, S>
|
||||
where
|
||||
K: FromPyObject<'source> + cmp::Eq + hash::Hash,
|
||||
V: FromPyObject<'source>,
|
||||
S: hash::BuildHasher + Default,
|
||||
{
|
||||
fn extract(ob: &'source PyAny) -> Result<Self, PyErr> {
|
||||
let dict = <PyDict as PyTryFrom>::try_from(ob)?;
|
||||
let mut ret = hashbrown::HashMap::with_capacity_and_hasher(dict.len(), S::default());
|
||||
for (k, v) in dict.iter() {
|
||||
ret.insert(K::extract(k)?, V::extract(v)?);
|
||||
}
|
||||
Ok(ret)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ToPyObject for hashbrown::HashSet<T>
|
||||
where
|
||||
T: hash::Hash + Eq + ToPyObject,
|
||||
{
|
||||
fn to_object(&self, py: Python) -> PyObject {
|
||||
let set = PySet::new::<T>(py, &[]).expect("Failed to construct empty set");
|
||||
{
|
||||
for val in self {
|
||||
set.add(val).expect("Failed to add to set");
|
||||
}
|
||||
}
|
||||
set.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, S> IntoPy<PyObject> for hashbrown::HashSet<K, S>
|
||||
where
|
||||
K: IntoPy<PyObject> + Eq + hash::Hash,
|
||||
S: hash::BuildHasher + Default,
|
||||
{
|
||||
fn into_py(self, py: Python) -> PyObject {
|
||||
let set = PySet::empty(py).expect("Failed to construct empty set");
|
||||
{
|
||||
for val in self {
|
||||
set.add(val.into_py(py)).expect("Failed to add to set");
|
||||
}
|
||||
}
|
||||
set.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'source, K, S> FromPyObject<'source> for hashbrown::HashSet<K, S>
|
||||
where
|
||||
K: FromPyObject<'source> + cmp::Eq + hash::Hash,
|
||||
S: hash::BuildHasher + Default,
|
||||
{
|
||||
fn extract(ob: &'source PyAny) -> PyResult<Self> {
|
||||
let set: &PySet = ob.downcast()?;
|
||||
set.iter().map(K::extract).collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_hashbrown_hashmap_to_python() {
|
||||
Python::with_gil(|py| {
|
||||
let mut map = hashbrown::HashMap::<i32, i32>::new();
|
||||
map.insert(1, 1);
|
||||
|
||||
let m = map.to_object(py);
|
||||
let py_map = <PyDict as PyTryFrom>::try_from(m.as_ref(py)).unwrap();
|
||||
|
||||
assert!(py_map.len() == 1);
|
||||
assert!(py_map.get_item(1).unwrap().extract::<i32>().unwrap() == 1);
|
||||
assert_eq!(map, py_map.extract().unwrap());
|
||||
});
|
||||
}
|
||||
#[test]
|
||||
fn test_hashbrown_hashmap_into_python() {
|
||||
Python::with_gil(|py| {
|
||||
let mut map = hashbrown::HashMap::<i32, i32>::new();
|
||||
map.insert(1, 1);
|
||||
|
||||
let m: PyObject = map.into_py(py);
|
||||
let py_map = <PyDict as PyTryFrom>::try_from(m.as_ref(py)).unwrap();
|
||||
|
||||
assert!(py_map.len() == 1);
|
||||
assert!(py_map.get_item(1).unwrap().extract::<i32>().unwrap() == 1);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hashbrown_hashmap_into_dict() {
|
||||
Python::with_gil(|py| {
|
||||
let mut map = hashbrown::HashMap::<i32, i32>::new();
|
||||
map.insert(1, 1);
|
||||
|
||||
let py_map = map.into_py_dict(py);
|
||||
|
||||
assert_eq!(py_map.len(), 1);
|
||||
assert_eq!(py_map.get_item(1).unwrap().extract::<i32>().unwrap(), 1);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_hashbrown_hashset() {
|
||||
Python::with_gil(|py| {
|
||||
let set = PySet::new(py, &[1, 2, 3, 4, 5]).unwrap();
|
||||
let hash_set: hashbrown::HashSet<usize> = set.extract().unwrap();
|
||||
assert_eq!(hash_set, [1, 2, 3, 4, 5].iter().copied().collect());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hashbrown_hashset_into_py() {
|
||||
Python::with_gil(|py| {
|
||||
let hs: hashbrown::HashSet<u64> = [1, 2, 3, 4, 5].iter().cloned().collect();
|
||||
|
||||
let hso: PyObject = hs.clone().into_py(py);
|
||||
|
||||
assert_eq!(hs, hso.extract(py).unwrap());
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,3 +1,6 @@
|
|||
#![cfg(feature = "indexmap")]
|
||||
#![cfg_attr(docsrs, doc(cfg(feature = "indexmap")))]
|
||||
|
||||
//! Conversions to and from [indexmap](https://docs.rs/indexmap/)’s
|
||||
//! `IndexMap`.
|
||||
//!
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
//! This module contains conversions between various Rust object and their representation in Python.
|
||||
|
||||
mod array;
|
||||
#[cfg(feature = "indexmap")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "indexmap")))]
|
||||
pub mod hashbrown;
|
||||
pub mod indexmap;
|
||||
pub mod num_bigint;
|
||||
pub mod num_complex;
|
||||
mod osstr;
|
||||
mod path;
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
docsrs,
|
||||
doc(cfg(all(feature = "num-bigint", not(any(Py_LIMITED_API, PyPy)))))
|
||||
)]
|
||||
|
||||
//! Conversions to and from [num-bigint](https://docs.rs/num-bigint)’s [`BigInt`] and [`BigUint`] types.
|
||||
//!
|
||||
//! This is useful for converting Python integers when they may not fit in Rust's built-in integer types.
|
14
src/lib.rs
14
src/lib.rs
|
@ -69,12 +69,12 @@
|
|||
//! so that your module can also be used with statically linked Python interpreters.
|
||||
//! Use this feature when building an extension module.
|
||||
//
|
||||
//! - `hashbrown`: Enables conversions between Python objects and
|
||||
//! - [`hashbrown`](./hashbrown/index.html): Enables conversions between Python objects and
|
||||
//! [hashbrown](https://docs.rs/hashbrown)'s
|
||||
//! [`HashMap`](https://docs.rs/hashbrown/latest/hashbrown/struct.HashMap.html) and
|
||||
//! [`HashSet`](https://docs.rs/hashbrown/latest/hashbrown/struct.HashSet.html) types.
|
||||
//
|
||||
//! - [`indexmap`](crate::indexmap): Enables conversions between Python dictionary and
|
||||
//! - [`indexmap`](./indexmap/index.html): Enables conversions between Python dictionary and
|
||||
//! [indexmap](https://docs.rs/indexmap)'s
|
||||
//! [`IndexMap`](https://docs.rs/indexmap/latest/indexmap/map/struct.IndexMap.html).
|
||||
//
|
||||
|
@ -88,7 +88,7 @@
|
|||
//! [`BigInt`](https://docs.rs/num-bigint/latest/num_bigint/struct.BigInt.html) and
|
||||
//! [`BigUint`](https://docs.rs/num-bigint/latest/num_bigint/struct.BigUint.html) types.
|
||||
//
|
||||
//! - [`num-complex`](crate::num_complex): Enables conversions between Python objects and
|
||||
//! - [`num-complex`](./num_complex/index.html): Enables conversions between Python objects and
|
||||
//! [num-complex](https://docs.rs/num-complex)'s
|
||||
//! [`Complex`](https://docs.rs/num-complex/latest/num_complex/struct.Complex.html) type.
|
||||
//
|
||||
|
@ -331,13 +331,7 @@ mod python;
|
|||
pub mod type_object;
|
||||
pub mod types;
|
||||
|
||||
pub mod num_bigint;
|
||||
|
||||
pub mod num_complex;
|
||||
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "indexmap")))]
|
||||
#[cfg(feature = "indexmap")]
|
||||
pub use crate::conversions::indexmap;
|
||||
pub use crate::conversions::*;
|
||||
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
|
||||
#[cfg(feature = "serde")]
|
||||
|
|
|
@ -364,94 +364,6 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "hashbrown")]
|
||||
mod hashbrown_hashmap_conversion {
|
||||
use super::*;
|
||||
use crate::{FromPyObject, PyErr, PyObject, ToPyObject};
|
||||
|
||||
impl<K, V, H> ToPyObject for hashbrown::HashMap<K, V, H>
|
||||
where
|
||||
K: hash::Hash + cmp::Eq + ToPyObject,
|
||||
V: ToPyObject,
|
||||
H: hash::BuildHasher,
|
||||
{
|
||||
fn to_object(&self, py: Python) -> PyObject {
|
||||
IntoPyDict::into_py_dict(self, py).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V, H> IntoPy<PyObject> for hashbrown::HashMap<K, V, H>
|
||||
where
|
||||
K: hash::Hash + cmp::Eq + IntoPy<PyObject>,
|
||||
V: IntoPy<PyObject>,
|
||||
H: hash::BuildHasher,
|
||||
{
|
||||
fn into_py(self, py: Python) -> PyObject {
|
||||
let iter = self
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k.into_py(py), v.into_py(py)));
|
||||
IntoPyDict::into_py_dict(iter, py).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'source, K, V, S> FromPyObject<'source> for hashbrown::HashMap<K, V, S>
|
||||
where
|
||||
K: FromPyObject<'source> + cmp::Eq + hash::Hash,
|
||||
V: FromPyObject<'source>,
|
||||
S: hash::BuildHasher + Default,
|
||||
{
|
||||
fn extract(ob: &'source PyAny) -> Result<Self, PyErr> {
|
||||
let dict = <PyDict as PyTryFrom>::try_from(ob)?;
|
||||
let mut ret = hashbrown::HashMap::with_capacity_and_hasher(dict.len(), S::default());
|
||||
for (k, v) in dict.iter() {
|
||||
ret.insert(K::extract(k)?, V::extract(v)?);
|
||||
}
|
||||
Ok(ret)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hashbrown_hashmap_to_python() {
|
||||
Python::with_gil(|py| {
|
||||
let mut map = hashbrown::HashMap::<i32, i32>::new();
|
||||
map.insert(1, 1);
|
||||
|
||||
let m = map.to_object(py);
|
||||
let py_map = <PyDict as PyTryFrom>::try_from(m.as_ref(py)).unwrap();
|
||||
|
||||
assert!(py_map.len() == 1);
|
||||
assert!(py_map.get_item(1).unwrap().extract::<i32>().unwrap() == 1);
|
||||
assert_eq!(map, py_map.extract().unwrap());
|
||||
});
|
||||
}
|
||||
#[test]
|
||||
fn test_hashbrown_hashmap_into_python() {
|
||||
Python::with_gil(|py| {
|
||||
let mut map = hashbrown::HashMap::<i32, i32>::new();
|
||||
map.insert(1, 1);
|
||||
|
||||
let m: PyObject = map.into_py(py);
|
||||
let py_map = <PyDict as PyTryFrom>::try_from(m.as_ref(py)).unwrap();
|
||||
|
||||
assert!(py_map.len() == 1);
|
||||
assert!(py_map.get_item(1).unwrap().extract::<i32>().unwrap() == 1);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hashbrown_hashmap_into_dict() {
|
||||
Python::with_gil(|py| {
|
||||
let mut map = hashbrown::HashMap::<i32, i32>::new();
|
||||
map.insert(1, 1);
|
||||
|
||||
let py_map = map.into_py_dict(py);
|
||||
|
||||
assert_eq!(py_map.len(), 1);
|
||||
assert_eq!(py_map.get_item(1).unwrap().extract::<i32>().unwrap(), 1);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::conversion::IntoPy;
|
||||
|
|
|
@ -332,75 +332,6 @@ impl<'a> std::iter::IntoIterator for &'a PyFrozenSet {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "hashbrown")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "hashbrown")))]
|
||||
mod hashbrown_hashset_conversion {
|
||||
use super::*;
|
||||
use crate::{FromPyObject, PyObject, PyResult, ToPyObject};
|
||||
|
||||
impl<T> ToPyObject for hashbrown::HashSet<T>
|
||||
where
|
||||
T: hash::Hash + Eq + ToPyObject,
|
||||
{
|
||||
fn to_object(&self, py: Python) -> PyObject {
|
||||
let set = PySet::new::<T>(py, &[]).expect("Failed to construct empty set");
|
||||
{
|
||||
for val in self {
|
||||
set.add(val).expect("Failed to add to set");
|
||||
}
|
||||
}
|
||||
set.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, S> IntoPy<PyObject> for hashbrown::HashSet<K, S>
|
||||
where
|
||||
K: IntoPy<PyObject> + Eq + hash::Hash,
|
||||
S: hash::BuildHasher + Default,
|
||||
{
|
||||
fn into_py(self, py: Python) -> PyObject {
|
||||
let set = PySet::empty(py).expect("Failed to construct empty set");
|
||||
{
|
||||
for val in self {
|
||||
set.add(val.into_py(py)).expect("Failed to add to set");
|
||||
}
|
||||
}
|
||||
set.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'source, K, S> FromPyObject<'source> for hashbrown::HashSet<K, S>
|
||||
where
|
||||
K: FromPyObject<'source> + cmp::Eq + hash::Hash,
|
||||
S: hash::BuildHasher + Default,
|
||||
{
|
||||
fn extract(ob: &'source PyAny) -> PyResult<Self> {
|
||||
let set: &PySet = ob.downcast()?;
|
||||
set.iter().map(K::extract).collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_hashbrown_hashset() {
|
||||
Python::with_gil(|py| {
|
||||
let set = PySet::new(py, &[1, 2, 3, 4, 5]).unwrap();
|
||||
let hash_set: hashbrown::HashSet<usize> = set.extract().unwrap();
|
||||
assert_eq!(hash_set, [1, 2, 3, 4, 5].iter().copied().collect());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hashbrown_hashset_into_py() {
|
||||
Python::with_gil(|py| {
|
||||
let hs: hashbrown::HashSet<u64> = [1, 2, 3, 4, 5].iter().cloned().collect();
|
||||
|
||||
let hso: PyObject = hs.clone().into_py(py);
|
||||
|
||||
assert_eq!(hs, hso.extract(py).unwrap());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{PyFrozenSet, PySet};
|
||||
|
|
Loading…
Reference in New Issue