comparison mercurial/error.py @ 51304:f15cb5111a1e

pytype: move some type comment to proper annotation We support direct type annotations now, while pytype is starting to complains about them.
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Tue, 19 Dec 2023 21:29:34 +0100
parents 81224afd938d
children 8b2ea2246a5f
comparison
equal deleted inserted replaced
51303:81224afd938d 51304:f15cb5111a1e
38 Sequence, 38 Sequence,
39 Union, 39 Union,
40 ] 40 ]
41 41
42 42
43 def _tobytes(exc): 43 def _tobytes(exc) -> bytes:
44 # type: (...) -> bytes
45 """Byte-stringify exception in the same way as BaseException_str()""" 44 """Byte-stringify exception in the same way as BaseException_str()"""
46 if not exc.args: 45 if not exc.args:
47 return b'' 46 return b''
48 if len(exc.args) == 1: 47 if len(exc.args) == 1:
49 return pycompat.bytestr(exc.args[0]) 48 return pycompat.bytestr(exc.args[0])
66 """Base class for Mercurial errors.""" 65 """Base class for Mercurial errors."""
67 66
68 coarse_exit_code = None 67 coarse_exit_code = None
69 detailed_exit_code = None 68 detailed_exit_code = None
70 69
71 def __init__(self, message, hint=None): 70 def __init__(self, message: bytes, hint: Optional[bytes] = None) -> None:
72 # type: (bytes, Optional[bytes]) -> None
73 self.message = message 71 self.message = message
74 self.hint = hint 72 self.hint = hint
75 # Pass the message into the Exception constructor to help extensions 73 # Pass the message into the Exception constructor to help extensions
76 # that look for exc.args[0]. 74 # that look for exc.args[0].
77 Exception.__init__(self, message) 75 Exception.__init__(self, message)
78 76
79 def __bytes__(self): 77 def __bytes__(self):
80 return self.message 78 return self.message
81 79
82 def __str__(self): 80 def __str__(self) -> str:
83 # type: () -> str
84 # the output would be unreadable if the message was translated, 81 # the output would be unreadable if the message was translated,
85 # but do not replace it with encoding.strfromlocal(), which 82 # but do not replace it with encoding.strfromlocal(), which
86 # may raise another exception. 83 # may raise another exception.
87 return pycompat.sysstr(self.__bytes__()) 84 return pycompat.sysstr(self.__bytes__())
88 85
89 def format(self): 86 def format(self) -> bytes:
90 # type: () -> bytes
91 from .i18n import _ 87 from .i18n import _
92 88
93 message = _(b"abort: %s\n") % self.message 89 message = _(b"abort: %s\n") % self.message
94 if self.hint: 90 if self.hint:
95 message += _(b"(%s)\n") % self.hint 91 message += _(b"(%s)\n") % self.hint
112 class RevlogError(StorageError): 108 class RevlogError(StorageError):
113 pass 109 pass
114 110
115 111
116 class SidedataHashError(RevlogError): 112 class SidedataHashError(RevlogError):
117 def __init__(self, key, expected, got): 113 def __init__(self, key: int, expected: bytes, got: bytes) -> None:
118 # type: (int, bytes, bytes) -> None
119 self.hint = None 114 self.hint = None
120 self.sidedatakey = key 115 self.sidedatakey = key
121 self.expecteddigest = expected 116 self.expecteddigest = expected
122 self.actualdigest = got 117 self.actualdigest = got
123 118
125 class FilteredIndexError(IndexError): 120 class FilteredIndexError(IndexError):
126 __bytes__ = _tobytes 121 __bytes__ = _tobytes
127 122
128 123
129 class LookupError(RevlogError, KeyError): 124 class LookupError(RevlogError, KeyError):
130 def __init__(self, name, index, message): 125 def __init__(self, name: bytes, index: bytes, message: bytes) -> None:
131 # type: (bytes, bytes, bytes) -> None
132 self.name = name 126 self.name = name
133 self.index = index 127 self.index = index
134 # this can't be called 'message' because at least some installs of 128 # this can't be called 'message' because at least some installs of
135 # Python 2.6+ complain about the 'message' property being deprecated 129 # Python 2.6+ complain about the 'message' property being deprecated
136 self.lookupmessage = message 130 self.lookupmessage = message
163 157
164 158
165 class CommandError(Exception): 159 class CommandError(Exception):
166 """Exception raised on errors in parsing the command line.""" 160 """Exception raised on errors in parsing the command line."""
167 161
168 def __init__(self, command, message): 162 def __init__(self, command: Optional[bytes], message: bytes) -> None:
169 # type: (Optional[bytes], bytes) -> None
170 self.command = command 163 self.command = command
171 self.message = message 164 self.message = message
172 super(CommandError, self).__init__() 165 super(CommandError, self).__init__()
173 166
174 __bytes__ = _tobytes 167 __bytes__ = _tobytes
175 168
176 169
177 class UnknownCommand(Exception): 170 class UnknownCommand(Exception):
178 """Exception raised if command is not in the command table.""" 171 """Exception raised if command is not in the command table."""
179 172
180 def __init__(self, command, all_commands=None): 173 def __init__(
181 # type: (bytes, Optional[List[bytes]]) -> None 174 self,
175 command: bytes,
176 all_commands: Optional[List[bytes]] = None,
177 ) -> None:
182 self.command = command 178 self.command = command
183 self.all_commands = all_commands 179 self.all_commands = all_commands
184 super(UnknownCommand, self).__init__() 180 super(UnknownCommand, self).__init__()
185 181
186 __bytes__ = _tobytes 182 __bytes__ = _tobytes
187 183
188 184
189 class AmbiguousCommand(Exception): 185 class AmbiguousCommand(Exception):
190 """Exception raised if command shortcut matches more than one command.""" 186 """Exception raised if command shortcut matches more than one command."""
191 187
192 def __init__(self, prefix, matches): 188 def __init__(self, prefix: bytes, matches: List[bytes]) -> None:
193 # type: (bytes, List[bytes]) -> None
194 self.prefix = prefix 189 self.prefix = prefix
195 self.matches = matches 190 self.matches = matches
196 super(AmbiguousCommand, self).__init__() 191 super(AmbiguousCommand, self).__init__()
197 192
198 __bytes__ = _tobytes 193 __bytes__ = _tobytes
199 194
200 195
201 class WorkerError(Exception): 196 class WorkerError(Exception):
202 """Exception raised when a worker process dies.""" 197 """Exception raised when a worker process dies."""
203 198
204 def __init__(self, status_code): 199 def __init__(self, status_code: int) -> None:
205 # type: (int) -> None
206 self.status_code = status_code 200 self.status_code = status_code
207 # Pass status code to superclass just so it becomes part of __bytes__ 201 # Pass status code to superclass just so it becomes part of __bytes__
208 super(WorkerError, self).__init__(status_code) 202 super(WorkerError, self).__init__(status_code)
209 203
210 __bytes__ = _tobytes 204 __bytes__ = _tobytes
214 """Exception raised when a command requires human intervention.""" 208 """Exception raised when a command requires human intervention."""
215 209
216 coarse_exit_code = 1 210 coarse_exit_code = 1
217 detailed_exit_code = 240 211 detailed_exit_code = 240
218 212
219 def format(self): 213 def format(self) -> bytes:
220 # type: () -> bytes
221 from .i18n import _ 214 from .i18n import _
222 215
223 message = _(b"%s\n") % self.message 216 message = _(b"%s\n") % self.message
224 if self.hint: 217 if self.hint:
225 message += _(b"(%s)\n") % self.hint 218 message += _(b"(%s)\n") % self.hint
227 220
228 221
229 class ConflictResolutionRequired(InterventionRequired): 222 class ConflictResolutionRequired(InterventionRequired):
230 """Exception raised when a continuable command required merge conflict resolution.""" 223 """Exception raised when a continuable command required merge conflict resolution."""
231 224
232 def __init__(self, opname): 225 def __init__(self, opname: bytes) -> None:
233 # type: (bytes) -> None
234 from .i18n import _ 226 from .i18n import _
235 227
236 self.opname = opname 228 self.opname = opname
237 InterventionRequired.__init__( 229 InterventionRequired.__init__(
238 self, 230 self,
297 class ConfigError(Abort): 289 class ConfigError(Abort):
298 """Exception raised when parsing config files""" 290 """Exception raised when parsing config files"""
299 291
300 detailed_exit_code = 30 292 detailed_exit_code = 30
301 293
302 def __init__(self, message, location=None, hint=None): 294 def __init__(
303 # type: (bytes, Optional[bytes], Optional[bytes]) -> None 295 self,
296 message: bytes,
297 location: Optional[bytes] = None,
298 hint: Optional[bytes] = None,
299 ) -> None:
304 super(ConfigError, self).__init__(message, hint=hint) 300 super(ConfigError, self).__init__(message, hint=hint)
305 self.location = location 301 self.location = location
306 302
307 def format(self): 303 def format(self) -> bytes:
308 # type: () -> bytes
309 from .i18n import _ 304 from .i18n import _
310 305
311 if self.location is not None: 306 if self.location is not None:
312 message = _(b"config error at %s: %s\n") % ( 307 message = _(b"config error at %s: %s\n") % (
313 pycompat.bytestr(self.location), 308 pycompat.bytestr(self.location),
352 347
353 348
354 class OutOfBandError(RemoteError): 349 class OutOfBandError(RemoteError):
355 """Exception raised when a remote repo reports failure""" 350 """Exception raised when a remote repo reports failure"""
356 351
357 def __init__(self, message=None, hint=None): 352 def __init__(
358 # type: (Optional[bytes], Optional[bytes]) -> None 353 self,
354 message: Optional[bytes] = None,
355 hint: Optional[bytes] = None,
356 ):
359 from .i18n import _ 357 from .i18n import _
360 358
361 if message: 359 if message:
362 # Abort.format() adds a trailing newline 360 # Abort.format() adds a trailing newline
363 message = _(b"remote error:\n%s") % message.rstrip(b'\n') 361 message = _(b"remote error:\n%s") % message.rstrip(b'\n')
369 class ParseError(Abort): 367 class ParseError(Abort):
370 """Raised when parsing config files and {rev,file}sets (msg[, pos])""" 368 """Raised when parsing config files and {rev,file}sets (msg[, pos])"""
371 369
372 detailed_exit_code = 10 370 detailed_exit_code = 10
373 371
374 def __init__(self, message, location=None, hint=None): 372 def __init__(
375 # type: (bytes, Optional[Union[bytes, int]], Optional[bytes]) -> None 373 self,
374 message: bytes,
375 location: Optional[Union[bytes, int]] = None,
376 hint: Optional[bytes] = None,
377 ):
376 super(ParseError, self).__init__(message, hint=hint) 378 super(ParseError, self).__init__(message, hint=hint)
377 self.location = location 379 self.location = location
378 380
379 def format(self): 381 def format(self) -> bytes:
380 # type: () -> bytes
381 from .i18n import _ 382 from .i18n import _
382 383
383 if self.location is not None: 384 if self.location is not None:
384 message = _(b"hg: parse error at %s: %s\n") % ( 385 message = _(b"hg: parse error at %s: %s\n") % (
385 pycompat.bytestr(self.location), 386 pycompat.bytestr(self.location),
402 403
403 class PatchApplicationError(PatchError): 404 class PatchApplicationError(PatchError):
404 __bytes__ = _tobytes 405 __bytes__ = _tobytes
405 406
406 407
407 def getsimilar(symbols, value): 408 def getsimilar(symbols: Iterable[bytes], value: bytes) -> List[bytes]:
408 # type: (Iterable[bytes], bytes) -> List[bytes]
409 sim = lambda x: difflib.SequenceMatcher(None, value, x).ratio() 409 sim = lambda x: difflib.SequenceMatcher(None, value, x).ratio()
410 # The cutoff for similarity here is pretty arbitrary. It should 410 # The cutoff for similarity here is pretty arbitrary. It should
411 # probably be investigated and tweaked. 411 # probably be investigated and tweaked.
412 return [s for s in symbols if sim(s) > 0.6] 412 return [s for s in symbols if sim(s) > 0.6]
413 413
414 414
415 def similarity_hint(similar): 415 def similarity_hint(similar: List[bytes]) -> Optional[bytes]:
416 # type: (List[bytes]) -> Optional[bytes]
417 from .i18n import _ 416 from .i18n import _
418 417
419 if len(similar) == 1: 418 if len(similar) == 1:
420 return _(b"did you mean %s?") % similar[0] 419 return _(b"did you mean %s?") % similar[0]
421 elif similar: 420 elif similar:
426 425
427 426
428 class UnknownIdentifier(ParseError): 427 class UnknownIdentifier(ParseError):
429 """Exception raised when a {rev,file}set references an unknown identifier""" 428 """Exception raised when a {rev,file}set references an unknown identifier"""
430 429
431 def __init__(self, function, symbols): 430 def __init__(self, function: bytes, symbols: Iterable[bytes]) -> None:
432 # type: (bytes, Iterable[bytes]) -> None
433 from .i18n import _ 431 from .i18n import _
434 432
435 similar = getsimilar(symbols, function) 433 similar = getsimilar(symbols, function)
436 hint = similarity_hint(similar) 434 hint = similarity_hint(similar)
437 435
461 459
462 460
463 class StdioError(IOError): 461 class StdioError(IOError):
464 """Raised if I/O to stdout or stderr fails""" 462 """Raised if I/O to stdout or stderr fails"""
465 463
466 def __init__(self, err): 464 def __init__(self, err: IOError) -> None:
467 # type: (IOError) -> None
468 IOError.__init__(self, err.errno, err.strerror) 465 IOError.__init__(self, err.errno, err.strerror)
469 466
470 # no __bytes__() because error message is derived from the standard IOError 467 # no __bytes__() because error message is derived from the standard IOError
471 468
472 469
473 class UnsupportedMergeRecords(Abort): 470 class UnsupportedMergeRecords(Abort):
474 def __init__(self, recordtypes): 471 def __init__(self, recordtypes: Iterable[bytes]) -> None:
475 # type: (Iterable[bytes]) -> None
476 from .i18n import _ 472 from .i18n import _
477 473
478 self.recordtypes = sorted(recordtypes) 474 self.recordtypes = sorted(recordtypes)
479 s = b' '.join(self.recordtypes) 475 s = b' '.join(self.recordtypes)
480 Abort.__init__( 476 Abort.__init__(
488 484
489 485
490 class UnknownVersion(Abort): 486 class UnknownVersion(Abort):
491 """generic exception for aborting from an encounter with an unknown version""" 487 """generic exception for aborting from an encounter with an unknown version"""
492 488
493 def __init__(self, msg, hint=None, version=None): 489 def __init__(
494 # type: (bytes, Optional[bytes], Optional[bytes]) -> None 490 self,
491 msg: bytes,
492 hint: Optional[bytes] = None,
493 version: Optional[bytes] = None,
494 ) -> None:
495 self.version = version 495 self.version = version
496 super(UnknownVersion, self).__init__(msg, hint=hint) 496 super(UnknownVersion, self).__init__(msg, hint=hint)
497 497
498 498
499 class LockError(IOError): 499 class LockError(IOError):
500 def __init__(self, errno, strerror, filename, desc): 500 def __init__(
501 # _type: (int, str, bytes, bytes) -> None 501 self,
502 errno: int,
503 strerror: str,
504 filename: bytes,
505 desc: Optional[bytes],
506 ) -> None:
502 IOError.__init__(self, errno, strerror, filename) 507 IOError.__init__(self, errno, strerror, filename)
503 self.desc = desc 508 self.desc = desc
504 509
505 # no __bytes__() because error message is derived from the standard IOError 510 # no __bytes__() because error message is derived from the standard IOError
506 511
507 512
508 class LockHeld(LockError): 513 class LockHeld(LockError):
509 def __init__(self, errno, filename, desc, locker): 514 def __init__(
515 self,
516 errno: int,
517 filename: bytes,
518 desc: Optional[bytes],
519 locker,
520 ):
510 LockError.__init__(self, errno, 'Lock held', filename, desc) 521 LockError.__init__(self, errno, 'Lock held', filename, desc)
522 self.filename: bytes = filename
511 self.locker = locker 523 self.locker = locker
512 524
513 525
514 class LockUnavailable(LockError): 526 class LockUnavailable(LockError):
515 pass 527 pass
542 554
543 555
544 class ProgrammingError(Hint, RuntimeError): 556 class ProgrammingError(Hint, RuntimeError):
545 """Raised if a mercurial (core or extension) developer made a mistake""" 557 """Raised if a mercurial (core or extension) developer made a mistake"""
546 558
547 def __init__(self, msg, *args, **kwargs): 559 def __init__(self, msg: AnyStr, *args, **kwargs):
548 # type: (AnyStr, Any, Any) -> None
549 # On Python 3, turn the message back into a string since this is 560 # On Python 3, turn the message back into a string since this is
550 # an internal-only error that won't be printed except in a 561 # an internal-only error that won't be printed except in a
551 # stack traces. 562 # stack traces.
552 msg = pycompat.sysstr(msg) 563 msg = pycompat.sysstr(msg)
553 super(ProgrammingError, self).__init__(msg, *args, **kwargs) 564 super(ProgrammingError, self).__init__(msg, *args, **kwargs)
620 """error raised when content verification fails on a censored node 631 """error raised when content verification fails on a censored node
621 632
622 Also contains the tombstone data substituted for the uncensored data. 633 Also contains the tombstone data substituted for the uncensored data.
623 """ 634 """
624 635
625 def __init__(self, filename, node, tombstone): 636 def __init__(self, filename: bytes, node: bytes, tombstone: bytes):
626 # type: (bytes, bytes, bytes) -> None
627 from .node import short 637 from .node import short
628 638
629 StorageError.__init__(self, b'%s:%s' % (filename, short(node))) 639 StorageError.__init__(self, b'%s:%s' % (filename, short(node)))
630 self.tombstone = tombstone 640 self.tombstone = tombstone
631 641
683 Should only be thrown by wire protocol version 2 commands. 693 Should only be thrown by wire protocol version 2 commands.
684 694
685 The error is a formatter string and an optional iterable of arguments. 695 The error is a formatter string and an optional iterable of arguments.
686 """ 696 """
687 697
688 def __init__(self, message, args=None): 698 def __init__(
689 # type: (bytes, Optional[Sequence[bytes]]) -> None 699 self,
700 message: bytes,
701 args: Optional[Sequence[bytes]] = None,
702 ) -> None:
690 self.message = message 703 self.message = message
691 self.messageargs = args 704 self.messageargs = args