1010from mypy .scope import Scope
1111from mypy .options import Options
1212from mypy .version import __version__ as mypy_version
13- from mypy .errorcodes import ErrorCode
13+ from mypy .errorcodes import ErrorCode , IMPORT
1414from mypy import errorcodes as codes
1515from mypy .util import DEFAULT_SOURCE_OFFSET , is_typeshed_file
1616
@@ -65,6 +65,10 @@ class ErrorInfo:
6565 # Fine-grained incremental target where this was reported
6666 target = None # type: Optional[str]
6767
68+ # If True, don't show this message in output, but still record the error (needed
69+ # by mypy daemon)
70+ hidden = False
71+
6872 def __init__ (self ,
6973 import_ctx : List [Tuple [str , int ]],
7074 file : str ,
@@ -158,6 +162,10 @@ class Errors:
158162 target_module = None # type: Optional[str]
159163 scope = None # type: Optional[Scope]
160164
165+ # Have we seen an import-related error so far? If yes, we filter out other messages
166+ # in some cases to avoid reporting huge numbers of errors.
167+ seen_import_error = False
168+
161169 def __init__ (self ,
162170 show_error_context : bool = False ,
163171 show_column_numbers : bool = False ,
@@ -166,7 +174,8 @@ def __init__(self,
166174 read_source : Optional [Callable [[str ], Optional [List [str ]]]] = None ,
167175 show_absolute_path : bool = False ,
168176 enabled_error_codes : Optional [Set [ErrorCode ]] = None ,
169- disabled_error_codes : Optional [Set [ErrorCode ]] = None ) -> None :
177+ disabled_error_codes : Optional [Set [ErrorCode ]] = None ,
178+ many_errors_threshold : int = - 1 ) -> None :
170179 self .show_error_context = show_error_context
171180 self .show_column_numbers = show_column_numbers
172181 self .show_error_codes = show_error_codes
@@ -176,6 +185,7 @@ def __init__(self,
176185 self .read_source = read_source
177186 self .enabled_error_codes = enabled_error_codes or set ()
178187 self .disabled_error_codes = disabled_error_codes or set ()
188+ self .many_errors_threshold = many_errors_threshold
179189 self .initialize ()
180190
181191 def initialize (self ) -> None :
@@ -189,6 +199,7 @@ def initialize(self) -> None:
189199 self .only_once_messages = set ()
190200 self .scope = None
191201 self .target_module = None
202+ self .seen_import_error = False
192203
193204 def reset (self ) -> None :
194205 self .initialize ()
@@ -201,12 +212,14 @@ def copy(self) -> 'Errors':
201212 self .read_source ,
202213 self .show_absolute_path ,
203214 self .enabled_error_codes ,
204- self .disabled_error_codes )
215+ self .disabled_error_codes ,
216+ self .many_errors_threshold )
205217 new .file = self .file
206218 new .import_ctx = self .import_ctx [:]
207219 new .function_or_member = self .function_or_member [:]
208220 new .target_module = self .target_module
209221 new .scope = self .scope
222+ new .seen_import_error = self .seen_import_error
210223 return new
211224
212225 def total_errors (self ) -> int :
@@ -330,6 +343,8 @@ def _add_error_info(self, file: str, info: ErrorInfo) -> None:
330343 if file not in self .error_info_map :
331344 self .error_info_map [file ] = []
332345 self .error_info_map [file ].append (info )
346+ if info .code is IMPORT :
347+ self .seen_import_error = True
333348
334349 def add_error_info (self , info : ErrorInfo ) -> None :
335350 file , line , end_line = info .origin
@@ -354,8 +369,52 @@ def add_error_info(self, info: ErrorInfo) -> None:
354369 if info .message in self .only_once_messages :
355370 return
356371 self .only_once_messages .add (info .message )
372+ if self .seen_import_error and info .code is not IMPORT and self .has_many_errors ():
373+ # Missing stubs can easily cause thousands of errors about
374+ # Any types, especially when upgrading to mypy 0.900,
375+ # which no longer bundles third-party library stubs. Avoid
376+ # showing too many errors to make it easier to see
377+ # import-related errors.
378+ info .hidden = True
379+ self .report_hidden_errors (info )
357380 self ._add_error_info (file , info )
358381
382+ def has_many_errors (self ) -> bool :
383+ if self .many_errors_threshold < 0 :
384+ return False
385+ if len (self .error_info_map ) >= self .many_errors_threshold :
386+ return True
387+ if sum (len (errors )
388+ for errors in self .error_info_map .values ()) >= self .many_errors_threshold :
389+ return True
390+ return False
391+
392+ def report_hidden_errors (self , info : ErrorInfo ) -> None :
393+ message = (
394+ '(Skipping most remaining errors due to unresolved imports or missing stubs; ' +
395+ 'fix these first)'
396+ )
397+ if message in self .only_once_messages :
398+ return
399+ self .only_once_messages .add (message )
400+ new_info = ErrorInfo (
401+ import_ctx = info .import_ctx ,
402+ file = info .file ,
403+ module = info .module ,
404+ typ = None ,
405+ function_or_member = None ,
406+ line = info .line ,
407+ column = info .line ,
408+ severity = 'note' ,
409+ message = message ,
410+ code = None ,
411+ blocker = False ,
412+ only_once = True ,
413+ origin = info .origin ,
414+ target = info .target ,
415+ )
416+ self ._add_error_info (info .origin [0 ], new_info )
417+
359418 def is_ignored_error (self , line : int , info : ErrorInfo , ignores : Dict [int , List [str ]]) -> bool :
360419 if info .blocker :
361420 # Blocking errors can never be ignored
@@ -453,6 +512,7 @@ def format_messages(self, error_info: List[ErrorInfo],
453512 severity 'error').
454513 """
455514 a = [] # type: List[str]
515+ error_info = [info for info in error_info if not info .hidden ]
456516 errors = self .render_messages (self .sort_messages (error_info ))
457517 errors = self .remove_duplicates (errors )
458518 for file , line , column , severity , message , code in errors :
0 commit comments