@@ -273,9 +273,78 @@ def parse_errors(self, output: Sequence[str]) -> dict[int, list[str]]:
273273 line_to_errors .setdefault (int (lineno ), []).append (line )
274274 return line_to_errors
275275
276+ class ZubanLSTypeChecker (MypyTypeChecker ):
277+ @property
278+ def name (self ) -> str :
279+ return "zuban"
280+
281+ def install (self ) -> bool :
282+ try :
283+ # Uninstall any existing version if present.
284+ run (
285+ [sys .executable , "-m" , "pip" , "uninstall" , "zuban" , "-y" ],
286+ check = True ,
287+ )
288+
289+ # Install the latest version.
290+ run (
291+ [sys .executable , "-m" , "pip" , "install" , "zuban" ],
292+ check = True ,
293+ )
294+
295+ # Run "mypy --version" to ensure that it's installed and to work
296+ # around timing issues caused by malware scanners on some systems.
297+ self .get_version ()
298+
299+ return True
300+ except CalledProcessError :
301+ print ("Unable to install zuban" )
302+ return False
303+
304+ def get_version (self ) -> str :
305+ proc = run (["zmypy" , "--version" ], stdout = PIPE , text = True )
306+ return proc .stdout .strip ().replace ("zmypy" , "zuban" )
307+
308+ def run_tests (self , test_files : Sequence [str ]) -> dict [str , str ]:
309+ command = [
310+ "zmypy" ,
311+ "." ,
312+ "--disable-error-code" ,
313+ "empty-body" ,
314+ "--enable-error-code" ,
315+ "deprecated" ,
316+ "--no-warn-unreachable" ,
317+ "--no-mypy-compatible" ,
318+ ]
319+ proc = run (command , stdout = PIPE , text = True , encoding = "utf-8" )
320+ lines = proc .stdout .split ("\n " )
321+
322+ # Add results to a dictionary keyed by the file name.
323+ results_dict : dict [str , str ] = {}
324+ for line in lines :
325+ file_name = line .split (":" )[0 ].strip ()
326+ results_dict [file_name ] = results_dict .get (file_name , "" ) + line + "\n "
327+
328+ return results_dict
329+
330+ def parse_errors (self , output : Sequence [str ]) -> dict [int , list [str ]]:
331+ # narrowing_typeguard.py:102: error: TypeGuard functions must have a positional argument [valid-type]
332+ line_to_errors : dict [int , list [str ]] = {}
333+ for line in output :
334+ if line .count (":" ) < 3 :
335+ continue
336+ _ , lineno , kind , _ = line .split (":" , maxsplit = 3 )
337+ kind = kind .strip ()
338+ if kind != "error" :
339+ continue
340+ line_to_errors .setdefault (int (lineno ), []).append (line )
341+ return line_to_errors
342+
343+
276344
277345TYPE_CHECKERS : Sequence [TypeChecker ] = (
278346 MypyTypeChecker (),
279347 PyrightTypeChecker (),
280348 * ([] if os .name == "nt" else [PyreTypeChecker ()]),
349+ ZubanLSTypeChecker (),
281350)
0 commit comments