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