1111"""
1212
1313import sys
14+ import os
1415import logging
1516from argparse import ArgumentParser
1617from pathlib import Path
@@ -54,7 +55,7 @@ def make_sarif_run(tool_name: str) -> dict:
5455 return sarif_run
5556
5657
57- def flake8_linter (target : Path ) -> None :
58+ def flake8_linter (target : Path , * args ) -> None :
5859 """Run the flake8 linter.
5960
6061 In contrast to the other linters, flake8 has plugin architecture.
@@ -154,7 +155,7 @@ def ruff_format_sarif(results: List[Dict[str, Any]], target: Path) -> dict:
154155 return sarif_run
155156
156157
157- def ruff_linter (target : Path ) -> Optional [dict ]:
158+ def ruff_linter (target : Path , * args ) -> Optional [dict ]:
158159 """Run the ruff linter."""
159160 try :
160161 # pylint: disable=import-outside-toplevel
@@ -256,7 +257,7 @@ def pylint_format_sarif(results: List[Dict[str, Any]], target: Path) -> dict:
256257 return sarif_run
257258
258259
259- def pylint_linter (target : Path ) -> Optional [dict ]:
260+ def pylint_linter (target : Path , * args ) -> Optional [dict ]:
260261 """Run the pylint linter."""
261262 process = run (
262263 ["pylint" , "--output-format=json" , "--recursive=y" , target .absolute ().as_posix ()],
@@ -371,7 +372,7 @@ def mypy_format_sarif(mypy_results: str, target: Path) -> dict:
371372 return sarif_run
372373
373374
374- def mypy_linter (target : Path ) -> Optional [dict ]:
375+ def mypy_linter (target : Path , typeshed_path : Path ) -> Optional [dict ]:
375376 """Run the mypy linter."""
376377 mypy_args = [
377378 "--install-types" ,
@@ -383,6 +384,8 @@ def mypy_linter(target: Path) -> Optional[dict]:
383384 "--show-column-numbers" ,
384385 "--show-error-end" ,
385386 "--show-absolute-path" ,
387+ "--custom-typeshed-dir" ,
388+ typeshed_path .as_posix (),
386389 ]
387390
388391 process_lint = run (["mypy" , * mypy_args , target .absolute ().as_posix ()], capture_output = True , check = False )
@@ -462,9 +465,13 @@ def pyright_format_sarif(results: dict, target: Path) -> dict:
462465 return sarif_run
463466
464467
465- def pyright_linter (target : Path ) -> Optional [dict ]:
468+ def pyright_linter (target : Path , typeshed_path : Path ) -> Optional [dict ]:
466469 """Run the pyright linter."""
467- process = run (["pyright" , "--outputjson" , target .absolute ().as_posix ()], capture_output = True , check = False )
470+ process = run (
471+ ["pyright" , "--outputjson" , "--typeshedpath" , typeshed_path , target .absolute ().as_posix ()],
472+ capture_output = True ,
473+ check = False ,
474+ )
468475
469476 if process .stderr :
470477 LOG .error ("STDERR: %s" , process .stderr .decode ("utf-8" ))
@@ -545,10 +552,14 @@ def pytype_format_sarif(results: str, target: Path) -> dict:
545552 return sarif_run
546553
547554
548- def pytype_linter (target : Path ) -> Optional [dict ]:
555+ def pytype_linter (target : Path , typeshed_path : Path ) -> Optional [dict ]:
549556 """Run the pytype linter."""
557+ os .environ ["TYPESHED_HOME" ] = typeshed_path .as_posix ()
558+
550559 process = run (
551- ["pytype" , "--exclude" , ".pytype/" , "--" , target .absolute ().as_posix ()], capture_output = True , check = False
560+ ["pytype" , "--exclude" , ".pytype/" , "--" , target .as_posix ()],
561+ capture_output = True ,
562+ check = False ,
552563 )
553564
554565 if process .stderr :
@@ -567,6 +578,44 @@ def pytype_linter(target: Path) -> Optional[dict]:
567578 return sarif_run
568579
569580
581+ def pyre_linter (target : Path , typeshed_path : Path ) -> Optional [dict ]:
582+ """Run the pytype linter."""
583+ process = run (
584+ [
585+ "pyre" ,
586+ "--source-directory" ,
587+ target .as_posix (),
588+ "--output" ,
589+ "sarif" ,
590+ "--typeshed" ,
591+ typeshed_path .as_posix (),
592+ "check" ,
593+ ],
594+ capture_output = True ,
595+ check = False ,
596+ )
597+
598+ if process .stderr :
599+ LOG .debug ("STDERR: %s" , process .stderr .decode ("utf-8" ))
600+
601+ if not process .stdout :
602+ LOG .error ("No output from pytype" )
603+ return None
604+
605+ try :
606+ sarif = json .loads (process .stdout .decode ("utf-8" ))
607+ except json .JSONDecodeError as err :
608+ LOG .error ("Unable to parse pyre output: %s" , err )
609+ LOG .debug ("Output: %s" , process .stdout .decode ("utf-8" ))
610+ return None
611+
612+ if "runs" in sarif and len (sarif ["runs" ]) > 0 :
613+ return sarif ["runs" ][0 ]
614+
615+ LOG .error ("SARIF not correctly formed, or no runs to output" )
616+ return None
617+
618+
570619def make_fixit_description (rule : str ) -> str :
571620 """Format 'SomeRuleDescription' into 'Some rule description'."""
572621 rule = FIND_CAMEL_CASE .sub (lambda x : x .group (0 ).lower () + " " , rule )
@@ -670,6 +719,7 @@ def make_paths_relative_to_target(runs: List[dict], target: Path) -> None:
670719 "mypy" : mypy_linter ,
671720 "pyright" : pyright_linter ,
672721 "fixit" : fixit_linter ,
722+ "pyre" : pyre_linter ,
673723}
674724
675725# pytype is only supported on Python 3.10 and below, at the time of writing
@@ -682,6 +732,7 @@ def add_args(parser: ArgumentParser) -> None:
682732 parser .add_argument ("linter" , choices = LINTERS .keys (), nargs = "+" , help = "The linter(s) to use" )
683733 parser .add_argument ("--target" , "-t" , default = "." , required = False , help = "Target path for the linter" )
684734 parser .add_argument ("--output" , "-o" , default = "python_linter.sarif" , required = False , help = "Output filename" )
735+ parser .add_argument ("--typeshed-path" , required = False , help = "Path to typeshed" )
685736 parser .add_argument ("--debug" , "-d" , action = "store_true" , required = False , help = "Enable debug logging" )
686737
687738
@@ -700,11 +751,12 @@ def main() -> None:
700751 sarif_runs : List [dict ] = []
701752
702753 target = Path (args .target ).resolve ().absolute ()
754+ typeshed_path = Path (args .typeshed_path ).resolve ().absolute ()
703755
704756 for linter in args .linter :
705757 LOG .debug ("Running %s" , linter )
706758
707- sarif_run = LINTERS [linter ](target )
759+ sarif_run = LINTERS [linter ](target , typeshed_path )
708760
709761 if sarif_run is not None :
710762 sarif_runs .append (sarif_run )
0 commit comments