robustapply.py 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  1. """Robust apply mechanism
  2. Provides a function "call", which can sort out
  3. what arguments a given callable object can take,
  4. and subset the given arguments to match only
  5. those which are acceptable.
  6. """
  7. import sys
  8. if sys.hexversion >= 0x3000000:
  9. im_func = '__func__'
  10. im_self = '__self__'
  11. im_code = '__code__'
  12. func_code = '__code__'
  13. else:
  14. im_func = 'im_func'
  15. im_self = 'im_self'
  16. im_code = 'im_code'
  17. func_code = 'func_code'
  18. def function(receiver):
  19. """Get function-like callable object for given receiver
  20. returns (function_or_method, codeObject, fromMethod)
  21. If fromMethod is true, then the callable already
  22. has its first argument bound
  23. """
  24. if hasattr(receiver, '__call__'):
  25. # Reassign receiver to the actual method that will be called.
  26. if hasattr(receiver.__call__, im_func) or hasattr(receiver.__call__,
  27. im_code):
  28. receiver = receiver.__call__
  29. if hasattr(receiver, im_func):
  30. # an instance-method...
  31. return receiver, getattr(getattr(receiver, im_func), func_code), 1
  32. elif not hasattr(receiver, func_code):
  33. raise ValueError('unknown reciever type %s %s' % (receiver,
  34. type(receiver)))
  35. return receiver, getattr(receiver, func_code), 0
  36. def robustApply(receiver, *arguments, **named):
  37. """Call receiver with arguments and an appropriate subset of named
  38. """
  39. receiver, codeObject, startIndex = function(receiver)
  40. acceptable = codeObject.co_varnames[startIndex+len(arguments):codeObject.co_argcount]
  41. for name in codeObject.co_varnames[startIndex:startIndex+len(arguments)]:
  42. if name in named:
  43. raise TypeError(
  44. """Argument %r specified both positionally and as a keyword"""
  45. """ for calling %r""" % (name, receiver)
  46. )
  47. if not (codeObject.co_flags & 8):
  48. # fc does not have a **kwds type parameter, therefore
  49. # remove unacceptable arguments.
  50. for arg in named.keys():
  51. if arg not in acceptable:
  52. del named[arg]
  53. return receiver(*arguments, **named)