1# Lint as: python2, python3 2# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6from __future__ import absolute_import 7from __future__ import division 8from __future__ import print_function 9try: 10 from inspect import getfullargspec as get_args 11except ImportError: 12 from inspect import getargspec as get_args 13import logging 14import six 15import sys 16 17 18TYPE_KEY = 'xmlrpc_struct_type_key' 19 20 21def deserialize(serialized, module=None): 22 """Deserialize an XmlRpcStruct. 23 24 Because Python XMLRPC doesn't understand anything more than basic 25 types, we're forced to reinvent type serialization. This is one way 26 to do it. 27 28 This function looks at the constructor and figures out what subset of 29 |serialized| will be accepted as arguments. 30 31 @param serialized dict representing a serialized XmlRpcStruct. 32 @param module module object to pull classes from. Defaults to 33 this file. 34 @return an XmlRpcStruct object built from |serialized|. 35 36 """ 37 if TYPE_KEY not in serialized: 38 logging.error('Failed to deserialize XmlRpcStruct because of ' 39 'missing type.') 40 logging.error('Got serialized object: %r', serialized) 41 return None 42 43 if module is None: 44 module = sys.modules[__name__] 45 klass = getattr(module, serialized[TYPE_KEY]) 46 constructor_args = get_args(klass.__init__) 47 optional_args = [] 48 if constructor_args.defaults: 49 # Valid args should now be a list of all the parameters that have 50 # default values. 51 optional_args = constructor_args.args[-len(constructor_args.defaults):] 52 # skip to argument 1 because the first argument is always |self|. 53 required_args = constructor_args.args[1:-len(constructor_args.defaults)] 54 args = [] 55 for arg in required_args: 56 if arg not in serialized: 57 logging.error('Failed to find non-keyword argument %s', arg) 58 return None 59 60 args.append(serialized[arg]) 61 kwargs = dict([k_v for k_v in six.iteritems(serialized) if k_v[0] in optional_args]) 62 logging.debug('Constructing %s object with args=%r, kwargs=%r', 63 serialized[TYPE_KEY], args, kwargs) 64 return klass(*args, **kwargs) 65 66 67class XmlRpcStruct(object): 68 """Enables deserialization by injecting the proper class type. 69 70 To make deserilization work for you, write a constructor like: 71 72 def __init__(self, arg1, arg2='foo'): 73 # It is important that self.arg1 = arg1. Otherwise arguments 74 # won't be sent over the wire correctly. 75 self.arg1 = arg1 76 self.arg2 = arg2 77 78 Serialization happens automatically when using XMLRPC proxies, since 79 Python's XMLRPC framework will take all fields from your object and drop 80 them into a dict to go over the wire. To deserialize your object on the 81 other side, call deserialize() and pass in the module defining the 82 requisite class. 83 84 serialize() is provided to allow objects to fake that they are XMLRPC 85 proxies. 86 87 """ 88 89 def __init__(self): 90 super(XmlRpcStruct, self).__init__() 91 setattr(self, TYPE_KEY, self.__class__.__name__) 92 93 94 def serialize(self): 95 """@return dict of object fields.""" 96 return self.__dict__ 97