Skip to content

Commit b71d355

Browse files
authored
Better handling for uncaught top-level exceptions (python#20749)
Fixes python#20633 This has few improvements: * Much more robust guarantee that the exit code will be 2 (not 1) on any internal errors. * Respect `--show-traceback` (if possible), and write official error message. * Fix formatting of the official error message (there were some extra colons). This mostly affects bug in incremental mode, where a crash occurs in top-level and/or cache loading, rather that during processing files (where we already have the error catching logic).
1 parent 2f9d807 commit b71d355

File tree

2 files changed

+22
-8
lines changed

2 files changed

+22
-8
lines changed

mypy/__main__.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,19 @@ def console_entry() -> None:
3131
sys.stdout.flush()
3232
sys.stderr.flush()
3333
sys.exit(2)
34+
except Exception as e:
35+
# Try reporting any uncaught error canonically, otherwise just flush the traceback.
36+
try:
37+
import mypy.errors
38+
39+
_, options = process_options(args=sys.argv[1:])
40+
mypy.errors.report_internal_error(e, None, 0, None, options)
41+
except Exception:
42+
pass
43+
sys.stdout.write(traceback.format_exc())
44+
sys.stdout.flush()
45+
sys.stderr.flush()
46+
sys.exit(2)
3447

3548

3649
if __name__ == "__main__":

mypy/errors.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1397,7 +1397,7 @@ def report_internal_error(
13971397
err: Exception,
13981398
file: str | None,
13991399
line: int,
1400-
errors: Errors,
1400+
errors: Errors | None,
14011401
options: Options,
14021402
stdout: TextIO | None = None,
14031403
stderr: TextIO | None = None,
@@ -1410,11 +1410,12 @@ def report_internal_error(
14101410
stderr = stderr or sys.stderr
14111411
# Dump out errors so far, they often provide a clue.
14121412
# But catch unexpected errors rendering them.
1413-
try:
1414-
for msg in errors.new_messages():
1415-
print(msg)
1416-
except Exception as e:
1417-
print("Failed to dump errors:", repr(e), file=stderr)
1413+
if errors:
1414+
try:
1415+
for msg in errors.new_messages():
1416+
print(msg)
1417+
except Exception as e:
1418+
print("Failed to dump errors:", repr(e), file=stderr)
14181419

14191420
# Compute file:line prefix for official-looking error messages.
14201421
if file:
@@ -1456,7 +1457,7 @@ def report_internal_error(
14561457
if not options.show_traceback:
14571458
if not options.pdb:
14581459
print(
1459-
"{}: note: please use --show-traceback to print a traceback "
1460+
"{}note: please use --show-traceback to print a traceback "
14601461
"when reporting a bug".format(prefix),
14611462
file=stderr,
14621463
)
@@ -1467,7 +1468,7 @@ def report_internal_error(
14671468
for s in traceback.format_list(tb + tb2):
14681469
print(s.rstrip("\n"))
14691470
print(f"{type(err).__name__}: {err}", file=stdout)
1470-
print(f"{prefix}: note: use --pdb to drop into pdb", file=stderr)
1471+
print(f"{prefix}note: use --pdb to drop into pdb", file=stderr)
14711472

14721473
# Exit. The caller has nothing more to say.
14731474
# We use exit code 2 to signal that this is no ordinary error.

0 commit comments

Comments
 (0)