contextlib.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. """Utilities for with-statement contexts. See PEP 343."""
  2. import sys
  3. from functools import wraps
  4. from warnings import warn
  5. __all__ = ["contextmanager", "nested", "closing"]
  6. class GeneratorContextManager(object):
  7. """Helper for @contextmanager decorator."""
  8. def __init__(self, gen):
  9. self.gen = gen
  10. def __enter__(self):
  11. try:
  12. return self.gen.next()
  13. except StopIteration:
  14. raise RuntimeError("generator didn't yield")
  15. def __exit__(self, type, value, traceback):
  16. if type is None:
  17. try:
  18. self.gen.next()
  19. except StopIteration:
  20. return
  21. else:
  22. raise RuntimeError("generator didn't stop")
  23. else:
  24. if value is None:
  25. # Need to force instantiation so we can reliably
  26. # tell if we get the same exception back
  27. value = type()
  28. try:
  29. self.gen.throw(type, value, traceback)
  30. raise RuntimeError("generator didn't stop after throw()")
  31. except StopIteration, exc:
  32. # Suppress the exception *unless* it's the same exception that
  33. # was passed to throw(). This prevents a StopIteration
  34. # raised inside the "with" statement from being suppressed
  35. return exc is not value
  36. except:
  37. # only re-raise if it's *not* the exception that was
  38. # passed to throw(), because __exit__() must not raise
  39. # an exception unless __exit__() itself failed. But throw()
  40. # has to raise the exception to signal propagation, so this
  41. # fixes the impedance mismatch between the throw() protocol
  42. # and the __exit__() protocol.
  43. #
  44. if sys.exc_info()[1] is not value:
  45. raise
  46. def contextmanager(func):
  47. """@contextmanager decorator.
  48. Typical usage:
  49. @contextmanager
  50. def some_generator(<arguments>):
  51. <setup>
  52. try:
  53. yield <value>
  54. finally:
  55. <cleanup>
  56. This makes this:
  57. with some_generator(<arguments>) as <variable>:
  58. <body>
  59. equivalent to this:
  60. <setup>
  61. try:
  62. <variable> = <value>
  63. <body>
  64. finally:
  65. <cleanup>
  66. """
  67. @wraps(func)
  68. def helper(*args, **kwds):
  69. return GeneratorContextManager(func(*args, **kwds))
  70. return helper
  71. @contextmanager
  72. def nested(*managers):
  73. """Combine multiple context managers into a single nested context manager.
  74. This function has been deprecated in favour of the multiple manager form
  75. of the with statement.
  76. The one advantage of this function over the multiple manager form of the
  77. with statement is that argument unpacking allows it to be
  78. used with a variable number of context managers as follows:
  79. with nested(*managers):
  80. do_something()
  81. """
  82. warn("With-statements now directly support multiple context managers",
  83. DeprecationWarning, 3)
  84. exits = []
  85. vars = []
  86. exc = (None, None, None)
  87. try:
  88. for mgr in managers:
  89. exit = mgr.__exit__
  90. enter = mgr.__enter__
  91. vars.append(enter())
  92. exits.append(exit)
  93. yield vars
  94. except:
  95. exc = sys.exc_info()
  96. finally:
  97. while exits:
  98. exit = exits.pop()
  99. try:
  100. if exit(*exc):
  101. exc = (None, None, None)
  102. except:
  103. exc = sys.exc_info()
  104. if exc != (None, None, None):
  105. # Don't rely on sys.exc_info() still containing
  106. # the right information. Another exception may
  107. # have been raised and caught by an exit method
  108. raise exc[0], exc[1], exc[2]
  109. class closing(object):
  110. """Context to automatically close something at the end of a block.
  111. Code like this:
  112. with closing(<module>.open(<arguments>)) as f:
  113. <block>
  114. is equivalent to this:
  115. f = <module>.open(<arguments>)
  116. try:
  117. <block>
  118. finally:
  119. f.close()
  120. """
  121. def __init__(self, thing):
  122. self.thing = thing
  123. def __enter__(self):
  124. return self.thing
  125. def __exit__(self, *exc_info):
  126. self.thing.close()