Skip to content

Commit

Permalink
refactor: move std conversions into common module
Browse files Browse the repository at this point in the history
  • Loading branch information
davidhewitt committed Oct 13, 2022
1 parent 125af9b commit ec89bfe
Show file tree
Hide file tree
Showing 18 changed files with 1,099 additions and 1,058 deletions.
4 changes: 1 addition & 3 deletions src/conversions/mod.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
//! This module contains conversions between various Rust object and their representation in Python.
pub mod anyhow;
mod array;
pub mod chrono;
pub mod eyre;
pub mod hashbrown;
pub mod indexmap;
pub mod num_bigint;
pub mod num_complex;
mod osstr;
mod path;
pub mod serde;
mod std;
File renamed without changes.
167 changes: 167 additions & 0 deletions src/conversions/std/map.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
use std::{cmp, collections, hash};

use crate::{
inspect::types::TypeInfo,
types::{IntoPyDict, PyDict},
FromPyObject, IntoPy, PyAny, PyErr, PyObject, PyTryFrom, Python, ToPyObject,
};

impl<K, V, H> ToPyObject for collections::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> ToPyObject for collections::BTreeMap<K, V>
where
K: cmp::Eq + ToPyObject,
V: ToPyObject,
{
fn to_object(&self, py: Python<'_>) -> PyObject {
IntoPyDict::into_py_dict(self, py).into()
}
}

impl<K, V, H> IntoPy<PyObject> for collections::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()
}

fn type_output() -> TypeInfo {
TypeInfo::dict_of(K::type_output(), V::type_output())
}
}

impl<K, V> IntoPy<PyObject> for collections::BTreeMap<K, V>
where
K: cmp::Eq + IntoPy<PyObject>,
V: IntoPy<PyObject>,
{
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()
}

fn type_output() -> TypeInfo {
TypeInfo::dict_of(K::type_output(), V::type_output())
}
}

impl<'source, K, V, S> FromPyObject<'source> for collections::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 = collections::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)
}

fn type_input() -> TypeInfo {
TypeInfo::mapping_of(K::type_input(), V::type_input())
}
}

impl<'source, K, V> FromPyObject<'source> for collections::BTreeMap<K, V>
where
K: FromPyObject<'source> + cmp::Ord,
V: FromPyObject<'source>,
{
fn extract(ob: &'source PyAny) -> Result<Self, PyErr> {
let dict = <PyDict as PyTryFrom>::try_from(ob)?;
let mut ret = collections::BTreeMap::new();
for (k, v) in dict.iter() {
ret.insert(K::extract(k)?, V::extract(v)?);
}
Ok(ret)
}

fn type_input() -> TypeInfo {
TypeInfo::mapping_of(K::type_input(), V::type_input())
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::{IntoPy, PyObject, PyTryFrom, Python, ToPyObject};
use std::collections::{BTreeMap, HashMap};

#[test]
fn test_hashmap_to_python() {
Python::with_gil(|py| {
let mut map = 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_btreemap_to_python() {
Python::with_gil(|py| {
let mut map = BTreeMap::<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_hashmap_into_python() {
Python::with_gil(|py| {
let mut map = 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_btreemap_into_py() {
Python::with_gil(|py| {
let mut map = BTreeMap::<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);
});
}
}
9 changes: 9 additions & 0 deletions src/conversions/std/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
mod array;
mod map;
mod num;
mod osstr;
mod path;
mod set;
mod slice;
mod string;
mod vec;
Loading

0 comments on commit ec89bfe

Please # to comment.