2014-03-10 05:18:05 +00:00
|
|
|
import types
|
|
|
|
import inspect
|
|
|
|
import re
|
|
|
|
import traceback
|
|
|
|
|
2015-06-16 16:19:43 +00:00
|
|
|
from jsonrpclib import config
|
2014-03-10 05:18:05 +00:00
|
|
|
|
|
|
|
iter_types = [
|
|
|
|
types.DictType,
|
|
|
|
types.ListType,
|
|
|
|
types.TupleType
|
|
|
|
]
|
|
|
|
|
|
|
|
string_types = [
|
|
|
|
types.StringType,
|
|
|
|
types.UnicodeType
|
|
|
|
]
|
|
|
|
|
|
|
|
numeric_types = [
|
|
|
|
types.IntType,
|
|
|
|
types.LongType,
|
|
|
|
types.FloatType
|
|
|
|
]
|
|
|
|
|
|
|
|
value_types = [
|
|
|
|
types.BooleanType,
|
|
|
|
types.NoneType
|
|
|
|
]
|
|
|
|
|
|
|
|
supported_types = iter_types+string_types+numeric_types+value_types
|
|
|
|
invalid_module_chars = r'[^a-zA-Z0-9\_\.]'
|
|
|
|
|
|
|
|
class TranslationError(Exception):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def dump(obj, serialize_method=None, ignore_attribute=None, ignore=[]):
|
|
|
|
if not serialize_method:
|
|
|
|
serialize_method = config.serialize_method
|
|
|
|
if not ignore_attribute:
|
|
|
|
ignore_attribute = config.ignore_attribute
|
|
|
|
obj_type = type(obj)
|
|
|
|
# Parse / return default "types"...
|
|
|
|
if obj_type in numeric_types+string_types+value_types:
|
|
|
|
return obj
|
|
|
|
if obj_type in iter_types:
|
|
|
|
if obj_type in (types.ListType, types.TupleType):
|
|
|
|
new_obj = []
|
|
|
|
for item in obj:
|
|
|
|
new_obj.append(dump(item, serialize_method,
|
|
|
|
ignore_attribute, ignore))
|
|
|
|
if obj_type is types.TupleType:
|
|
|
|
new_obj = tuple(new_obj)
|
|
|
|
return new_obj
|
|
|
|
# It's a dict...
|
|
|
|
else:
|
|
|
|
new_obj = {}
|
|
|
|
for key, value in obj.iteritems():
|
|
|
|
new_obj[key] = dump(value, serialize_method,
|
|
|
|
ignore_attribute, ignore)
|
|
|
|
return new_obj
|
|
|
|
# It's not a standard type, so it needs __jsonclass__
|
|
|
|
module_name = inspect.getmodule(obj).__name__
|
|
|
|
class_name = obj.__class__.__name__
|
|
|
|
json_class = class_name
|
|
|
|
if module_name not in ['', '__main__']:
|
|
|
|
json_class = '%s.%s' % (module_name, json_class)
|
|
|
|
return_obj = {"__jsonclass__":[json_class,]}
|
|
|
|
# If a serialization method is defined..
|
|
|
|
if serialize_method in dir(obj):
|
|
|
|
# Params can be a dict (keyword) or list (positional)
|
|
|
|
# Attrs MUST be a dict.
|
|
|
|
serialize = getattr(obj, serialize_method)
|
|
|
|
params, attrs = serialize()
|
|
|
|
return_obj['__jsonclass__'].append(params)
|
|
|
|
return_obj.update(attrs)
|
|
|
|
return return_obj
|
|
|
|
# Otherwise, try to figure it out
|
|
|
|
# Obviously, we can't assume to know anything about the
|
|
|
|
# parameters passed to __init__
|
|
|
|
return_obj['__jsonclass__'].append([])
|
|
|
|
attrs = {}
|
|
|
|
ignore_list = getattr(obj, ignore_attribute, [])+ignore
|
|
|
|
for attr_name, attr_value in obj.__dict__.iteritems():
|
|
|
|
if type(attr_value) in supported_types and \
|
|
|
|
attr_name not in ignore_list and \
|
|
|
|
attr_value not in ignore_list:
|
|
|
|
attrs[attr_name] = dump(attr_value, serialize_method,
|
|
|
|
ignore_attribute, ignore)
|
|
|
|
return_obj.update(attrs)
|
|
|
|
return return_obj
|
|
|
|
|
|
|
|
def load(obj):
|
|
|
|
if type(obj) in string_types+numeric_types+value_types:
|
|
|
|
return obj
|
|
|
|
if type(obj) is types.ListType:
|
|
|
|
return_list = []
|
|
|
|
for entry in obj:
|
|
|
|
return_list.append(load(entry))
|
|
|
|
return return_list
|
|
|
|
# Othewise, it's a dict type
|
|
|
|
if '__jsonclass__' not in obj.keys():
|
|
|
|
return_dict = {}
|
|
|
|
for key, value in obj.iteritems():
|
|
|
|
new_value = load(value)
|
|
|
|
return_dict[key] = new_value
|
|
|
|
return return_dict
|
|
|
|
# It's a dict, and it's a __jsonclass__
|
|
|
|
orig_module_name = obj['__jsonclass__'][0]
|
|
|
|
params = obj['__jsonclass__'][1]
|
|
|
|
if orig_module_name == '':
|
|
|
|
raise TranslationError('Module name empty.')
|
|
|
|
json_module_clean = re.sub(invalid_module_chars, '', orig_module_name)
|
|
|
|
if json_module_clean != orig_module_name:
|
|
|
|
raise TranslationError('Module name %s has invalid characters.' %
|
|
|
|
orig_module_name)
|
|
|
|
json_module_parts = json_module_clean.split('.')
|
|
|
|
json_class = None
|
|
|
|
if len(json_module_parts) == 1:
|
|
|
|
# Local class name -- probably means it won't work
|
|
|
|
if json_module_parts[0] not in config.classes.keys():
|
|
|
|
raise TranslationError('Unknown class or module %s.' %
|
|
|
|
json_module_parts[0])
|
|
|
|
json_class = config.classes[json_module_parts[0]]
|
|
|
|
else:
|
|
|
|
json_class_name = json_module_parts.pop()
|
|
|
|
json_module_tree = '.'.join(json_module_parts)
|
|
|
|
try:
|
|
|
|
temp_module = __import__(json_module_tree)
|
|
|
|
except ImportError:
|
|
|
|
raise TranslationError('Could not import %s from module %s.' %
|
|
|
|
(json_class_name, json_module_tree))
|
2015-06-16 16:19:43 +00:00
|
|
|
|
|
|
|
# The returned class is the top-level module, not the one we really
|
|
|
|
# want. (E.g., if we import a.b.c, we now have a.) Walk through other
|
|
|
|
# path components to get to b and c.
|
|
|
|
for i in json_module_parts[1:]:
|
|
|
|
temp_module = getattr(temp_module, i)
|
|
|
|
|
2014-03-10 05:18:05 +00:00
|
|
|
json_class = getattr(temp_module, json_class_name)
|
|
|
|
# Creating the object...
|
|
|
|
new_obj = None
|
|
|
|
if type(params) is types.ListType:
|
|
|
|
new_obj = json_class(*params)
|
|
|
|
elif type(params) is types.DictType:
|
|
|
|
new_obj = json_class(**params)
|
|
|
|
else:
|
|
|
|
raise TranslationError('Constructor args must be a dict or list.')
|
|
|
|
for key, value in obj.iteritems():
|
|
|
|
if key == '__jsonclass__':
|
|
|
|
continue
|
|
|
|
setattr(new_obj, key, value)
|
|
|
|
return new_obj
|