Skip to content

Instantly share code, notes, and snippets.

@grafuls
Last active August 29, 2015 14:24
Show Gist options
  • Save grafuls/b56522cec980ac4cd79b to your computer and use it in GitHub Desktop.
Save grafuls/b56522cec980ac4cd79b to your computer and use it in GitHub Desktop.
Virtual method implementation example - some magic to bind an XML-RPC method to an RPC server
class VirtualMethod(object):
# some magic to bind an XML-RPC method to an RPC server.
# supports "nested" methods (e.g. examples.getStateName)
# supports named arguments (if server does)
def __init__(self, func, name):
self.__func = func
self.__name = name
def __getattr__(self, name):
return type(self)(self.__func, "%s.%s" % (self.__name, name))
def __call__(self, *args, **opts):
return self.__func(self.__name,args,opts)
class ClientSession(object):
def __init__(self, opts=None):
if opts == None:
opts = {}
else:
opts = opts.copy()
self.opts = opts
self.multicall = False
self._calls = []
def __getattr__(self,name):
return VirtualMethod(self._callMethod,name)
def _callMethod(self, name, args, kwargs=None):
"""Make a call to the hub with retries and other niceties"""
if self.multicall:
if kwargs is None:
kwargs = {}
args = encode_args(*args, **kwargs)
self._calls.append({'methodName': name, 'params': args})
return MultiCallInProgress
else:
handler, headers, request = self._prepCall(name, args, kwargs)
tries = 0
self.retries = 0
debug = self.opts.get('debug',False)
max_retries = self.opts.get('max_retries',30)
interval = self.opts.get('retry_interval',20)
while True:
tries += 1
self.retries += 1
try:
return self._sendCall(handler, headers, request)
#basically, we want to retry on most errors, with a few exceptions
# - faults (this means the call completed and failed)
# - SystemExit, KeyboardInterrupt
# note that, for logged-in sessions the server should tell us (via a RetryError fault)
# if the call cannot be retried. For non-logged-in sessions, all calls should be read-only
# and hence retryable.
except Fault, fault:
#try to convert the fault to a known exception
err = convertFault(fault)
if isinstance(err, ServerOffline):
if self.opts.get('offline_retry',False):
secs = self.opts.get('offline_retry_interval', interval)
self.logger.debug("Server offline. Retrying in %i seconds", secs)
time.sleep(secs)
#reset try count - this isn't a typical error, this is a running server
#correctly reporting an outage
tries = 0
continue
raise err
except (SystemExit, KeyboardInterrupt):
#(depending on the python version, these may or may not be subclasses of Exception)
raise
except Exception, e:
if not self.logged_in:
#in the past, non-logged-in sessions did not retry. For compatibility purposes
#this behavior is governed by the anon_retry opt.
if not self.opts.get('anon_retry',False):
raise
if tries > max_retries:
raise
#otherwise keep retrying
if self.logger.isEnabledFor(logging.DEBUG):
tb_str = ''.join(traceback.format_exception(*sys.exc_info()))
self.logger.debug(tb_str)
self.logger.info("Try #%s for call %s (%s) failed: %s", tries, self.callnum, name, e)
if tries > 1:
# first retry is immediate, after that we honor retry_interval
time.sleep(interval)
#not reached
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment