_gcutils.py 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. """
  2. Module for testing automatic garbage collection of objects
  3. .. autosummary::
  4. :toctree: generated/
  5. set_gc_state - enable or disable garbage collection
  6. gc_state - context manager for given state of garbage collector
  7. assert_deallocated - context manager to check for circular references on object
  8. """
  9. import weakref
  10. import gc
  11. from contextlib import contextmanager
  12. from platform import python_implementation
  13. __all__ = ['set_gc_state', 'gc_state', 'assert_deallocated']
  14. IS_PYPY = python_implementation() == 'PyPy'
  15. class ReferenceError(AssertionError):
  16. pass
  17. def set_gc_state(state):
  18. """ Set status of garbage collector """
  19. if gc.isenabled() == state:
  20. return
  21. if state:
  22. gc.enable()
  23. else:
  24. gc.disable()
  25. @contextmanager
  26. def gc_state(state):
  27. """ Context manager to set state of garbage collector to `state`
  28. Parameters
  29. ----------
  30. state : bool
  31. True for gc enabled, False for disabled
  32. Examples
  33. --------
  34. >>> with gc_state(False):
  35. ... assert not gc.isenabled()
  36. >>> with gc_state(True):
  37. ... assert gc.isenabled()
  38. """
  39. orig_state = gc.isenabled()
  40. set_gc_state(state)
  41. yield
  42. set_gc_state(orig_state)
  43. @contextmanager
  44. def assert_deallocated(func, *args, **kwargs):
  45. """Context manager to check that object is deallocated
  46. This is useful for checking that an object can be freed directly by
  47. reference counting, without requiring gc to break reference cycles.
  48. GC is disabled inside the context manager.
  49. This check is not available on PyPy.
  50. Parameters
  51. ----------
  52. func : callable
  53. Callable to create object to check
  54. \\*args : sequence
  55. positional arguments to `func` in order to create object to check
  56. \\*\\*kwargs : dict
  57. keyword arguments to `func` in order to create object to check
  58. Examples
  59. --------
  60. >>> class C: pass
  61. >>> with assert_deallocated(C) as c:
  62. ... # do something
  63. ... del c
  64. >>> class C:
  65. ... def __init__(self):
  66. ... self._circular = self # Make circular reference
  67. >>> with assert_deallocated(C) as c: #doctest: +IGNORE_EXCEPTION_DETAIL
  68. ... # do something
  69. ... del c
  70. Traceback (most recent call last):
  71. ...
  72. ReferenceError: Remaining reference(s) to object
  73. """
  74. if IS_PYPY:
  75. raise RuntimeError("assert_deallocated is unavailable on PyPy")
  76. with gc_state(False):
  77. obj = func(*args, **kwargs)
  78. ref = weakref.ref(obj)
  79. yield obj
  80. del obj
  81. if ref() is not None:
  82. raise ReferenceError("Remaining reference(s) to object")