You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

110 lines
4.2 KiB

  1. from nilmdb.utils.printf import *
  2. import threading
  3. import warnings
  4. import types
  5. def verify_proxy(obj_or_type, exception = False, check_thread = True,
  6. check_concurrent = True):
  7. """Wrap the given object or type in a VerifyObjectProxy.
  8. Returns a VerifyObjectProxy that proxies all method calls to the
  9. given object, as well as attribute retrievals.
  10. When calling methods, the following checks are performed. If
  11. exception is True, an exception is raised. Otherwise, a warning
  12. is printed.
  13. check_thread = True # Warn/fail if two different threads call methods.
  14. check_concurrent = True # Warn/fail if two functions are concurrently
  15. # run through this proxy
  16. """
  17. class Namespace(object):
  18. pass
  19. class VerifyCallProxy(object):
  20. def __init__(self, func, parent_namespace):
  21. self.func = func
  22. self.parent_namespace = parent_namespace
  23. def __call__(self, *args, **kwargs):
  24. p = self.parent_namespace
  25. this = threading.current_thread()
  26. try:
  27. callee = self.func.__name__
  28. except AttributeError:
  29. callee = "???"
  30. if p.thread is None:
  31. p.thread = this
  32. p.thread_callee = callee
  33. if check_thread and p.thread != this:
  34. err = sprintf("unsafe threading: %s called %s.%s,"
  35. " but %s called %s.%s",
  36. p.thread.name, p.classname, p.thread_callee,
  37. this.name, p.classname, callee)
  38. if exception:
  39. raise AssertionError(err)
  40. else: # pragma: no cover
  41. warnings.warn(err)
  42. need_concur_unlock = False
  43. if check_concurrent:
  44. if p.concur_lock.acquire(False) == False:
  45. err = sprintf("unsafe concurrency: %s called %s.%s "
  46. "while %s is still in %s.%s",
  47. this.name, p.classname, callee,
  48. p.concur_tname, p.classname, p.concur_callee)
  49. if exception:
  50. raise AssertionError(err)
  51. else: # pragma: no cover
  52. warnings.warn(err)
  53. else:
  54. p.concur_tname = this.name
  55. p.concur_callee = callee
  56. need_concur_unlock = True
  57. try:
  58. ret = self.func(*args, **kwargs)
  59. finally:
  60. if need_concur_unlock:
  61. p.concur_lock.release()
  62. return ret
  63. class VerifyObjectProxy(object):
  64. def __init__(self, obj_or_type, *args, **kwargs):
  65. p = Namespace()
  66. self.__ns = p
  67. p.thread = None
  68. p.thread_callee = None
  69. p.concur_lock = threading.Lock()
  70. p.concur_tname = None
  71. p.concur_callee = None
  72. self.__obj = obj_or_type
  73. try:
  74. if type(obj_or_type) in (types.TypeType, types.ClassType):
  75. p.classname = self.__obj.__name__
  76. else:
  77. p.classname = self.__obj.__class__.__name__
  78. except AttributeError: # pragma: no cover
  79. p.classname = "???"
  80. def __getattr__(self, key):
  81. if key.startswith("_VerifyObjectProxy__"): # pragma: no cover
  82. raise AttributeError
  83. attr = getattr(self.__obj, key)
  84. if not callable(attr):
  85. return VerifyCallProxy(getattr, self.__ns)(self.__obj, key)
  86. return VerifyCallProxy(attr, self.__ns)
  87. def __call__(self, *args, **kwargs):
  88. """Call this to instantiate the type, if a type was passed
  89. to verify_proxy. Otherwise, pass the call through."""
  90. ret = VerifyCallProxy(self.__obj, self.__ns)(*args, **kwargs)
  91. if type(self.__obj) in (types.TypeType, types.ClassType):
  92. # Instantiation
  93. self.__obj = ret
  94. return self
  95. return ret
  96. return VerifyObjectProxy(obj_or_type)