]> 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 1ed2d0f70c9fc18f7afc2011a2e55381aa047ca4..00575036e27cc46f7fb6b3408e273831526b9daa 100644 (file)
@@ -1,6 +1,5 @@
 
 from __future__ import absolute_import, print_function
 
 from __future__ import absolute_import, print_function
-from array import array
 from binascii import hexlify
 from traceback import print_exception
 import os, sys
 from binascii import hexlify
 from traceback import print_exception
 import os, sys
@@ -13,9 +12,14 @@ py3 = py_maj >= 3
 
 if py3:
 
 
 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
+    # pylint: disable=undefined-variable
+    # (for python2 looking here)
+    ModuleNotFoundError = ModuleNotFoundError
     input = input
     range = range
     str_type = str
     input = input
     range = range
     str_type = str
@@ -36,6 +40,25 @@ if py3:
         """Do nothing (already handled by Python 3 infrastructure)."""
         return ex
 
         """Do nothing (already handled by Python 3 infrastructure)."""
         return ex
 
+    class pending_raise:
+        """If rethrow is true, rethrow ex (if any), unless the body throws.
+
+        (Supports Python 2 compatibility.)
+
+        """
+        def __init__(self, ex, rethrow=True):
+            self.closed = False
+            self.ex = ex
+            self.rethrow = rethrow
+        def __enter__(self):
+            return None
+        def __exit__(self, exc_type, exc_value, traceback):
+            self.closed = True
+            if not exc_type and self.ex and self.rethrow:
+                raise self.ex
+        def __del__(self):
+            assert self.closed
+
     def items(x):
         return x.items()
 
     def items(x):
         return x.items()
 
@@ -64,6 +87,10 @@ if py3:
 
 else:  # Python 2
 
 
 else:  # Python 2
 
+    from contextlib import contextmanager
+
+    ModuleNotFoundError = ImportError
+
     def fsdecode(x):
         return x
 
     def fsdecode(x):
         return x
 
@@ -71,13 +98,24 @@ else:  # Python 2
         return x
 
     from pipes import quote
         return x
 
     from pipes import quote
+    # pylint: disable=unused-import
     from os import environ, getcwd
 
     from os import environ, getcwd
 
+    # pylint: disable=unused-import
     from bup.py2raise import reraise
 
     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
     input = raw_input
+    # pylint: disable=undefined-variable
     range = xrange
     range = xrange
+    # pylint: disable=undefined-variable
     str_type = basestring
     str_type = basestring
+    # pylint: disable=undefined-variable
     int_types = (int, long)
 
     hexstr = hexlify
     int_types = (int, long)
 
     hexstr = hexlify
@@ -100,6 +138,34 @@ else:  # Python 2
                 ex.__context__ = context_ex
         return ex
 
                 ex.__context__ = context_ex
         return ex
 
+    class pending_raise:
+        """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, rethrow=True):
+            self.closed = False
+            self.ex = ex
+            self.rethrow = rethrow
+        def __enter__(self):
+            if self.ex:
+                add_ex_tb(self.ex)
+        def __exit__(self, exc_type, exc_value, traceback):
+            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
+        def __del__(self):
+            assert self.closed
+
     def dump_traceback(ex):
         stack = [ex]
         next_ex = getattr(ex, '__context__', None)
     def dump_traceback(ex):
         stack = [ex]
         next_ex = getattr(ex, '__context__', None)
@@ -116,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()
 
@@ -132,41 +223,27 @@ else:  # Python 2
 
     buffer = buffer
 
 
     buffer = buffer
 
+try:
+    import bup_main
+except ModuleNotFoundError:
+    bup_main = None
 
 
-argv = None
-argvb = None
-
-def _configure_argv():
-    global argv, argvb
-    assert not argv
-    assert not argvb
-    if len(sys.argv) > 1:
-        if environ.get(b'BUP_ARGV_0'):
-            print('error: BUP_ARGV* set and sys.argv not empty', file=sys.stderr)
-            sys.exit(2)
-        argv = sys.argv
-        argvb = [argv_bytes(x) for x in argv]
-        return
-    args = []
-    i = 0
-    arg = environ.get(b'BUP_ARGV_%d' % i)
-    while arg is not None:
-        args.append(arg)
-        i += 1
-        arg = environ.get(b'BUP_ARGV_%d' % i)
-    i -= 1
-    while i >= 0:
-        del environ[b'BUP_ARGV_%d' % i]
-        i -= 1
-    argvb = args
-    # System encoding?
+if bup_main:
+    def get_argvb():
+        "Return a new list containing the current process argv bytes."
+        return bup_main.argv()
     if py3:
     if py3:
-        argv = [x.decode(errors='surrogateescape') for x in args]
+        def get_argv():
+            "Return a new list containing the current process argv strings."
+            return [x.decode(errors='surrogateescape') for x in bup_main.argv()]
     else:
     else:
-        argv = argvb
-
-_configure_argv()
-
+        def get_argv():
+            return bup_main.argv()
+else:
+    def get_argvb():
+        raise Exception('get_argvb requires the bup_main module');
+    def get_argv():
+        raise Exception('get_argv requires the bup_main module');
 
 def wrap_main(main):
     """Run main() and raise a SystemExit with the return value if it
 
 def wrap_main(main):
     """Run main() and raise a SystemExit with the return value if it