-from __future__ import absolute_import, print_function
-from array import array
-from traceback import print_exception
-import sys
-
-# Please see CODINGSTYLE for important exception handling guidelines
-# and the rationale behind add_ex_tb(), add_ex_ctx(), etc.
-
-py_maj = sys.version_info[0]
-py3 = py_maj >= 3
-
-if py3:
-
- from os import environb as environ
-
- lc_ctype = environ.get(b'LC_CTYPE')
- if lc_ctype and lc_ctype.lower() != b'iso-8859-1':
- # Because of argv, options.py, pwd, grp, and any number of other issues
- print('error: bup currently only works with ISO-8859-1, not LC_CTYPE=%s'
- % lc_ctype.decode(),
- file=sys.stderr)
- print('error: this should already have been arranged, so indicates a bug',
- file=sys.stderr)
- sys.exit(2)
-
- from os import fsencode
- from shlex import quote
- range = range
- str_type = str
-
- def add_ex_tb(ex):
- """Do nothing (already handled by Python 3 infrastructure)."""
- return ex
-
- def add_ex_ctx(ex, context_ex):
- """Do nothing (already handled by Python 3 infrastructure)."""
- return ex
-
- def items(x):
- return x.items()
-
- def argv_bytes(x):
- """Return the original bytes passed to main() for an argv argument."""
- return fsencode(x)
-
- def bytes_from_uint(i):
- return bytes((i,))
-
- byte_int = lambda x: x
-
- def buffer(object, offset=None, size=None):
- if size:
- assert offset is not None
- return memoryview(object)[offset:offset + size]
- if offset:
- return memoryview(object)[offset:]
- return memoryview(object)
-
- def join_bytes(*items):
- """Return the concatenated bytes or memoryview arguments as bytes."""
- return b''.join(items)
-
-else: # Python 2
-
- def fsencode(x):
- return x
-
- from pipes import quote
- from os import environ
- range = xrange
- str_type = basestring
-
- def add_ex_tb(ex):
- """Add a traceback to ex if it doesn't already have one. Return ex.
-
- """
- if not getattr(ex, '__traceback__', None):
- ex.__traceback__ = sys.exc_info()[2]
- return ex
-
- def add_ex_ctx(ex, context_ex):
- """Make context_ex the __context__ of ex (unless it already has one).
- Return ex.
-
- """
- if context_ex:
- if not getattr(ex, '__context__', None):
- ex.__context__ = context_ex
- return ex
-
- def dump_traceback(ex):
- stack = [ex]
- next_ex = getattr(ex, '__context__', None)
- while next_ex:
- stack.append(next_ex)
- next_ex = getattr(next_ex, '__context__', None)
- stack = reversed(stack)
- ex = next(stack)
- tb = getattr(ex, '__traceback__', None)
- print_exception(type(ex), ex, tb)
- for ex in stack:
- print('\nDuring handling of the above exception, another exception occurred:\n',
- file=sys.stderr)
- tb = getattr(ex, '__traceback__', None)
- print_exception(type(ex), ex, tb)
-
- def items(x):
- return x.iteritems()
-
- def argv_bytes(x):
- """Return the original bytes passed to main() for an argv argument."""
- return x
-
- def bytes_from_uint(i):
- return chr(i)
-
- byte_int = ord
-
- buffer = buffer
-
- def join_bytes(x, y):
- """Return the concatenated bytes or buffer arguments as bytes."""
- if type(x) == buffer:
- assert type(y) in (bytes, buffer)
- return x + y
- assert type(x) == bytes
- if type(y) == bytes:
- return b''.join((x, y))
- assert type(y) in (bytes, buffer)
- return buffer(x) + y
+import os, sys
+
+py_maj = sys.version_info.major
+assert py_maj >= 3
+
+# pylint: disable=unused-import
+from contextlib import ExitStack, nullcontext
+from os import environb as environ
+from os import fsdecode, fsencode
+from shlex import quote
+
+range = range
+str_type = str
+int_types = (int,)
+
+def hexstr(b):
+ """Return hex string (not bytes as with hexlify) representation of b."""
+ return b.hex()
+
+def reraise(ex):
+ raise ex.with_traceback(sys.exc_info()[2])
+
+# These three functions (add_ex_tb, add_ex_ctx, and pending_raise) are
+# vestigial, and code that uses them can probably be rewritten more
+# simply now that we require Python versions that automatically
+# populate the tracebacks and automatically chain pending exceptions.
+
+def add_ex_tb(ex):
+ """Do nothing (already handled by Python 3 infrastructure)."""
+ return ex
+
+def add_ex_ctx(ex, context_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.)
+
+ """
+ # This is completely vestigial, and should be removed
+ 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 argv_bytes(x):
+ """Return the original bytes passed to main() for an argv argument."""
+ return fsencode(x)
+
+def bytes_from_uint(i):
+ return bytes((i,))
+
+def bytes_from_byte(b): # python > 2: b[3] returns ord('x'), not b'x'
+ return bytes((b,))
+
+byte_int = lambda x: x
+
+def buffer(object, offset=None, size=None):
+ if size:
+ assert offset is not None
+ return memoryview(object)[offset:offset + size]
+ if offset:
+ return memoryview(object)[offset:]
+ return memoryview(object)
+
+def getcwd():
+ return fsencode(os.getcwd())
+
+
+try:
+ import bup_main
+except ModuleNotFoundError:
+ bup_main = None
+
+if bup_main:
+ def get_argvb():
+ "Return a new list containing the current process argv bytes."
+ return bup_main.argv()
+ 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:
+ 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
sys.exit(main())
except KeyboardInterrupt as ex:
sys.exit(130)
- except SystemExit as ex:
- raise
- except BaseException as ex:
- if py3:
- raise
- add_ex_tb(ex)
- dump_traceback(ex)
- sys.exit(1)
-
-
-# Excepting wrap_main() in the traceback, these should produce similar output:
-# python2 lib/bup/compat.py
-# python3 lib/bup/compat.py
-# i.e.:
-# diff -u <(python2 lib/bup/compat.py 2>&1) <(python3 lib/bup/compat.py 2>&1)
-#
-# Though the python3 output for 'second' will include a stacktrace
-# starting from wrap_main, rather than from outer().
-
-if __name__ == '__main__':
-
- def inner():
- raise Exception('first')
-
- def outer():
- try:
- inner()
- except Exception as ex:
- add_ex_tb(ex)
- try:
- raise Exception('second')
- except Exception as ex2:
- raise add_ex_ctx(add_ex_tb(ex2), ex)
-
- wrap_main(outer)