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