PythonOnlineDebugger/user_code/precompiled/__deserializer__.py

249 lines
8.4 KiB
Python

import orjson
import ujson as json
from .listnode import ListNode
from .nestedinteger import NestedInteger
from .treenode import TreeNode
class DeserializeError(Exception):
def __init__(self, value):
self.value = value
def __str__(self):
return str(self.value)
class __Deserializer__:
def _deserialize(self, s, t):
if t[-2:] == "[]":
return [
self._deserialize(
json.dumps(x, escape_forward_slashes=False), t[:-2]
)
for x in json.loads(s)
]
elif t[-1:] == ">":
subt = t[5:-1]
return [
self._deserialize(
json.dumps(x, escape_forward_slashes=False), subt
)
for x in json.loads(s)
]
elif t == "integer":
return int(s)
elif t == "long":
return int(s)
elif t == "double":
return float(s)
elif t == "character":
return json.loads(s)
elif t == "boolean":
return json.loads(s)
elif t == "string":
return json.loads(s)
elif t == "ListNode":
return ListNode.deserialize(s)
elif t == "TreeNode":
return TreeNode.deserialize(s)
elif t == "NestedInteger":
return NestedInteger.deserialize(s)
# deserialization with validation
# TODO: we should probably give one of those helper (?) tooltips in the run_code panel, which redirects ppl to a FAQ section # noqa: B950
# which details what are the allowed values of each type.
# TODO: write more granular error messages for each input type (do this later after the specification for allowed values is decided) # noqa: B950
def _deserialize_with_checks(self, s, t): # , validate=False # noqa: C901
if t[-2:] == "[]":
try:
j = json.loads(s)
assert type(j) == list
except Exception:
raise DeserializeError(s + " is not a valid value of type " + t)
return [
self._deserialize_with_checks(
json.dumps(x, escape_forward_slashes=False), t[:-2]
)
for x in j
]
elif t[-1:] == ">":
subt = t[5:-1]
try:
j = json.loads(s)
assert type(j) == list
except Exception:
raise DeserializeError(s + " is not a valid value of type " + t)
return [
self._deserialize_with_checks(
json.dumps(x, escape_forward_slashes=False), subt
)
for x in j
]
elif t == "integer":
try:
x = int(s)
assert str(x) == s and x <= 2147483647 and x >= -2147483648
except Exception:
raise DeserializeError(s + " is not a valid value of type " + t)
return x
elif t == "long":
try:
x = int(s)
assert (
str(x) == s and 9007199254740991 >= x >= -9007199254740991
)
except Exception:
raise DeserializeError(
s + " is not a valid value of type long or "
"is out of range [-(2^53-1), 2^53-1]"
)
return x
elif t == "double":
# TODO: we need to set a tighter specification on what is the allowable input for leetcode double. # noqa: B950
# It will probably be a very small subset of the strings which can be cast to float in python. # noqa: B950
# maybe we will only allow numbers like 4532.345 and -0.432432
# ^ specification might be similar to this problem https://leetcode.com/problems/valid-number/ # noqa: B950
try:
return float(s)
except Exception:
raise DeserializeError(s + " is not a valid value of type " + t)
elif t == "character":
# TODO: we also need a tighter specification on what the allowable values of char are for leetcode. # noqa: B950
# I would strongly prefer to be on the tighter side at first. Eg. only the chars which we have ever used in testcases for existing problems # noqa: B950
# and no other characters for now.
# would could dump all such characters into a "permitted.charset"
try:
j = json.loads(s)
c = str(j)
assert len(c) == 1
except Exception:
raise DeserializeError(s + " is not a valid value of type " + t)
return c
elif t == "boolean":
try:
assert s == "true" or s == "false"
except Exception:
raise DeserializeError(s + " is not a valid value of type " + t)
return s
elif t == "string":
# TODO: need tighter specification on the allowable values of char (eg. ascii only) # noqa: B950
try:
j = json.loads(s)
s = str(j)
except Exception:
raise DeserializeError(s + " is not a valid value of type " + t)
return s
elif t == "ListNode":
try:
return ListNode.deserialize(s)
except Exception:
raise DeserializeError(s + " is not a valid value of type " + t)
elif t == "TreeNode":
try:
return TreeNode.deserialize(s)
except Exception:
raise DeserializeError(s + " is not a valid value of type " + t)
elif t == "NestedInteger":
try:
return NestedInteger.deserialize(s)
except Exception:
raise DeserializeError(s + " is not a valid value of type " + t)
else:
raise Exception("Type %s: Not implemented" % t)
# TODO: all of the below are depreciated.
# Remove after new serializer/deserializer is deployed
def to_integer(self, line):
return int(line)
def to_double(self, line):
return float(line)
def to_char(self, line):
return json.loads(line)
def to_string(self, line):
return json.loads(line)
def to_int_array(self, line):
return json.loads(line)
def to_double_array(self, line):
return json.loads(line)
def to_double_2d_array(self, line):
return json.loads(line)
def to_int_2d_array(self, line):
return json.loads(line)
def to_char_array(self, line):
return json.loads(line)
def to_char_2d_array(self, line):
return json.loads(line)
def to_string_array(self, line):
return json.loads(line)
def to_string_set(self, line):
return set(json.loads(line))
def to_string_2d_array(self, line):
return json.loads(line)
def to_list_node(self, line):
return ListNode.deserialize(line)
def to_list_node_array(self, line):
arr2d = json.loads(line)
lists = []
for arr in arr2d:
lists.append(ListNode._array_to_list_node(arr))
return lists
def to_tree_node(self, line):
return TreeNode.deserialize(line)
def to_nested_integer(self, line):
return NestedInteger.deserialize(line)
def to_nested_integer_array(self, line):
ni = NestedInteger.deserialize(line)
return ni.getList()
def deserialize_default(obj, type_str):
if type_str == "ListNode":
return ListNode._array_to_list_node(obj)
elif type_str == "TreeNode":
return TreeNode._array_to_tree_node(obj)
elif type_str == "NestedInteger":
return NestedInteger._token_to_nested_integer(obj)
else:
return obj
class __DeserializerRapid__:
def _deserialize_node(self, obj, type_str):
if type_str[-2:] == "[]":
return [self._deserialize_node(x, type_str[:-2]) for x in obj]
elif type_str[-1:] == ">":
return [self._deserialize_node(x, type_str[5:-1]) for x in obj]
else:
return deserialize_default(obj, type_str)
def _deserialize(self, obj_str, type_str):
obj = orjson.loads(obj_str)
if (
"ListNode" in type_str
or "TreeNode" in type_str
or "NestedInteger" in type_str
):
return self._deserialize_node(obj, type_str)
else:
return obj