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