Merge pull request #113 from OddBloke/json

Rework json.dumps to support subclasses
This commit is contained in:
Windel Bouwman
2018-08-31 19:37:37 +02:00
committed by GitHub
5 changed files with 82 additions and 23 deletions

View File

@@ -11,12 +11,19 @@ assert "1" == json.dumps(1)
assert "1.0" == json.dumps(1.0)
assert "true" == json.dumps(True)
assert "false" == json.dumps(False)
assert 'null' == json.dumps(None)
assert '[]' == json.dumps([])
assert '[1]' == json.dumps([1])
assert '[[1]]' == json.dumps([[1]])
round_trip_test([1, "string", 1.0, True])
assert '[]' == json.dumps(())
assert '[1]' == json.dumps((1,))
assert '[[1]]' == json.dumps(((1,),))
# tuples don't round-trip through json
assert [1, "string", 1.0, True] == json.loads(json.dumps((1, "string", 1.0, True)))
assert '{}' == json.dumps({})
# TODO: uncomment once dict comparison is implemented
# round_trip_test({'a': 'b'})
@@ -35,3 +42,17 @@ assert False == json.loads('false')
assert [] == json.loads('[]')
assert ['a'] == json.loads('["a"]')
assert [['a'], 'b'] == json.loads('[["a"], "b"]')
class String(str): pass
assert '"string"' == json.dumps(String("string"))
# TODO: Uncomment and test once int/float construction is supported
# class Int(int): pass
# class Float(float): pass
# TODO: Uncomment and test once sequence/dict subclasses are supported by
# json.dumps
# class List(list): pass
# class Tuple(tuple): pass
# class Dict(dict): pass

View File

@@ -26,7 +26,7 @@ pub fn new(dict_type: PyObjectRef) -> PyObjectRef {
)
}
fn get_elements(obj: &PyObjectRef) -> HashMap<String, PyObjectRef> {
pub fn get_elements(obj: &PyObjectRef) -> HashMap<String, PyObjectRef> {
if let PyObjectKind::Dict { elements } = &obj.borrow().kind {
elements.clone()
} else {

View File

@@ -26,7 +26,7 @@ pub fn set_item(
}
}
fn get_elements(obj: PyObjectRef) -> Vec<PyObjectRef> {
pub fn get_elements(obj: PyObjectRef) -> Vec<PyObjectRef> {
if let PyObjectKind::List { elements } = &obj.borrow().kind {
elements.to_vec()
} else {

View File

@@ -98,3 +98,11 @@ pub fn get_item(
))),
}
}
pub fn get_elements(obj: PyObjectRef) -> Vec<PyObjectRef> {
if let PyObjectKind::Tuple { elements } = &obj.borrow().kind {
elements.to_vec()
} else {
panic!("Cannot extract list elements from non-list");
}
}

View File

@@ -6,41 +6,70 @@ use serde::de::Visitor;
use serde::ser::{SerializeMap, SerializeSeq};
use serde_json;
use super::super::objtype;
use super::super::pyobject::{
DictProtocol, PyContext, PyFuncArgs, PyObject, PyObjectKind, PyObjectRef, PyResult,
TypeProtocol,
};
use super::super::VirtualMachine;
use super::super::{objbool, objdict, objfloat, objint, objlist, objsequence, objstr, objtype};
impl serde::Serialize for PyObjectKind {
// We need to have a VM available to serialise a PyObject based on its subclass, so we implement
// PyObject serialisation via a proxy object which holds a reference to a VM
struct PyObjectSerializer<'s> {
pyobject: &'s PyObjectRef,
vm: &'s VirtualMachine,
}
impl<'s> PyObjectSerializer<'s> {
fn clone_with_object(&self, pyobject: &'s PyObjectRef) -> PyObjectSerializer {
PyObjectSerializer {
pyobject,
vm: self.vm,
}
}
}
impl<'s> serde::Serialize for PyObjectSerializer<'s> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self {
PyObjectKind::String { value } => serializer.serialize_str(value),
PyObjectKind::Integer { value } => serializer.serialize_i32(*value),
PyObjectKind::Float { value } => serializer.serialize_f64(*value),
PyObjectKind::Boolean { value } => serializer.serialize_bool(*value),
PyObjectKind::List { elements } | PyObjectKind::Tuple { elements } => {
let serialize_seq_elements =
|serializer: S, elements: Vec<PyObjectRef>| -> Result<S::Ok, S::Error> {
let mut seq = serializer.serialize_seq(Some(elements.len()))?;
for e in elements {
match e.borrow().kind {
ref kind => seq.serialize_element(kind)?,
}
seq.serialize_element(&self.clone_with_object(&e))?;
}
seq.end()
};
if objtype::isinstance(self.pyobject.clone(), self.vm.ctx.str_type()) {
serializer.serialize_str(&objstr::get_value(&self.pyobject))
} else if objtype::isinstance(self.pyobject.clone(), self.vm.ctx.int_type()) {
serializer.serialize_i32(objint::get_value(self.pyobject.clone()))
} else if objtype::isinstance(self.pyobject.clone(), self.vm.ctx.float_type()) {
serializer.serialize_f64(objfloat::get_value(self.pyobject.clone()))
} else if objtype::isinstance(self.pyobject.clone(), self.vm.ctx.bool_type()) {
serializer.serialize_bool(objbool::get_value(self.pyobject))
} else if objtype::isinstance(self.pyobject.clone(), self.vm.ctx.list_type()) {
let elements = objlist::get_elements(self.pyobject.clone());
serialize_seq_elements(serializer, elements)
} else if objtype::isinstance(self.pyobject.clone(), self.vm.ctx.tuple_type()) {
let elements = objsequence::get_elements(self.pyobject.clone());
serialize_seq_elements(serializer, elements)
} else if objtype::isinstance(self.pyobject.clone(), self.vm.ctx.dict_type()) {
let elements = objdict::get_elements(self.pyobject);
let mut map = serializer.serialize_map(Some(elements.len()))?;
for (key, e) in elements {
map.serialize_entry(&key, &self.clone_with_object(&e))?;
}
PyObjectKind::Dict { elements } => {
let mut map = serializer.serialize_map(Some(elements.len()))?;
for (key, e) in elements {
map.serialize_entry(key, &e.borrow().kind)?;
}
map.end()
}
PyObjectKind::None => serializer.serialize_none(),
kind => unimplemented!("Object of type '{:?}' is not serializable", kind),
map.end()
} else if let PyObjectKind::None = self.pyobject.borrow().kind {
serializer.serialize_none()
} else {
unimplemented!(
"Object of type '{:?}' is not serializable",
self.pyobject.typ()
);
}
}
}
@@ -172,7 +201,8 @@ fn dumps(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
// TODO: Implement non-trivial serialisation case
arg_check!(vm, args, required = [(obj, None)]);
// TODO: Raise an exception for serialisation errors
let string = serde_json::to_string(&obj.borrow().kind).unwrap();
let serializer = PyObjectSerializer { pyobject: obj, vm };
let string = serde_json::to_string(&serializer).unwrap();
Ok(vm.context().new_str(string))
}