logo

oasis-root

Compiled tree of Oasis Linux based on own branch at <https://hacktivis.me/git/oasis/> git clone https://anongit.hacktivis.me/git/oasis-root.git

contextlib.py (25882B)


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