Skip to content

Instantly share code, notes, and snippets.

Created July 5, 2016 07:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anonymous/789e66b8ca009dfc0ee53df4e9d584fc to your computer and use it in GitHub Desktop.
Save anonymous/789e66b8ca009dfc0ee53df4e9d584fc to your computer and use it in GitHub Desktop.
Wrapping of python's incoherent error messages
class RpcException(Exception):
""" An RPC-specific exception. Details should be in the message """
pass
def rpc_call(target, data, fn_prefix='_cmd_'):
""" Common logic for exposing several methods from a class as RPC
(at the level after the data-parsing).
Note: likely only works in CPython. """
if not isinstance(data, dict):
raise RpcException('`data` is not a dict')
try:
cmd = data.pop('cmd')
except KeyError as e:
raise RpcException('`cmd` is not specified in the data', e)
try:
fn = getattr(target, '%s%s' % (fn_prefix, cmd))
except AttributeError as e:
raise RpcException('command %r not found' % (cmd,), e)
_log.debug("Calling: %r, %r", fn, data)
try:
return fn(**data)
except TypeError as e:
# More-sane-than-default processing of a case `parameter ... was not specified` (py2)
if fn.func_code.co_flags & 0x04:
# it accepts `*ar`, so not the case
raise
f_vars = fn.func_code.co_varnames
f_defvars_count = len(fn.func_defaults)
f_posvars = f_vars[:-f_defvars_count]
extra_args = list(set(data.keys()) - set(f_vars))
# TODO: `inspect.getargspec`
# XXX: it catches `self` in a bound method as required. (also, classmethods?)
missing_args = list(set(f_posvars) - set(data.keys()))
if missing_args: # is the case, raise it verbosely.
msg = "Required argument(s) not specified: %s" % (
', '.join(missing_args),)
if extra_args:
msg += "; additionally, there are extraneous arguments: %s" % (
', '.join(extra_args))
raise TypeError(msg, e)
raise
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment