mirror of
https://github.com/jazzband/contextlib2.git
synced 2026-03-17 14:10:25 +00:00
147 lines
5.2 KiB
Diff
147 lines
5.2 KiB
Diff
--- ../cpython/Lib/contextlib.py 2021-06-26 16:28:03.835372955 +1000
|
|
+++ contextlib2.py 2021-06-26 17:40:30.047079570 +1000
|
|
@@ -1,19 +1,32 @@
|
|
-"""Utilities for with-statement contexts. See PEP 343."""
|
|
+"""contextlib2 - backports and enhancements to the contextlib module"""
|
|
+
|
|
import abc
|
|
import sys
|
|
+import warnings
|
|
import _collections_abc
|
|
from collections import deque
|
|
from functools import wraps
|
|
-from types import MethodType, GenericAlias
|
|
+from types import MethodType
|
|
+
|
|
+# Python 3.6/3.7/3.8 compatibility: GenericAlias may not be defined
|
|
+try:
|
|
+ from types import GenericAlias
|
|
+except ImportError:
|
|
+ # If the real GenericAlias type doesn't exist, __class_getitem__ won't be used,
|
|
+ # so the fallback placeholder doesn't need to provide any meaningful behaviour
|
|
+ class GenericAlias:
|
|
+ pass
|
|
+
|
|
|
|
__all__ = ["asynccontextmanager", "contextmanager", "closing", "nullcontext",
|
|
"AbstractContextManager", "AbstractAsyncContextManager",
|
|
"AsyncExitStack", "ContextDecorator", "ExitStack",
|
|
"redirect_stdout", "redirect_stderr", "suppress", "aclosing"]
|
|
|
|
+# Backwards compatibility
|
|
+__all__ += ["ContextStack"]
|
|
|
|
class AbstractContextManager(abc.ABC):
|
|
-
|
|
"""An abstract base class for context managers."""
|
|
|
|
__class_getitem__ = classmethod(GenericAlias)
|
|
@@ -60,6 +73,23 @@
|
|
class ContextDecorator(object):
|
|
"A base class or mixin that enables context managers to work as decorators."
|
|
|
|
+ def refresh_cm(self):
|
|
+ """Returns the context manager used to actually wrap the call to the
|
|
+ decorated function.
|
|
+
|
|
+ The default implementation just returns *self*.
|
|
+
|
|
+ Overriding this method allows otherwise one-shot context managers
|
|
+ like _GeneratorContextManager to support use as decorators via
|
|
+ implicit recreation.
|
|
+
|
|
+ DEPRECATED: refresh_cm was never added to the standard library's
|
|
+ ContextDecorator API
|
|
+ """
|
|
+ warnings.warn("refresh_cm was never added to the standard library",
|
|
+ DeprecationWarning)
|
|
+ return self._recreate_cm()
|
|
+
|
|
def _recreate_cm(self):
|
|
"""Return a recreated instance of self.
|
|
|
|
@@ -430,7 +460,9 @@
|
|
return MethodType(cm_exit, cm)
|
|
|
|
@staticmethod
|
|
- def _create_cb_wrapper(callback, /, *args, **kwds):
|
|
+ def _create_cb_wrapper(*args, **kwds):
|
|
+ # Python 3.6/3.7 compatibility: no native positional-only args syntax
|
|
+ callback, *args = args
|
|
def _exit_wrapper(exc_type, exc, tb):
|
|
callback(*args, **kwds)
|
|
return _exit_wrapper
|
|
@@ -479,11 +511,18 @@
|
|
self._push_cm_exit(cm, _exit)
|
|
return result
|
|
|
|
- def callback(self, callback, /, *args, **kwds):
|
|
+ def callback(*args, **kwds):
|
|
"""Registers an arbitrary callback and arguments.
|
|
|
|
Cannot suppress exceptions.
|
|
"""
|
|
+ # Python 3.6/3.7 compatibility: no native positional-only args syntax
|
|
+ try:
|
|
+ self, callback, *args = args
|
|
+ except ValueError as exc:
|
|
+ exc_details = str(exc).partition("(")[2]
|
|
+ msg = "Not enough positional arguments {}".format(exc_details)
|
|
+ raise TypeError(msg) from None
|
|
_exit_wrapper = self._create_cb_wrapper(callback, *args, **kwds)
|
|
|
|
# We changed the signature, so using @wraps is not appropriate, but
|
|
@@ -589,7 +628,9 @@
|
|
return MethodType(cm_exit, cm)
|
|
|
|
@staticmethod
|
|
- def _create_async_cb_wrapper(callback, /, *args, **kwds):
|
|
+ def _create_async_cb_wrapper(*args, **kwds):
|
|
+ # Python 3.6/3.7 compatibility: no native positional-only args syntax
|
|
+ callback, *args = args
|
|
async def _exit_wrapper(exc_type, exc, tb):
|
|
await callback(*args, **kwds)
|
|
return _exit_wrapper
|
|
@@ -624,11 +665,18 @@
|
|
self._push_async_cm_exit(exit, exit_method)
|
|
return exit # Allow use as a decorator
|
|
|
|
- def push_async_callback(self, callback, /, *args, **kwds):
|
|
+ def push_async_callback(*args, **kwds):
|
|
"""Registers an arbitrary coroutine function and arguments.
|
|
|
|
Cannot suppress exceptions.
|
|
"""
|
|
+ # Python 3.6/3.7 compatibility: no native positional-only args syntax
|
|
+ try:
|
|
+ self, callback, *args = args
|
|
+ except ValueError as exc:
|
|
+ exc_details = str(exc).partition("(")[2]
|
|
+ msg = "Not enough positional arguments {}".format(exc_details)
|
|
+ raise TypeError(msg) from None
|
|
_exit_wrapper = self._create_async_cb_wrapper(callback, *args, **kwds)
|
|
|
|
# We changed the signature, so using @wraps is not appropriate, but
|
|
@@ -729,3 +777,22 @@
|
|
|
|
async def __aexit__(self, *excinfo):
|
|
pass
|
|
+
|
|
+
|
|
+# Preserve backwards compatibility
|
|
+class ContextStack(ExitStack):
|
|
+ """Backwards compatibility alias for ExitStack"""
|
|
+
|
|
+ def __init__(self):
|
|
+ warnings.warn("ContextStack has been renamed to ExitStack",
|
|
+ DeprecationWarning)
|
|
+ super(ContextStack, self).__init__()
|
|
+
|
|
+ def register_exit(self, callback):
|
|
+ return self.push(callback)
|
|
+
|
|
+ def register(self, callback, *args, **kwds):
|
|
+ return self.callback(callback, *args, **kwds)
|
|
+
|
|
+ def preserve(self):
|
|
+ return self.pop_all()
|