]> arthur.ath.cx Git - bup.git/blob - lib/bup/compat.py
507a21e5367c1cfb4a66f4e78839ba3d8a7a9101
[bup.git] / lib / bup / compat.py
1
2 import os, sys
3
4 py_maj = sys.version_info.major
5 assert py_maj >= 3
6
7 # pylint: disable=unused-import
8 from contextlib import ExitStack, nullcontext
9 from os import environb as environ
10 from os import fsdecode, fsencode
11 from shlex import quote
12
13 str_type = str
14 int_types = (int,)
15
16 def hexstr(b):
17     """Return hex string (not bytes as with hexlify) representation of b."""
18     return b.hex()
19
20 def reraise(ex):
21     raise ex.with_traceback(sys.exc_info()[2])
22
23 # These three functions (add_ex_tb, add_ex_ctx, and pending_raise) are
24 # vestigial, and code that uses them can probably be rewritten more
25 # simply now that we require Python versions that automatically
26 # populate the tracebacks and automatically chain pending exceptions.
27
28 def add_ex_tb(ex):
29     """Do nothing (already handled by Python 3 infrastructure)."""
30     return ex
31
32 def add_ex_ctx(ex, context_ex):
33     """Do nothing (already handled by Python 3 infrastructure)."""
34     return ex
35
36 class pending_raise:
37     """If rethrow is true, rethrow ex (if any), unless the body throws.
38
39     (Supports Python 2 compatibility.)
40
41     """
42     # This is completely vestigial, and should be removed
43     def __init__(self, ex, rethrow=True):
44         self.closed = False
45         self.ex = ex
46         self.rethrow = rethrow
47     def __enter__(self):
48         return None
49     def __exit__(self, exc_type, exc_value, traceback):
50         self.closed = True
51         if not exc_type and self.ex and self.rethrow:
52             raise self.ex
53     def __del__(self):
54         assert self.closed
55
56 def items(x):
57     return x.items()
58
59 def argv_bytes(x):
60     """Return the original bytes passed to main() for an argv argument."""
61     return fsencode(x)
62
63 def bytes_from_uint(i):
64     return bytes((i,))
65
66 def bytes_from_byte(b):  # python > 2: b[3] returns ord('x'), not b'x'
67     return bytes((b,))
68
69 byte_int = lambda x: x
70
71 def buffer(object, offset=None, size=None):
72     if size:
73         assert offset is not None
74         return memoryview(object)[offset:offset + size]
75     if offset:
76         return memoryview(object)[offset:]
77     return memoryview(object)
78
79 def getcwd():
80     return fsencode(os.getcwd())
81
82
83 try:
84     import bup_main
85 except ModuleNotFoundError:
86     bup_main = None
87
88 if bup_main:
89     def get_argvb():
90         "Return a new list containing the current process argv bytes."
91         return bup_main.argv()
92     def get_argv():
93         "Return a new list containing the current process argv strings."
94         return [x.decode(errors='surrogateescape') for x in bup_main.argv()]
95 else:
96     def get_argvb():
97         raise Exception('get_argvb requires the bup_main module');
98     def get_argv():
99         raise Exception('get_argv requires the bup_main module');
100
101 def wrap_main(main):
102     """Run main() and raise a SystemExit with the return value if it
103     returns, pass along any SystemExit it raises, convert
104     KeyboardInterrupts into exit(130), and print a Python 3 style
105     contextual backtrace for other exceptions in both Python 2 and
106     3)."""
107     try:
108         sys.exit(main())
109     except KeyboardInterrupt as ex:
110         sys.exit(130)