mercurial/error.py
changeset 51284 f15cb5111a1e
parent 51283 81224afd938d
child 51285 8b2ea2246a5f
equal deleted inserted replaced
51283:81224afd938d 51284: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