Skip to content

Commit dca3940

Browse files
Fix inconsistent argument exit code when argparse exit with its own error code (#7931) (#7943)
Returning 2 here is confusing as it doesn't match the documentation: https://pylint.pycqa.org/en/latest/user_guide/usage/run.html#exit-codes * pylint: use exit code 32 when invalid arguments are passed * pylint: add failing test when ambiguous abbreviated parameters are set in a config file This is confusing behaviour. The output is: ``` usage: pylint [options] pylint: error: ambiguous option: --no could match --notes, --notes-rgx, --no-docstring-rgx ``` The exit code is 2 which doesn't match the documentation: https://pylint.pycqa.org/en/latest/user_guide/usage/run.html#exit-codes * pylint: use exit code 32 when ambiguous abbreviated parameters are set in a config file Co-authored-by: Pierre Sassoulas <[email protected]> (cherry picked from commit 62232b3) Co-authored-by: David Lawson <[email protected]>
1 parent 494e514 commit dca3940

File tree

5 files changed

+58
-4
lines changed

5 files changed

+58
-4
lines changed

doc/whatsnew/fragments/7931.bugfix

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
When pylint exit due to bad arguments being provided the exit code will now be the expected ``32``.
2+
3+
Refs #7931

pylint/config/arguments_manager.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -252,9 +252,12 @@ def _load_default_argument_values(self) -> None:
252252

253253
def _parse_configuration_file(self, arguments: list[str]) -> None:
254254
"""Parse the arguments found in a configuration file into the namespace."""
255-
self.config, parsed_args = self._arg_parser.parse_known_args(
256-
arguments, self.config
257-
)
255+
try:
256+
self.config, parsed_args = self._arg_parser.parse_known_args(
257+
arguments, self.config
258+
)
259+
except SystemExit:
260+
sys.exit(32)
258261
unrecognized_options: list[str] = []
259262
for opt in parsed_args:
260263
if opt.startswith("--"):

pylint/config/config_initialization.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,10 @@ def _config_initialization(
8787
unrecognized_options.append(opt[1:])
8888
if unrecognized_options:
8989
msg = ", ".join(unrecognized_options)
90-
linter._arg_parser.error(f"Unrecognized option found: {msg}")
90+
try:
91+
linter._arg_parser.error(f"Unrecognized option found: {msg}")
92+
except SystemExit:
93+
sys.exit(32)
9194

9295
# Now that config file and command line options have been loaded
9396
# with all disables, it is safe to emit messages

tests/lint/test_run_pylint.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
2+
# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
3+
# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
4+
5+
from pathlib import Path
6+
7+
import pytest
8+
from _pytest.capture import CaptureFixture
9+
10+
from pylint import run_pylint
11+
12+
13+
def test_run_pylint_with_invalid_argument(capsys: CaptureFixture[str]) -> None:
14+
"""Check that appropriate exit code is used with invalid argument."""
15+
with pytest.raises(SystemExit) as ex:
16+
run_pylint(["--never-use-this"])
17+
captured = capsys.readouterr()
18+
assert captured.err.startswith("usage: pylint [options]")
19+
assert ex.value.code == 32
20+
21+
22+
def test_run_pylint_with_invalid_argument_in_config(
23+
capsys: CaptureFixture[str], tmp_path: Path
24+
) -> None:
25+
"""Check that appropriate exit code is used with an ambiguous
26+
argument in a config file.
27+
"""
28+
test_file = tmp_path / "testpylintrc"
29+
with open(test_file, "w", encoding="utf-8") as f:
30+
f.write("[MASTER]\nno=")
31+
32+
with pytest.raises(SystemExit) as ex:
33+
run_pylint(["--rcfile", f"{test_file}"])
34+
captured = capsys.readouterr()
35+
assert captured.err.startswith("usage: pylint [options]")
36+
assert ex.value.code == 32

tests/lint/unittest_expand_modules.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,14 @@ def test__is_in_ignore_list_re_match() -> None:
6969
"path": str(TEST_DIRECTORY / "lint/test_utils.py"),
7070
}
7171

72+
test_run_pylint = {
73+
"basename": "lint",
74+
"basepath": INIT_PATH,
75+
"isarg": False,
76+
"name": "lint.test_run_pylint",
77+
"path": str(TEST_DIRECTORY / "lint/test_run_pylint.py"),
78+
}
79+
7280
test_pylinter = {
7381
"basename": "lint",
7482
"basepath": INIT_PATH,
@@ -102,6 +110,7 @@ def _list_expected_package_modules(
102110
init_of_package,
103111
test_caching,
104112
test_pylinter,
113+
test_run_pylint,
105114
test_utils,
106115
this_file_from_init_deduplicated if deduplicating else this_file_from_init,
107116
unittest_lint,

0 commit comments

Comments
 (0)