8000 Exit with code 2 on blocking errors (#4443) · python/mypy@42482e8 · GitHub
[go: up one dir, main page]

Skip to content

Commit 42482e8

Browse files
msullivangvanrossum
authored andcommitted
Exit with code 2 on blocking errors (#4443)
Exit with code 2 on blocking errors, while continuing to exit with 1 when there are nonblocking errors. Add test infrastructure. Fixes #2754.
1 parent 548078c commit 42482e8

File tree

4 files changed

+49
-3
lines changed

4 files changed

+49
-3
lines changed

mypy/main.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,14 @@ def flush_errors(new_messages: List[str], serious: bool) -> None:
7272
f.write(msg + '\n')
7373
f.flush()
7474
except BrokenPipeError:
75-
sys.exit(1)
75+
sys.exit(2)
7676

7777
serious = False
78+
blockers = False
7879
try:
7980
type_check_only(sources, bin_dir, options, flush_errors)
8081
except CompileError as e:
82+
blockers = True
8183
if not e.use_stdout:
8284
serious = True
8385
if options.warn_unused_configs and options.unused_configs:
@@ -89,7 +91,8 @@ def flush_errors(new_messages: List[str], serious: bool) -> None:
8991
t1 = time.time()
9092
util.write_junit_xml(t1 - t0, serious, messages, options.junit_xml)
9193
if messages:
92-
sys.exit(1)
94+
code = 2 if blockers else 1
95+
sys.exit(code)
9396

9497

9598
def find_bin_directory(script_path: str) -> str:

mypy/test/testcmdline.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ def test_python_cmdline(testcase: DataDrivenTestCase) -> None:
5858
outb = process.stdout.read()
5959
# Split output into lines.
6060
out = [s.rstrip('\n\r') for s in str(outb, 'utf8').splitlines()]
61+
result = process.wait()
6162
# Remove temp file.
6263
os.remove(program_path)
6364
# Compare actual output to expected.
@@ -78,6 +79,9 @@ def test_python_cmdline(testcase: DataDrivenTestCase) -> None:
7879
path))
7980
else:
8081
out = normalize_error_messages(out)
82+
obvious_result = 1 if out else 0
83+
if obvious_result != result:
84+
out.append('== Return code: {}'.format(result))
8185
assert_string_arrays_equal(testcase.output, out,
8286
'Invalid output ({}, line {})'.format(
8387
testcase.file, testcase.line))

test-data/unit/cmdline.test

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44
-- The initial line specifies the command line, in the format
55
--
66
-- # cmd: mypy <options>
7-
7+
--
8+
-- '== Return code: <value>' is added to the output when the process return code
9+
-- is "nonobvious" -- that is, when it is something other than 0 if there are no
10+
-- messages and 1 if there are.
811

912
-- Directories/packages on the command line
1013
-- ----------------------------------------
@@ -89,6 +92,7 @@ sub.pkg is not a valid Python package name
8992
# coding: uft-8
9093
[out]
9194
mypy: can't decode file 'a.py': unknown encoding: uft-8
95+
== Return code: 2
9296

9397
[case testCannotIgnoreDuplicateModule]
9498
# cmd: mypy one/mod/__init__.py two/mod/__init__.py
@@ -98,6 +102,7 @@ mypy: can't decode file 'a.py': unknown encoding: uft-8
98102
# type: ignore
99103
[out]
100104
two/mod/__init__.py: error: Duplicate module named 'mod'
105+
== Return code: 2
101106

102107
[case testFlagsFile]
103108
# cmd: mypy @flagsfile
@@ -225,6 +230,7 @@ x.py:1: error: Function is missing a type annotation
225230
[file mypy.ini]
226231
[out]
227232
mypy.ini: No [mypy] section in config file
233+
== Return code: 0
228234

229235
[case testConfigErrorUnknownFlag]
230236
# cmd: mypy -c pass
@@ -233,6 +239,7 @@ mypy.ini: No [mypy] section in config file
233239
bad = 0
234240
[out]
235241
mypy.ini: [mypy]: Unrecognized option: bad = 0
242+
== Return code: 0
236243

237244
[case testConfigErrorBadBoolean]
238245
# cmd: mypy -c pass
@@ -241,6 +248,7 @@ mypy.ini: [mypy]: Unrecognized option: bad = 0
241248
ignore_missing_imports = nah
242249
[out]
243250
mypy.ini: [mypy]: ignore_missing_imports: Not a boolean: nah
251+
== Return code: 0
244252

245253
[case testConfigErrorNotPerFile]
246254
# cmd: mypy -c pass
@@ -250,6 +258,7 @@ mypy.ini: [mypy]: ignore_missing_imports: Not a boolean: nah
250258
python_version = 3.4
251259
[out]
252260
mypy.ini: [mypy-*]: Per-module sections should only specify per-module flags (python_version)
261+
== Return code: 0
253262

254263
[case testConfigMypyPath]
255264
# cmd: mypy file.py
@@ -467,6 +476,7 @@ main.py:1: error: Cannot find module named 'a.b'
467476
python_version = 1.0
468477
[out]
469478
mypy.ini: [mypy]: python_version: Python major version '1' out of range (must be 2 or 3)
479+
== Return code: 0
470480

471481
[case testPythonVersionTooOld26]
472482
# cmd: mypy -c pass
@@ -475,6 +485,7 @@ mypy.ini: [mypy]: python_version: Python major version '1' out of range (must be
475485
python_version = 2.6
476486
[out]
477487
mypy.ini: [mypy]: python_version: Python 2.6 is not supported (must be 2.7)
488+
== Return code: 0
478489

479490
[case testPythonVersionTooOld32]
480491
# cmd: mypy -c pass
@@ -483,6 +494,7 @@ mypy.ini: [mypy]: python_version: Python 2.6 is not supported (must be 2.7)
483494
python_version = 3.2
484495
[out]
485496
mypy.ini: [mypy]: python_version: Python 3.2 is not supported (must be 3.3 or higher)
497+
== Return code: 0
486498

487499
[case testPythonVersionTooNew28]
488500
# cmd: mypy -c pass
@@ -491,6 +503,7 @@ mypy.ini: [mypy]: python_version: Python 3.2 is not supported (must be 3.3 or hi
491503
python_version = 2.8
492504
[out]
493505
mypy.ini: [mypy]: python_version: Python 2.8 is not supported (must be 2.7)
506+
== Return code: 0
494507

495508
[case testPythonVersionTooNew40]
496509
# cmd: mypy -c pass
@@ -499,6 +512,7 @@ mypy.ini: [mypy]: python_version: Python 2.8 is not supported (must be 2.7)
499512
python_version = 4.0
500513
[out]
501514
mypy.ini: [mypy]: python_version: Python major version '4' out of range (must be 2 or 3)
515+
== Return code: 0
502516

503517
[case testPythonVersionAccepted27]
504518
# cmd: mypy -c pass
@@ -1008,3 +1022,27 @@ def get_tasks(self):
10081022
return 'whatever'
10091023
[out]
10101024
a.py:1: error: Function is missing a type annotation
1025+
1026+
[case testMissingFile]
1027+
# cmd: mypy nope.py
1028+
[out]
1029+
mypy: can't read file 'nope.py': No such file or directory
1030+
== Return code: 2
1031+
1032+
[case testParseError]
1033+
# cmd: mypy a.py
1034+
[file a.py]
1035+
def foo(
1036+
[out]
1037+
a.py:1: error: unexpected EOF while parsing
1038+
== Return code: 2
1039+
1040+
[case testParseErrorAnnots]
1041+
# cmd: mypy a.py
1042+
[file a.py]
1043+
def foo(x):
1044+
# type: (str, int) -> None
1045+
return
1046+
[out]
1047+
a.py:1: error: Type signature has too many arguments
1048+
== Return code: 2

test-data/unit/reports.test

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
bad_report = .
1313
[out]
1414
mypy.ini: [mypy]: Unrecognized report type: bad_report
15+
== Return code: 0
1516

1617
[case testCoberturaParser]
1718
# cmd: mypy --cobertura-xml-report build pkg

0 commit comments

Comments
 (0)
0