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