]> arthur.barton.de Git - bup.git/blob - lib/bup/compat.py
895a84a3da86ac65c63ba184e94c341cdef15d14
[bup.git] / lib / bup / compat.py
1
2 from __future__ import absolute_import, print_function
3 from traceback import print_exception
4 import sys
5
6 # Please see CODINGSTYLE for important exception handling guidelines
7 # and the rationale behind add_ex_tb(), chain_ex(), etc.
8
9 py_maj = sys.version_info[0]
10 py3 = py_maj >= 3
11
12 if py3:
13
14     range = range
15     str_type = str
16
17     def add_ex_tb(ex):
18         """Do nothing (already handled by Python 3 infrastructure)."""
19         return ex
20
21     def chain_ex(ex, context_ex):
22         """Do nothing (already handled by Python 3 infrastructure)."""
23         return ex
24
25     def items(x):
26         return x.items()
27
28 else:  # Python 2
29
30     range = xrange
31     str_type = basestring
32
33     def add_ex_tb(ex):
34         """Add a traceback to ex if it doesn't already have one.  Return ex.
35
36         """
37         if not getattr(ex, '__traceback__', None):
38             ex.__traceback__ = sys.exc_info()[2]
39         return ex
40
41     def chain_ex(ex, context_ex):
42         """Chain context_ex to ex as the __context__ (unless it already has
43         one).  Return ex.
44
45         """
46         if context_ex:
47             if not getattr(ex, '__context__', None):
48                 ex.__context__ = context_ex
49         return ex
50
51     def dump_traceback(ex):
52         stack = [ex]
53         next_ex = getattr(ex, '__context__', None)
54         while next_ex:
55             stack.append(next_ex)
56             next_ex = getattr(next_ex, '__context__', None)
57         stack = reversed(stack)
58         ex = next(stack)
59         tb = getattr(ex, '__traceback__', None)
60         print_exception(type(ex), ex, tb)
61         for ex in stack:
62             print('\nDuring handling of the above exception, another exception occurred:\n',
63                   file=sys.stderr)
64             tb = getattr(ex, '__traceback__', None)
65             print_exception(type(ex), ex, tb)
66
67     def items(x):
68         return x.iteritems()
69
70
71 def wrap_main(main):
72     """Run main() and raise a SystemExit with the return value if it
73     returns, pass along any SystemExit it raises, convert
74     KeyboardInterrupts into exit(130), and print a Python 3 style
75     contextual backtrace for other exceptions in both Python 2 and
76     3)."""
77     try:
78         sys.exit(main())
79     except KeyboardInterrupt as ex:
80         sys.exit(130)
81     except SystemExit as ex:
82         raise
83     except BaseException as ex:
84         if py3:
85             raise
86         add_ex_tb(ex)
87         dump_traceback(ex)
88         sys.exit(1)
89
90
91 # Excepting wrap_main() in the traceback, these should produce similar output:
92 #   python2 lib/bup/compat.py
93 #   python3 lib/bup/compat.py
94 # i.e.:
95 #   diff -u <(python2 lib/bup/compat.py 2>&1) <(python3 lib/bup/compat.py 2>&1)
96 #
97 # Though the python3 output for 'second' will include a stacktrace
98 # starting from wrap_main, rather than from outer().
99
100 if __name__ == '__main__':
101
102     def inner():
103         raise Exception('first')
104
105     def outer():
106         try:
107             inner()
108         except Exception as ex:
109             add_ex_tb(ex)
110             try:
111                 raise Exception('second')
112             except Exception as ex2:
113                 raise chain_ex(add_ex_tb(ex2), ex)
114
115     wrap_main(outer)