mercurial/error.py
changeset 46899 8b6e36e4b553
parent 46819 d4ba4d51f85f
child 46975 14ddb1dca2c0
equal deleted inserted replaced
46898:e1d75c514ced 46899:8b6e36e4b553
    18 # Do not import anything but pycompat here, please
    18 # Do not import anything but pycompat here, please
    19 from . import pycompat
    19 from . import pycompat
    20 
    20 
    21 if pycompat.TYPE_CHECKING:
    21 if pycompat.TYPE_CHECKING:
    22     from typing import (
    22     from typing import (
       
    23         Any,
       
    24         AnyStr,
       
    25         Iterable,
       
    26         List,
    23         Optional,
    27         Optional,
       
    28         Sequence,
       
    29         Union,
    24     )
    30     )
    25 
    31 
    26 
    32 
    27 def _tobytes(exc):
    33 def _tobytes(exc):
    28     """Byte-stringify exception in the same way as BaseException_str()"""
    34     """Byte-stringify exception in the same way as BaseException_str()"""
   107 
   113 
   108 class CommandError(Exception):
   114 class CommandError(Exception):
   109     """Exception raised on errors in parsing the command line."""
   115     """Exception raised on errors in parsing the command line."""
   110 
   116 
   111     def __init__(self, command, message):
   117     def __init__(self, command, message):
       
   118         # type: (bytes, bytes) -> None
   112         self.command = command
   119         self.command = command
   113         self.message = message
   120         self.message = message
   114         super(CommandError, self).__init__()
   121         super(CommandError, self).__init__()
   115 
   122 
   116     __bytes__ = _tobytes
   123     __bytes__ = _tobytes
   118 
   125 
   119 class UnknownCommand(Exception):
   126 class UnknownCommand(Exception):
   120     """Exception raised if command is not in the command table."""
   127     """Exception raised if command is not in the command table."""
   121 
   128 
   122     def __init__(self, command, all_commands=None):
   129     def __init__(self, command, all_commands=None):
       
   130         # type: (bytes, Optional[List[bytes]]) -> None
   123         self.command = command
   131         self.command = command
   124         self.all_commands = all_commands
   132         self.all_commands = all_commands
   125         super(UnknownCommand, self).__init__()
   133         super(UnknownCommand, self).__init__()
   126 
   134 
   127     __bytes__ = _tobytes
   135     __bytes__ = _tobytes
   129 
   137 
   130 class AmbiguousCommand(Exception):
   138 class AmbiguousCommand(Exception):
   131     """Exception raised if command shortcut matches more than one command."""
   139     """Exception raised if command shortcut matches more than one command."""
   132 
   140 
   133     def __init__(self, prefix, matches):
   141     def __init__(self, prefix, matches):
       
   142         # type: (bytes, List[bytes]) -> None
   134         self.prefix = prefix
   143         self.prefix = prefix
   135         self.matches = matches
   144         self.matches = matches
   136         super(AmbiguousCommand, self).__init__()
   145         super(AmbiguousCommand, self).__init__()
   137 
   146 
   138     __bytes__ = _tobytes
   147     __bytes__ = _tobytes
   140 
   149 
   141 class WorkerError(Exception):
   150 class WorkerError(Exception):
   142     """Exception raised when a worker process dies."""
   151     """Exception raised when a worker process dies."""
   143 
   152 
   144     def __init__(self, status_code):
   153     def __init__(self, status_code):
       
   154         # type: (int) -> None
   145         self.status_code = status_code
   155         self.status_code = status_code
   146         # Pass status code to superclass just so it becomes part of __bytes__
   156         # Pass status code to superclass just so it becomes part of __bytes__
   147         super(WorkerError, self).__init__(status_code)
   157         super(WorkerError, self).__init__(status_code)
   148 
   158 
   149     __bytes__ = _tobytes
   159     __bytes__ = _tobytes
   157 
   167 
   158 class ConflictResolutionRequired(InterventionRequired):
   168 class ConflictResolutionRequired(InterventionRequired):
   159     """Exception raised when a continuable command required merge conflict resolution."""
   169     """Exception raised when a continuable command required merge conflict resolution."""
   160 
   170 
   161     def __init__(self, opname):
   171     def __init__(self, opname):
       
   172         # type: (bytes) -> None
   162         from .i18n import _
   173         from .i18n import _
   163 
   174 
   164         self.opname = opname
   175         self.opname = opname
   165         InterventionRequired.__init__(
   176         InterventionRequired.__init__(
   166             self,
   177             self,
   192             # but do not replace it with encoding.strfromlocal(), which
   203             # but do not replace it with encoding.strfromlocal(), which
   193             # may raise another exception.
   204             # may raise another exception.
   194             return pycompat.sysstr(self.__bytes__())
   205             return pycompat.sysstr(self.__bytes__())
   195 
   206 
   196     def format(self):
   207     def format(self):
       
   208         # type: () -> bytes
   197         from .i18n import _
   209         from .i18n import _
   198 
   210 
   199         message = _(b"abort: %s\n") % self.message
   211         message = _(b"abort: %s\n") % self.message
   200         if self.hint:
   212         if self.hint:
   201             message += _(b"(%s)\n") % self.hint
   213             message += _(b"(%s)\n") % self.hint
   245 
   257 
   246 class ConfigError(Abort):
   258 class ConfigError(Abort):
   247     """Exception raised when parsing config files"""
   259     """Exception raised when parsing config files"""
   248 
   260 
   249     def __init__(self, message, location=None, hint=None):
   261     def __init__(self, message, location=None, hint=None):
       
   262         # type: (bytes, Optional[bytes], Optional[bytes]) -> None
   250         super(ConfigError, self).__init__(message, hint=hint)
   263         super(ConfigError, self).__init__(message, hint=hint)
   251         self.location = location
   264         self.location = location
   252 
   265 
   253     def format(self):
   266     def format(self):
       
   267         # type: () -> bytes
   254         from .i18n import _
   268         from .i18n import _
   255 
   269 
   256         if self.location is not None:
   270         if self.location is not None:
   257             message = _(b"config error at %s: %s\n") % (
   271             message = _(b"config error at %s: %s\n") % (
   258                 pycompat.bytestr(self.location),
   272                 pycompat.bytestr(self.location),
   298 
   312 
   299 class ParseError(Abort):
   313 class ParseError(Abort):
   300     """Raised when parsing config files and {rev,file}sets (msg[, pos])"""
   314     """Raised when parsing config files and {rev,file}sets (msg[, pos])"""
   301 
   315 
   302     def __init__(self, message, location=None, hint=None):
   316     def __init__(self, message, location=None, hint=None):
       
   317         # type: (bytes, Optional[Union[bytes, int]], Optional[bytes]) -> None
   303         super(ParseError, self).__init__(message, hint=hint)
   318         super(ParseError, self).__init__(message, hint=hint)
   304         self.location = location
   319         self.location = location
   305 
   320 
   306     def format(self):
   321     def format(self):
       
   322         # type: () -> bytes
   307         from .i18n import _
   323         from .i18n import _
   308 
   324 
   309         if self.location is not None:
   325         if self.location is not None:
   310             message = _(b"hg: parse error at %s: %s\n") % (
   326             message = _(b"hg: parse error at %s: %s\n") % (
   311                 pycompat.bytestr(self.location),
   327                 pycompat.bytestr(self.location),
   321 class PatchError(Exception):
   337 class PatchError(Exception):
   322     __bytes__ = _tobytes
   338     __bytes__ = _tobytes
   323 
   339 
   324 
   340 
   325 def getsimilar(symbols, value):
   341 def getsimilar(symbols, value):
       
   342     # type: (Iterable[bytes], bytes) -> List[bytes]
   326     sim = lambda x: difflib.SequenceMatcher(None, value, x).ratio()
   343     sim = lambda x: difflib.SequenceMatcher(None, value, x).ratio()
   327     # The cutoff for similarity here is pretty arbitrary. It should
   344     # The cutoff for similarity here is pretty arbitrary. It should
   328     # probably be investigated and tweaked.
   345     # probably be investigated and tweaked.
   329     return [s for s in symbols if sim(s) > 0.6]
   346     return [s for s in symbols if sim(s) > 0.6]
   330 
   347 
   331 
   348 
   332 def similarity_hint(similar):
   349 def similarity_hint(similar):
       
   350     # type: (List[bytes]) -> Optional[bytes]
   333     from .i18n import _
   351     from .i18n import _
   334 
   352 
   335     if len(similar) == 1:
   353     if len(similar) == 1:
   336         return _(b"did you mean %s?") % similar[0]
   354         return _(b"did you mean %s?") % similar[0]
   337     elif similar:
   355     elif similar:
   343 
   361 
   344 class UnknownIdentifier(ParseError):
   362 class UnknownIdentifier(ParseError):
   345     """Exception raised when a {rev,file}set references an unknown identifier"""
   363     """Exception raised when a {rev,file}set references an unknown identifier"""
   346 
   364 
   347     def __init__(self, function, symbols):
   365     def __init__(self, function, symbols):
       
   366         # type: (bytes, Iterable[bytes]) -> None
   348         from .i18n import _
   367         from .i18n import _
   349 
   368 
   350         similar = getsimilar(symbols, function)
   369         similar = getsimilar(symbols, function)
   351         hint = similarity_hint(similar)
   370         hint = similarity_hint(similar)
   352 
   371 
   377 
   396 
   378 class StdioError(IOError):
   397 class StdioError(IOError):
   379     """Raised if I/O to stdout or stderr fails"""
   398     """Raised if I/O to stdout or stderr fails"""
   380 
   399 
   381     def __init__(self, err):
   400     def __init__(self, err):
       
   401         # type: (IOError) -> None
   382         IOError.__init__(self, err.errno, err.strerror)
   402         IOError.__init__(self, err.errno, err.strerror)
   383 
   403 
   384     # no __bytes__() because error message is derived from the standard IOError
   404     # no __bytes__() because error message is derived from the standard IOError
   385 
   405 
   386 
   406 
   387 class UnsupportedMergeRecords(Abort):
   407 class UnsupportedMergeRecords(Abort):
   388     def __init__(self, recordtypes):
   408     def __init__(self, recordtypes):
       
   409         # type: (Iterable[bytes]) -> None
   389         from .i18n import _
   410         from .i18n import _
   390 
   411 
   391         self.recordtypes = sorted(recordtypes)
   412         self.recordtypes = sorted(recordtypes)
   392         s = b' '.join(self.recordtypes)
   413         s = b' '.join(self.recordtypes)
   393         Abort.__init__(
   414         Abort.__init__(
   402 
   423 
   403 class UnknownVersion(Abort):
   424 class UnknownVersion(Abort):
   404     """generic exception for aborting from an encounter with an unknown version"""
   425     """generic exception for aborting from an encounter with an unknown version"""
   405 
   426 
   406     def __init__(self, msg, hint=None, version=None):
   427     def __init__(self, msg, hint=None, version=None):
       
   428         # type: (bytes, Optional[bytes], Optional[bytes]) -> None
   407         self.version = version
   429         self.version = version
   408         super(UnknownVersion, self).__init__(msg, hint=hint)
   430         super(UnknownVersion, self).__init__(msg, hint=hint)
   409 
   431 
   410 
   432 
   411 class LockError(IOError):
   433 class LockError(IOError):
   412     def __init__(self, errno, strerror, filename, desc):
   434     def __init__(self, errno, strerror, filename, desc):
       
   435         # TODO: figure out if this should be bytes or str
       
   436         # _type: (int, str, str, bytes) -> None
   413         IOError.__init__(self, errno, strerror, filename)
   437         IOError.__init__(self, errno, strerror, filename)
   414         self.desc = desc
   438         self.desc = desc
   415 
   439 
   416     # no __bytes__() because error message is derived from the standard IOError
   440     # no __bytes__() because error message is derived from the standard IOError
   417 
   441 
   454 
   478 
   455 class ProgrammingError(Hint, RuntimeError):
   479 class ProgrammingError(Hint, RuntimeError):
   456     """Raised if a mercurial (core or extension) developer made a mistake"""
   480     """Raised if a mercurial (core or extension) developer made a mistake"""
   457 
   481 
   458     def __init__(self, msg, *args, **kwargs):
   482     def __init__(self, msg, *args, **kwargs):
       
   483         # type: (AnyStr, Any, Any) -> None
   459         # On Python 3, turn the message back into a string since this is
   484         # On Python 3, turn the message back into a string since this is
   460         # an internal-only error that won't be printed except in a
   485         # an internal-only error that won't be printed except in a
   461         # stack traces.
   486         # stack traces.
   462         msg = pycompat.sysstr(msg)
   487         msg = pycompat.sysstr(msg)
   463         super(ProgrammingError, self).__init__(msg, *args, **kwargs)
   488         super(ProgrammingError, self).__init__(msg, *args, **kwargs)
   497                     entries.append(val)
   522                     entries.append(val)
   498                 else:
   523                 else:
   499                     entries.append(b"%s=%r" % (par, pycompat.maybebytestr(val)))
   524                     entries.append(b"%s=%r" % (par, pycompat.maybebytestr(val)))
   500         if entries:
   525         if entries:
   501             msg = b'%s - %s' % (msg, b', '.join(entries))
   526             msg = b'%s - %s' % (msg, b', '.join(entries))
   502         ValueError.__init__(self, msg)
   527         ValueError.__init__(self, msg)  # TODO: convert to str?
   503 
   528 
   504 
   529 
   505 class ReadOnlyPartError(RuntimeError):
   530 class ReadOnlyPartError(RuntimeError):
   506     """error raised when code tries to alter a part being generated"""
   531     """error raised when code tries to alter a part being generated"""
   507 
   532 
   531 
   556 
   532     Also contains the tombstone data substituted for the uncensored data.
   557     Also contains the tombstone data substituted for the uncensored data.
   533     """
   558     """
   534 
   559 
   535     def __init__(self, filename, node, tombstone):
   560     def __init__(self, filename, node, tombstone):
       
   561         # type: (bytes, bytes, bytes) -> None
   536         from .node import short
   562         from .node import short
   537 
   563 
   538         StorageError.__init__(self, b'%s:%s' % (filename, short(node)))
   564         StorageError.__init__(self, b'%s:%s' % (filename, short(node)))
   539         self.tombstone = tombstone
   565         self.tombstone = tombstone
   540 
   566 
   586 
   612 
   587     The error is a formatter string and an optional iterable of arguments.
   613     The error is a formatter string and an optional iterable of arguments.
   588     """
   614     """
   589 
   615 
   590     def __init__(self, message, args=None):
   616     def __init__(self, message, args=None):
       
   617         # type: (bytes, Optional[Sequence[bytes]]) -> None
   591         self.message = message
   618         self.message = message
   592         self.messageargs = args
   619         self.messageargs = args