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