use pyo3::exceptions::PyKeyError;
use pyo3::{intern, types::*, Bound};
use serde::de::{self, IntoDeserializer};
use serde::Deserialize;
use crate::error::{ErrorImpl, PythonizeError, Result};
#[cfg(feature = "arbitrary_precision")]
const TOKEN: &str = "$serde_json::private::Number";
pub fn depythonize<'a, 'py, T>(obj: &'a Bound<'py, PyAny>) -> Result<T>
where
T: Deserialize<'a>,
{
T::deserialize(&mut Depythonizer::from_object(obj))
}
pub struct Depythonizer<'a, 'py> {
input: &'a Bound<'py, PyAny>,
}
impl<'a, 'py> Depythonizer<'a, 'py> {
pub fn from_object(input: &'a Bound<'py, PyAny>) -> Self {
Depythonizer { input }
}
fn sequence_access(&self, expected_len: Option<usize>) -> Result<PySequenceAccess<'a, 'py>> {
let seq = self.input.cast::<PySequence>()?;
let len = self.input.len()?;
match expected_len {
Some(expected) if expected != len => {
Err(PythonizeError::incorrect_sequence_length(expected, len))
}
_ => Ok(PySequenceAccess::new(seq, len)),
}
}
fn set_access(&self) -> Result<PySetAsSequence<'py>> {
match self.input.cast::<PySet>() {
Ok(set) => Ok(PySetAsSequence::from_set(set)),
Err(e) => {
if let Ok(f) = self.input.cast::<PyFrozenSet>() {
Ok(PySetAsSequence::from_frozenset(f))
} else {
Err(e.into())
}
}
}
}
fn dict_access(&self) -> Result<PyMappingAccess<'py>> {
PyMappingAccess::new(self.input.cast()?)
}
fn dataclass_access(&self) -> Result<Option<PyDataclassAccess<'py>>> {
if let Some(dc) = DataclassCandidate::try_new(self.input) {
Some(PyDataclassAccess::new(dc)).transpose()
} else {
Ok(None)
}
}
fn deserialize_any_int<'de, V>(&self, int: &Bound<'_, PyInt>, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
if let Ok(x) = int.extract::<u128>() {
if let Ok(x) = u8::try_from(x) {
visitor.visit_u8(x)
} else if let Ok(x) = u16::try_from(x) {
visitor.visit_u16(x)
} else if let Ok(x) = u32::try_from(x) {
visitor.visit_u32(x)
} else if let Ok(x) = u64::try_from(x) {
visitor.visit_u64(x)
} else {
visitor.visit_u128(x)
}
} else if let Ok(x) = int.extract::<i128>() {
if let Ok(x) = i8::try_from(x) {
visitor.visit_i8(x)
} else if let Ok(x) = i16::try_from(x) {
visitor.visit_i16(x)
} else if let Ok(x) = i32::try_from(x) {
visitor.visit_i32(x)
} else if let Ok(x) = i64::try_from(x) {
visitor.visit_i64(x)
} else {
visitor.visit_i128(x)
}
} else {
#[cfg(feature = "arbitrary_precision")]
{
visitor.visit_map(NumberDeserializer {
number: Some(int.to_string()),
})
}
#[cfg(not(feature = "arbitrary_precision"))]
{
let _: i128 = int.extract()?;
unreachable!()
}
}
}
}
macro_rules! deserialize_type {
($method:ident => $visit:ident) => {
fn $method<V>(self, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
visitor.$visit(self.input.extract()?)
}
};
}
impl<'de> de::Deserializer<'de> for &'_ mut Depythonizer<'_, '_> {
type Error = PythonizeError;
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
let obj = self.input;
if obj.is_none() {
self.deserialize_unit(visitor)
} else if obj.is_instance_of::<PyBool>() {
self.deserialize_bool(visitor)
} else if let Ok(x) = obj.cast::<PyInt>() {
self.deserialize_any_int(x, visitor)
} else if obj.is_instance_of::<PyList>() || obj.is_instance_of::<PyTuple>() {
self.deserialize_tuple(obj.len()?, visitor)
} else if obj.is_instance_of::<PyDict>() {
self.deserialize_map(visitor)
} else if obj.is_instance_of::<PyString>() {
self.deserialize_str(visitor)
}
else if obj.is_instance_of::<PyBytes>() || obj.is_instance_of::<PyByteArray>() {
self.deserialize_bytes(visitor)
} else if obj.is_instance_of::<PyFloat>() {
self.deserialize_f64(visitor)
} else if obj.is_instance_of::<PyFrozenSet>() || obj.is_instance_of::<PySet>() {
self.deserialize_seq(visitor)
} else if obj.cast::<PySequence>().is_ok() {
self.deserialize_tuple(obj.len()?, visitor)
} else if obj.cast::<PyMapping>().is_ok() {
self.deserialize_map(visitor)
} else if let Some(dc) = DataclassCandidate::try_new(obj) {
visitor.visit_map(PyDataclassAccess::new(dc)?)
} else {
Err(obj.get_type().qualname().map_or_else(
|_| PythonizeError::unsupported_type("unknown"),
PythonizeError::unsupported_type,
))
}
}
fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
visitor.visit_bool(self.input.is_truthy()?)
}
fn deserialize_char<V>(self, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
let s = self.input.cast::<PyString>()?.to_cow()?;
if s.len() != 1 {
return Err(PythonizeError::invalid_length_char());
}
visitor.visit_char(s.chars().next().unwrap())
}
deserialize_type!(deserialize_i8 => visit_i8);
deserialize_type!(deserialize_i16 => visit_i16);
deserialize_type!(deserialize_i32 => visit_i32);
deserialize_type!(deserialize_i64 => visit_i64);
deserialize_type!(deserialize_i128 => visit_i128);
deserialize_type!(deserialize_u8 => visit_u8);
deserialize_type!(deserialize_u16 => visit_u16);
deserialize_type!(deserialize_u32 => visit_u32);
deserialize_type!(deserialize_u64 => visit_u64);
deserialize_type!(deserialize_u128 => visit_u128);
deserialize_type!(deserialize_f32 => visit_f32);
deserialize_type!(deserialize_f64 => visit_f64);
fn deserialize_str<V>(self, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
let s = self.input.cast::<PyString>()?;
visitor.visit_str(&s.to_cow()?)
}
fn deserialize_string<V>(self, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
self.deserialize_str(visitor)
}
fn deserialize_bytes<V>(self, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
let b = self.input.cast::<PyBytes>()?;
visitor.visit_bytes(b.as_bytes())
}
fn deserialize_byte_buf<V>(self, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
self.deserialize_bytes(visitor)
}
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
if self.input.is_none() {
visitor.visit_none()
} else {
visitor.visit_some(self)
}
}
fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
if self.input.is_none() {
visitor.visit_unit()
} else {
Err(PythonizeError::msg("expected None"))
}
}
fn deserialize_unit_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
self.deserialize_unit(visitor)
}
fn deserialize_newtype_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
visitor.visit_newtype_struct(self)
}
fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
match self.sequence_access(None) {
Ok(seq) => visitor.visit_seq(seq),
Err(e) => {
if matches!(*e.inner, ErrorImpl::UnexpectedType(_)) {
if let Ok(set) = self.set_access() {
return visitor.visit_seq(set);
}
}
Err(e)
}
}
}
fn deserialize_tuple<V>(self, len: usize, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
visitor.visit_seq(self.sequence_access(Some(len))?)
}
fn deserialize_tuple_struct<V>(
self,
_name: &'static str,
len: usize,
visitor: V,
) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
visitor.visit_seq(self.sequence_access(Some(len))?)
}
fn deserialize_map<V>(self, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
if let Some(dc_access) = self.dataclass_access()? {
visitor.visit_map(dc_access)
} else {
visitor.visit_map(self.dict_access()?)
}
}
fn deserialize_struct<V>(
self,
_name: &'static str,
_fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
self.deserialize_map(visitor)
}
fn deserialize_enum<V>(
self,
_name: &'static str,
_variants: &'static [&'static str],
visitor: V,
) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
let item = &self.input;
if let Ok(s) = item.cast::<PyString>() {
visitor.visit_enum(s.to_cow()?.into_deserializer())
} else if let Ok(m) = item.cast::<PyMapping>() {
if m.len()? != 1 {
return Err(PythonizeError::invalid_length_enum());
}
let variant: Bound<PyString> = m
.keys()?
.get_item(0)?
.cast_into::<PyString>()
.map_err(|_| PythonizeError::dict_key_not_string())?;
let value = m.get_item(&variant)?;
visitor.visit_enum(PyEnumAccess::new(&value, variant))
} else {
Err(PythonizeError::invalid_enum_type())
}
}
fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
let s = self
.input
.cast::<PyString>()
.map_err(|_| PythonizeError::dict_key_not_string())?;
visitor.visit_str(&s.to_cow()?)
}
fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
visitor.visit_unit()
}
}
struct PySequenceAccess<'a, 'py> {
seq: &'a Bound<'py, PySequence>,
index: usize,
len: usize,
}
impl<'a, 'py> PySequenceAccess<'a, 'py> {
fn new(seq: &'a Bound<'py, PySequence>, len: usize) -> Self {
Self { seq, index: 0, len }
}
}
impl<'de> de::SeqAccess<'de> for PySequenceAccess<'_, '_> {
type Error = PythonizeError;
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
where
T: de::DeserializeSeed<'de>,
{
if self.index < self.len {
let item = self.seq.get_item(self.index)?;
self.index += 1;
seed.deserialize(&mut Depythonizer::from_object(&item))
.map(Some)
} else {
Ok(None)
}
}
}
struct PySetAsSequence<'py> {
iter: Bound<'py, PyIterator>,
}
impl<'py> PySetAsSequence<'py> {
fn from_set(set: &Bound<'py, PySet>) -> Self {
Self {
iter: PyIterator::from_object(set).expect("set is always iterable"),
}
}
fn from_frozenset(set: &Bound<'py, PyFrozenSet>) -> Self {
Self {
iter: PyIterator::from_object(set).expect("frozenset is always iterable"),
}
}
}
impl<'de> de::SeqAccess<'de> for PySetAsSequence<'_> {
type Error = PythonizeError;
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
where
T: de::DeserializeSeed<'de>,
{
match self.iter.next() {
Some(item) => seed
.deserialize(&mut Depythonizer::from_object(&item?))
.map(Some),
None => Ok(None),
}
}
}
struct PyMappingAccess<'py> {
keys: Bound<'py, PyList>,
values: Bound<'py, PyList>,
key_idx: usize,
val_idx: usize,
len: usize,
}
impl<'py> PyMappingAccess<'py> {
fn new(map: &Bound<'py, PyMapping>) -> Result<Self> {
let keys = map.keys()?;
let values = map.values()?;
let len = map.len()?;
Ok(Self {
keys,
values,
key_idx: 0,
val_idx: 0,
len,
})
}
}
impl<'de> de::MapAccess<'de> for PyMappingAccess<'_> {
type Error = PythonizeError;
fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>>
where
K: de::DeserializeSeed<'de>,
{
if self.key_idx < self.len {
let item = self.keys.get_item(self.key_idx)?;
self.key_idx += 1;
seed.deserialize(&mut Depythonizer::from_object(&item))
.map(Some)
} else {
Ok(None)
}
}
fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value>
where
V: de::DeserializeSeed<'de>,
{
let item = self.values.get_item(self.val_idx)?;
self.val_idx += 1;
seed.deserialize(&mut Depythonizer::from_object(&item))
}
}
struct DataclassCandidate<'a, 'py> {
obj: &'a Bound<'py, PyAny>,
fields: Bound<'py, PyAny>,
}
impl<'a, 'py> DataclassCandidate<'a, 'py> {
fn try_new(obj: &'a Bound<'py, PyAny>) -> Option<Self> {
let fields = obj
.getattr_opt(intern!(obj.py(), "__dataclass_fields__"))
.ok()
.flatten()?;
Some(Self { obj, fields })
}
}
struct PyDataclassAccess<'py> {
fields: Bound<'py, PyList>,
dict: Bound<'py, PyDict>,
field_idx: usize,
val_idx: usize,
len: usize,
}
impl<'py> PyDataclassAccess<'py> {
fn new(dc: DataclassCandidate<'_, 'py>) -> Result<Self> {
let fields = dc.fields.cast::<PyDict>()?.keys();
let dict = dc
.obj
.getattr(intern!(dc.obj.py(), "__dict__"))?
.cast_into()?;
let len = fields.len();
Ok(Self {
fields,
dict,
field_idx: 0,
val_idx: 0,
len,
})
}
}
impl<'de> de::MapAccess<'de> for PyDataclassAccess<'_> {
type Error = PythonizeError;
fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>>
where
K: de::DeserializeSeed<'de>,
{
if self.field_idx < self.len {
let item = self.fields.get_item(self.field_idx)?;
self.field_idx += 1;
seed.deserialize(&mut Depythonizer::from_object(&item))
.map(Some)
} else {
Ok(None)
}
}
fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value>
where
V: de::DeserializeSeed<'de>,
{
let key = self.fields.get_item(self.val_idx)?;
let value = self
.dict
.get_item(&key)?
.ok_or_else(|| PyKeyError::new_err(key.unbind()))?;
self.val_idx += 1;
seed.deserialize(&mut Depythonizer::from_object(&value))
}
}
struct PyEnumAccess<'a, 'py> {
de: Depythonizer<'a, 'py>,
variant: Bound<'py, PyString>,
}
impl<'a, 'py> PyEnumAccess<'a, 'py> {
fn new(obj: &'a Bound<'py, PyAny>, variant: Bound<'py, PyString>) -> Self {
Self {
de: Depythonizer::from_object(obj),
variant,
}
}
}
impl<'de> de::EnumAccess<'de> for PyEnumAccess<'_, '_> {
type Error = PythonizeError;
type Variant = Self;
fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant)>
where
V: de::DeserializeSeed<'de>,
{
let cow = self.variant.to_cow()?;
let de: de::value::StrDeserializer<'_, PythonizeError> = cow.as_ref().into_deserializer();
let val = seed.deserialize(de)?;
Ok((val, self))
}
}
impl<'de> de::VariantAccess<'de> for PyEnumAccess<'_, '_> {
type Error = PythonizeError;
fn unit_variant(self) -> Result<()> {
Ok(())
}
fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value>
where
T: de::DeserializeSeed<'de>,
{
seed.deserialize(&mut { self.de })
}
fn tuple_variant<V>(self, len: usize, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
visitor.visit_seq(self.de.sequence_access(Some(len))?)
}
fn struct_variant<V>(self, _fields: &'static [&'static str], visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
visitor.visit_map(self.de.dict_access()?)
}
}
#[cfg(feature = "arbitrary_precision")]
struct NumberDeserializer {
number: Option<String>,
}
#[cfg(feature = "arbitrary_precision")]
impl<'de> de::MapAccess<'de> for NumberDeserializer {
type Error = PythonizeError;
fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>>
where
K: de::DeserializeSeed<'de>,
{
if self.number.is_none() {
return Ok(None);
}
seed.deserialize(TOKEN.into_deserializer()).map(Some)
}
fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value>
where
V: de::DeserializeSeed<'de>,
{
seed.deserialize(self.number.take().unwrap().into_deserializer())
}
}
#[cfg(test)]
mod test {
use std::{collections::HashMap, ffi::CStr};
use super::*;
use crate::error::ErrorImpl;
use maplit::hashmap;
use pyo3::{IntoPyObject, Python};
use serde_json::{json, Value as JsonValue};
fn test_de<T>(code: &CStr, expected: &T, expected_json: &JsonValue)
where
T: de::DeserializeOwned + PartialEq + std::fmt::Debug,
{
Python::attach(|py| {
let obj = py.eval(code, None, None).unwrap();
test_de_with_obj(&obj, expected, expected_json);
});
}
fn test_de_with_obj<T>(obj: &Bound<'_, PyAny>, expected: &T, expected_json: &JsonValue)
where
T: de::DeserializeOwned + PartialEq + std::fmt::Debug,
{
let actual: T = depythonize(obj).unwrap();
assert_eq!(&actual, expected);
let actual_json: JsonValue = depythonize(obj).unwrap();
assert_eq!(&actual_json, expected_json);
}
#[test]
fn test_empty_struct() {
#[derive(Debug, Deserialize, PartialEq)]
struct Empty;
let expected = Empty;
let expected_json = json!(null);
let code = c"None";
test_de(code, &expected, &expected_json);
}
#[test]
fn test_struct() {
#[derive(Debug, Deserialize, PartialEq)]
struct Struct {
foo: String,
bar: usize,
baz: f32,
qux: bool,
}
let expected = Struct {
foo: "Foo".to_string(),
bar: 8usize,
baz: 45.23,
qux: true,
};
let expected_json = json!({
"foo": "Foo",
"bar": 8,
"baz": 45.23,
"qux": true
});
let code = c"{'foo': 'Foo', 'bar': 8, 'baz': 45.23, 'qux': True}";
test_de(code, &expected, &expected_json);
}
#[test]
fn test_struct_missing_key() {
#[derive(Debug, Deserialize, PartialEq)]
struct Struct {
foo: String,
bar: usize,
}
let code = c"{'foo': 'Foo'}";
Python::attach(|py| {
let locals = PyDict::new(py);
let obj = py.eval(code, None, Some(&locals)).unwrap();
assert!(matches!(
*depythonize::<Struct>(&obj).unwrap_err().inner,
ErrorImpl::Message(msg) if msg == "missing field `bar`"
));
})
}
#[test]
fn test_tuple_struct() {
#[derive(Debug, Deserialize, PartialEq)]
struct TupleStruct(String, f64);
let expected = TupleStruct("cat".to_string(), -10.05);
let expected_json = json!(["cat", -10.05]);
let code = c"('cat', -10.05)";
test_de(code, &expected, &expected_json);
}
#[test]
fn test_tuple_too_long() {
#[derive(Debug, Deserialize, PartialEq)]
struct TupleStruct(String, f64);
let code = c"('cat', -10.05, 'foo')";
Python::attach(|py| {
let locals = PyDict::new(py);
let obj = py.eval(code, None, Some(&locals)).unwrap();
assert!(matches!(
*depythonize::<TupleStruct>(&obj).unwrap_err().inner,
ErrorImpl::IncorrectSequenceLength { expected, got } if expected == 2 && got == 3
));
})
}
#[test]
fn test_tuple_struct_from_pylist() {
#[derive(Debug, Deserialize, PartialEq)]
struct TupleStruct(String, f64);
let expected = TupleStruct("cat".to_string(), -10.05);
let expected_json = json!(["cat", -10.05]);
let code = c"['cat', -10.05]";
test_de(code, &expected, &expected_json);
}
#[test]
fn test_tuple() {
let expected = ("foo".to_string(), 5);
let expected_json = json!(["foo", 5]);
let code = c"('foo', 5)";
test_de(code, &expected, &expected_json);
}
#[test]
fn test_tuple_from_pylist() {
let expected = ("foo".to_string(), 5);
let expected_json = json!(["foo", 5]);
let code = c"['foo', 5]";
test_de(code, &expected, &expected_json);
}
#[test]
fn test_vec_from_pyset() {
let expected = vec!["foo".to_string()];
let expected_json = json!(["foo"]);
let code = c"{'foo'}";
test_de(code, &expected, &expected_json);
}
#[test]
fn test_vec_from_pyfrozenset() {
let expected = vec!["foo".to_string()];
let expected_json = json!(["foo"]);
let code = c"frozenset({'foo'})";
test_de(code, &expected, &expected_json);
}
#[test]
fn test_vec() {
let expected = vec![3, 2, 1];
let expected_json = json!([3, 2, 1]);
let code = c"[3, 2, 1]";
test_de(code, &expected, &expected_json);
}
#[test]
fn test_vec_from_tuple() {
let expected = vec![3, 2, 1];
let expected_json = json!([3, 2, 1]);
let code = c"(3, 2, 1)";
test_de(code, &expected, &expected_json);
}
#[test]
fn test_hashmap() {
let expected = hashmap! {"foo".to_string() => 4};
let expected_json = json!({"foo": 4 });
let code = c"{'foo': 4}";
test_de(code, &expected, &expected_json);
}
#[test]
fn test_enum_variant() {
#[derive(Debug, Deserialize, PartialEq)]
enum Foo {
Variant,
}
let expected = Foo::Variant;
let expected_json = json!("Variant");
let code = c"'Variant'";
test_de(code, &expected, &expected_json);
}
#[test]
fn test_enum_tuple_variant() {
#[derive(Debug, Deserialize, PartialEq)]
enum Foo {
Tuple(i32, String),
}
let expected = Foo::Tuple(12, "cat".to_string());
let expected_json = json!({"Tuple": [12, "cat"]});
let code = c"{'Tuple': [12, 'cat']}";
test_de(code, &expected, &expected_json);
}
#[test]
fn test_enum_newtype_variant() {
#[derive(Debug, Deserialize, PartialEq)]
enum Foo {
NewType(String),
}
let expected = Foo::NewType("cat".to_string());
let expected_json = json!({"NewType": "cat" });
let code = c"{'NewType': 'cat'}";
test_de(code, &expected, &expected_json);
}
#[test]
fn test_enum_struct_variant() {
#[derive(Debug, Deserialize, PartialEq)]
enum Foo {
Struct { foo: String, bar: usize },
}
let expected = Foo::Struct {
foo: "cat".to_string(),
bar: 25,
};
let expected_json = json!({"Struct": {"foo": "cat", "bar": 25 }});
let code = c"{'Struct': {'foo': 'cat', 'bar': 25}}";
test_de(code, &expected, &expected_json);
}
#[test]
fn test_enum_untagged_tuple_variant() {
#[derive(Debug, Deserialize, PartialEq)]
#[serde(untagged)]
enum Foo {
Tuple(f32, char),
}
let expected = Foo::Tuple(12.0, 'c');
let expected_json = json!([12.0, 'c']);
let code = c"[12.0, 'c']";
test_de(code, &expected, &expected_json);
}
#[test]
fn test_enum_untagged_newtype_variant() {
#[derive(Debug, Deserialize, PartialEq)]
#[serde(untagged)]
enum Foo {
NewType(String),
}
let expected = Foo::NewType("cat".to_string());
let expected_json = json!("cat");
let code = c"'cat'";
test_de(code, &expected, &expected_json);
}
#[test]
fn test_enum_untagged_struct_variant() {
#[derive(Debug, Deserialize, PartialEq)]
#[serde(untagged)]
enum Foo {
Struct { foo: Vec<char>, bar: [u8; 4] },
}
let expected = Foo::Struct {
foo: vec!['a', 'b', 'c'],
bar: [2, 5, 3, 1],
};
let expected_json = json!({"foo": ["a", "b", "c"], "bar": [2, 5, 3, 1]});
let code = c"{'foo': ['a', 'b', 'c'], 'bar': [2, 5, 3, 1]}";
test_de(code, &expected, &expected_json);
}
#[test]
fn test_nested_type() {
#[derive(Debug, Deserialize, PartialEq)]
struct Foo {
name: String,
bar: Bar,
}
#[derive(Debug, Deserialize, PartialEq)]
struct Bar {
value: usize,
variant: Baz,
}
#[derive(Debug, Deserialize, PartialEq)]
enum Baz {
Basic,
Tuple(f32, u32),
}
let expected = Foo {
name: "SomeFoo".to_string(),
bar: Bar {
value: 13,
variant: Baz::Tuple(-1.5, 8),
},
};
let expected_json =
json!({"name": "SomeFoo", "bar": { "value": 13, "variant": { "Tuple": [-1.5, 8]}}});
let code = c"{'name': 'SomeFoo', 'bar': {'value': 13, 'variant': {'Tuple': [-1.5, 8]}}}";
test_de(code, &expected, &expected_json);
}
#[test]
fn test_int_limits() {
Python::attach(|py| {
let _: serde_json::Value = depythonize(&u8::MAX.into_pyobject(py).unwrap()).unwrap();
let _: serde_json::Value = depythonize(&u8::MIN.into_pyobject(py).unwrap()).unwrap();
let _: serde_json::Value = depythonize(&i8::MAX.into_pyobject(py).unwrap()).unwrap();
let _: serde_json::Value = depythonize(&i8::MIN.into_pyobject(py).unwrap()).unwrap();
let _: serde_json::Value = depythonize(&u16::MAX.into_pyobject(py).unwrap()).unwrap();
let _: serde_json::Value = depythonize(&u16::MIN.into_pyobject(py).unwrap()).unwrap();
let _: serde_json::Value = depythonize(&i16::MAX.into_pyobject(py).unwrap()).unwrap();
let _: serde_json::Value = depythonize(&i16::MIN.into_pyobject(py).unwrap()).unwrap();
let _: serde_json::Value = depythonize(&u32::MAX.into_pyobject(py).unwrap()).unwrap();
let _: serde_json::Value = depythonize(&u32::MIN.into_pyobject(py).unwrap()).unwrap();
let _: serde_json::Value = depythonize(&i32::MAX.into_pyobject(py).unwrap()).unwrap();
let _: serde_json::Value = depythonize(&i32::MIN.into_pyobject(py).unwrap()).unwrap();
let _: serde_json::Value = depythonize(&u64::MAX.into_pyobject(py).unwrap()).unwrap();
let _: serde_json::Value = depythonize(&u64::MIN.into_pyobject(py).unwrap()).unwrap();
let _: serde_json::Value = depythonize(&i64::MAX.into_pyobject(py).unwrap()).unwrap();
let _: serde_json::Value = depythonize(&i64::MIN.into_pyobject(py).unwrap()).unwrap();
let _: u128 = depythonize(&u128::MAX.into_pyobject(py).unwrap()).unwrap();
let _: i128 = depythonize(&u128::MIN.into_pyobject(py).unwrap()).unwrap();
let _: i128 = depythonize(&i128::MAX.into_pyobject(py).unwrap()).unwrap();
let _: i128 = depythonize(&i128::MIN.into_pyobject(py).unwrap()).unwrap();
});
}
#[test]
fn test_deserialize_bytes() {
Python::attach(|py| {
let obj = PyBytes::new(py, "hello".as_bytes());
let actual: Vec<u8> = depythonize(&obj).unwrap();
assert_eq!(actual, b"hello");
})
}
#[test]
fn test_char() {
let expected = 'a';
let expected_json = json!("a");
let code = c"'a'";
test_de(code, &expected, &expected_json);
}
#[test]
fn test_unknown_type() {
Python::attach(|py| {
let obj = py
.import("decimal")
.unwrap()
.getattr("Decimal")
.unwrap()
.call0()
.unwrap();
let err = depythonize::<serde_json::Value>(&obj).unwrap_err();
assert!(matches!(
*err.inner,
ErrorImpl::UnsupportedType(name) if name == "Decimal"
));
});
}
#[test]
fn test_dataclass() {
let code = c"\
from dataclasses import dataclass
@dataclass
class Point:
x: int
y: int
point = Point(1, 2)";
#[derive(Debug, Deserialize, PartialEq)]
struct Point {
x: i32,
y: i32,
}
let expected = Point { x: 1, y: 2 };
let expected_json = json!({"x": 1, "y": 2});
Python::attach(|py| {
let locals = PyDict::new(py);
py.run(code, None, Some(&locals)).unwrap();
let obj = locals.get_item("point").unwrap().unwrap();
test_de_with_obj(&obj, &expected, &expected_json);
let map: HashMap<String, i32> = depythonize(&obj).unwrap();
assert_eq!(map.len(), 2);
assert_eq!(*map.get("x").unwrap(), 1);
assert_eq!(*map.get("y").unwrap(), 2);
});
}
#[test]
fn test_dataclass_missing_field() {
let code = c"\
from dataclasses import dataclass
@dataclass
class Point:
x: int
y: int
point = Point(1, 2)";
#[derive(Debug, Deserialize, PartialEq)]
struct Point {
x: i32,
y: i32,
z: i32,
}
Python::attach(|py| {
let locals = PyDict::new(py);
py.run(code, None, Some(&locals)).unwrap();
let obj = locals.get_item("point").unwrap().unwrap();
let err = depythonize::<Point>(&obj).unwrap_err();
assert!(matches!(
*err.inner,
ErrorImpl::Message(msg) if msg == "missing field `z`"
));
});
}
#[test]
fn test_dataclass_extra_field() {
let code = c"\
from dataclasses import dataclass
@dataclass
class Point:
x: int
y: int
z: int
point = Point(1, 2, 3)";
#[derive(Debug, Deserialize, PartialEq)]
#[serde(deny_unknown_fields)]
struct Point {
x: i32,
y: i32,
}
Python::attach(|py| {
let locals = PyDict::new(py);
py.run(code, None, Some(&locals)).unwrap();
let obj = locals.get_item("point").unwrap().unwrap();
let err = depythonize::<Point>(&obj).unwrap_err();
assert!(matches!(
*err.inner,
ErrorImpl::Message(msg) if msg == "unknown field `z`, expected `x` or `y`"
));
});
}
}