Mercurial > public > mercurial-scm > hg
comparison mercurial/error.py @ 46899:8b6e36e4b553
typing: add type hints to mercurial/error.py
The only slightly unusual things here are that `location` is passed to
`ParseError` and both bytes and an int (so this accepts both), and the message
passed `ProgrammingError` is immediately converted to str. Therefore it is
typed as `AnyStr`, because there are a couple of instances that are already
passed as str.
There are a couple of places where bytes are being passed to builtin exceptions
that might need to be converted to str.
Differential Revision: https://phab.mercurial-scm.org/D10274
author | Matt Harbison <matt_harbison@yahoo.com> |
---|---|
date | Thu, 25 Mar 2021 18:59:14 -0400 |
parents | d4ba4d51f85f |
children | 14ddb1dca2c0 |
comparison
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 |