@@ -232,10 +232,7 @@ def get_sort_arg_defs(self):
232232 del dict [word ]
233233 return self .sort_arg_dict
234234
235- def sort_stats (self , * field ):
236- if not field :
237- self .fcn_list = 0
238- return self
235+ def get_sort_tuple_and_type (self , * field ):
239236 if len (field ) == 1 and isinstance (field [0 ], int ):
240237 # Be compatible with old profiler
241238 field = [ {- 1 : "stdname" ,
@@ -249,26 +246,35 @@ def sort_stats(self, *field):
249246
250247 sort_arg_defs = self .get_sort_arg_defs ()
251248
252- sort_tuple = ()
253- self .sort_type = ""
254- connector = ""
249+ sort_fields = []
250+ sort_field_types = []
255251 for word in field :
256252 if isinstance (word , SortKey ):
257253 word = word .value
258- sort_tuple = sort_tuple + sort_arg_defs [word ][0 ]
259- self .sort_type += connector + sort_arg_defs [word ][1 ]
260- connector = ", "
254+ sort_fields .extend (sort_arg_defs [word ][0 ])
255+ sort_field_types .append (sort_arg_defs [word ][1 ])
256+
257+ return (tuple (sort_fields ), ", " .join (sort_field_types ))
258+
259+ def sort_stats (self , * field ):
260+ if not field :
261+ self .fcn_list = 0
262+ return self
263+
264+ sort_tuple , self .sort_type = self .get_sort_tuple_and_type (* field )
261265
262266 stats_list = []
263267 for func , (cc , nc , tt , ct , callers ) in self .stats .items ():
268+ # matches sort_arg_defs: cc, nc, tt, ct, module, line, name, stdname
264269 stats_list .append ((cc , nc , tt , ct ) + func +
265- (func_std_string (func ), func ))
270+ (func_std_string (func ),))
266271
267272 stats_list .sort (key = cmp_to_key (TupleComp (sort_tuple ).compare ))
268273
274+ # store only the order of the funcs
269275 self .fcn_list = fcn_list = []
270276 for tuple in stats_list :
271- fcn_list .append (tuple [- 1 ])
277+ fcn_list .append (tuple [4 : 7 ])
272278 return self
273279
274280 def reverse_order (self ):
@@ -432,28 +438,48 @@ def print_stats(self, *amount):
432438 print (file = self .stream )
433439 return self
434440
435- def print_callees (self , * amount ):
441+ def print_callees (self , * amount , callees_sort_key = None , callees_filter = () ):
436442 width , list = self .get_print_list (amount )
437443 if list :
438444 self .calc_callees ()
439445
446+ sort_tuple = None
447+ sort_type = None
448+ if callees_sort_key :
449+ if isinstance (callees_sort_key , str ):
450+ callees_sort_key = (callees_sort_key ,)
451+ sort_tuple , sort_type = self .get_sort_tuple_and_type (* callees_sort_key )
452+ print (" Callees ordered by: " + sort_type + "\n " )
453+ if not isinstance (callees_filter , tuple ):
454+ callees_filter = (callees_filter ,)
440455 self .print_call_heading (width , "called..." )
441456 for func in list :
442457 if func in self .all_callees :
443- self .print_call_line (width , func , self .all_callees [func ])
458+ self .print_call_line (width , func , self .all_callees [func ],
459+ sort_tuple = sort_tuple , sort_type = sort_type , sel_list = callees_filter )
444460 else :
445461 self .print_call_line (width , func , {})
446462 print (file = self .stream )
447463 print (file = self .stream )
448464 return self
449465
450- def print_callers (self , * amount ):
466+ def print_callers (self , * amount , callers_sort_key = None , callers_filter = () ):
451467 width , list = self .get_print_list (amount )
452468 if list :
469+ sort_tuple = None
470+ sort_type = None
471+ if callers_sort_key :
472+ if isinstance (callers_sort_key , str ):
473+ callers_sort_key = (callers_sort_key ,)
474+ sort_tuple , sort_type = self .get_sort_tuple_and_type (* callers_sort_key )
475+ print (" Callers ordered by: " + sort_type + "\n " )
476+ if not isinstance (callers_filter , tuple ):
477+ callers_filter = (callers_filter ,)
453478 self .print_call_heading (width , "was called by..." )
454479 for func in list :
455480 cc , nc , tt , ct , callers = self .stats [func ]
456- self .print_call_line (width , func , callers , "<-" )
481+ self .print_call_line (width , func , callers , arrow = "<-" ,
482+ sort_tuple = sort_tuple , sort_type = sort_type , sel_list = callers_filter )
457483 print (file = self .stream )
458484 print (file = self .stream )
459485 return self
@@ -470,14 +496,42 @@ def print_call_heading(self, name_size, column_title):
470496 if subheader :
471497 print (" " * name_size + " ncalls tottime cumtime" , file = self .stream )
472498
473- def print_call_line (self , name_size , source , call_dict , arrow = "->" ):
499+ def print_call_line (self , name_size , source , call_dict , arrow = "->" , sort_tuple = None , sort_type = None , sel_list = () ):
474500 print (func_std_string (source ).ljust (name_size ) + arrow , end = ' ' , file = self .stream )
475501 if not call_dict :
476502 print (file = self .stream )
477503 return
478- clist = sorted (call_dict .keys ())
504+
505+ if sort_tuple :
506+ stats_list = []
507+ calls_only = False
508+ for func , value in call_dict .items ():
509+ if isinstance (value , tuple ):
510+ nc , cc , tt , ct = value # cProfile orders it this way
511+ # matches sort_arg_defs: cc, nc, tt, ct, module, line, name, stdname
512+ stats_list .append ((cc , nc , tt , ct ) + func +
513+ (func_std_string (func ),))
514+ else :
515+ if not calls_only :
516+ if "time" in sort_type :
517+ raise TypeError ("Caller/callee stats for %s do not have time information. "
518+ "Try using cProfile instead of profile if you wish to record time by caller/callee."
519+ % func_std_string (func ))
520+ calls_only = True
521+ stats_list .append ((None , value , None , None ) + func +
522+ (func_std_string (func ),))
523+
524+ stats_list .sort (key = cmp_to_key (TupleComp (sort_tuple ).compare ))
525+ funclist = [t [4 :7 ] for t in stats_list ]
526+ else :
527+ funclist = list (sorted (call_dict .keys ()))
528+
529+ msg = ""
530+ for selection in sel_list :
531+ funclist , msg = self .eval_print_amount (selection , funclist , msg )
532+
479533 indent = ""
480- for func in clist :
534+ for func in funclist :
481535 name = func_std_string (func )
482536 value = call_dict [func ]
483537 if isinstance (value , tuple ):
@@ -494,6 +548,8 @@ def print_call_line(self, name_size, source, call_dict, arrow="->"):
494548 left_width = name_size + 3
495549 print (indent * left_width + substats , file = self .stream )
496550 indent = " "
551+ if msg :
552+ print (msg , file = self .stream )
497553
498554 def print_title (self ):
499555 print (' ncalls tottime percall cumtime percall' , end = ' ' , file = self .stream )
0 commit comments