@@ -66,6 +66,14 @@ def main(script_path: Optional[str],
66
66
messages = []
67
67
formatter = util .FancyFormatter (stdout , stderr , options .show_error_codes )
68
68
69
+ if options .install_types and (stdout is not sys .stdout or stderr is not sys .stderr ):
70
+ # Since --install-types performs user input, we want regular stdout and stderr.
71
+ fail ("--install-types not supported in this mode of running mypy" , stderr , options )
72
+
73
+ if options .install_types and not sources :
74
+ install_types (options .cache_dir , formatter )
75
+ return
76
+
69
77
def flush_errors (new_messages : List [str ], serious : bool ) -> None :
70
78
if options .pretty :
71
79
new_messages = formatter .fit_in_terminal (new_messages )
@@ -116,6 +124,11 @@ def flush_errors(new_messages: List[str], serious: bool) -> None:
116
124
stdout .write (formatter .format_success (len (sources ),
117
125
options .color_output ) + '\n ' )
118
126
stdout .flush ()
127
+
128
+ if options .install_types :
129
+ install_types (options .cache_dir , formatter , after_run = True )
130
+ return
131
+
119
132
if options .fast_exit :
120
133
# Exit without freeing objects -- it's faster.
121
134
#
@@ -722,6 +735,10 @@ def add_invertible_flag(flag: str,
722
735
'--scripts-are-modules' , action = 'store_true' ,
723
736
help = "Script x becomes module x instead of __main__" )
724
737
738
+ add_invertible_flag ('--install-types' , default = False , strict_flag = False ,
739
+ help = "Install detected missing library stub packages using pip" ,
740
+ group = other_group )
741
+
725
742
if server_options :
726
743
# TODO: This flag is superfluous; remove after a short transition (2018-03-16)
727
744
other_group .add_argument (
@@ -849,7 +866,7 @@ def set_strict_flags() -> None:
849
866
code_methods = sum (bool (c ) for c in [special_opts .modules + special_opts .packages ,
850
867
special_opts .command ,
851
868
special_opts .files ])
852
- if code_methods == 0 :
869
+ if code_methods == 0 and not options . install_types :
853
870
parser .error ("Missing target module, package, files, or command." )
854
871
elif code_methods > 1 :
855
872
parser .error ("May only specify one of: module/package, files, or command." )
@@ -996,3 +1013,31 @@ def fail(msg: str, stderr: TextIO, options: Options) -> None:
996
1013
stderr .write ('%s\n ' % msg )
997
1014
maybe_write_junit_xml (0.0 , serious = True , messages = [msg ], options = options )
998
1015
sys .exit (2 )
1016
+
1017
+
1018
+ def install_types (cache_dir : str ,
1019
+ formatter : util .FancyFormatter ,
1020
+ after_run : bool = False ) -> None :
1021
+ """Install stub packages using pip if some missing stubs were detected."""
1022
+ if not os .path .isdir (cache_dir ):
1023
+ sys .stderr .write (
1024
+ "Error: no mypy cache directory (you must enable incremental mode)\n " )
1025
+ sys .exit (2 )
1026
+ fnam = build .missing_stubs_file (cache_dir )
1027
+ if not os .path .isfile (fnam ):
1028
+ # If there are no missing stubs, generate no output.
1029
+ return
1030
+ with open (fnam ) as f :
1031
+ packages = [line .strip () for line in f .readlines ()]
1032
+ if after_run :
1033
+ print ()
1034
+ print ('Installing missing stub packages:' )
1035
+ cmd = ['python3' , '-m' , 'pip' , 'install' ] + packages
1036
+ print (formatter .style (' ' .join (cmd ), 'none' , bold = True ))
1037
+ print ()
1038
+ x = input ('Install? [yN] ' )
1039
+ if not x .strip () or not x .lower ().startswith ('y' ):
1040
+ print (formatter .style ('mypy: Skipping installation' , 'red' , bold = True ))
1041
+ sys .exit (2 )
1042
+ print ()
1043
+ subprocess .run (cmd )
0 commit comments