Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
 
 
 
 

799 righe
27 KiB

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