1*cda5da8dSAndroid Build Coastguard Worker"""Utilities for with-statement contexts. See PEP 343.""" 2*cda5da8dSAndroid Build Coastguard Workerimport abc 3*cda5da8dSAndroid Build Coastguard Workerimport os 4*cda5da8dSAndroid Build Coastguard Workerimport sys 5*cda5da8dSAndroid Build Coastguard Workerimport _collections_abc 6*cda5da8dSAndroid Build Coastguard Workerfrom collections import deque 7*cda5da8dSAndroid Build Coastguard Workerfrom functools import wraps 8*cda5da8dSAndroid Build Coastguard Workerfrom types import MethodType, GenericAlias 9*cda5da8dSAndroid Build Coastguard Worker 10*cda5da8dSAndroid Build Coastguard Worker__all__ = ["asynccontextmanager", "contextmanager", "closing", "nullcontext", 11*cda5da8dSAndroid Build Coastguard Worker "AbstractContextManager", "AbstractAsyncContextManager", 12*cda5da8dSAndroid Build Coastguard Worker "AsyncExitStack", "ContextDecorator", "ExitStack", 13*cda5da8dSAndroid Build Coastguard Worker "redirect_stdout", "redirect_stderr", "suppress", "aclosing", 14*cda5da8dSAndroid Build Coastguard Worker "chdir"] 15*cda5da8dSAndroid Build Coastguard Worker 16*cda5da8dSAndroid Build Coastguard Worker 17*cda5da8dSAndroid Build Coastguard Workerclass AbstractContextManager(abc.ABC): 18*cda5da8dSAndroid Build Coastguard Worker 19*cda5da8dSAndroid Build Coastguard Worker """An abstract base class for context managers.""" 20*cda5da8dSAndroid Build Coastguard Worker 21*cda5da8dSAndroid Build Coastguard Worker __class_getitem__ = classmethod(GenericAlias) 22*cda5da8dSAndroid Build Coastguard Worker 23*cda5da8dSAndroid Build Coastguard Worker def __enter__(self): 24*cda5da8dSAndroid Build Coastguard Worker """Return `self` upon entering the runtime context.""" 25*cda5da8dSAndroid Build Coastguard Worker return self 26*cda5da8dSAndroid Build Coastguard Worker 27*cda5da8dSAndroid Build Coastguard Worker @abc.abstractmethod 28*cda5da8dSAndroid Build Coastguard Worker def __exit__(self, exc_type, exc_value, traceback): 29*cda5da8dSAndroid Build Coastguard Worker """Raise any exception triggered within the runtime context.""" 30*cda5da8dSAndroid Build Coastguard Worker return None 31*cda5da8dSAndroid Build Coastguard Worker 32*cda5da8dSAndroid Build Coastguard Worker @classmethod 33*cda5da8dSAndroid Build Coastguard Worker def __subclasshook__(cls, C): 34*cda5da8dSAndroid Build Coastguard Worker if cls is AbstractContextManager: 35*cda5da8dSAndroid Build Coastguard Worker return _collections_abc._check_methods(C, "__enter__", "__exit__") 36*cda5da8dSAndroid Build Coastguard Worker return NotImplemented 37*cda5da8dSAndroid Build Coastguard Worker 38*cda5da8dSAndroid Build Coastguard Worker 39*cda5da8dSAndroid Build Coastguard Workerclass AbstractAsyncContextManager(abc.ABC): 40*cda5da8dSAndroid Build Coastguard Worker 41*cda5da8dSAndroid Build Coastguard Worker """An abstract base class for asynchronous context managers.""" 42*cda5da8dSAndroid Build Coastguard Worker 43*cda5da8dSAndroid Build Coastguard Worker __class_getitem__ = classmethod(GenericAlias) 44*cda5da8dSAndroid Build Coastguard Worker 45*cda5da8dSAndroid Build Coastguard Worker async def __aenter__(self): 46*cda5da8dSAndroid Build Coastguard Worker """Return `self` upon entering the runtime context.""" 47*cda5da8dSAndroid Build Coastguard Worker return self 48*cda5da8dSAndroid Build Coastguard Worker 49*cda5da8dSAndroid Build Coastguard Worker @abc.abstractmethod 50*cda5da8dSAndroid Build Coastguard Worker async def __aexit__(self, exc_type, exc_value, traceback): 51*cda5da8dSAndroid Build Coastguard Worker """Raise any exception triggered within the runtime context.""" 52*cda5da8dSAndroid Build Coastguard Worker return None 53*cda5da8dSAndroid Build Coastguard Worker 54*cda5da8dSAndroid Build Coastguard Worker @classmethod 55*cda5da8dSAndroid Build Coastguard Worker def __subclasshook__(cls, C): 56*cda5da8dSAndroid Build Coastguard Worker if cls is AbstractAsyncContextManager: 57*cda5da8dSAndroid Build Coastguard Worker return _collections_abc._check_methods(C, "__aenter__", 58*cda5da8dSAndroid Build Coastguard Worker "__aexit__") 59*cda5da8dSAndroid Build Coastguard Worker return NotImplemented 60*cda5da8dSAndroid Build Coastguard Worker 61*cda5da8dSAndroid Build Coastguard Worker 62*cda5da8dSAndroid Build Coastguard Workerclass ContextDecorator(object): 63*cda5da8dSAndroid Build Coastguard Worker "A base class or mixin that enables context managers to work as decorators." 64*cda5da8dSAndroid Build Coastguard Worker 65*cda5da8dSAndroid Build Coastguard Worker def _recreate_cm(self): 66*cda5da8dSAndroid Build Coastguard Worker """Return a recreated instance of self. 67*cda5da8dSAndroid Build Coastguard Worker 68*cda5da8dSAndroid Build Coastguard Worker Allows an otherwise one-shot context manager like 69*cda5da8dSAndroid Build Coastguard Worker _GeneratorContextManager to support use as 70*cda5da8dSAndroid Build Coastguard Worker a decorator via implicit recreation. 71*cda5da8dSAndroid Build Coastguard Worker 72*cda5da8dSAndroid Build Coastguard Worker This is a private interface just for _GeneratorContextManager. 73*cda5da8dSAndroid Build Coastguard Worker See issue #11647 for details. 74*cda5da8dSAndroid Build Coastguard Worker """ 75*cda5da8dSAndroid Build Coastguard Worker return self 76*cda5da8dSAndroid Build Coastguard Worker 77*cda5da8dSAndroid Build Coastguard Worker def __call__(self, func): 78*cda5da8dSAndroid Build Coastguard Worker @wraps(func) 79*cda5da8dSAndroid Build Coastguard Worker def inner(*args, **kwds): 80*cda5da8dSAndroid Build Coastguard Worker with self._recreate_cm(): 81*cda5da8dSAndroid Build Coastguard Worker return func(*args, **kwds) 82*cda5da8dSAndroid Build Coastguard Worker return inner 83*cda5da8dSAndroid Build Coastguard Worker 84*cda5da8dSAndroid Build Coastguard Worker 85*cda5da8dSAndroid Build Coastguard Workerclass AsyncContextDecorator(object): 86*cda5da8dSAndroid Build Coastguard Worker "A base class or mixin that enables async context managers to work as decorators." 87*cda5da8dSAndroid Build Coastguard Worker 88*cda5da8dSAndroid Build Coastguard Worker def _recreate_cm(self): 89*cda5da8dSAndroid Build Coastguard Worker """Return a recreated instance of self. 90*cda5da8dSAndroid Build Coastguard Worker """ 91*cda5da8dSAndroid Build Coastguard Worker return self 92*cda5da8dSAndroid Build Coastguard Worker 93*cda5da8dSAndroid Build Coastguard Worker def __call__(self, func): 94*cda5da8dSAndroid Build Coastguard Worker @wraps(func) 95*cda5da8dSAndroid Build Coastguard Worker async def inner(*args, **kwds): 96*cda5da8dSAndroid Build Coastguard Worker async with self._recreate_cm(): 97*cda5da8dSAndroid Build Coastguard Worker return await func(*args, **kwds) 98*cda5da8dSAndroid Build Coastguard Worker return inner 99*cda5da8dSAndroid Build Coastguard Worker 100*cda5da8dSAndroid Build Coastguard Worker 101*cda5da8dSAndroid Build Coastguard Workerclass _GeneratorContextManagerBase: 102*cda5da8dSAndroid Build Coastguard Worker """Shared functionality for @contextmanager and @asynccontextmanager.""" 103*cda5da8dSAndroid Build Coastguard Worker 104*cda5da8dSAndroid Build Coastguard Worker def __init__(self, func, args, kwds): 105*cda5da8dSAndroid Build Coastguard Worker self.gen = func(*args, **kwds) 106*cda5da8dSAndroid Build Coastguard Worker self.func, self.args, self.kwds = func, args, kwds 107*cda5da8dSAndroid Build Coastguard Worker # Issue 19330: ensure context manager instances have good docstrings 108*cda5da8dSAndroid Build Coastguard Worker doc = getattr(func, "__doc__", None) 109*cda5da8dSAndroid Build Coastguard Worker if doc is None: 110*cda5da8dSAndroid Build Coastguard Worker doc = type(self).__doc__ 111*cda5da8dSAndroid Build Coastguard Worker self.__doc__ = doc 112*cda5da8dSAndroid Build Coastguard Worker # Unfortunately, this still doesn't provide good help output when 113*cda5da8dSAndroid Build Coastguard Worker # inspecting the created context manager instances, since pydoc 114*cda5da8dSAndroid Build Coastguard Worker # currently bypasses the instance docstring and shows the docstring 115*cda5da8dSAndroid Build Coastguard Worker # for the class instead. 116*cda5da8dSAndroid Build Coastguard Worker # See http://bugs.python.org/issue19404 for more details. 117*cda5da8dSAndroid Build Coastguard Worker 118*cda5da8dSAndroid Build Coastguard Worker def _recreate_cm(self): 119*cda5da8dSAndroid Build Coastguard Worker # _GCMB instances are one-shot context managers, so the 120*cda5da8dSAndroid Build Coastguard Worker # CM must be recreated each time a decorated function is 121*cda5da8dSAndroid Build Coastguard Worker # called 122*cda5da8dSAndroid Build Coastguard Worker return self.__class__(self.func, self.args, self.kwds) 123*cda5da8dSAndroid Build Coastguard Worker 124*cda5da8dSAndroid Build Coastguard Worker 125*cda5da8dSAndroid Build Coastguard Workerclass _GeneratorContextManager( 126*cda5da8dSAndroid Build Coastguard Worker _GeneratorContextManagerBase, 127*cda5da8dSAndroid Build Coastguard Worker AbstractContextManager, 128*cda5da8dSAndroid Build Coastguard Worker ContextDecorator, 129*cda5da8dSAndroid Build Coastguard Worker): 130*cda5da8dSAndroid Build Coastguard Worker """Helper for @contextmanager decorator.""" 131*cda5da8dSAndroid Build Coastguard Worker 132*cda5da8dSAndroid Build Coastguard Worker def __enter__(self): 133*cda5da8dSAndroid Build Coastguard Worker # do not keep args and kwds alive unnecessarily 134*cda5da8dSAndroid Build Coastguard Worker # they are only needed for recreation, which is not possible anymore 135*cda5da8dSAndroid Build Coastguard Worker del self.args, self.kwds, self.func 136*cda5da8dSAndroid Build Coastguard Worker try: 137*cda5da8dSAndroid Build Coastguard Worker return next(self.gen) 138*cda5da8dSAndroid Build Coastguard Worker except StopIteration: 139*cda5da8dSAndroid Build Coastguard Worker raise RuntimeError("generator didn't yield") from None 140*cda5da8dSAndroid Build Coastguard Worker 141*cda5da8dSAndroid Build Coastguard Worker def __exit__(self, typ, value, traceback): 142*cda5da8dSAndroid Build Coastguard Worker if typ is None: 143*cda5da8dSAndroid Build Coastguard Worker try: 144*cda5da8dSAndroid Build Coastguard Worker next(self.gen) 145*cda5da8dSAndroid Build Coastguard Worker except StopIteration: 146*cda5da8dSAndroid Build Coastguard Worker return False 147*cda5da8dSAndroid Build Coastguard Worker else: 148*cda5da8dSAndroid Build Coastguard Worker raise RuntimeError("generator didn't stop") 149*cda5da8dSAndroid Build Coastguard Worker else: 150*cda5da8dSAndroid Build Coastguard Worker if value is None: 151*cda5da8dSAndroid Build Coastguard Worker # Need to force instantiation so we can reliably 152*cda5da8dSAndroid Build Coastguard Worker # tell if we get the same exception back 153*cda5da8dSAndroid Build Coastguard Worker value = typ() 154*cda5da8dSAndroid Build Coastguard Worker try: 155*cda5da8dSAndroid Build Coastguard Worker self.gen.throw(typ, value, traceback) 156*cda5da8dSAndroid Build Coastguard Worker except StopIteration as exc: 157*cda5da8dSAndroid Build Coastguard Worker # Suppress StopIteration *unless* it's the same exception that 158*cda5da8dSAndroid Build Coastguard Worker # was passed to throw(). This prevents a StopIteration 159*cda5da8dSAndroid Build Coastguard Worker # raised inside the "with" statement from being suppressed. 160*cda5da8dSAndroid Build Coastguard Worker return exc is not value 161*cda5da8dSAndroid Build Coastguard Worker except RuntimeError as exc: 162*cda5da8dSAndroid Build Coastguard Worker # Don't re-raise the passed in exception. (issue27122) 163*cda5da8dSAndroid Build Coastguard Worker if exc is value: 164*cda5da8dSAndroid Build Coastguard Worker exc.__traceback__ = traceback 165*cda5da8dSAndroid Build Coastguard Worker return False 166*cda5da8dSAndroid Build Coastguard Worker # Avoid suppressing if a StopIteration exception 167*cda5da8dSAndroid Build Coastguard Worker # was passed to throw() and later wrapped into a RuntimeError 168*cda5da8dSAndroid Build Coastguard Worker # (see PEP 479 for sync generators; async generators also 169*cda5da8dSAndroid Build Coastguard Worker # have this behavior). But do this only if the exception wrapped 170*cda5da8dSAndroid Build Coastguard Worker # by the RuntimeError is actually Stop(Async)Iteration (see 171*cda5da8dSAndroid Build Coastguard Worker # issue29692). 172*cda5da8dSAndroid Build Coastguard Worker if ( 173*cda5da8dSAndroid Build Coastguard Worker isinstance(value, StopIteration) 174*cda5da8dSAndroid Build Coastguard Worker and exc.__cause__ is value 175*cda5da8dSAndroid Build Coastguard Worker ): 176*cda5da8dSAndroid Build Coastguard Worker value.__traceback__ = traceback 177*cda5da8dSAndroid Build Coastguard Worker return False 178*cda5da8dSAndroid Build Coastguard Worker raise 179*cda5da8dSAndroid Build Coastguard Worker except BaseException as exc: 180*cda5da8dSAndroid Build Coastguard Worker # only re-raise if it's *not* the exception that was 181*cda5da8dSAndroid Build Coastguard Worker # passed to throw(), because __exit__() must not raise 182*cda5da8dSAndroid Build Coastguard Worker # an exception unless __exit__() itself failed. But throw() 183*cda5da8dSAndroid Build Coastguard Worker # has to raise the exception to signal propagation, so this 184*cda5da8dSAndroid Build Coastguard Worker # fixes the impedance mismatch between the throw() protocol 185*cda5da8dSAndroid Build Coastguard Worker # and the __exit__() protocol. 186*cda5da8dSAndroid Build Coastguard Worker if exc is not value: 187*cda5da8dSAndroid Build Coastguard Worker raise 188*cda5da8dSAndroid Build Coastguard Worker exc.__traceback__ = traceback 189*cda5da8dSAndroid Build Coastguard Worker return False 190*cda5da8dSAndroid Build Coastguard Worker raise RuntimeError("generator didn't stop after throw()") 191*cda5da8dSAndroid Build Coastguard Worker 192*cda5da8dSAndroid Build Coastguard Workerclass _AsyncGeneratorContextManager( 193*cda5da8dSAndroid Build Coastguard Worker _GeneratorContextManagerBase, 194*cda5da8dSAndroid Build Coastguard Worker AbstractAsyncContextManager, 195*cda5da8dSAndroid Build Coastguard Worker AsyncContextDecorator, 196*cda5da8dSAndroid Build Coastguard Worker): 197*cda5da8dSAndroid Build Coastguard Worker """Helper for @asynccontextmanager decorator.""" 198*cda5da8dSAndroid Build Coastguard Worker 199*cda5da8dSAndroid Build Coastguard Worker async def __aenter__(self): 200*cda5da8dSAndroid Build Coastguard Worker # do not keep args and kwds alive unnecessarily 201*cda5da8dSAndroid Build Coastguard Worker # they are only needed for recreation, which is not possible anymore 202*cda5da8dSAndroid Build Coastguard Worker del self.args, self.kwds, self.func 203*cda5da8dSAndroid Build Coastguard Worker try: 204*cda5da8dSAndroid Build Coastguard Worker return await anext(self.gen) 205*cda5da8dSAndroid Build Coastguard Worker except StopAsyncIteration: 206*cda5da8dSAndroid Build Coastguard Worker raise RuntimeError("generator didn't yield") from None 207*cda5da8dSAndroid Build Coastguard Worker 208*cda5da8dSAndroid Build Coastguard Worker async def __aexit__(self, typ, value, traceback): 209*cda5da8dSAndroid Build Coastguard Worker if typ is None: 210*cda5da8dSAndroid Build Coastguard Worker try: 211*cda5da8dSAndroid Build Coastguard Worker await anext(self.gen) 212*cda5da8dSAndroid Build Coastguard Worker except StopAsyncIteration: 213*cda5da8dSAndroid Build Coastguard Worker return False 214*cda5da8dSAndroid Build Coastguard Worker else: 215*cda5da8dSAndroid Build Coastguard Worker raise RuntimeError("generator didn't stop") 216*cda5da8dSAndroid Build Coastguard Worker else: 217*cda5da8dSAndroid Build Coastguard Worker if value is None: 218*cda5da8dSAndroid Build Coastguard Worker # Need to force instantiation so we can reliably 219*cda5da8dSAndroid Build Coastguard Worker # tell if we get the same exception back 220*cda5da8dSAndroid Build Coastguard Worker value = typ() 221*cda5da8dSAndroid Build Coastguard Worker try: 222*cda5da8dSAndroid Build Coastguard Worker await self.gen.athrow(typ, value, traceback) 223*cda5da8dSAndroid Build Coastguard Worker except StopAsyncIteration as exc: 224*cda5da8dSAndroid Build Coastguard Worker # Suppress StopIteration *unless* it's the same exception that 225*cda5da8dSAndroid Build Coastguard Worker # was passed to throw(). This prevents a StopIteration 226*cda5da8dSAndroid Build Coastguard Worker # raised inside the "with" statement from being suppressed. 227*cda5da8dSAndroid Build Coastguard Worker return exc is not value 228*cda5da8dSAndroid Build Coastguard Worker except RuntimeError as exc: 229*cda5da8dSAndroid Build Coastguard Worker # Don't re-raise the passed in exception. (issue27122) 230*cda5da8dSAndroid Build Coastguard Worker if exc is value: 231*cda5da8dSAndroid Build Coastguard Worker exc.__traceback__ = traceback 232*cda5da8dSAndroid Build Coastguard Worker return False 233*cda5da8dSAndroid Build Coastguard Worker # Avoid suppressing if a Stop(Async)Iteration exception 234*cda5da8dSAndroid Build Coastguard Worker # was passed to athrow() and later wrapped into a RuntimeError 235*cda5da8dSAndroid Build Coastguard Worker # (see PEP 479 for sync generators; async generators also 236*cda5da8dSAndroid Build Coastguard Worker # have this behavior). But do this only if the exception wrapped 237*cda5da8dSAndroid Build Coastguard Worker # by the RuntimeError is actually Stop(Async)Iteration (see 238*cda5da8dSAndroid Build Coastguard Worker # issue29692). 239*cda5da8dSAndroid Build Coastguard Worker if ( 240*cda5da8dSAndroid Build Coastguard Worker isinstance(value, (StopIteration, StopAsyncIteration)) 241*cda5da8dSAndroid Build Coastguard Worker and exc.__cause__ is value 242*cda5da8dSAndroid Build Coastguard Worker ): 243*cda5da8dSAndroid Build Coastguard Worker value.__traceback__ = traceback 244*cda5da8dSAndroid Build Coastguard Worker return False 245*cda5da8dSAndroid Build Coastguard Worker raise 246*cda5da8dSAndroid Build Coastguard Worker except BaseException as exc: 247*cda5da8dSAndroid Build Coastguard Worker # only re-raise if it's *not* the exception that was 248*cda5da8dSAndroid Build Coastguard Worker # passed to throw(), because __exit__() must not raise 249*cda5da8dSAndroid Build Coastguard Worker # an exception unless __exit__() itself failed. But throw() 250*cda5da8dSAndroid Build Coastguard Worker # has to raise the exception to signal propagation, so this 251*cda5da8dSAndroid Build Coastguard Worker # fixes the impedance mismatch between the throw() protocol 252*cda5da8dSAndroid Build Coastguard Worker # and the __exit__() protocol. 253*cda5da8dSAndroid Build Coastguard Worker if exc is not value: 254*cda5da8dSAndroid Build Coastguard Worker raise 255*cda5da8dSAndroid Build Coastguard Worker exc.__traceback__ = traceback 256*cda5da8dSAndroid Build Coastguard Worker return False 257*cda5da8dSAndroid Build Coastguard Worker raise RuntimeError("generator didn't stop after athrow()") 258*cda5da8dSAndroid Build Coastguard Worker 259*cda5da8dSAndroid Build Coastguard Worker 260*cda5da8dSAndroid Build Coastguard Workerdef contextmanager(func): 261*cda5da8dSAndroid Build Coastguard Worker """@contextmanager decorator. 262*cda5da8dSAndroid Build Coastguard Worker 263*cda5da8dSAndroid Build Coastguard Worker Typical usage: 264*cda5da8dSAndroid Build Coastguard Worker 265*cda5da8dSAndroid Build Coastguard Worker @contextmanager 266*cda5da8dSAndroid Build Coastguard Worker def some_generator(<arguments>): 267*cda5da8dSAndroid Build Coastguard Worker <setup> 268*cda5da8dSAndroid Build Coastguard Worker try: 269*cda5da8dSAndroid Build Coastguard Worker yield <value> 270*cda5da8dSAndroid Build Coastguard Worker finally: 271*cda5da8dSAndroid Build Coastguard Worker <cleanup> 272*cda5da8dSAndroid Build Coastguard Worker 273*cda5da8dSAndroid Build Coastguard Worker This makes this: 274*cda5da8dSAndroid Build Coastguard Worker 275*cda5da8dSAndroid Build Coastguard Worker with some_generator(<arguments>) as <variable>: 276*cda5da8dSAndroid Build Coastguard Worker <body> 277*cda5da8dSAndroid Build Coastguard Worker 278*cda5da8dSAndroid Build Coastguard Worker equivalent to this: 279*cda5da8dSAndroid Build Coastguard Worker 280*cda5da8dSAndroid Build Coastguard Worker <setup> 281*cda5da8dSAndroid Build Coastguard Worker try: 282*cda5da8dSAndroid Build Coastguard Worker <variable> = <value> 283*cda5da8dSAndroid Build Coastguard Worker <body> 284*cda5da8dSAndroid Build Coastguard Worker finally: 285*cda5da8dSAndroid Build Coastguard Worker <cleanup> 286*cda5da8dSAndroid Build Coastguard Worker """ 287*cda5da8dSAndroid Build Coastguard Worker @wraps(func) 288*cda5da8dSAndroid Build Coastguard Worker def helper(*args, **kwds): 289*cda5da8dSAndroid Build Coastguard Worker return _GeneratorContextManager(func, args, kwds) 290*cda5da8dSAndroid Build Coastguard Worker return helper 291*cda5da8dSAndroid Build Coastguard Worker 292*cda5da8dSAndroid Build Coastguard Worker 293*cda5da8dSAndroid Build Coastguard Workerdef asynccontextmanager(func): 294*cda5da8dSAndroid Build Coastguard Worker """@asynccontextmanager decorator. 295*cda5da8dSAndroid Build Coastguard Worker 296*cda5da8dSAndroid Build Coastguard Worker Typical usage: 297*cda5da8dSAndroid Build Coastguard Worker 298*cda5da8dSAndroid Build Coastguard Worker @asynccontextmanager 299*cda5da8dSAndroid Build Coastguard Worker async def some_async_generator(<arguments>): 300*cda5da8dSAndroid Build Coastguard Worker <setup> 301*cda5da8dSAndroid Build Coastguard Worker try: 302*cda5da8dSAndroid Build Coastguard Worker yield <value> 303*cda5da8dSAndroid Build Coastguard Worker finally: 304*cda5da8dSAndroid Build Coastguard Worker <cleanup> 305*cda5da8dSAndroid Build Coastguard Worker 306*cda5da8dSAndroid Build Coastguard Worker This makes this: 307*cda5da8dSAndroid Build Coastguard Worker 308*cda5da8dSAndroid Build Coastguard Worker async with some_async_generator(<arguments>) as <variable>: 309*cda5da8dSAndroid Build Coastguard Worker <body> 310*cda5da8dSAndroid Build Coastguard Worker 311*cda5da8dSAndroid Build Coastguard Worker equivalent to this: 312*cda5da8dSAndroid Build Coastguard Worker 313*cda5da8dSAndroid Build Coastguard Worker <setup> 314*cda5da8dSAndroid Build Coastguard Worker try: 315*cda5da8dSAndroid Build Coastguard Worker <variable> = <value> 316*cda5da8dSAndroid Build Coastguard Worker <body> 317*cda5da8dSAndroid Build Coastguard Worker finally: 318*cda5da8dSAndroid Build Coastguard Worker <cleanup> 319*cda5da8dSAndroid Build Coastguard Worker """ 320*cda5da8dSAndroid Build Coastguard Worker @wraps(func) 321*cda5da8dSAndroid Build Coastguard Worker def helper(*args, **kwds): 322*cda5da8dSAndroid Build Coastguard Worker return _AsyncGeneratorContextManager(func, args, kwds) 323*cda5da8dSAndroid Build Coastguard Worker return helper 324*cda5da8dSAndroid Build Coastguard Worker 325*cda5da8dSAndroid Build Coastguard Worker 326*cda5da8dSAndroid Build Coastguard Workerclass closing(AbstractContextManager): 327*cda5da8dSAndroid Build Coastguard Worker """Context to automatically close something at the end of a block. 328*cda5da8dSAndroid Build Coastguard Worker 329*cda5da8dSAndroid Build Coastguard Worker Code like this: 330*cda5da8dSAndroid Build Coastguard Worker 331*cda5da8dSAndroid Build Coastguard Worker with closing(<module>.open(<arguments>)) as f: 332*cda5da8dSAndroid Build Coastguard Worker <block> 333*cda5da8dSAndroid Build Coastguard Worker 334*cda5da8dSAndroid Build Coastguard Worker is equivalent to this: 335*cda5da8dSAndroid Build Coastguard Worker 336*cda5da8dSAndroid Build Coastguard Worker f = <module>.open(<arguments>) 337*cda5da8dSAndroid Build Coastguard Worker try: 338*cda5da8dSAndroid Build Coastguard Worker <block> 339*cda5da8dSAndroid Build Coastguard Worker finally: 340*cda5da8dSAndroid Build Coastguard Worker f.close() 341*cda5da8dSAndroid Build Coastguard Worker 342*cda5da8dSAndroid Build Coastguard Worker """ 343*cda5da8dSAndroid Build Coastguard Worker def __init__(self, thing): 344*cda5da8dSAndroid Build Coastguard Worker self.thing = thing 345*cda5da8dSAndroid Build Coastguard Worker def __enter__(self): 346*cda5da8dSAndroid Build Coastguard Worker return self.thing 347*cda5da8dSAndroid Build Coastguard Worker def __exit__(self, *exc_info): 348*cda5da8dSAndroid Build Coastguard Worker self.thing.close() 349*cda5da8dSAndroid Build Coastguard Worker 350*cda5da8dSAndroid Build Coastguard Worker 351*cda5da8dSAndroid Build Coastguard Workerclass aclosing(AbstractAsyncContextManager): 352*cda5da8dSAndroid Build Coastguard Worker """Async context manager for safely finalizing an asynchronously cleaned-up 353*cda5da8dSAndroid Build Coastguard Worker resource such as an async generator, calling its ``aclose()`` method. 354*cda5da8dSAndroid Build Coastguard Worker 355*cda5da8dSAndroid Build Coastguard Worker Code like this: 356*cda5da8dSAndroid Build Coastguard Worker 357*cda5da8dSAndroid Build Coastguard Worker async with aclosing(<module>.fetch(<arguments>)) as agen: 358*cda5da8dSAndroid Build Coastguard Worker <block> 359*cda5da8dSAndroid Build Coastguard Worker 360*cda5da8dSAndroid Build Coastguard Worker is equivalent to this: 361*cda5da8dSAndroid Build Coastguard Worker 362*cda5da8dSAndroid Build Coastguard Worker agen = <module>.fetch(<arguments>) 363*cda5da8dSAndroid Build Coastguard Worker try: 364*cda5da8dSAndroid Build Coastguard Worker <block> 365*cda5da8dSAndroid Build Coastguard Worker finally: 366*cda5da8dSAndroid Build Coastguard Worker await agen.aclose() 367*cda5da8dSAndroid Build Coastguard Worker 368*cda5da8dSAndroid Build Coastguard Worker """ 369*cda5da8dSAndroid Build Coastguard Worker def __init__(self, thing): 370*cda5da8dSAndroid Build Coastguard Worker self.thing = thing 371*cda5da8dSAndroid Build Coastguard Worker async def __aenter__(self): 372*cda5da8dSAndroid Build Coastguard Worker return self.thing 373*cda5da8dSAndroid Build Coastguard Worker async def __aexit__(self, *exc_info): 374*cda5da8dSAndroid Build Coastguard Worker await self.thing.aclose() 375*cda5da8dSAndroid Build Coastguard Worker 376*cda5da8dSAndroid Build Coastguard Worker 377*cda5da8dSAndroid Build Coastguard Workerclass _RedirectStream(AbstractContextManager): 378*cda5da8dSAndroid Build Coastguard Worker 379*cda5da8dSAndroid Build Coastguard Worker _stream = None 380*cda5da8dSAndroid Build Coastguard Worker 381*cda5da8dSAndroid Build Coastguard Worker def __init__(self, new_target): 382*cda5da8dSAndroid Build Coastguard Worker self._new_target = new_target 383*cda5da8dSAndroid Build Coastguard Worker # We use a list of old targets to make this CM re-entrant 384*cda5da8dSAndroid Build Coastguard Worker self._old_targets = [] 385*cda5da8dSAndroid Build Coastguard Worker 386*cda5da8dSAndroid Build Coastguard Worker def __enter__(self): 387*cda5da8dSAndroid Build Coastguard Worker self._old_targets.append(getattr(sys, self._stream)) 388*cda5da8dSAndroid Build Coastguard Worker setattr(sys, self._stream, self._new_target) 389*cda5da8dSAndroid Build Coastguard Worker return self._new_target 390*cda5da8dSAndroid Build Coastguard Worker 391*cda5da8dSAndroid Build Coastguard Worker def __exit__(self, exctype, excinst, exctb): 392*cda5da8dSAndroid Build Coastguard Worker setattr(sys, self._stream, self._old_targets.pop()) 393*cda5da8dSAndroid Build Coastguard Worker 394*cda5da8dSAndroid Build Coastguard Worker 395*cda5da8dSAndroid Build Coastguard Workerclass redirect_stdout(_RedirectStream): 396*cda5da8dSAndroid Build Coastguard Worker """Context manager for temporarily redirecting stdout to another file. 397*cda5da8dSAndroid Build Coastguard Worker 398*cda5da8dSAndroid Build Coastguard Worker # How to send help() to stderr 399*cda5da8dSAndroid Build Coastguard Worker with redirect_stdout(sys.stderr): 400*cda5da8dSAndroid Build Coastguard Worker help(dir) 401*cda5da8dSAndroid Build Coastguard Worker 402*cda5da8dSAndroid Build Coastguard Worker # How to write help() to a file 403*cda5da8dSAndroid Build Coastguard Worker with open('help.txt', 'w') as f: 404*cda5da8dSAndroid Build Coastguard Worker with redirect_stdout(f): 405*cda5da8dSAndroid Build Coastguard Worker help(pow) 406*cda5da8dSAndroid Build Coastguard Worker """ 407*cda5da8dSAndroid Build Coastguard Worker 408*cda5da8dSAndroid Build Coastguard Worker _stream = "stdout" 409*cda5da8dSAndroid Build Coastguard Worker 410*cda5da8dSAndroid Build Coastguard Worker 411*cda5da8dSAndroid Build Coastguard Workerclass redirect_stderr(_RedirectStream): 412*cda5da8dSAndroid Build Coastguard Worker """Context manager for temporarily redirecting stderr to another file.""" 413*cda5da8dSAndroid Build Coastguard Worker 414*cda5da8dSAndroid Build Coastguard Worker _stream = "stderr" 415*cda5da8dSAndroid Build Coastguard Worker 416*cda5da8dSAndroid Build Coastguard Worker 417*cda5da8dSAndroid Build Coastguard Workerclass suppress(AbstractContextManager): 418*cda5da8dSAndroid Build Coastguard Worker """Context manager to suppress specified exceptions 419*cda5da8dSAndroid Build Coastguard Worker 420*cda5da8dSAndroid Build Coastguard Worker After the exception is suppressed, execution proceeds with the next 421*cda5da8dSAndroid Build Coastguard Worker statement following the with statement. 422*cda5da8dSAndroid Build Coastguard Worker 423*cda5da8dSAndroid Build Coastguard Worker with suppress(FileNotFoundError): 424*cda5da8dSAndroid Build Coastguard Worker os.remove(somefile) 425*cda5da8dSAndroid Build Coastguard Worker # Execution still resumes here if the file was already removed 426*cda5da8dSAndroid Build Coastguard Worker """ 427*cda5da8dSAndroid Build Coastguard Worker 428*cda5da8dSAndroid Build Coastguard Worker def __init__(self, *exceptions): 429*cda5da8dSAndroid Build Coastguard Worker self._exceptions = exceptions 430*cda5da8dSAndroid Build Coastguard Worker 431*cda5da8dSAndroid Build Coastguard Worker def __enter__(self): 432*cda5da8dSAndroid Build Coastguard Worker pass 433*cda5da8dSAndroid Build Coastguard Worker 434*cda5da8dSAndroid Build Coastguard Worker def __exit__(self, exctype, excinst, exctb): 435*cda5da8dSAndroid Build Coastguard Worker # Unlike isinstance and issubclass, CPython exception handling 436*cda5da8dSAndroid Build Coastguard Worker # currently only looks at the concrete type hierarchy (ignoring 437*cda5da8dSAndroid Build Coastguard Worker # the instance and subclass checking hooks). While Guido considers 438*cda5da8dSAndroid Build Coastguard Worker # that a bug rather than a feature, it's a fairly hard one to fix 439*cda5da8dSAndroid Build Coastguard Worker # due to various internal implementation details. suppress provides 440*cda5da8dSAndroid Build Coastguard Worker # the simpler issubclass based semantics, rather than trying to 441*cda5da8dSAndroid Build Coastguard Worker # exactly reproduce the limitations of the CPython interpreter. 442*cda5da8dSAndroid Build Coastguard Worker # 443*cda5da8dSAndroid Build Coastguard Worker # See http://bugs.python.org/issue12029 for more details 444*cda5da8dSAndroid Build Coastguard Worker return exctype is not None and issubclass(exctype, self._exceptions) 445*cda5da8dSAndroid Build Coastguard Worker 446*cda5da8dSAndroid Build Coastguard Worker 447*cda5da8dSAndroid Build Coastguard Workerclass _BaseExitStack: 448*cda5da8dSAndroid Build Coastguard Worker """A base class for ExitStack and AsyncExitStack.""" 449*cda5da8dSAndroid Build Coastguard Worker 450*cda5da8dSAndroid Build Coastguard Worker @staticmethod 451*cda5da8dSAndroid Build Coastguard Worker def _create_exit_wrapper(cm, cm_exit): 452*cda5da8dSAndroid Build Coastguard Worker return MethodType(cm_exit, cm) 453*cda5da8dSAndroid Build Coastguard Worker 454*cda5da8dSAndroid Build Coastguard Worker @staticmethod 455*cda5da8dSAndroid Build Coastguard Worker def _create_cb_wrapper(callback, /, *args, **kwds): 456*cda5da8dSAndroid Build Coastguard Worker def _exit_wrapper(exc_type, exc, tb): 457*cda5da8dSAndroid Build Coastguard Worker callback(*args, **kwds) 458*cda5da8dSAndroid Build Coastguard Worker return _exit_wrapper 459*cda5da8dSAndroid Build Coastguard Worker 460*cda5da8dSAndroid Build Coastguard Worker def __init__(self): 461*cda5da8dSAndroid Build Coastguard Worker self._exit_callbacks = deque() 462*cda5da8dSAndroid Build Coastguard Worker 463*cda5da8dSAndroid Build Coastguard Worker def pop_all(self): 464*cda5da8dSAndroid Build Coastguard Worker """Preserve the context stack by transferring it to a new instance.""" 465*cda5da8dSAndroid Build Coastguard Worker new_stack = type(self)() 466*cda5da8dSAndroid Build Coastguard Worker new_stack._exit_callbacks = self._exit_callbacks 467*cda5da8dSAndroid Build Coastguard Worker self._exit_callbacks = deque() 468*cda5da8dSAndroid Build Coastguard Worker return new_stack 469*cda5da8dSAndroid Build Coastguard Worker 470*cda5da8dSAndroid Build Coastguard Worker def push(self, exit): 471*cda5da8dSAndroid Build Coastguard Worker """Registers a callback with the standard __exit__ method signature. 472*cda5da8dSAndroid Build Coastguard Worker 473*cda5da8dSAndroid Build Coastguard Worker Can suppress exceptions the same way __exit__ method can. 474*cda5da8dSAndroid Build Coastguard Worker Also accepts any object with an __exit__ method (registering a call 475*cda5da8dSAndroid Build Coastguard Worker to the method instead of the object itself). 476*cda5da8dSAndroid Build Coastguard Worker """ 477*cda5da8dSAndroid Build Coastguard Worker # We use an unbound method rather than a bound method to follow 478*cda5da8dSAndroid Build Coastguard Worker # the standard lookup behaviour for special methods. 479*cda5da8dSAndroid Build Coastguard Worker _cb_type = type(exit) 480*cda5da8dSAndroid Build Coastguard Worker 481*cda5da8dSAndroid Build Coastguard Worker try: 482*cda5da8dSAndroid Build Coastguard Worker exit_method = _cb_type.__exit__ 483*cda5da8dSAndroid Build Coastguard Worker except AttributeError: 484*cda5da8dSAndroid Build Coastguard Worker # Not a context manager, so assume it's a callable. 485*cda5da8dSAndroid Build Coastguard Worker self._push_exit_callback(exit) 486*cda5da8dSAndroid Build Coastguard Worker else: 487*cda5da8dSAndroid Build Coastguard Worker self._push_cm_exit(exit, exit_method) 488*cda5da8dSAndroid Build Coastguard Worker return exit # Allow use as a decorator. 489*cda5da8dSAndroid Build Coastguard Worker 490*cda5da8dSAndroid Build Coastguard Worker def enter_context(self, cm): 491*cda5da8dSAndroid Build Coastguard Worker """Enters the supplied context manager. 492*cda5da8dSAndroid Build Coastguard Worker 493*cda5da8dSAndroid Build Coastguard Worker If successful, also pushes its __exit__ method as a callback and 494*cda5da8dSAndroid Build Coastguard Worker returns the result of the __enter__ method. 495*cda5da8dSAndroid Build Coastguard Worker """ 496*cda5da8dSAndroid Build Coastguard Worker # We look up the special methods on the type to match the with 497*cda5da8dSAndroid Build Coastguard Worker # statement. 498*cda5da8dSAndroid Build Coastguard Worker cls = type(cm) 499*cda5da8dSAndroid Build Coastguard Worker try: 500*cda5da8dSAndroid Build Coastguard Worker _enter = cls.__enter__ 501*cda5da8dSAndroid Build Coastguard Worker _exit = cls.__exit__ 502*cda5da8dSAndroid Build Coastguard Worker except AttributeError: 503*cda5da8dSAndroid Build Coastguard Worker raise TypeError(f"'{cls.__module__}.{cls.__qualname__}' object does " 504*cda5da8dSAndroid Build Coastguard Worker f"not support the context manager protocol") from None 505*cda5da8dSAndroid Build Coastguard Worker result = _enter(cm) 506*cda5da8dSAndroid Build Coastguard Worker self._push_cm_exit(cm, _exit) 507*cda5da8dSAndroid Build Coastguard Worker return result 508*cda5da8dSAndroid Build Coastguard Worker 509*cda5da8dSAndroid Build Coastguard Worker def callback(self, callback, /, *args, **kwds): 510*cda5da8dSAndroid Build Coastguard Worker """Registers an arbitrary callback and arguments. 511*cda5da8dSAndroid Build Coastguard Worker 512*cda5da8dSAndroid Build Coastguard Worker Cannot suppress exceptions. 513*cda5da8dSAndroid Build Coastguard Worker """ 514*cda5da8dSAndroid Build Coastguard Worker _exit_wrapper = self._create_cb_wrapper(callback, *args, **kwds) 515*cda5da8dSAndroid Build Coastguard Worker 516*cda5da8dSAndroid Build Coastguard Worker # We changed the signature, so using @wraps is not appropriate, but 517*cda5da8dSAndroid Build Coastguard Worker # setting __wrapped__ may still help with introspection. 518*cda5da8dSAndroid Build Coastguard Worker _exit_wrapper.__wrapped__ = callback 519*cda5da8dSAndroid Build Coastguard Worker self._push_exit_callback(_exit_wrapper) 520*cda5da8dSAndroid Build Coastguard Worker return callback # Allow use as a decorator 521*cda5da8dSAndroid Build Coastguard Worker 522*cda5da8dSAndroid Build Coastguard Worker def _push_cm_exit(self, cm, cm_exit): 523*cda5da8dSAndroid Build Coastguard Worker """Helper to correctly register callbacks to __exit__ methods.""" 524*cda5da8dSAndroid Build Coastguard Worker _exit_wrapper = self._create_exit_wrapper(cm, cm_exit) 525*cda5da8dSAndroid Build Coastguard Worker self._push_exit_callback(_exit_wrapper, True) 526*cda5da8dSAndroid Build Coastguard Worker 527*cda5da8dSAndroid Build Coastguard Worker def _push_exit_callback(self, callback, is_sync=True): 528*cda5da8dSAndroid Build Coastguard Worker self._exit_callbacks.append((is_sync, callback)) 529*cda5da8dSAndroid Build Coastguard Worker 530*cda5da8dSAndroid Build Coastguard Worker 531*cda5da8dSAndroid Build Coastguard Worker# Inspired by discussions on http://bugs.python.org/issue13585 532*cda5da8dSAndroid Build Coastguard Workerclass ExitStack(_BaseExitStack, AbstractContextManager): 533*cda5da8dSAndroid Build Coastguard Worker """Context manager for dynamic management of a stack of exit callbacks. 534*cda5da8dSAndroid Build Coastguard Worker 535*cda5da8dSAndroid Build Coastguard Worker For example: 536*cda5da8dSAndroid Build Coastguard Worker with ExitStack() as stack: 537*cda5da8dSAndroid Build Coastguard Worker files = [stack.enter_context(open(fname)) for fname in filenames] 538*cda5da8dSAndroid Build Coastguard Worker # All opened files will automatically be closed at the end of 539*cda5da8dSAndroid Build Coastguard Worker # the with statement, even if attempts to open files later 540*cda5da8dSAndroid Build Coastguard Worker # in the list raise an exception. 541*cda5da8dSAndroid Build Coastguard Worker """ 542*cda5da8dSAndroid Build Coastguard Worker 543*cda5da8dSAndroid Build Coastguard Worker def __enter__(self): 544*cda5da8dSAndroid Build Coastguard Worker return self 545*cda5da8dSAndroid Build Coastguard Worker 546*cda5da8dSAndroid Build Coastguard Worker def __exit__(self, *exc_details): 547*cda5da8dSAndroid Build Coastguard Worker received_exc = exc_details[0] is not None 548*cda5da8dSAndroid Build Coastguard Worker 549*cda5da8dSAndroid Build Coastguard Worker # We manipulate the exception state so it behaves as though 550*cda5da8dSAndroid Build Coastguard Worker # we were actually nesting multiple with statements 551*cda5da8dSAndroid Build Coastguard Worker frame_exc = sys.exc_info()[1] 552*cda5da8dSAndroid Build Coastguard Worker def _fix_exception_context(new_exc, old_exc): 553*cda5da8dSAndroid Build Coastguard Worker # Context may not be correct, so find the end of the chain 554*cda5da8dSAndroid Build Coastguard Worker while 1: 555*cda5da8dSAndroid Build Coastguard Worker exc_context = new_exc.__context__ 556*cda5da8dSAndroid Build Coastguard Worker if exc_context is None or exc_context is old_exc: 557*cda5da8dSAndroid Build Coastguard Worker # Context is already set correctly (see issue 20317) 558*cda5da8dSAndroid Build Coastguard Worker return 559*cda5da8dSAndroid Build Coastguard Worker if exc_context is frame_exc: 560*cda5da8dSAndroid Build Coastguard Worker break 561*cda5da8dSAndroid Build Coastguard Worker new_exc = exc_context 562*cda5da8dSAndroid Build Coastguard Worker # Change the end of the chain to point to the exception 563*cda5da8dSAndroid Build Coastguard Worker # we expect it to reference 564*cda5da8dSAndroid Build Coastguard Worker new_exc.__context__ = old_exc 565*cda5da8dSAndroid Build Coastguard Worker 566*cda5da8dSAndroid Build Coastguard Worker # Callbacks are invoked in LIFO order to match the behaviour of 567*cda5da8dSAndroid Build Coastguard Worker # nested context managers 568*cda5da8dSAndroid Build Coastguard Worker suppressed_exc = False 569*cda5da8dSAndroid Build Coastguard Worker pending_raise = False 570*cda5da8dSAndroid Build Coastguard Worker while self._exit_callbacks: 571*cda5da8dSAndroid Build Coastguard Worker is_sync, cb = self._exit_callbacks.pop() 572*cda5da8dSAndroid Build Coastguard Worker assert is_sync 573*cda5da8dSAndroid Build Coastguard Worker try: 574*cda5da8dSAndroid Build Coastguard Worker if cb(*exc_details): 575*cda5da8dSAndroid Build Coastguard Worker suppressed_exc = True 576*cda5da8dSAndroid Build Coastguard Worker pending_raise = False 577*cda5da8dSAndroid Build Coastguard Worker exc_details = (None, None, None) 578*cda5da8dSAndroid Build Coastguard Worker except: 579*cda5da8dSAndroid Build Coastguard Worker new_exc_details = sys.exc_info() 580*cda5da8dSAndroid Build Coastguard Worker # simulate the stack of exceptions by setting the context 581*cda5da8dSAndroid Build Coastguard Worker _fix_exception_context(new_exc_details[1], exc_details[1]) 582*cda5da8dSAndroid Build Coastguard Worker pending_raise = True 583*cda5da8dSAndroid Build Coastguard Worker exc_details = new_exc_details 584*cda5da8dSAndroid Build Coastguard Worker if pending_raise: 585*cda5da8dSAndroid Build Coastguard Worker try: 586*cda5da8dSAndroid Build Coastguard Worker # bare "raise exc_details[1]" replaces our carefully 587*cda5da8dSAndroid Build Coastguard Worker # set-up context 588*cda5da8dSAndroid Build Coastguard Worker fixed_ctx = exc_details[1].__context__ 589*cda5da8dSAndroid Build Coastguard Worker raise exc_details[1] 590*cda5da8dSAndroid Build Coastguard Worker except BaseException: 591*cda5da8dSAndroid Build Coastguard Worker exc_details[1].__context__ = fixed_ctx 592*cda5da8dSAndroid Build Coastguard Worker raise 593*cda5da8dSAndroid Build Coastguard Worker return received_exc and suppressed_exc 594*cda5da8dSAndroid Build Coastguard Worker 595*cda5da8dSAndroid Build Coastguard Worker def close(self): 596*cda5da8dSAndroid Build Coastguard Worker """Immediately unwind the context stack.""" 597*cda5da8dSAndroid Build Coastguard Worker self.__exit__(None, None, None) 598*cda5da8dSAndroid Build Coastguard Worker 599*cda5da8dSAndroid Build Coastguard Worker 600*cda5da8dSAndroid Build Coastguard Worker# Inspired by discussions on https://bugs.python.org/issue29302 601*cda5da8dSAndroid Build Coastguard Workerclass AsyncExitStack(_BaseExitStack, AbstractAsyncContextManager): 602*cda5da8dSAndroid Build Coastguard Worker """Async context manager for dynamic management of a stack of exit 603*cda5da8dSAndroid Build Coastguard Worker callbacks. 604*cda5da8dSAndroid Build Coastguard Worker 605*cda5da8dSAndroid Build Coastguard Worker For example: 606*cda5da8dSAndroid Build Coastguard Worker async with AsyncExitStack() as stack: 607*cda5da8dSAndroid Build Coastguard Worker connections = [await stack.enter_async_context(get_connection()) 608*cda5da8dSAndroid Build Coastguard Worker for i in range(5)] 609*cda5da8dSAndroid Build Coastguard Worker # All opened connections will automatically be released at the 610*cda5da8dSAndroid Build Coastguard Worker # end of the async with statement, even if attempts to open a 611*cda5da8dSAndroid Build Coastguard Worker # connection later in the list raise an exception. 612*cda5da8dSAndroid Build Coastguard Worker """ 613*cda5da8dSAndroid Build Coastguard Worker 614*cda5da8dSAndroid Build Coastguard Worker @staticmethod 615*cda5da8dSAndroid Build Coastguard Worker def _create_async_exit_wrapper(cm, cm_exit): 616*cda5da8dSAndroid Build Coastguard Worker return MethodType(cm_exit, cm) 617*cda5da8dSAndroid Build Coastguard Worker 618*cda5da8dSAndroid Build Coastguard Worker @staticmethod 619*cda5da8dSAndroid Build Coastguard Worker def _create_async_cb_wrapper(callback, /, *args, **kwds): 620*cda5da8dSAndroid Build Coastguard Worker async def _exit_wrapper(exc_type, exc, tb): 621*cda5da8dSAndroid Build Coastguard Worker await callback(*args, **kwds) 622*cda5da8dSAndroid Build Coastguard Worker return _exit_wrapper 623*cda5da8dSAndroid Build Coastguard Worker 624*cda5da8dSAndroid Build Coastguard Worker async def enter_async_context(self, cm): 625*cda5da8dSAndroid Build Coastguard Worker """Enters the supplied async context manager. 626*cda5da8dSAndroid Build Coastguard Worker 627*cda5da8dSAndroid Build Coastguard Worker If successful, also pushes its __aexit__ method as a callback and 628*cda5da8dSAndroid Build Coastguard Worker returns the result of the __aenter__ method. 629*cda5da8dSAndroid Build Coastguard Worker """ 630*cda5da8dSAndroid Build Coastguard Worker cls = type(cm) 631*cda5da8dSAndroid Build Coastguard Worker try: 632*cda5da8dSAndroid Build Coastguard Worker _enter = cls.__aenter__ 633*cda5da8dSAndroid Build Coastguard Worker _exit = cls.__aexit__ 634*cda5da8dSAndroid Build Coastguard Worker except AttributeError: 635*cda5da8dSAndroid Build Coastguard Worker raise TypeError(f"'{cls.__module__}.{cls.__qualname__}' object does " 636*cda5da8dSAndroid Build Coastguard Worker f"not support the asynchronous context manager protocol" 637*cda5da8dSAndroid Build Coastguard Worker ) from None 638*cda5da8dSAndroid Build Coastguard Worker result = await _enter(cm) 639*cda5da8dSAndroid Build Coastguard Worker self._push_async_cm_exit(cm, _exit) 640*cda5da8dSAndroid Build Coastguard Worker return result 641*cda5da8dSAndroid Build Coastguard Worker 642*cda5da8dSAndroid Build Coastguard Worker def push_async_exit(self, exit): 643*cda5da8dSAndroid Build Coastguard Worker """Registers a coroutine function with the standard __aexit__ method 644*cda5da8dSAndroid Build Coastguard Worker signature. 645*cda5da8dSAndroid Build Coastguard Worker 646*cda5da8dSAndroid Build Coastguard Worker Can suppress exceptions the same way __aexit__ method can. 647*cda5da8dSAndroid Build Coastguard Worker Also accepts any object with an __aexit__ method (registering a call 648*cda5da8dSAndroid Build Coastguard Worker to the method instead of the object itself). 649*cda5da8dSAndroid Build Coastguard Worker """ 650*cda5da8dSAndroid Build Coastguard Worker _cb_type = type(exit) 651*cda5da8dSAndroid Build Coastguard Worker try: 652*cda5da8dSAndroid Build Coastguard Worker exit_method = _cb_type.__aexit__ 653*cda5da8dSAndroid Build Coastguard Worker except AttributeError: 654*cda5da8dSAndroid Build Coastguard Worker # Not an async context manager, so assume it's a coroutine function 655*cda5da8dSAndroid Build Coastguard Worker self._push_exit_callback(exit, False) 656*cda5da8dSAndroid Build Coastguard Worker else: 657*cda5da8dSAndroid Build Coastguard Worker self._push_async_cm_exit(exit, exit_method) 658*cda5da8dSAndroid Build Coastguard Worker return exit # Allow use as a decorator 659*cda5da8dSAndroid Build Coastguard Worker 660*cda5da8dSAndroid Build Coastguard Worker def push_async_callback(self, callback, /, *args, **kwds): 661*cda5da8dSAndroid Build Coastguard Worker """Registers an arbitrary coroutine function and arguments. 662*cda5da8dSAndroid Build Coastguard Worker 663*cda5da8dSAndroid Build Coastguard Worker Cannot suppress exceptions. 664*cda5da8dSAndroid Build Coastguard Worker """ 665*cda5da8dSAndroid Build Coastguard Worker _exit_wrapper = self._create_async_cb_wrapper(callback, *args, **kwds) 666*cda5da8dSAndroid Build Coastguard Worker 667*cda5da8dSAndroid Build Coastguard Worker # We changed the signature, so using @wraps is not appropriate, but 668*cda5da8dSAndroid Build Coastguard Worker # setting __wrapped__ may still help with introspection. 669*cda5da8dSAndroid Build Coastguard Worker _exit_wrapper.__wrapped__ = callback 670*cda5da8dSAndroid Build Coastguard Worker self._push_exit_callback(_exit_wrapper, False) 671*cda5da8dSAndroid Build Coastguard Worker return callback # Allow use as a decorator 672*cda5da8dSAndroid Build Coastguard Worker 673*cda5da8dSAndroid Build Coastguard Worker async def aclose(self): 674*cda5da8dSAndroid Build Coastguard Worker """Immediately unwind the context stack.""" 675*cda5da8dSAndroid Build Coastguard Worker await self.__aexit__(None, None, None) 676*cda5da8dSAndroid Build Coastguard Worker 677*cda5da8dSAndroid Build Coastguard Worker def _push_async_cm_exit(self, cm, cm_exit): 678*cda5da8dSAndroid Build Coastguard Worker """Helper to correctly register coroutine function to __aexit__ 679*cda5da8dSAndroid Build Coastguard Worker method.""" 680*cda5da8dSAndroid Build Coastguard Worker _exit_wrapper = self._create_async_exit_wrapper(cm, cm_exit) 681*cda5da8dSAndroid Build Coastguard Worker self._push_exit_callback(_exit_wrapper, False) 682*cda5da8dSAndroid Build Coastguard Worker 683*cda5da8dSAndroid Build Coastguard Worker async def __aenter__(self): 684*cda5da8dSAndroid Build Coastguard Worker return self 685*cda5da8dSAndroid Build Coastguard Worker 686*cda5da8dSAndroid Build Coastguard Worker async def __aexit__(self, *exc_details): 687*cda5da8dSAndroid Build Coastguard Worker received_exc = exc_details[0] is not None 688*cda5da8dSAndroid Build Coastguard Worker 689*cda5da8dSAndroid Build Coastguard Worker # We manipulate the exception state so it behaves as though 690*cda5da8dSAndroid Build Coastguard Worker # we were actually nesting multiple with statements 691*cda5da8dSAndroid Build Coastguard Worker frame_exc = sys.exc_info()[1] 692*cda5da8dSAndroid Build Coastguard Worker def _fix_exception_context(new_exc, old_exc): 693*cda5da8dSAndroid Build Coastguard Worker # Context may not be correct, so find the end of the chain 694*cda5da8dSAndroid Build Coastguard Worker while 1: 695*cda5da8dSAndroid Build Coastguard Worker exc_context = new_exc.__context__ 696*cda5da8dSAndroid Build Coastguard Worker if exc_context is None or exc_context is old_exc: 697*cda5da8dSAndroid Build Coastguard Worker # Context is already set correctly (see issue 20317) 698*cda5da8dSAndroid Build Coastguard Worker return 699*cda5da8dSAndroid Build Coastguard Worker if exc_context is frame_exc: 700*cda5da8dSAndroid Build Coastguard Worker break 701*cda5da8dSAndroid Build Coastguard Worker new_exc = exc_context 702*cda5da8dSAndroid Build Coastguard Worker # Change the end of the chain to point to the exception 703*cda5da8dSAndroid Build Coastguard Worker # we expect it to reference 704*cda5da8dSAndroid Build Coastguard Worker new_exc.__context__ = old_exc 705*cda5da8dSAndroid Build Coastguard Worker 706*cda5da8dSAndroid Build Coastguard Worker # Callbacks are invoked in LIFO order to match the behaviour of 707*cda5da8dSAndroid Build Coastguard Worker # nested context managers 708*cda5da8dSAndroid Build Coastguard Worker suppressed_exc = False 709*cda5da8dSAndroid Build Coastguard Worker pending_raise = False 710*cda5da8dSAndroid Build Coastguard Worker while self._exit_callbacks: 711*cda5da8dSAndroid Build Coastguard Worker is_sync, cb = self._exit_callbacks.pop() 712*cda5da8dSAndroid Build Coastguard Worker try: 713*cda5da8dSAndroid Build Coastguard Worker if is_sync: 714*cda5da8dSAndroid Build Coastguard Worker cb_suppress = cb(*exc_details) 715*cda5da8dSAndroid Build Coastguard Worker else: 716*cda5da8dSAndroid Build Coastguard Worker cb_suppress = await cb(*exc_details) 717*cda5da8dSAndroid Build Coastguard Worker 718*cda5da8dSAndroid Build Coastguard Worker if cb_suppress: 719*cda5da8dSAndroid Build Coastguard Worker suppressed_exc = True 720*cda5da8dSAndroid Build Coastguard Worker pending_raise = False 721*cda5da8dSAndroid Build Coastguard Worker exc_details = (None, None, None) 722*cda5da8dSAndroid Build Coastguard Worker except: 723*cda5da8dSAndroid Build Coastguard Worker new_exc_details = sys.exc_info() 724*cda5da8dSAndroid Build Coastguard Worker # simulate the stack of exceptions by setting the context 725*cda5da8dSAndroid Build Coastguard Worker _fix_exception_context(new_exc_details[1], exc_details[1]) 726*cda5da8dSAndroid Build Coastguard Worker pending_raise = True 727*cda5da8dSAndroid Build Coastguard Worker exc_details = new_exc_details 728*cda5da8dSAndroid Build Coastguard Worker if pending_raise: 729*cda5da8dSAndroid Build Coastguard Worker try: 730*cda5da8dSAndroid Build Coastguard Worker # bare "raise exc_details[1]" replaces our carefully 731*cda5da8dSAndroid Build Coastguard Worker # set-up context 732*cda5da8dSAndroid Build Coastguard Worker fixed_ctx = exc_details[1].__context__ 733*cda5da8dSAndroid Build Coastguard Worker raise exc_details[1] 734*cda5da8dSAndroid Build Coastguard Worker except BaseException: 735*cda5da8dSAndroid Build Coastguard Worker exc_details[1].__context__ = fixed_ctx 736*cda5da8dSAndroid Build Coastguard Worker raise 737*cda5da8dSAndroid Build Coastguard Worker return received_exc and suppressed_exc 738*cda5da8dSAndroid Build Coastguard Worker 739*cda5da8dSAndroid Build Coastguard Worker 740*cda5da8dSAndroid Build Coastguard Workerclass nullcontext(AbstractContextManager, AbstractAsyncContextManager): 741*cda5da8dSAndroid Build Coastguard Worker """Context manager that does no additional processing. 742*cda5da8dSAndroid Build Coastguard Worker 743*cda5da8dSAndroid Build Coastguard Worker Used as a stand-in for a normal context manager, when a particular 744*cda5da8dSAndroid Build Coastguard Worker block of code is only sometimes used with a normal context manager: 745*cda5da8dSAndroid Build Coastguard Worker 746*cda5da8dSAndroid Build Coastguard Worker cm = optional_cm if condition else nullcontext() 747*cda5da8dSAndroid Build Coastguard Worker with cm: 748*cda5da8dSAndroid Build Coastguard Worker # Perform operation, using optional_cm if condition is True 749*cda5da8dSAndroid Build Coastguard Worker """ 750*cda5da8dSAndroid Build Coastguard Worker 751*cda5da8dSAndroid Build Coastguard Worker def __init__(self, enter_result=None): 752*cda5da8dSAndroid Build Coastguard Worker self.enter_result = enter_result 753*cda5da8dSAndroid Build Coastguard Worker 754*cda5da8dSAndroid Build Coastguard Worker def __enter__(self): 755*cda5da8dSAndroid Build Coastguard Worker return self.enter_result 756*cda5da8dSAndroid Build Coastguard Worker 757*cda5da8dSAndroid Build Coastguard Worker def __exit__(self, *excinfo): 758*cda5da8dSAndroid Build Coastguard Worker pass 759*cda5da8dSAndroid Build Coastguard Worker 760*cda5da8dSAndroid Build Coastguard Worker async def __aenter__(self): 761*cda5da8dSAndroid Build Coastguard Worker return self.enter_result 762*cda5da8dSAndroid Build Coastguard Worker 763*cda5da8dSAndroid Build Coastguard Worker async def __aexit__(self, *excinfo): 764*cda5da8dSAndroid Build Coastguard Worker pass 765*cda5da8dSAndroid Build Coastguard Worker 766*cda5da8dSAndroid Build Coastguard Worker 767*cda5da8dSAndroid Build Coastguard Workerclass chdir(AbstractContextManager): 768*cda5da8dSAndroid Build Coastguard Worker """Non thread-safe context manager to change the current working directory.""" 769*cda5da8dSAndroid Build Coastguard Worker 770*cda5da8dSAndroid Build Coastguard Worker def __init__(self, path): 771*cda5da8dSAndroid Build Coastguard Worker self.path = path 772*cda5da8dSAndroid Build Coastguard Worker self._old_cwd = [] 773*cda5da8dSAndroid Build Coastguard Worker 774*cda5da8dSAndroid Build Coastguard Worker def __enter__(self): 775*cda5da8dSAndroid Build Coastguard Worker self._old_cwd.append(os.getcwd()) 776*cda5da8dSAndroid Build Coastguard Worker os.chdir(self.path) 777*cda5da8dSAndroid Build Coastguard Worker 778*cda5da8dSAndroid Build Coastguard Worker def __exit__(self, *excinfo): 779*cda5da8dSAndroid Build Coastguard Worker os.chdir(self._old_cwd.pop()) 780