@@ -401,6 +401,15 @@ impl PetClient {
401401 . join ( "\n " )
402402 }
403403
404+ fn interpreter_probe_timeout_labels ( & self ) -> Vec < & ' static str > {
405+ self . stderr_tail
406+ . lock ( )
407+ . expect ( "PET stderr tail mutex poisoned" )
408+ . iter ( )
409+ . filter_map ( |line| interpreter_probe_timeout_label ( line) )
410+ . collect ( )
411+ }
412+
404413 /// Configure the server
405414 pub fn configure ( & mut self , config : Value ) -> Result < Duration , String > {
406415 let start = Instant :: now ( ) ;
@@ -542,6 +551,39 @@ fn get_workspace_dir() -> PathBuf {
542551 } )
543552}
544553
554+ fn interpreter_probe_timeout_label ( line : & str ) -> Option < & ' static str > {
555+ if !line. contains ( "Timed out after" ) || !line. contains ( "resolving Python via spawn" ) {
556+ return None ;
557+ }
558+ if line. contains ( "/usr/bin/python3" ) {
559+ Some ( "usrBinPython3" )
560+ } else if line. contains ( "CommandLineTools" ) {
561+ Some ( "commandLineTools" )
562+ } else if line. contains ( "hostedtoolcache" ) {
563+ Some ( "hostedToolcache" )
564+ } else if line. contains ( "/Library/Frameworks/Python.framework" ) {
565+ Some ( "pythonOrgFramework" )
566+ } else if line. contains ( "/usr/local/bin" ) {
567+ Some ( "usrLocalBin" )
568+ } else {
569+ Some ( "other" )
570+ }
571+ }
572+
573+ #[ test]
574+ fn interpreter_probe_timeouts_are_classified_without_exposing_paths ( ) {
575+ assert_eq ! (
576+ interpreter_probe_timeout_label(
577+ r#"Timed out after 15s resolving Python via spawn for "/usr/bin/python3"; killing child."#
578+ ) ,
579+ Some ( "usrBinPython3" )
580+ ) ;
581+ assert_eq ! (
582+ interpreter_probe_timeout_label( "ordinary PET warning" ) ,
583+ None
584+ ) ;
585+ }
586+
545587fn read_jsonrpc_message ( reader : & mut impl BufRead ) -> Result < Value , String > {
546588 let mut content_length = None ;
547589 loop {
@@ -1212,6 +1254,7 @@ fn test_performance_summary() {
12121254 let mut time_to_first_env_stats = StatisticalMetrics :: new ( ) ;
12131255 let mut phase_stats = BTreeMap :: new ( ) ;
12141256 let mut locator_stats = BTreeMap :: new ( ) ;
1257+ let mut probe_timeout_counts: BTreeMap < String , usize > = BTreeMap :: new ( ) ;
12151258 let mut env_count = 0usize ;
12161259 let mut manager_count = 0usize ;
12171260
@@ -1254,6 +1297,15 @@ fn test_performance_summary() {
12541297 & mut phase_stats,
12551298 & mut locator_stats,
12561299 ) ;
1300+ let timeout_labels = client. interpreter_probe_timeout_labels ( ) ;
1301+ for label in & timeout_labels {
1302+ * probe_timeout_counts
1303+ . entry ( ( * label) . to_string ( ) )
1304+ . or_default ( ) += 1 ;
1305+ }
1306+ if !timeout_labels. is_empty ( ) {
1307+ println ! ( " Interpreter probe timeouts: {timeout_labels:?}" ) ;
1308+ }
12571309
12581310 println ! (
12591311 " Iteration {}: startup={}ms, refresh={}ms, envs={}" ,
@@ -1315,7 +1367,8 @@ fn test_performance_summary() {
13151367 "time_to_first_env" : time_to_first_env_stats. to_json( )
13161368 } ,
13171369 "phases" : phase_json,
1318- "locators" : locator_json
1370+ "locators" : locator_json,
1371+ "interpreter_probe_timeouts" : probe_timeout_counts
13191372 } ) )
13201373 . unwrap ( ) ;
13211374
0 commit comments