@@ -83,6 +83,11 @@ def __init__(self, prog: str) -> None:
8383p .add_argument ("--junit-xml" , help = "Write junit.xml to the given file" )
8484p .add_argument ("--perf-stats-file" , help = "write performance information to the given file" )
8585p .add_argument ("files" , metavar = "FILE" , nargs = "+" , help = "File (or directory) to check" )
86+ p .add_argument (
87+ "--export-types" ,
88+ action = "store_true" ,
89+ help = "Store types of all expressions in a shared location (useful for inspections)" ,
90+ )
8691
8792run_parser = p = subparsers .add_parser (
8893 "run" ,
@@ -96,6 +101,11 @@ def __init__(self, prog: str) -> None:
96101 "--timeout" , metavar = "TIMEOUT" , type = int , help = "Server shutdown timeout (in seconds)"
97102)
98103p .add_argument ("--log-file" , metavar = "FILE" , type = str , help = "Direct daemon stdout/stderr to FILE" )
104+ p .add_argument (
105+ "--export-types" ,
106+ action = "store_true" ,
107+ help = "Store types of all expressions in a shared location (useful for inspections)" ,
108+ )
99109p .add_argument (
100110 "flags" ,
101111 metavar = "ARG" ,
@@ -113,6 +123,11 @@ def __init__(self, prog: str) -> None:
113123p .add_argument ("-q" , "--quiet" , action = "store_true" , help = argparse .SUPPRESS ) # Deprecated
114124p .add_argument ("--junit-xml" , help = "Write junit.xml to the given file" )
115125p .add_argument ("--perf-stats-file" , help = "write performance information to the given file" )
126+ p .add_argument (
127+ "--export-types" ,
128+ action = "store_true" ,
129+ help = "Store types of all expressions in a shared location (useful for inspections)" ,
130+ )
116131p .add_argument (
117132 "--update" ,
118133 metavar = "FILE" ,
@@ -164,6 +179,68 @@ def __init__(self, prog: str) -> None:
164179 help = "Set the maximum number of types to try for a function (default 64)" ,
165180)
166181
182+ inspect_parser = p = subparsers .add_parser (
183+ "inspect" , help = "Locate and statically inspect expression(s)"
184+ )
185+ p .add_argument (
186+ "location" ,
187+ metavar = "LOCATION" ,
188+ type = str ,
189+ help = "Location specified as path/to/file.py:line:column[:end_line:end_column]."
190+ " If position is given (i.e. only line and column), this will return all"
191+ " enclosing expressions" ,
192+ )
193+ p .add_argument (
194+ "--show" ,
195+ metavar = "INSPECTION" ,
196+ type = str ,
197+ default = "type" ,
198+ choices = ["type" , "attrs" , "definition" ],
199+ help = "What kind of inspection to run" ,
200+ )
201+ p .add_argument (
202+ "--verbose" ,
203+ "-v" ,
204+ action = "count" ,
205+ default = 0 ,
206+ help = "Increase verbosity of the type string representation (can be repeated)" ,
207+ )
208+ p .add_argument (
209+ "--limit" ,
210+ metavar = "NUM" ,
211+ type = int ,
212+ default = 0 ,
213+ help = "Return at most NUM innermost expressions (if position is given); 0 means no limit" ,
214+ )
215+ p .add_argument (
216+ "--include-span" ,
217+ action = "store_true" ,
218+ help = "Prepend each inspection result with the span of corresponding expression"
219+ ' (e.g. 1:2:3:4:"int")' ,
220+ )
221+ p .add_argument (
222+ "--include-kind" ,
223+ action = "store_true" ,
224+ help = "Prepend each inspection result with the kind of corresponding expression"
225+ ' (e.g. NameExpr:"int")' ,
226+ )
227+ p .add_argument (
228+ "--include-object-attrs" ,
229+ action = "store_true" ,
230+ help = 'Include attributes of "object" in "attrs" inspection' ,
231+ )
232+ p .add_argument (
233+ "--union-attrs" ,
234+ action = "store_true" ,
235+ help = "Include attributes valid for some of possible expression types"
236+ " (by default an intersection is returned)" ,
237+ )
238+ p .add_argument (
239+ "--force-reload" ,
240+ action = "store_true" ,
241+ help = "Re-parse and re-type-check file before inspection (may be slow)" ,
242+ )
243+
167244hang_parser = p = subparsers .add_parser ("hang" , help = "Hang for 100 seconds" )
168245
169246daemon_parser = p = subparsers .add_parser ("daemon" , help = "Run daemon in foreground" )
@@ -321,12 +398,24 @@ def do_run(args: argparse.Namespace) -> None:
321398 # Bad or missing status file or dead process; good to start.
322399 start_server (args , allow_sources = True )
323400 t0 = time .time ()
324- response = request (args .status_file , "run" , version = __version__ , args = args .flags )
401+ response = request (
402+ args .status_file ,
403+ "run" ,
404+ version = __version__ ,
405+ args = args .flags ,
406+ export_types = args .export_types ,
407+ )
325408 # If the daemon signals that a restart is necessary, do it
326409 if "restart" in response :
327410 print (f"Restarting: { response ['restart' ]} " )
328411 restart_server (args , allow_sources = True )
329- response = request (args .status_file , "run" , version = __version__ , args = args .flags )
412+ response = request (
413+ args .status_file ,
414+ "run" ,
415+ version = __version__ ,
416+ args = args .flags ,
417+ export_types = args .export_types ,
418+ )
330419
331420 t1 = time .time ()
332421 response ["roundtrip_time" ] = t1 - t0
@@ -383,7 +472,7 @@ def do_kill(args: argparse.Namespace) -> None:
383472def do_check (args : argparse .Namespace ) -> None :
384473 """Ask the daemon to check a list of files."""
385474 t0 = time .time ()
386- response = request (args .status_file , "check" , files = args .files )
475+ response = request (args .status_file , "check" , files = args .files , export_types = args . export_types )
387476 t1 = time .time ()
388477 response ["roundtrip_time" ] = t1 - t0
389478 check_output (response , args .verbose , args .junit_xml , args .perf_stats_file )
@@ -406,9 +495,15 @@ def do_recheck(args: argparse.Namespace) -> None:
406495 """
407496 t0 = time .time ()
408497 if args .remove is not None or args .update is not None :
409- response = request (args .status_file , "recheck" , remove = args .remove , update = args .update )
498+ response = request (
499+ args .status_file ,
500+ "recheck" ,
501+ export_types = args .export_types ,
502+ remove = args .remove ,
503+ update = args .update ,
504+ )
410505 else :
411- response = request (args .status_file , "recheck" )
506+ response = request (args .status_file , "recheck" , export_types = args . export_types )
412507 t1 = time .time ()
413508 response ["roundtrip_time" ] = t1 - t0
414509 check_output (response , args .verbose , args .junit_xml , args .perf_stats_file )
@@ -437,6 +532,25 @@ def do_suggest(args: argparse.Namespace) -> None:
437532 check_output (response , verbose = False , junit_xml = None , perf_stats_file = None )
438533
439534
535+ @action (inspect_parser )
536+ def do_inspect (args : argparse .Namespace ) -> None :
537+ """Ask daemon to print the type of an expression."""
538+ response = request (
539+ args .status_file ,
540+ "inspect" ,
541+ show = args .show ,
542+ location = args .location ,
543+ verbosity = args .verbose ,
544+ limit = args .limit ,
545+ include_span = args .include_span ,
546+ include_kind = args .include_kind ,
547+ include_object_attrs = args .include_object_attrs ,
548+ union_attrs = args .union_attrs ,
549+ force_reload = args .force_reload ,
550+ )
551+ check_output (response , verbose = False , junit_xml = None , perf_stats_file = None )
552+
553+
440554def check_output (
441555 response : Dict [str , Any ],
442556 verbose : bool ,
0 commit comments