1 2from __future__ import absolute_import, print_function 3from array import array 4from binascii import hexlify 5from traceback import print_exception 6import os, sys 7 8# Please see CODINGSTYLE for important exception handling guidelines 9# and the rationale behind add_ex_tb(), add_ex_ctx(), etc. 10 11py_maj = sys.version_info[0] 12py3 = py_maj >= 3 13 14if py3: 15 16 from os import environb as environ 17 from os import fsdecode, fsencode 18 from shlex import quote 19 ModuleNotFoundError = ModuleNotFoundError 20 input = input 21 range = range 22 str_type = str 23 int_types = (int,) 24 25 def hexstr(b): 26 """Return hex string (not bytes as with hexlify) representation of b.""" 27 return b.hex() 28 29 def reraise(ex): 30 raise ex.with_traceback(sys.exc_info()[2]) 31 32 def add_ex_tb(ex): 33 """Do nothing (already handled by Python 3 infrastructure).""" 34 return ex 35 36 def add_ex_ctx(ex, context_ex): 37 """Do nothing (already handled by Python 3 infrastructure).""" 38 return ex 39 40 def items(x): 41 return x.items() 42 43 def argv_bytes(x): 44 """Return the original bytes passed to main() for an argv argument.""" 45 return fsencode(x) 46 47 def bytes_from_uint(i): 48 return bytes((i,)) 49 50 def bytes_from_byte(b): # python > 2: b[3] returns ord('x'), not b'x' 51 return bytes((b,)) 52 53 byte_int = lambda x: x 54 55 def buffer(object, offset=None, size=None): 56 if size: 57 assert offset is not None 58 return memoryview(object)[offset:offset + size] 59 if offset: 60 return memoryview(object)[offset:] 61 return memoryview(object) 62 63 def getcwd(): 64 return fsencode(os.getcwd()) 65 66else: # Python 2 67 68 ModuleNotFoundError = ImportError 69 70 def fsdecode(x): 71 return x 72 73 def fsencode(x): 74 return x 75 76 from pipes import quote 77 from os import environ, getcwd 78 79 from bup.py2raise import reraise 80 81 input = raw_input 82 range = xrange 83 str_type = basestring 84 int_types = (int, long) 85 86 hexstr = hexlify 87 88 def add_ex_tb(ex): 89 """Add a traceback to ex if it doesn't already have one. Return ex. 90 91 """ 92 if not getattr(ex, '__traceback__', None): 93 ex.__traceback__ = sys.exc_info()[2] 94 return ex 95 96 def add_ex_ctx(ex, context_ex): 97 """Make context_ex the __context__ of ex (unless it already has one). 98 Return ex. 99 100 """ 101 if context_ex: 102 if not getattr(ex, '__context__', None): 103 ex.__context__ = context_ex 104 return ex 105 106 def dump_traceback(ex): 107 stack = [ex] 108 next_ex = getattr(ex, '__context__', None) 109 while next_ex: 110 stack.append(next_ex) 111 next_ex = getattr(next_ex, '__context__', None) 112 stack = reversed(stack) 113 ex = next(stack) 114 tb = getattr(ex, '__traceback__', None) 115 print_exception(type(ex), ex, tb) 116 for ex in stack: 117 print('\nDuring handling of the above exception, another exception occurred:\n', 118 file=sys.stderr) 119 tb = getattr(ex, '__traceback__', None) 120 print_exception(type(ex), ex, tb) 121 122 def items(x): 123 return x.iteritems() 124 125 def argv_bytes(x): 126 """Return the original bytes passed to main() for an argv argument.""" 127 return x 128 129 bytes_from_uint = chr 130 131 def bytes_from_byte(b): 132 return b 133 134 byte_int = ord 135 136 buffer = buffer 137 138 139argv = None 140argvb = None 141 142def _configure_argv(): 143 global argv, argvb 144 assert not argv 145 assert not argvb 146 if len(sys.argv) > 1: 147 if environ.get(b'BUP_ARGV_0'): 148 print('error: BUP_ARGV* set and sys.argv not empty', file=sys.stderr) 149 sys.exit(2) 150 argv = sys.argv 151 argvb = [argv_bytes(x) for x in argv] 152 return 153 args = [] 154 i = 0 155 arg = environ.get(b'BUP_ARGV_%d' % i) 156 while arg is not None: 157 args.append(arg) 158 i += 1 159 arg = environ.get(b'BUP_ARGV_%d' % i) 160 i -= 1 161 while i >= 0: 162 del environ[b'BUP_ARGV_%d' % i] 163 i -= 1 164 argvb = args 165 # System encoding? 166 if py3: 167 argv = [x.decode(errors='surrogateescape') for x in args] 168 else: 169 argv = argvb 170 171_configure_argv() 172 173 174def wrap_main(main): 175 """Run main() and raise a SystemExit with the return value if it 176 returns, pass along any SystemExit it raises, convert 177 KeyboardInterrupts into exit(130), and print a Python 3 style 178 contextual backtrace for other exceptions in both Python 2 and 179 3).""" 180 try: 181 sys.exit(main()) 182 except KeyboardInterrupt as ex: 183 sys.exit(130) 184 except SystemExit as ex: 185 raise 186 except BaseException as ex: 187 if py3: 188 raise 189 add_ex_tb(ex) 190 dump_traceback(ex) 191 sys.exit(1) 192 193 194# Excepting wrap_main() in the traceback, these should produce similar output: 195# python2 lib/bup/compat.py 196# python3 lib/bup/compat.py 197# i.e.: 198# diff -u <(python2 lib/bup/compat.py 2>&1) <(python3 lib/bup/compat.py 2>&1) 199# 200# Though the python3 output for 'second' will include a stacktrace 201# starting from wrap_main, rather than from outer(). 202 203if __name__ == '__main__': 204 205 def inner(): 206 raise Exception('first') 207 208 def outer(): 209 try: 210 inner() 211 except Exception as ex: 212 add_ex_tb(ex) 213 try: 214 raise Exception('second') 215 except Exception as ex2: 216 raise add_ex_ctx(add_ex_tb(ex2), ex) 217 218 wrap_main(outer) 219