Skip to content

Commit a77b7e0

Browse files
committed
Have json.* throw an exception instead of panicking
1 parent 366f3e2 commit a77b7e0

File tree

1 file changed

+39
-14
lines changed

1 file changed

+39
-14
lines changed

vm/src/stdlib/json.rs

+39-14
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ use serde_json;
77

88
use super::super::obj::{objbool, objdict, objfloat, objint, objsequence, objstr, objtype};
99
use super::super::pyobject::{
10-
PyContext, PyFuncArgs, PyObjectKind, PyObjectRef, PyResult, TypeProtocol,
10+
create_type, DictProtocol, PyContext, PyFuncArgs, PyObjectKind, PyObjectRef, PyResult,
11+
TypeProtocol,
1112
};
1213
use super::super::VirtualMachine;
1314
use num_bigint::ToBigInt;
@@ -69,10 +70,10 @@ impl<'s> serde::Serialize for PyObjectSerializer<'s> {
6970
} else if let PyObjectKind::None = self.pyobject.borrow().kind {
7071
serializer.serialize_none()
7172
} else {
72-
unimplemented!(
73+
Err(serde::ser::Error::custom(format!(
7374
"Object of type '{:?}' is not serializable",
7475
self.pyobject.typ()
75-
);
76+
)))
7677
}
7778
}
7879
}
@@ -190,31 +191,55 @@ impl<'de> serde::de::DeserializeSeed<'de> for PyObjectDeserializer<'de> {
190191
fn dumps(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
191192
// TODO: Implement non-trivial serialisation case
192193
arg_check!(vm, args, required = [(obj, None)]);
193-
// TODO: Raise an exception for serialisation errors
194-
let serializer = PyObjectSerializer {
195-
pyobject: obj,
196-
ctx: &vm.ctx,
194+
let res = {
195+
let serializer = PyObjectSerializer {
196+
pyobject: obj,
197+
ctx: &vm.ctx,
198+
};
199+
serde_json::to_string(&serializer)
197200
};
198-
let string = serde_json::to_string(&serializer).unwrap();
201+
let string = res.map_err(|err| vm.new_type_error(format!("{}", err)))?;
199202
Ok(vm.context().new_str(string))
200203
}
201204

202205
fn loads(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
203206
// TODO: Implement non-trivial deserialisation case
204207
arg_check!(vm, args, required = [(string, Some(vm.ctx.str_type()))]);
205-
// TODO: Raise an exception for deserialisation errors
206-
let de = PyObjectDeserializer { ctx: &vm.ctx };
207-
// TODO: Support deserializing string sub-classes
208-
Ok(de
209-
.deserialize(&mut serde_json::Deserializer::from_str(&objstr::get_value(
208+
let res = {
209+
let de = PyObjectDeserializer { ctx: &vm.ctx };
210+
// TODO: Support deserializing string sub-classes
211+
de.deserialize(&mut serde_json::Deserializer::from_str(&objstr::get_value(
210212
&string,
211213
)))
212-
.unwrap())
214+
};
215+
216+
res.map_err(|err| {
217+
let json_decode_error = vm
218+
.sys_module
219+
.get_item("modules")
220+
.unwrap()
221+
.get_item("json")
222+
.unwrap()
223+
.get_item("JSONDecodeError")
224+
.unwrap();
225+
let exc = vm.new_exception(json_decode_error, format!("{}", err));
226+
vm.ctx.set_item(&exc, "lineno", vm.ctx.new_int(err.line().into()));
227+
vm.ctx.set_item(&exc, "colno", vm.ctx.new_int(err.column().into()));
228+
exc
229+
})
213230
}
214231

215232
pub fn mk_module(ctx: &PyContext) -> PyObjectRef {
216233
let json_mod = ctx.new_module(&"json".to_string(), ctx.new_scope(None));
217234
ctx.set_attr(&json_mod, "dumps", ctx.new_rustfunc(dumps));
218235
ctx.set_attr(&json_mod, "loads", ctx.new_rustfunc(loads));
236+
// TODO: Make this a proper type with a constructor
237+
let json_decode_error = create_type(
238+
"JSONDecodeError",
239+
&ctx.type_type,
240+
&ctx.exceptions.exception_type,
241+
&ctx.dict_type,
242+
);
243+
ctx.set_attr(&json_mod, "JSONDecodeError", json_decode_error);
219244
json_mod
220245
}

0 commit comments

Comments
 (0)