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.
 
 
 

65 lines
2.5 KiB

  1. from nilmdb.utils.printf import *
  2. import sys
  3. import inspect
  4. import decorator
  5. def must_close(errorfile = sys.stderr, wrap_verify = False):
  6. """Class decorator that warns on 'errorfile' at deletion time if
  7. the class's close() member wasn't called.
  8. If 'wrap_verify' is True, every class method is wrapped with a
  9. verifier that will raise AssertionError if the .close() method has
  10. already been called."""
  11. def class_decorator(cls):
  12. # Helper to replace a class method with a wrapper function,
  13. # while maintaining argument specs etc.
  14. def wrap_class_method(wrapper_func):
  15. method = wrapper_func.__name__
  16. if method in cls.__dict__:
  17. orig = getattr(cls, method).im_func
  18. else:
  19. orig = lambda self: None
  20. setattr(cls, method, decorator.decorator(wrapper_func, orig))
  21. @wrap_class_method
  22. def __init__(orig, self, *args, **kwargs):
  23. ret = orig(self, *args, **kwargs)
  24. self.__dict__["_must_close"] = True
  25. self.__dict__["_must_close_initialized"] = True
  26. return ret
  27. @wrap_class_method
  28. def __del__(orig, self, *args, **kwargs):
  29. if "_must_close" in self.__dict__:
  30. fprintf(errorfile, "error: %s.close() wasn't called!\n",
  31. self.__class__.__name__)
  32. return orig(self, *args, **kwargs)
  33. @wrap_class_method
  34. def close(orig, self, *args, **kwargs):
  35. if "_must_close" in self.__dict__:
  36. del self._must_close
  37. return orig(self, *args, **kwargs)
  38. # Optionally wrap all other functions
  39. def verifier(orig, self, *args, **kwargs):
  40. if ("_must_close" not in self.__dict__ and
  41. "_must_close_initialized" in self.__dict__):
  42. raise AssertionError("called " + str(orig) + " after close")
  43. return orig(self, *args, **kwargs)
  44. if wrap_verify:
  45. for (name, method) in inspect.getmembers(cls, inspect.ismethod):
  46. # Skip class methods
  47. if method.__self__ is not None:
  48. continue
  49. # Skip some methods
  50. if name in [ "__del__", "__init__" ]:
  51. continue
  52. # Set up wrapper
  53. setattr(cls, name, decorator.decorator(verifier,
  54. method.im_func))
  55. return cls
  56. return class_decorator