@@ -168,27 +168,53 @@ def _print_top_functions(stats_list, title, key_func, format_line, n=3):
168168
169169 # Aggregate stats by fully qualified function name (ignoring line numbers)
170170 func_aggregated = {}
171- for func , prim_calls , total_calls , total_time , cumulative_time , callers in stats_list :
171+ for (
172+ func ,
173+ prim_calls ,
174+ total_calls ,
175+ total_time ,
176+ cumulative_time ,
177+ callers ,
178+ ) in stats_list :
172179 # Use filename:function_name as the key to get fully qualified name
173180 qualified_name = f"{ func [0 ]} :{ func [2 ]} "
174181 if qualified_name not in func_aggregated :
175- func_aggregated [qualified_name ] = [0 , 0 , 0 , 0 ] # prim_calls, total_calls, total_time, cumulative_time
182+ func_aggregated [qualified_name ] = [
183+ 0 ,
184+ 0 ,
185+ 0 ,
186+ 0 ,
187+ ] # prim_calls, total_calls, total_time, cumulative_time
176188 func_aggregated [qualified_name ][0 ] += prim_calls
177189 func_aggregated [qualified_name ][1 ] += total_calls
178190 func_aggregated [qualified_name ][2 ] += total_time
179191 func_aggregated [qualified_name ][3 ] += cumulative_time
180192
181193 # Convert aggregated data back to list format for processing
182194 aggregated_stats = []
183- for qualified_name , (prim_calls , total_calls , total_time , cumulative_time ) in func_aggregated .items ():
195+ for qualified_name , (
196+ prim_calls ,
197+ total_calls ,
198+ total_time ,
199+ cumulative_time ,
200+ ) in func_aggregated .items ():
184201 # Parse the qualified name back to filename and function name
185202 if ":" in qualified_name :
186203 filename , func_name = qualified_name .rsplit (":" , 1 )
187204 else :
188205 filename , func_name = "" , qualified_name
189206 # Create a dummy func tuple with filename and function name for display
190207 dummy_func = (filename , "" , func_name )
191- aggregated_stats .append ((dummy_func , prim_calls , total_calls , total_time , cumulative_time , {}))
208+ aggregated_stats .append (
209+ (
210+ dummy_func ,
211+ prim_calls ,
212+ total_calls ,
213+ total_time ,
214+ cumulative_time ,
215+ {},
216+ )
217+ )
192218
193219 # Most time-consuming functions (by total time)
194220 def format_time_consuming (stat ):
@@ -294,30 +320,29 @@ def sample(
294320 else :
295321 collector .export (filename )
296322
323+
297324def _validate_collapsed_format_args (args , parser ):
298325 # Check for incompatible pstats options
299326 invalid_opts = []
300-
327+
301328 # Get list of pstats-specific options
302- pstats_options = {
303- 'sort' : None ,
304- 'limit' : None ,
305- 'no_summary' : False
306- }
329+ pstats_options = {"sort" : None , "limit" : None , "no_summary" : False }
307330
308331 # Find the default values from the argument definitions
309332 for action in parser ._actions :
310- if action .dest in pstats_options and hasattr (action , ' default' ):
333+ if action .dest in pstats_options and hasattr (action , " default" ):
311334 pstats_options [action .dest ] = action .default
312335
313336 # Check if any pstats-specific options were provided by comparing with defaults
314337 for opt , default in pstats_options .items ():
315338 if getattr (args , opt ) != default :
316- invalid_opts .append (opt .replace (' no_' , '' ))
317-
339+ invalid_opts .append (opt .replace (" no_" , "" ))
340+
318341 if invalid_opts :
319- parser .error (f"The following options are only valid with --pstats format: { ', ' .join (invalid_opts )} " )
320-
342+ parser .error (
343+ f"The following options are only valid with --pstats format: { ', ' .join (invalid_opts )} "
344+ )
345+
321346 # Set default output filename for collapsed format
322347 if not args .outfile :
323348 args .outfile = f"collapsed.{ args .pid } .txt"
@@ -329,14 +354,14 @@ def main():
329354 description = (
330355 "Sample a process's stack frames and generate profiling data.\n "
331356 "Supports two output formats:\n "
332- " - pstats: Detailed profiling statistics with sorting options\n "
357+ " - pstats: Detailed profiling statistics with sorting options\n "
333358 " - collapsed: Stack traces for generating flamegraphs\n "
334359 "\n "
335360 "Examples:\n "
336361 " # Profile process 1234 for 10 seconds with default settings\n "
337362 " python -m profile.sample 1234\n "
338363 "\n "
339- " # Profile with custom interval and duration, save to file\n "
364+ " # Profile with custom interval and duration, save to file\n "
340365 " python -m profile.sample -i 50 -d 30 -o profile.stats 1234\n "
341366 "\n "
342367 " # Generate collapsed stacks for flamegraph\n "
@@ -354,34 +379,33 @@ def main():
354379 " # Profile all threads and save collapsed stacks\n "
355380 " python -m profile.sample -a --collapsed -o stacks.txt 1234"
356381 ),
357- formatter_class = argparse .RawDescriptionHelpFormatter
382+ formatter_class = argparse .RawDescriptionHelpFormatter ,
358383 )
359384
360385 # Required arguments
361- parser .add_argument (
362- "pid" ,
363- type = int ,
364- help = "Process ID to sample"
365- )
386+ parser .add_argument ("pid" , type = int , help = "Process ID to sample" )
366387
367388 # Sampling options
368389 sampling_group = parser .add_argument_group ("Sampling configuration" )
369390 sampling_group .add_argument (
370- "-i" , "--interval" ,
391+ "-i" ,
392+ "--interval" ,
371393 type = int ,
372394 default = 100 ,
373- help = "Sampling interval in microseconds (default: 100)"
395+ help = "Sampling interval in microseconds (default: 100)" ,
374396 )
375397 sampling_group .add_argument (
376- "-d" , "--duration" ,
398+ "-d" ,
399+ "--duration" ,
377400 type = int ,
378401 default = 10 ,
379- help = "Sampling duration in seconds (default: 10)"
402+ help = "Sampling duration in seconds (default: 10)" ,
380403 )
381404 sampling_group .add_argument (
382- "-a" , "--all-threads" ,
405+ "-a" ,
406+ "--all-threads" ,
383407 action = "store_true" ,
384- help = "Sample all threads in the process instead of just the main thread"
408+ help = "Sample all threads in the process instead of just the main thread" ,
385409 )
386410
387411 # Output format selection
@@ -393,20 +417,21 @@ def main():
393417 const = "pstats" ,
394418 dest = "format" ,
395419 default = "pstats" ,
396- help = "Generate pstats output (default)"
420+ help = "Generate pstats output (default)" ,
397421 )
398422 output_format .add_argument (
399423 "--collapsed" ,
400- action = "store_const" ,
424+ action = "store_const" ,
401425 const = "collapsed" ,
402426 dest = "format" ,
403- help = "Generate collapsed stack traces for flamegraphs"
427+ help = "Generate collapsed stack traces for flamegraphs" ,
404428 )
405-
429+
406430 output_group .add_argument (
407- "-o" , "--outfile" ,
431+ "-o" ,
432+ "--outfile" ,
408433 help = "Save output to a file (if omitted, prints to stdout for pstats, "
409- "or saves to collapsed.<pid>.txt for collapsed format)"
434+ "or saves to collapsed.<pid>.txt for collapsed format)" ,
410435 )
411436
412437 # pstats-specific options
@@ -417,55 +442,56 @@ def main():
417442 action = "store_const" ,
418443 const = 0 ,
419444 dest = "sort" ,
420- help = "Sort by number of calls"
445+ help = "Sort by number of calls" ,
421446 )
422447 sort_group .add_argument (
423448 "--sort-time" ,
424449 action = "store_const" ,
425450 const = 1 ,
426451 dest = "sort" ,
427- help = "Sort by total time"
452+ help = "Sort by total time" ,
428453 )
429454 sort_group .add_argument (
430455 "--sort-cumulative" ,
431456 action = "store_const" ,
432457 const = 2 ,
433458 dest = "sort" ,
434459 default = 2 ,
435- help = "Sort by cumulative time (default)"
460+ help = "Sort by cumulative time (default)" ,
436461 )
437462 sort_group .add_argument (
438463 "--sort-percall" ,
439464 action = "store_const" ,
440465 const = 3 ,
441466 dest = "sort" ,
442- help = "Sort by time per call"
467+ help = "Sort by time per call" ,
443468 )
444469 sort_group .add_argument (
445470 "--sort-cumpercall" ,
446471 action = "store_const" ,
447472 const = 4 ,
448473 dest = "sort" ,
449- help = "Sort by cumulative time per call"
474+ help = "Sort by cumulative time per call" ,
450475 )
451476 sort_group .add_argument (
452477 "--sort-name" ,
453478 action = "store_const" ,
454479 const = 5 ,
455480 dest = "sort" ,
456- help = "Sort by function name"
481+ help = "Sort by function name" ,
457482 )
458483
459484 pstats_group .add_argument (
460- "-l" , "--limit" ,
485+ "-l" ,
486+ "--limit" ,
461487 type = int ,
462488 help = "Limit the number of rows in the output" ,
463489 default = 15 ,
464490 )
465491 pstats_group .add_argument (
466492 "--no-summary" ,
467493 action = "store_true" ,
468- help = "Disable the summary section in the output"
494+ help = "Disable the summary section in the output" ,
469495 )
470496
471497 args = parser .parse_args ()
@@ -485,5 +511,7 @@ def main():
485511 show_summary = not args .no_summary ,
486512 output_format = args .format ,
487513 )
514+
515+
488516if __name__ == "__main__" :
489517 main ()
0 commit comments