import types import inspect import re import traceback from jsonrpclib import config 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)) # 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) 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