##// END OF EJS Templates
error: add `hint` attribute to `SidedataHashError`...
Raphaël Gomès -
r47370:3941fe53 default
parent child Browse files
Show More
@@ -1,585 +1,586
1 1 # error.py - Mercurial exceptions
2 2 #
3 3 # Copyright 2005-2008 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 """Mercurial exceptions.
9 9
10 10 This allows us to catch exceptions at higher levels without forcing
11 11 imports.
12 12 """
13 13
14 14 from __future__ import absolute_import
15 15
16 16 import difflib
17 17
18 18 # Do not import anything but pycompat here, please
19 19 from . import pycompat
20 20
21 21
22 22 def _tobytes(exc):
23 23 """Byte-stringify exception in the same way as BaseException_str()"""
24 24 if not exc.args:
25 25 return b''
26 26 if len(exc.args) == 1:
27 27 return pycompat.bytestr(exc.args[0])
28 28 return b'(%s)' % b', '.join(b"'%s'" % pycompat.bytestr(a) for a in exc.args)
29 29
30 30
31 31 class Hint(object):
32 32 """Mix-in to provide a hint of an error
33 33
34 34 This should come first in the inheritance list to consume a hint and
35 35 pass remaining arguments to the exception class.
36 36 """
37 37
38 38 def __init__(self, *args, **kw):
39 39 self.hint = kw.pop('hint', None)
40 40 super(Hint, self).__init__(*args, **kw)
41 41
42 42
43 43 class StorageError(Hint, Exception):
44 44 """Raised when an error occurs in a storage layer.
45 45
46 46 Usually subclassed by a storage-specific exception.
47 47 """
48 48
49 49 __bytes__ = _tobytes
50 50
51 51
52 52 class RevlogError(StorageError):
53 53 pass
54 54
55 55
56 56 class SidedataHashError(RevlogError):
57 57 def __init__(self, key, expected, got):
58 self.hint = None
58 59 self.sidedatakey = key
59 60 self.expecteddigest = expected
60 61 self.actualdigest = got
61 62
62 63
63 64 class FilteredIndexError(IndexError):
64 65 __bytes__ = _tobytes
65 66
66 67
67 68 class LookupError(RevlogError, KeyError):
68 69 def __init__(self, name, index, message):
69 70 self.name = name
70 71 self.index = index
71 72 # this can't be called 'message' because at least some installs of
72 73 # Python 2.6+ complain about the 'message' property being deprecated
73 74 self.lookupmessage = message
74 75 if isinstance(name, bytes) and len(name) == 20:
75 76 from .node import short
76 77
77 78 name = short(name)
78 79 # if name is a binary node, it can be None
79 80 RevlogError.__init__(
80 81 self, b'%s@%s: %s' % (index, pycompat.bytestr(name), message)
81 82 )
82 83
83 84 def __bytes__(self):
84 85 return RevlogError.__bytes__(self)
85 86
86 87 def __str__(self):
87 88 return RevlogError.__str__(self)
88 89
89 90
90 91 class AmbiguousPrefixLookupError(LookupError):
91 92 pass
92 93
93 94
94 95 class FilteredLookupError(LookupError):
95 96 pass
96 97
97 98
98 99 class ManifestLookupError(LookupError):
99 100 pass
100 101
101 102
102 103 class CommandError(Exception):
103 104 """Exception raised on errors in parsing the command line."""
104 105
105 106 def __init__(self, command, message):
106 107 self.command = command
107 108 self.message = message
108 109 super(CommandError, self).__init__()
109 110
110 111 __bytes__ = _tobytes
111 112
112 113
113 114 class UnknownCommand(Exception):
114 115 """Exception raised if command is not in the command table."""
115 116
116 117 def __init__(self, command, all_commands=None):
117 118 self.command = command
118 119 self.all_commands = all_commands
119 120 super(UnknownCommand, self).__init__()
120 121
121 122 __bytes__ = _tobytes
122 123
123 124
124 125 class AmbiguousCommand(Exception):
125 126 """Exception raised if command shortcut matches more than one command."""
126 127
127 128 def __init__(self, prefix, matches):
128 129 self.prefix = prefix
129 130 self.matches = matches
130 131 super(AmbiguousCommand, self).__init__()
131 132
132 133 __bytes__ = _tobytes
133 134
134 135
135 136 class WorkerError(Exception):
136 137 """Exception raised when a worker process dies."""
137 138
138 139 def __init__(self, status_code):
139 140 self.status_code = status_code
140 141 # Pass status code to superclass just so it becomes part of __bytes__
141 142 super(WorkerError, self).__init__(status_code)
142 143
143 144 __bytes__ = _tobytes
144 145
145 146
146 147 class InterventionRequired(Hint, Exception):
147 148 """Exception raised when a command requires human intervention."""
148 149
149 150 __bytes__ = _tobytes
150 151
151 152
152 153 class ConflictResolutionRequired(InterventionRequired):
153 154 """Exception raised when a continuable command required merge conflict resolution."""
154 155
155 156 def __init__(self, opname):
156 157 from .i18n import _
157 158
158 159 self.opname = opname
159 160 InterventionRequired.__init__(
160 161 self,
161 162 _(
162 163 b"unresolved conflicts (see 'hg resolve', then 'hg %s --continue')"
163 164 )
164 165 % opname,
165 166 )
166 167
167 168
168 169 class Abort(Hint, Exception):
169 170 """Raised if a command needs to print an error and exit."""
170 171
171 172 def __init__(self, message, hint=None):
172 173 self.message = message
173 174 self.hint = hint
174 175 # Pass the message into the Exception constructor to help extensions
175 176 # that look for exc.args[0].
176 177 Exception.__init__(self, message)
177 178
178 179 def __bytes__(self):
179 180 return self.message
180 181
181 182 if pycompat.ispy3:
182 183
183 184 def __str__(self):
184 185 # the output would be unreadable if the message was translated,
185 186 # but do not replace it with encoding.strfromlocal(), which
186 187 # may raise another exception.
187 188 return pycompat.sysstr(self.__bytes__())
188 189
189 190 def format(self):
190 191 from .i18n import _
191 192
192 193 message = _(b"abort: %s\n") % self.message
193 194 if self.hint:
194 195 message += _(b"(%s)\n") % self.hint
195 196 return message
196 197
197 198
198 199 class InputError(Abort):
199 200 """Indicates that the user made an error in their input.
200 201
201 202 Examples: Invalid command, invalid flags, invalid revision.
202 203 """
203 204
204 205
205 206 class StateError(Abort):
206 207 """Indicates that the operation might work if retried in a different state.
207 208
208 209 Examples: Unresolved merge conflicts, unfinished operations.
209 210 """
210 211
211 212
212 213 class CanceledError(Abort):
213 214 """Indicates that the user canceled the operation.
214 215
215 216 Examples: Close commit editor with error status, quit chistedit.
216 217 """
217 218
218 219
219 220 class SecurityError(Abort):
220 221 """Indicates that some aspect of security failed.
221 222
222 223 Examples: Bad server credentials, expired local credentials for network
223 224 filesystem, mismatched GPG signature, DoS protection.
224 225 """
225 226
226 227
227 228 class HookLoadError(Abort):
228 229 """raised when loading a hook fails, aborting an operation
229 230
230 231 Exists to allow more specialized catching."""
231 232
232 233
233 234 class HookAbort(Abort):
234 235 """raised when a validation hook fails, aborting an operation
235 236
236 237 Exists to allow more specialized catching."""
237 238
238 239
239 240 class ConfigError(Abort):
240 241 """Exception raised when parsing config files"""
241 242
242 243 def __init__(self, message, location=None, hint=None):
243 244 super(ConfigError, self).__init__(message, hint=hint)
244 245 self.location = location
245 246
246 247 def format(self):
247 248 from .i18n import _
248 249
249 250 if self.location is not None:
250 251 message = _(b"config error at %s: %s\n") % (
251 252 pycompat.bytestr(self.location),
252 253 self.message,
253 254 )
254 255 else:
255 256 message = _(b"config error: %s\n") % self.message
256 257 if self.hint:
257 258 message += _(b"(%s)\n") % self.hint
258 259 return message
259 260
260 261
261 262 class UpdateAbort(Abort):
262 263 """Raised when an update is aborted for destination issue"""
263 264
264 265
265 266 class MergeDestAbort(Abort):
266 267 """Raised when an update is aborted for destination issues"""
267 268
268 269
269 270 class NoMergeDestAbort(MergeDestAbort):
270 271 """Raised when an update is aborted because there is nothing to merge"""
271 272
272 273
273 274 class ManyMergeDestAbort(MergeDestAbort):
274 275 """Raised when an update is aborted because destination is ambiguous"""
275 276
276 277
277 278 class ResponseExpected(Abort):
278 279 """Raised when an EOF is received for a prompt"""
279 280
280 281 def __init__(self):
281 282 from .i18n import _
282 283
283 284 Abort.__init__(self, _(b'response expected'))
284 285
285 286
286 287 class OutOfBandError(Hint, Exception):
287 288 """Exception raised when a remote repo reports failure"""
288 289
289 290 __bytes__ = _tobytes
290 291
291 292
292 293 class ParseError(Abort):
293 294 """Raised when parsing config files and {rev,file}sets (msg[, pos])"""
294 295
295 296 def __init__(self, message, location=None, hint=None):
296 297 super(ParseError, self).__init__(message, hint=hint)
297 298 self.location = location
298 299
299 300 def format(self):
300 301 from .i18n import _
301 302
302 303 if self.location is not None:
303 304 message = _(b"hg: parse error at %s: %s\n") % (
304 305 pycompat.bytestr(self.location),
305 306 self.message,
306 307 )
307 308 else:
308 309 message = _(b"hg: parse error: %s\n") % self.message
309 310 if self.hint:
310 311 message += _(b"(%s)\n") % self.hint
311 312 return message
312 313
313 314
314 315 class PatchError(Exception):
315 316 __bytes__ = _tobytes
316 317
317 318
318 319 def getsimilar(symbols, value):
319 320 sim = lambda x: difflib.SequenceMatcher(None, value, x).ratio()
320 321 # The cutoff for similarity here is pretty arbitrary. It should
321 322 # probably be investigated and tweaked.
322 323 return [s for s in symbols if sim(s) > 0.6]
323 324
324 325
325 326 def similarity_hint(similar):
326 327 from .i18n import _
327 328
328 329 if len(similar) == 1:
329 330 return _(b"did you mean %s?") % similar[0]
330 331 elif similar:
331 332 ss = b", ".join(sorted(similar))
332 333 return _(b"did you mean one of %s?") % ss
333 334 else:
334 335 return None
335 336
336 337
337 338 class UnknownIdentifier(ParseError):
338 339 """Exception raised when a {rev,file}set references an unknown identifier"""
339 340
340 341 def __init__(self, function, symbols):
341 342 from .i18n import _
342 343
343 344 similar = getsimilar(symbols, function)
344 345 hint = similarity_hint(similar)
345 346
346 347 ParseError.__init__(
347 348 self, _(b"unknown identifier: %s") % function, hint=hint
348 349 )
349 350
350 351
351 352 class RepoError(Hint, Exception):
352 353 __bytes__ = _tobytes
353 354
354 355
355 356 class RepoLookupError(RepoError):
356 357 pass
357 358
358 359
359 360 class FilteredRepoLookupError(RepoLookupError):
360 361 pass
361 362
362 363
363 364 class CapabilityError(RepoError):
364 365 pass
365 366
366 367
367 368 class RequirementError(RepoError):
368 369 """Exception raised if .hg/requires has an unknown entry."""
369 370
370 371
371 372 class StdioError(IOError):
372 373 """Raised if I/O to stdout or stderr fails"""
373 374
374 375 def __init__(self, err):
375 376 IOError.__init__(self, err.errno, err.strerror)
376 377
377 378 # no __bytes__() because error message is derived from the standard IOError
378 379
379 380
380 381 class UnsupportedMergeRecords(Abort):
381 382 def __init__(self, recordtypes):
382 383 from .i18n import _
383 384
384 385 self.recordtypes = sorted(recordtypes)
385 386 s = b' '.join(self.recordtypes)
386 387 Abort.__init__(
387 388 self,
388 389 _(b'unsupported merge state records: %s') % s,
389 390 hint=_(
390 391 b'see https://mercurial-scm.org/wiki/MergeStateRecords for '
391 392 b'more information'
392 393 ),
393 394 )
394 395
395 396
396 397 class UnknownVersion(Abort):
397 398 """generic exception for aborting from an encounter with an unknown version"""
398 399
399 400 def __init__(self, msg, hint=None, version=None):
400 401 self.version = version
401 402 super(UnknownVersion, self).__init__(msg, hint=hint)
402 403
403 404
404 405 class LockError(IOError):
405 406 def __init__(self, errno, strerror, filename, desc):
406 407 IOError.__init__(self, errno, strerror, filename)
407 408 self.desc = desc
408 409
409 410 # no __bytes__() because error message is derived from the standard IOError
410 411
411 412
412 413 class LockHeld(LockError):
413 414 def __init__(self, errno, filename, desc, locker):
414 415 LockError.__init__(self, errno, b'Lock held', filename, desc)
415 416 self.locker = locker
416 417
417 418
418 419 class LockUnavailable(LockError):
419 420 pass
420 421
421 422
422 423 # LockError is for errors while acquiring the lock -- this is unrelated
423 424 class LockInheritanceContractViolation(RuntimeError):
424 425 __bytes__ = _tobytes
425 426
426 427
427 428 class ResponseError(Exception):
428 429 """Raised to print an error with part of output and exit."""
429 430
430 431 __bytes__ = _tobytes
431 432
432 433
433 434 # derived from KeyboardInterrupt to simplify some breakout code
434 435 class SignalInterrupt(KeyboardInterrupt):
435 436 """Exception raised on SIGTERM and SIGHUP."""
436 437
437 438
438 439 class SignatureError(Exception):
439 440 __bytes__ = _tobytes
440 441
441 442
442 443 class PushRaced(RuntimeError):
443 444 """An exception raised during unbundling that indicate a push race"""
444 445
445 446 __bytes__ = _tobytes
446 447
447 448
448 449 class ProgrammingError(Hint, RuntimeError):
449 450 """Raised if a mercurial (core or extension) developer made a mistake"""
450 451
451 452 def __init__(self, msg, *args, **kwargs):
452 453 # On Python 3, turn the message back into a string since this is
453 454 # an internal-only error that won't be printed except in a
454 455 # stack traces.
455 456 msg = pycompat.sysstr(msg)
456 457 super(ProgrammingError, self).__init__(msg, *args, **kwargs)
457 458
458 459 __bytes__ = _tobytes
459 460
460 461
461 462 class WdirUnsupported(Exception):
462 463 """An exception which is raised when 'wdir()' is not supported"""
463 464
464 465 __bytes__ = _tobytes
465 466
466 467
467 468 # bundle2 related errors
468 469 class BundleValueError(ValueError):
469 470 """error raised when bundle2 cannot be processed"""
470 471
471 472 __bytes__ = _tobytes
472 473
473 474
474 475 class BundleUnknownFeatureError(BundleValueError):
475 476 def __init__(self, parttype=None, params=(), values=()):
476 477 self.parttype = parttype
477 478 self.params = params
478 479 self.values = values
479 480 if self.parttype is None:
480 481 msg = b'Stream Parameter'
481 482 else:
482 483 msg = parttype
483 484 entries = self.params
484 485 if self.params and self.values:
485 486 assert len(self.params) == len(self.values)
486 487 entries = []
487 488 for idx, par in enumerate(self.params):
488 489 val = self.values[idx]
489 490 if val is None:
490 491 entries.append(val)
491 492 else:
492 493 entries.append(b"%s=%r" % (par, pycompat.maybebytestr(val)))
493 494 if entries:
494 495 msg = b'%s - %s' % (msg, b', '.join(entries))
495 496 ValueError.__init__(self, msg)
496 497
497 498
498 499 class ReadOnlyPartError(RuntimeError):
499 500 """error raised when code tries to alter a part being generated"""
500 501
501 502 __bytes__ = _tobytes
502 503
503 504
504 505 class PushkeyFailed(Abort):
505 506 """error raised when a pushkey part failed to update a value"""
506 507
507 508 def __init__(
508 509 self, partid, namespace=None, key=None, new=None, old=None, ret=None
509 510 ):
510 511 self.partid = partid
511 512 self.namespace = namespace
512 513 self.key = key
513 514 self.new = new
514 515 self.old = old
515 516 self.ret = ret
516 517 # no i18n expected to be processed into a better message
517 518 Abort.__init__(
518 519 self, b'failed to update value for "%s/%s"' % (namespace, key)
519 520 )
520 521
521 522
522 523 class CensoredNodeError(StorageError):
523 524 """error raised when content verification fails on a censored node
524 525
525 526 Also contains the tombstone data substituted for the uncensored data.
526 527 """
527 528
528 529 def __init__(self, filename, node, tombstone):
529 530 from .node import short
530 531
531 532 StorageError.__init__(self, b'%s:%s' % (filename, short(node)))
532 533 self.tombstone = tombstone
533 534
534 535
535 536 class CensoredBaseError(StorageError):
536 537 """error raised when a delta is rejected because its base is censored
537 538
538 539 A delta based on a censored revision must be formed as single patch
539 540 operation which replaces the entire base with new content. This ensures
540 541 the delta may be applied by clones which have not censored the base.
541 542 """
542 543
543 544
544 545 class InvalidBundleSpecification(Exception):
545 546 """error raised when a bundle specification is invalid.
546 547
547 548 This is used for syntax errors as opposed to support errors.
548 549 """
549 550
550 551 __bytes__ = _tobytes
551 552
552 553
553 554 class UnsupportedBundleSpecification(Exception):
554 555 """error raised when a bundle specification is not supported."""
555 556
556 557 __bytes__ = _tobytes
557 558
558 559
559 560 class CorruptedState(Exception):
560 561 """error raised when a command is not able to read its state from file"""
561 562
562 563 __bytes__ = _tobytes
563 564
564 565
565 566 class PeerTransportError(Abort):
566 567 """Transport-level I/O error when communicating with a peer repo."""
567 568
568 569
569 570 class InMemoryMergeConflictsError(Exception):
570 571 """Exception raised when merge conflicts arose during an in-memory merge."""
571 572
572 573 __bytes__ = _tobytes
573 574
574 575
575 576 class WireprotoCommandError(Exception):
576 577 """Represents an error during execution of a wire protocol command.
577 578
578 579 Should only be thrown by wire protocol version 2 commands.
579 580
580 581 The error is a formatter string and an optional iterable of arguments.
581 582 """
582 583
583 584 def __init__(self, message, args=None):
584 585 self.message = message
585 586 self.messageargs = args
General Comments 0
You need to be logged in to leave comments. Login now