Home | History | Annotate | Download | only in json_rpc
      1 
      2 """
      3   Copyright (c) 2007 Jan-Klaas Kollhof
      4 
      5   This file is part of jsonrpc.
      6 
      7   jsonrpc is free software; you can redistribute it and/or modify
      8   it under the terms of the GNU Lesser General Public License as published by
      9   the Free Software Foundation; either version 2.1 of the License, or
     10   (at your option) any later version.
     11 
     12   This software is distributed in the hope that it will be useful,
     13   but WITHOUT ANY WARRANTY; without even the implied warranty of
     14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15   GNU Lesser General Public License for more details.
     16 
     17   You should have received a copy of the GNU Lesser General Public License
     18   along with this software; if not, write to the Free Software
     19   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     20 """
     21 
     22 import os
     23 import urllib2
     24 from autotest_lib.client.common_lib import error as exceptions
     25 
     26 from json import decoder
     27 
     28 from json import encoder as json_encoder
     29 json_encoder_class = json_encoder.JSONEncoder
     30 
     31 
     32 # Try to upgrade to the Django JSON encoder. It uses the standard json encoder
     33 # but can handle DateTime
     34 try:
     35     # See http://crbug.com/418022 too see why the try except is needed here.
     36     from django import conf as django_conf
     37     # The serializers can't be imported if django isn't configured.
     38     # Using try except here doesn't work, as test_that initializes it's own
     39     # django environment (setup_django_lite_environment) which raises import
     40     # errors if the django dbutils have been previously imported, as importing
     41     # them leaves some state behind.
     42     # This the variable name must not be undefined or empty string.
     43     if os.environ.get(django_conf.ENVIRONMENT_VARIABLE, None):
     44         from django.core.serializers import json as django_encoder
     45         json_encoder_class = django_encoder.DjangoJSONEncoder
     46 except ImportError:
     47     pass
     48 
     49 
     50 class JSONRPCException(Exception):
     51     pass
     52 
     53 class ValidationError(JSONRPCException):
     54     """Raised when the RPC is malformed."""
     55     def __init__(self, error, formatted_message):
     56         """Constructor.
     57 
     58         @param error: a dict of error info like so:
     59                       {error['name']: 'ErrorKind',
     60                        error['message']: 'Pithy error description.',
     61                        error['traceback']: 'Multi-line stack trace'}
     62         @formatted_message: string representation of this exception.
     63         """
     64         self.problem_keys = eval(error['message'])
     65         self.traceback = error['traceback']
     66         super(ValidationError, self).__init__(formatted_message)
     67 
     68 def BuildException(error):
     69     """Exception factory.
     70 
     71     Given a dict of error info, determine which subclass of
     72     JSONRPCException to build and return.  If can't determine the right one,
     73     just return a JSONRPCException with a pretty-printed error string.
     74 
     75     @param error: a dict of error info like so:
     76                   {error['name']: 'ErrorKind',
     77                    error['message']: 'Pithy error description.',
     78                    error['traceback']: 'Multi-line stack trace'}
     79     """
     80     error_message = '%(name)s: %(message)s\n%(traceback)s' % error
     81     for cls in JSONRPCException.__subclasses__():
     82         if error['name'] == cls.__name__:
     83             return cls(error, error_message)
     84     for cls in (exceptions.CrosDynamicSuiteException.__subclasses__() +
     85                 exceptions.RPCException.__subclasses__()):
     86         if error['name'] == cls.__name__:
     87             return cls(error_message)
     88     return JSONRPCException(error_message)
     89 
     90 class ServiceProxy(object):
     91     def __init__(self, serviceURL, serviceName=None, headers=None):
     92         self.__serviceURL = serviceURL
     93         self.__serviceName = serviceName
     94         self.__headers = headers or {}
     95 
     96     def __getattr__(self, name):
     97         if self.__serviceName is not None:
     98             name = "%s.%s" % (self.__serviceName, name)
     99         return ServiceProxy(self.__serviceURL, name, self.__headers)
    100 
    101     def __call__(self, *args, **kwargs):
    102         postdata = json_encoder_class().encode({'method': self.__serviceName,
    103                                                 'params': args + (kwargs,),
    104                                                 'id': 'jsonrpc'})
    105         request = urllib2.Request(self.__serviceURL, data=postdata,
    106                                   headers=self.__headers)
    107         respdata = urllib2.urlopen(request).read()
    108         try:
    109             resp = decoder.JSONDecoder().decode(respdata)
    110         except ValueError:
    111             raise JSONRPCException('Error decoding JSON reponse:\n' + respdata)
    112         if resp['error'] is not None:
    113             raise BuildException(resp['error'])
    114         else:
    115             return resp['result']
    116