]> arthur.barton.de Git - bup.git/blobdiff - lib/bup/compat.py
Detect failures to explicitly close mmaps in py3 too
[bup.git] / lib / bup / compat.py
index 72722017addc5dd3cb52589576576b8a99a1b04b..00575036e27cc46f7fb6b3408e273831526b9daa 100644 (file)
@@ -13,6 +13,7 @@ py3 = py_maj >= 3
 if py3:
 
     # pylint: disable=unused-import
 if py3:
 
     # pylint: disable=unused-import
+    from contextlib import ExitStack, nullcontext
     from os import environb as environ
     from os import fsdecode, fsencode
     from shlex import quote
     from os import environb as environ
     from os import fsdecode, fsencode
     from shlex import quote
@@ -40,17 +41,23 @@ if py3:
         return ex
 
     class pending_raise:
         return ex
 
     class pending_raise:
-        """Rethrow either the provided ex, or any exception raised by the with
-        statement body.  (Supports Python 2 compatibility.)
+        """If rethrow is true, rethrow ex (if any), unless the body throws.
+
+        (Supports Python 2 compatibility.)
 
         """
 
         """
-        def __init__(self, ex):
+        def __init__(self, ex, rethrow=True):
+            self.closed = False
             self.ex = ex
             self.ex = ex
+            self.rethrow = rethrow
         def __enter__(self):
             return None
         def __exit__(self, exc_type, exc_value, traceback):
         def __enter__(self):
             return None
         def __exit__(self, exc_type, exc_value, traceback):
-            if not exc_type:
+            self.closed = True
+            if not exc_type and self.ex and self.rethrow:
                 raise self.ex
                 raise self.ex
+        def __del__(self):
+            assert self.closed
 
     def items(x):
         return x.items()
 
     def items(x):
         return x.items()
@@ -80,6 +87,8 @@ if py3:
 
 else:  # Python 2
 
 
 else:  # Python 2
 
+    from contextlib import contextmanager
+
     ModuleNotFoundError = ImportError
 
     def fsdecode(x):
     ModuleNotFoundError = ImportError
 
     def fsdecode(x):
@@ -95,6 +104,10 @@ else:  # Python 2
     # pylint: disable=unused-import
     from bup.py2raise import reraise
 
     # pylint: disable=unused-import
     from bup.py2raise import reraise
 
+    @contextmanager
+    def nullcontext(enter_result=None):
+        yield enter_result
+
     # on py3 this causes errors, obviously
     # pylint: disable=undefined-variable
     input = raw_input
     # on py3 this causes errors, obviously
     # pylint: disable=undefined-variable
     input = raw_input
@@ -126,22 +139,32 @@ else:  # Python 2
         return ex
 
     class pending_raise:
         return ex
 
     class pending_raise:
-        """Rethrow either the provided ex, or any exception raised by the with
-        statement body, after making ex the __context__ of the newer
-        exception (assuming there's no existing __context__).  Ensure
-        the exceptions have __tracebacks__.  (Supports Python 2
-        compatibility.)
+        """If rethrow is true, rethrow ex (if any), unless the body throws.
+
+        If the body does throw, make any provided ex the __context__
+        of the newer exception (assuming there's no existing
+        __context__).  Ensure the exceptions have __tracebacks__.
+        (Supports Python 2 compatibility.)
 
         """
 
         """
-        def __init__(self, ex):
+        def __init__(self, ex, rethrow=True):
+            self.closed = False
             self.ex = ex
             self.ex = ex
+            self.rethrow = rethrow
         def __enter__(self):
         def __enter__(self):
-            add_ex_tb(self.ex)
+            if self.ex:
+                add_ex_tb(self.ex)
         def __exit__(self, exc_type, exc_value, traceback):
         def __exit__(self, exc_type, exc_value, traceback):
-            if not exc_type:
+            self.closed = True
+            if exc_value:
+                if self.ex:
+                    add_ex_tb(exc_value)
+                    add_ex_ctx(exc_value, self.ex)
+                return
+            if self.rethrow and self.ex:
                 raise self.ex
                 raise self.ex
-            add_ex_tb(exc_value)
-            add_ex_ctx(exc_value, self.ex)
+        def __del__(self):
+            assert self.closed
 
     def dump_traceback(ex):
         stack = [ex]
 
     def dump_traceback(ex):
         stack = [ex]
@@ -159,6 +182,31 @@ else:  # Python 2
             tb = getattr(ex, '__traceback__', None)
             print_exception(type(ex), ex, tb)
 
             tb = getattr(ex, '__traceback__', None)
             print_exception(type(ex), ex, tb)
 
+    class ExitStack:
+        def __init__(self):
+            self.contexts = []
+
+        def __enter__(self):
+            return self
+
+        def __exit__(self, value_type, value, traceback):
+            init_value = value
+            for ctx in reversed(self.contexts):
+                try:
+                    ctx.__exit__(value_type, value, traceback)
+                except BaseException as ex:
+                    add_ex_tb(ex)
+                    if value:
+                        add_ex_ctx(ex, value)
+                    value_type = type(ex)
+                    value = ex
+                    traceback = ex.__traceback__
+            if value is not init_value:
+                raise value
+
+        def enter_context(self, x):
+            self.contexts.append(x)
+
     def items(x):
         return x.iteritems()
 
     def items(x):
         return x.iteritems()