""" Copyright (c) 2007 Jan-Klaas Kollhof This file is part of jsonrpc. jsonrpc is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this software; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA """ import os import socket import urllib2 from autotest_lib.client.common_lib import error as exceptions from json import decoder from json import encoder as json_encoder json_encoder_class = json_encoder.JSONEncoder # Try to upgrade to the Django JSON encoder. It uses the standard json encoder # but can handle DateTime try: # See http://crbug.com/418022 too see why the try except is needed here. from django import conf as django_conf # The serializers can't be imported if django isn't configured. # Using try except here doesn't work, as test_that initializes it's own # django environment (setup_django_lite_environment) which raises import # errors if the django dbutils have been previously imported, as importing # them leaves some state behind. # This the variable name must not be undefined or empty string. if os.environ.get(django_conf.ENVIRONMENT_VARIABLE, None): from django.core.serializers import json as django_encoder json_encoder_class = django_encoder.DjangoJSONEncoder except ImportError: pass class JSONRPCException(Exception): pass class ValidationError(JSONRPCException): """Raised when the RPC is malformed.""" def __init__(self, error, formatted_message): """Constructor. @param error: a dict of error info like so: {error['name']: 'ErrorKind', error['message']: 'Pithy error description.', error['traceback']: 'Multi-line stack trace'} @formatted_message: string representation of this exception. """ self.problem_keys = eval(error['message']) self.traceback = error['traceback'] super(ValidationError, self).__init__(formatted_message) def BuildException(error): """Exception factory. Given a dict of error info, determine which subclass of JSONRPCException to build and return. If can't determine the right one, just return a JSONRPCException with a pretty-printed error string. @param error: a dict of error info like so: {error['name']: 'ErrorKind', error['message']: 'Pithy error description.', error['traceback']: 'Multi-line stack trace'} """ error_message = '%(name)s: %(message)s\n%(traceback)s' % error for cls in JSONRPCException.__subclasses__(): if error['name'] == cls.__name__: return cls(error, error_message) for cls in (exceptions.CrosDynamicSuiteException.__subclasses__() + exceptions.RPCException.__subclasses__()): if error['name'] == cls.__name__: return cls(error_message) return JSONRPCException(error_message) class ServiceProxy(object): def __init__(self, serviceURL, serviceName=None, headers=None): self.__serviceURL = serviceURL self.__serviceName = serviceName self.__headers = headers or {} def __getattr__(self, name): if self.__serviceName is not None: name = "%s.%s" % (self.__serviceName, name) return ServiceProxy(self.__serviceURL, name, self.__headers) def __call__(self, *args, **kwargs): # Caller can pass in a minimum value of timeout to be used for urlopen # call. Otherwise, the default socket timeout will be used. min_rpc_timeout = kwargs.pop('min_rpc_timeout', None) postdata = json_encoder_class().encode({'method': self.__serviceName, 'params': args + (kwargs,), 'id': 'jsonrpc'}) request = urllib2.Request(self.__serviceURL, data=postdata, headers=self.__headers) default_timeout = socket.getdefaulttimeout() if not default_timeout: # If default timeout is None, socket will never time out. respdata = urllib2.urlopen(request).read() else: timeout = max(min_rpc_timeout, default_timeout) respdata = urllib2.urlopen(request, timeout=timeout).read() try: resp = decoder.JSONDecoder().decode(respdata) except ValueError: raise JSONRPCException('Error decoding JSON reponse:\n' + respdata) if resp['error'] is not None: raise BuildException(resp['error']) else: return resp['result']