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