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 22import os 23import socket 24import urllib2 25from autotest_lib.client.common_lib import error as exceptions 26 27from json import decoder 28 29from json import encoder as json_encoder 30json_encoder_class = json_encoder.JSONEncoder 31 32 33# Try to upgrade to the Django JSON encoder. It uses the standard json encoder 34# but can handle DateTime 35try: 36 # See http://crbug.com/418022 too see why the try except is needed here. 37 from django import conf as django_conf 38 # The serializers can't be imported if django isn't configured. 39 # Using try except here doesn't work, as test_that initializes it's own 40 # django environment (setup_django_lite_environment) which raises import 41 # errors if the django dbutils have been previously imported, as importing 42 # them leaves some state behind. 43 # This the variable name must not be undefined or empty string. 44 if os.environ.get(django_conf.ENVIRONMENT_VARIABLE, None): 45 from django.core.serializers import json as django_encoder 46 json_encoder_class = django_encoder.DjangoJSONEncoder 47except ImportError: 48 pass 49 50 51class JSONRPCException(Exception): 52 pass 53 54class ValidationError(JSONRPCException): 55 """Raised when the RPC is malformed.""" 56 def __init__(self, error, formatted_message): 57 """Constructor. 58 59 @param error: a dict of error info like so: 60 {error['name']: 'ErrorKind', 61 error['message']: 'Pithy error description.', 62 error['traceback']: 'Multi-line stack trace'} 63 @formatted_message: string representation of this exception. 64 """ 65 self.problem_keys = eval(error['message']) 66 self.traceback = error['traceback'] 67 super(ValidationError, self).__init__(formatted_message) 68 69def BuildException(error): 70 """Exception factory. 71 72 Given a dict of error info, determine which subclass of 73 JSONRPCException to build and return. If can't determine the right one, 74 just return a JSONRPCException with a pretty-printed error string. 75 76 @param error: a dict of error info like so: 77 {error['name']: 'ErrorKind', 78 error['message']: 'Pithy error description.', 79 error['traceback']: 'Multi-line stack trace'} 80 """ 81 error_message = '%(name)s: %(message)s\n%(traceback)s' % error 82 for cls in JSONRPCException.__subclasses__(): 83 if error['name'] == cls.__name__: 84 return cls(error, error_message) 85 for cls in (exceptions.CrosDynamicSuiteException.__subclasses__() + 86 exceptions.RPCException.__subclasses__()): 87 if error['name'] == cls.__name__: 88 return cls(error_message) 89 return JSONRPCException(error_message) 90 91class ServiceProxy(object): 92 def __init__(self, serviceURL, serviceName=None, headers=None): 93 self.__serviceURL = serviceURL 94 self.__serviceName = serviceName 95 self.__headers = headers or {} 96 97 def __getattr__(self, name): 98 if self.__serviceName is not None: 99 name = "%s.%s" % (self.__serviceName, name) 100 return ServiceProxy(self.__serviceURL, name, self.__headers) 101 102 def __call__(self, *args, **kwargs): 103 # Caller can pass in a minimum value of timeout to be used for urlopen 104 # call. Otherwise, the default socket timeout will be used. 105 min_rpc_timeout = kwargs.pop('min_rpc_timeout', None) 106 postdata = json_encoder_class().encode({'method': self.__serviceName, 107 'params': args + (kwargs,), 108 'id': 'jsonrpc'}) 109 request = urllib2.Request(self.__serviceURL, data=postdata, 110 headers=self.__headers) 111 default_timeout = socket.getdefaulttimeout() 112 if not default_timeout: 113 # If default timeout is None, socket will never time out. 114 respdata = urllib2.urlopen(request).read() 115 else: 116 timeout = max(min_rpc_timeout, default_timeout) 117 respdata = urllib2.urlopen(request, timeout=timeout).read() 118 try: 119 resp = decoder.JSONDecoder().decode(respdata) 120 except ValueError: 121 raise JSONRPCException('Error decoding JSON reponse:\n' + respdata) 122 if resp['error'] is not None: 123 raise BuildException(resp['error']) 124 else: 125 return resp['result'] 126