Skip to content

Commit d47025c

Browse files
committed
Support --install-types --non-interactive that doesn't prompt
It also doesn't show errors, making it useful for running CI jobs. Work on #10600.
1 parent 0e6e2f9 commit d47025c

File tree

3 files changed

+43
-12
lines changed

3 files changed

+43
-12
lines changed

mypy/main.py

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,18 @@ def main(script_path: Optional[str],
7272

7373
if options.install_types and (stdout is not sys.stdout or stderr is not sys.stderr):
7474
# Since --install-types performs user input, we want regular stdout and stderr.
75-
fail("--install-types not supported in this mode of running mypy", stderr, options)
75+
fail("Error: --install-types not supported in this mode of running mypy", stderr, options)
76+
77+
if options.non_interactive and not options.install_types:
78+
fail("Error: --non-interactive is only supported with --install-types", stderr, options)
7679

7780
if options.install_types and not sources:
78-
install_types(options.cache_dir, formatter)
81+
install_types(options.cache_dir, formatter, non_interactive=options.non_interactive)
7982
return
8083

8184
def flush_errors(new_messages: List[str], serious: bool) -> None:
85+
if options.non_interactive:
86+
return
8287
if options.pretty:
8388
new_messages = formatter.fit_in_terminal(new_messages)
8489
messages.extend(new_messages)
@@ -100,7 +105,10 @@ def flush_errors(new_messages: List[str], serious: bool) -> None:
100105
blockers = True
101106
if not e.use_stdout:
102107
serious = True
103-
if options.warn_unused_configs and options.unused_configs and not options.incremental:
108+
if (options.warn_unused_configs
109+
and options.unused_configs
110+
and not options.incremental
111+
and not options.non_interactive):
104112
print("Warning: unused section(s) in %s: %s" %
105113
(options.config_file,
106114
get_config_module_names(options.config_file,
@@ -116,7 +124,7 @@ def flush_errors(new_messages: List[str], serious: bool) -> None:
116124
code = 0
117125
if messages:
118126
code = 2 if blockers else 1
119-
if options.error_summary:
127+
if options.error_summary and not options.non_interactive:
120128
if messages:
121129
n_errors, n_files = util.count_stats(messages)
122130
if n_errors:
@@ -130,7 +138,8 @@ def flush_errors(new_messages: List[str], serious: bool) -> None:
130138
stdout.flush()
131139

132140
if options.install_types:
133-
install_types(options.cache_dir, formatter, after_run=True)
141+
install_types(options.cache_dir, formatter, after_run=True,
142+
non_interactive=options.non_interactive)
134143
return
135144

136145
if options.fast_exit:
@@ -751,6 +760,10 @@ def add_invertible_flag(flag: str,
751760
add_invertible_flag('--install-types', default=False, strict_flag=False,
752761
help="Install detected missing library stub packages using pip",
753762
group=other_group)
763+
add_invertible_flag('--non-interactive', default=False, strict_flag=False,
764+
help=("Install stubs without asking for confirmation and hide " +
765+
"errors, with --install-types"),
766+
group=other_group, inverse="--interactive")
754767

755768
if server_options:
756769
# TODO: This flag is superfluous; remove after a short transition (2018-03-16)
@@ -1072,7 +1085,9 @@ def fail(msg: str, stderr: TextIO, options: Options) -> None:
10721085

10731086
def install_types(cache_dir: str,
10741087
formatter: util.FancyFormatter,
1075-
after_run: bool = False) -> None:
1088+
*,
1089+
after_run: bool = False,
1090+
non_interactive: bool = False) -> None:
10761091
"""Install stub packages using pip if some missing stubs were detected."""
10771092
if not os.path.isdir(cache_dir):
10781093
sys.stderr.write(
@@ -1084,15 +1099,16 @@ def install_types(cache_dir: str,
10841099
return
10851100
with open(fnam) as f:
10861101
packages = [line.strip() for line in f.readlines()]
1087-
if after_run:
1102+
if after_run and not non_interactive:
10881103
print()
10891104
print('Installing missing stub packages:')
10901105
cmd = [sys.executable, '-m', 'pip', 'install'] + packages
10911106
print(formatter.style(' '.join(cmd), 'none', bold=True))
10921107
print()
1093-
x = input('Install? [yN] ')
1094-
if not x.strip() or not x.lower().startswith('y'):
1095-
print(formatter.style('mypy: Skipping installation', 'red', bold=True))
1096-
sys.exit(2)
1097-
print()
1108+
if not non_interactive:
1109+
x = input('Install? [yN] ')
1110+
if not x.strip() or not x.lower().startswith('y'):
1111+
print(formatter.style('mypy: Skipping installation', 'red', bold=True))
1112+
sys.exit(2)
1113+
print()
10981114
subprocess.run(cmd)

mypy/options.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,9 @@ def __init__(self) -> None:
297297
self.show_absolute_path = False # type: bool
298298
# Install missing stub packages if True
299299
self.install_types = False
300+
# Install missing stub packages in non-interactive mode (don't prompt for
301+
# confirmation, and don't show any errors)
302+
self.non_interactive = False
300303
# When we encounter errors that may cause many additional errors,
301304
# skip most errors after this many messages have been reported.
302305
# -1 means unlimited.

test-data/unit/cmdline.test

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1276,3 +1276,15 @@ x = 0 # type: str
12761276
y = 0 # type: str
12771277
[out]
12781278
pkg.py:1: error: Incompatible types in assignment (expression has type "int", variable has type "str")
1279+
1280+
[case testCmdlineNonInteractiveWithoutInstallTypes]
1281+
# cmd: mypy --non-interactive -m pkg
1282+
[out]
1283+
Error: --non-interactive is only supported with --install-types
1284+
== Return code: 2
1285+
1286+
[case testCmdlineNonInteractiveInstallTypesNothingToDo]
1287+
# cmd: mypy --install-types --non-interactive -m pkg
1288+
[file pkg.py]
1289+
1()
1290+
[out]

0 commit comments

Comments
 (0)