diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst index 09836e2ffd20..b73525a8857f 100644 --- a/docs/source/command_line.rst +++ b/docs/source/command_line.rst @@ -1083,6 +1083,13 @@ Miscellaneous stub packages were found, they are installed and then another run is performed. +.. option:: --user + + When used together with :option:`--install-types `, this causes mypy to install all suggested stub + packages using pip and installing them to the user's local python + package install location by passing `--user` to `pip` command. + .. option:: --junit-xml JUNIT_XML Causes mypy to generate a JUnit XML test result document with diff --git a/mypy/main.py b/mypy/main.py index 8a35c2056963..ec7d0f325b95 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -81,6 +81,9 @@ def main( if options.non_interactive and not options.install_types: fail("error: --non-interactive is only supported with --install-types", stderr, options) + if options.user and not options.install_types: + fail("error: --user is only supported with --install-types", stderr, options) + if options.install_types and not options.incremental: fail( "error: --install-types not supported with incremental mode disabled", stderr, options @@ -1094,6 +1097,14 @@ def add_invertible_flag( group=other_group, inverse="--interactive", ) + other_group.add_argument( + "--user", + action="store_true", + help=( + "Install stubs to the user's local python package install " + + "location when used together with --install-types" + ), + ) if server_options: # TODO: This flag is superfluous; remove after a short transition (2018-03-16) @@ -1561,7 +1572,8 @@ def install_types( print() print("Installing missing stub packages:") assert options.python_executable, "Python executable required to install types" - cmd = [options.python_executable, "-m", "pip", "install"] + packages + pip_options = ["--user"] if options.user else [] + cmd = [options.python_executable, "-m", "pip", "install"] + pip_options + packages print(formatter.style(" ".join(cmd), "none", bold=True)) print() if not non_interactive: diff --git a/mypy/options.py b/mypy/options.py index 38a87e423766..3bacddc35f90 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -358,6 +358,8 @@ def __init__(self) -> None: # Install missing stub packages in non-interactive mode (don't prompt for # confirmation, and don't show any errors) self.non_interactive = False + # Install missing stub packages into user's local package installation directory + self.user = False # When we encounter errors that may cause many additional errors, # skip most errors after this many messages have been reported. # -1 means unlimited. diff --git a/test-data/unit/cmdline.test b/test-data/unit/cmdline.test index f286f4781ed5..f0195203fe67 100644 --- a/test-data/unit/cmdline.test +++ b/test-data/unit/cmdline.test @@ -1348,6 +1348,25 @@ pkg.py:1: error: "int" not callable error: --install-types not supported without python executable or site packages == Return code: 2 +[case testCmdlineUserOptionWithoutInstallTypes] +# cmd: mypy --user -m pkg +[out] +error: --user is only supported with --install-types +== Return code: 2 + +[case testCmdlineUserOptionInstallTypesNothingToDo] +# cmd: mypy --install-types --user -m pkg +[file pkg.py] +1() +[out] +pkg.py:1: error: "int" not callable + +[case testCmdlineUserOptionInstallTypesNothingToDoNoError] +# cmd: mypy --install-types --user -m pkg +[file pkg.py] +1 + 2 +[out] + [case testCmdlineInteractiveInstallTypesNothingToDo] # cmd: mypy --install-types -m pkg [file pkg.py]