comparison mercurial/error.py @ 32639:6df193b5c437

py3: implement __bytes__() on most of our exception classes We store bytes in exc.args, which should be translated to a byte string without encode/decode dance. IOError subclasses are unchanged for now. We'll need to decide how our IOErrors should be caught.
author Yuya Nishihara <yuya@tcha.org>
date Thu, 01 Jun 2017 22:43:24 +0900
parents 19df975eb555
children 61714510220d
comparison
equal deleted inserted replaced
32638:c9318beb7c1a 32639:6df193b5c437
11 imports. 11 imports.
12 """ 12 """
13 13
14 from __future__ import absolute_import 14 from __future__ import absolute_import
15 15
16 # Do not import anything here, please 16 # Do not import anything but pycompat here, please
17 from . import pycompat
18
19 def _tobytes(exc):
20 """Byte-stringify exception in the same way as BaseException_str()"""
21 if not exc.args:
22 return b''
23 if len(exc.args) == 1:
24 return pycompat.bytestr(exc.args[0])
25 return b'(%s)' % b', '.join(b"'%s'" % pycompat.bytestr(a) for a in exc.args)
17 26
18 class Hint(object): 27 class Hint(object):
19 """Mix-in to provide a hint of an error 28 """Mix-in to provide a hint of an error
20 29
21 This should come first in the inheritance list to consume a hint and 30 This should come first in the inheritance list to consume a hint and
24 def __init__(self, *args, **kw): 33 def __init__(self, *args, **kw):
25 self.hint = kw.pop(r'hint', None) 34 self.hint = kw.pop(r'hint', None)
26 super(Hint, self).__init__(*args, **kw) 35 super(Hint, self).__init__(*args, **kw)
27 36
28 class RevlogError(Hint, Exception): 37 class RevlogError(Hint, Exception):
29 pass 38 __bytes__ = _tobytes
30 39
31 class FilteredIndexError(IndexError): 40 class FilteredIndexError(IndexError):
32 pass 41 __bytes__ = _tobytes
33 42
34 class LookupError(RevlogError, KeyError): 43 class LookupError(RevlogError, KeyError):
35 def __init__(self, name, index, message): 44 def __init__(self, name, index, message):
36 self.name = name 45 self.name = name
37 self.index = index 46 self.index = index
41 if isinstance(name, str) and len(name) == 20: 50 if isinstance(name, str) and len(name) == 20:
42 from .node import short 51 from .node import short
43 name = short(name) 52 name = short(name)
44 RevlogError.__init__(self, '%s@%s: %s' % (index, name, message)) 53 RevlogError.__init__(self, '%s@%s: %s' % (index, name, message))
45 54
55 def __bytes__(self):
56 return RevlogError.__bytes__(self)
57
46 def __str__(self): 58 def __str__(self):
47 return RevlogError.__str__(self) 59 return RevlogError.__str__(self)
48 60
49 class FilteredLookupError(LookupError): 61 class FilteredLookupError(LookupError):
50 pass 62 pass
52 class ManifestLookupError(LookupError): 64 class ManifestLookupError(LookupError):
53 pass 65 pass
54 66
55 class CommandError(Exception): 67 class CommandError(Exception):
56 """Exception raised on errors in parsing the command line.""" 68 """Exception raised on errors in parsing the command line."""
69 __bytes__ = _tobytes
57 70
58 class InterventionRequired(Hint, Exception): 71 class InterventionRequired(Hint, Exception):
59 """Exception raised when a command requires human intervention.""" 72 """Exception raised when a command requires human intervention."""
73 __bytes__ = _tobytes
60 74
61 class Abort(Hint, Exception): 75 class Abort(Hint, Exception):
62 """Raised if a command needs to print an error and exit.""" 76 """Raised if a command needs to print an error and exit."""
77 __bytes__ = _tobytes
63 78
64 class HookLoadError(Abort): 79 class HookLoadError(Abort):
65 """raised when loading a hook fails, aborting an operation 80 """raised when loading a hook fails, aborting an operation
66 81
67 Exists to allow more specialized catching.""" 82 Exists to allow more specialized catching."""
92 from .i18n import _ 107 from .i18n import _
93 Abort.__init__(self, _('response expected')) 108 Abort.__init__(self, _('response expected'))
94 109
95 class OutOfBandError(Hint, Exception): 110 class OutOfBandError(Hint, Exception):
96 """Exception raised when a remote repo reports failure""" 111 """Exception raised when a remote repo reports failure"""
112 __bytes__ = _tobytes
97 113
98 class ParseError(Hint, Exception): 114 class ParseError(Hint, Exception):
99 """Raised when parsing config files and {rev,file}sets (msg[, pos])""" 115 """Raised when parsing config files and {rev,file}sets (msg[, pos])"""
116 __bytes__ = _tobytes
100 117
101 class UnknownIdentifier(ParseError): 118 class UnknownIdentifier(ParseError):
102 """Exception raised when a {rev,file}set references an unknown identifier""" 119 """Exception raised when a {rev,file}set references an unknown identifier"""
103 120
104 def __init__(self, function, symbols): 121 def __init__(self, function, symbols):
106 ParseError.__init__(self, _("unknown identifier: %s") % function) 123 ParseError.__init__(self, _("unknown identifier: %s") % function)
107 self.function = function 124 self.function = function
108 self.symbols = symbols 125 self.symbols = symbols
109 126
110 class RepoError(Hint, Exception): 127 class RepoError(Hint, Exception):
111 pass 128 __bytes__ = _tobytes
112 129
113 class RepoLookupError(RepoError): 130 class RepoLookupError(RepoError):
114 pass 131 pass
115 132
116 class FilteredRepoLookupError(RepoLookupError): 133 class FilteredRepoLookupError(RepoLookupError):
125 class StdioError(IOError): 142 class StdioError(IOError):
126 """Raised if I/O to stdout or stderr fails""" 143 """Raised if I/O to stdout or stderr fails"""
127 144
128 def __init__(self, err): 145 def __init__(self, err):
129 IOError.__init__(self, err.errno, err.strerror) 146 IOError.__init__(self, err.errno, err.strerror)
147
148 # no __bytes__() because error message is derived from the standard IOError
130 149
131 class UnsupportedMergeRecords(Abort): 150 class UnsupportedMergeRecords(Abort):
132 def __init__(self, recordtypes): 151 def __init__(self, recordtypes):
133 from .i18n import _ 152 from .i18n import _
134 self.recordtypes = sorted(recordtypes) 153 self.recordtypes = sorted(recordtypes)
149 class LockError(IOError): 168 class LockError(IOError):
150 def __init__(self, errno, strerror, filename, desc): 169 def __init__(self, errno, strerror, filename, desc):
151 IOError.__init__(self, errno, strerror, filename) 170 IOError.__init__(self, errno, strerror, filename)
152 self.desc = desc 171 self.desc = desc
153 172
173 # no __bytes__() because error message is derived from the standard IOError
174
154 class LockHeld(LockError): 175 class LockHeld(LockError):
155 def __init__(self, errno, filename, desc, locker): 176 def __init__(self, errno, filename, desc, locker):
156 LockError.__init__(self, errno, 'Lock held', filename, desc) 177 LockError.__init__(self, errno, 'Lock held', filename, desc)
157 self.locker = locker 178 self.locker = locker
158 179
159 class LockUnavailable(LockError): 180 class LockUnavailable(LockError):
160 pass 181 pass
161 182
162 # LockError is for errors while acquiring the lock -- this is unrelated 183 # LockError is for errors while acquiring the lock -- this is unrelated
163 class LockInheritanceContractViolation(RuntimeError): 184 class LockInheritanceContractViolation(RuntimeError):
164 pass 185 __bytes__ = _tobytes
165 186
166 class ResponseError(Exception): 187 class ResponseError(Exception):
167 """Raised to print an error with part of output and exit.""" 188 """Raised to print an error with part of output and exit."""
189 __bytes__ = _tobytes
168 190
169 class UnknownCommand(Exception): 191 class UnknownCommand(Exception):
170 """Exception raised if command is not in the command table.""" 192 """Exception raised if command is not in the command table."""
193 __bytes__ = _tobytes
171 194
172 class AmbiguousCommand(Exception): 195 class AmbiguousCommand(Exception):
173 """Exception raised if command shortcut matches more than one command.""" 196 """Exception raised if command shortcut matches more than one command."""
197 __bytes__ = _tobytes
174 198
175 # derived from KeyboardInterrupt to simplify some breakout code 199 # derived from KeyboardInterrupt to simplify some breakout code
176 class SignalInterrupt(KeyboardInterrupt): 200 class SignalInterrupt(KeyboardInterrupt):
177 """Exception raised on SIGTERM and SIGHUP.""" 201 """Exception raised on SIGTERM and SIGHUP."""
178 202
179 class SignatureError(Exception): 203 class SignatureError(Exception):
180 pass 204 __bytes__ = _tobytes
181 205
182 class PushRaced(RuntimeError): 206 class PushRaced(RuntimeError):
183 """An exception raised during unbundling that indicate a push race""" 207 """An exception raised during unbundling that indicate a push race"""
208 __bytes__ = _tobytes
184 209
185 class ProgrammingError(Hint, RuntimeError): 210 class ProgrammingError(Hint, RuntimeError):
186 """Raised if a mercurial (core or extension) developer made a mistake""" 211 """Raised if a mercurial (core or extension) developer made a mistake"""
212 __bytes__ = _tobytes
187 213
188 class WdirUnsupported(Exception): 214 class WdirUnsupported(Exception):
189 """An exception which is raised when 'wdir()' is not supported""" 215 """An exception which is raised when 'wdir()' is not supported"""
216 __bytes__ = _tobytes
190 217
191 # bundle2 related errors 218 # bundle2 related errors
192 class BundleValueError(ValueError): 219 class BundleValueError(ValueError):
193 """error raised when bundle2 cannot be processed""" 220 """error raised when bundle2 cannot be processed"""
221 __bytes__ = _tobytes
194 222
195 class BundleUnknownFeatureError(BundleValueError): 223 class BundleUnknownFeatureError(BundleValueError):
196 def __init__(self, parttype=None, params=(), values=()): 224 def __init__(self, parttype=None, params=(), values=()):
197 self.parttype = parttype 225 self.parttype = parttype
198 self.params = params 226 self.params = params
215 msg = '%s - %s' % (msg, ', '.join(entries)) 243 msg = '%s - %s' % (msg, ', '.join(entries))
216 ValueError.__init__(self, msg) 244 ValueError.__init__(self, msg)
217 245
218 class ReadOnlyPartError(RuntimeError): 246 class ReadOnlyPartError(RuntimeError):
219 """error raised when code tries to alter a part being generated""" 247 """error raised when code tries to alter a part being generated"""
248 __bytes__ = _tobytes
220 249
221 class PushkeyFailed(Abort): 250 class PushkeyFailed(Abort):
222 """error raised when a pushkey part failed to update a value""" 251 """error raised when a pushkey part failed to update a value"""
223 252
224 def __init__(self, partid, namespace=None, key=None, new=None, old=None, 253 def __init__(self, partid, namespace=None, key=None, new=None, old=None,
255 class InvalidBundleSpecification(Exception): 284 class InvalidBundleSpecification(Exception):
256 """error raised when a bundle specification is invalid. 285 """error raised when a bundle specification is invalid.
257 286
258 This is used for syntax errors as opposed to support errors. 287 This is used for syntax errors as opposed to support errors.
259 """ 288 """
289 __bytes__ = _tobytes
260 290
261 class UnsupportedBundleSpecification(Exception): 291 class UnsupportedBundleSpecification(Exception):
262 """error raised when a bundle specification is not supported.""" 292 """error raised when a bundle specification is not supported."""
293 __bytes__ = _tobytes
263 294
264 class CorruptedState(Exception): 295 class CorruptedState(Exception):
265 """error raised when a command is not able to read its state from file""" 296 """error raised when a command is not able to read its state from file"""
297 __bytes__ = _tobytes
266 298
267 class PeerTransportError(Abort): 299 class PeerTransportError(Abort):
268 """Transport-level I/O error when communicating with a peer repo.""" 300 """Transport-level I/O error when communicating with a peer repo."""