Skip to content

Support --install-types --non-interactive that doesn't prompt #10616

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 28 additions & 12 deletions mypy/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,18 @@ def main(script_path: Optional[str],

if options.install_types and (stdout is not sys.stdout or stderr is not sys.stderr):
# Since --install-types performs user input, we want regular stdout and stderr.
fail("--install-types not supported in this mode of running mypy", stderr, options)
fail("Error: --install-types not supported in this mode of running mypy", stderr, options)

if options.non_interactive and not options.install_types:
fail("Error: --non-interactive is only supported with --install-types", stderr, options)

if options.install_types and not sources:
install_types(options.cache_dir, formatter)
install_types(options.cache_dir, formatter, non_interactive=options.non_interactive)
return

def flush_errors(new_messages: List[str], serious: bool) -> None:
if options.non_interactive:
return
if options.pretty:
new_messages = formatter.fit_in_terminal(new_messages)
messages.extend(new_messages)
Expand All @@ -100,7 +105,10 @@ def flush_errors(new_messages: List[str], serious: bool) -> None:
blockers = True
if not e.use_stdout:
serious = True
if options.warn_unused_configs and options.unused_configs and not options.incremental:
if (options.warn_unused_configs
and options.unused_configs
and not options.incremental
and not options.non_interactive):
print("Warning: unused section(s) in %s: %s" %
(options.config_file,
get_config_module_names(options.config_file,
Expand All @@ -116,7 +124,7 @@ def flush_errors(new_messages: List[str], serious: bool) -> None:
code = 0
if messages:
code = 2 if blockers else 1
if options.error_summary:
if options.error_summary and not options.non_interactive:
if messages:
n_errors, n_files = util.count_stats(messages)
if n_errors:
Expand All @@ -130,7 +138,8 @@ def flush_errors(new_messages: List[str], serious: bool) -> None:
stdout.flush()

if options.install_types:
install_types(options.cache_dir, formatter, after_run=True)
install_types(options.cache_dir, formatter, after_run=True,
non_interactive=options.non_interactive)
return

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

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

def install_types(cache_dir: str,
formatter: util.FancyFormatter,
after_run: bool = False) -> None:
*,
after_run: bool = False,
non_interactive: bool = False) -> None:
"""Install stub packages using pip if some missing stubs were detected."""
if not os.path.isdir(cache_dir):
sys.stderr.write(
Expand All @@ -1084,15 +1099,16 @@ def install_types(cache_dir: str,
return
with open(fnam) as f:
packages = [line.strip() for line in f.readlines()]
if after_run:
if after_run and not non_interactive:
print()
print('Installing missing stub packages:')
cmd = [sys.executable, '-m', 'pip', 'install'] + packages
print(formatter.style(' '.join(cmd), 'none', bold=True))
print()
x = input('Install? [yN] ')
if not x.strip() or not x.lower().startswith('y'):
print(formatter.style('mypy: Skipping installation', 'red', bold=True))
sys.exit(2)
print()
if not non_interactive:
x = input('Install? [yN] ')
if not x.strip() or not x.lower().startswith('y'):
print(formatter.style('mypy: Skipping installation', 'red', bold=True))
sys.exit(2)
print()
subprocess.run(cmd)
3 changes: 3 additions & 0 deletions mypy/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,9 @@ def __init__(self) -> None:
self.show_absolute_path = False # type: bool
# Install missing stub packages if True
self.install_types = False
# Install missing stub packages in non-interactive mode (don't prompt for
# confirmation, and don't show any errors)
self.non_interactive = False
# When we encounter errors that may cause many additional errors,
# skip most errors after this many messages have been reported.
# -1 means unlimited.
Expand Down
12 changes: 12 additions & 0 deletions test-data/unit/cmdline.test
Original file line number Diff line number Diff line change
Expand Up @@ -1276,3 +1276,15 @@ x = 0 # type: str
y = 0 # type: str
[out]
pkg.py:1: error: Incompatible types in assignment (expression has type "int", variable has type "str")

[case testCmdlineNonInteractiveWithoutInstallTypes]
# cmd: mypy --non-interactive -m pkg
[out]
Error: --non-interactive is only supported with --install-types
== Return code: 2

[case testCmdlineNonInteractiveInstallTypesNothingToDo]
# cmd: mypy --install-types --non-interactive -m pkg
[file pkg.py]
1()
[out]