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