+++ /dev/null
-"""Class for printing reports on profiled python code."""\r
-\r
-# Class for printing reports on profiled python code. rev 1.0 4/1/94\r
-#\r
-# Based on prior profile module by Sjoerd Mullender...\r
-# which was hacked somewhat by: Guido van Rossum\r
-#\r
-# see profile.py for more info.\r
-\r
-# Copyright 1994, by InfoSeek Corporation, all rights reserved.\r
-# Written by James Roskind\r
-#\r
-# Permission to use, copy, modify, and distribute this Python software\r
-# and its associated documentation for any purpose (subject to the\r
-# restriction in the following sentence) without fee is hereby granted,\r
-# provided that the above copyright notice appears in all copies, and\r
-# that both that copyright notice and this permission notice appear in\r
-# supporting documentation, and that the name of InfoSeek not be used in\r
-# advertising or publicity pertaining to distribution of the software\r
-# without specific, written prior permission. This permission is\r
-# explicitly restricted to the copying and modification of the software\r
-# to remain in Python, compiled Python, or other languages (such as C)\r
-# wherein the modified or derived code is exclusively imported into a\r
-# Python module.\r
-#\r
-# INFOSEEK CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS\r
-# SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND\r
-# FITNESS. IN NO EVENT SHALL INFOSEEK CORPORATION BE LIABLE FOR ANY\r
-# SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER\r
-# RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF\r
-# CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN\r
-# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\r
-\r
-\r
-import sys\r
-import os\r
-import time\r
-import marshal\r
-import re\r
-from functools import cmp_to_key\r
-\r
-__all__ = ["Stats"]\r
-\r
-class Stats:\r
- """This class is used for creating reports from data generated by the\r
- Profile class. It is a "friend" of that class, and imports data either\r
- by direct access to members of Profile class, or by reading in a dictionary\r
- that was emitted (via marshal) from the Profile class.\r
-\r
- The big change from the previous Profiler (in terms of raw functionality)\r
- is that an "add()" method has been provided to combine Stats from\r
- several distinct profile runs. Both the constructor and the add()\r
- method now take arbitrarily many file names as arguments.\r
-\r
- All the print methods now take an argument that indicates how many lines\r
- to print. If the arg is a floating point number between 0 and 1.0, then\r
- it is taken as a decimal percentage of the available lines to be printed\r
- (e.g., .1 means print 10% of all available lines). If it is an integer,\r
- it is taken to mean the number of lines of data that you wish to have\r
- printed.\r
-\r
- The sort_stats() method now processes some additional options (i.e., in\r
- addition to the old -1, 0, 1, or 2). It takes an arbitrary number of\r
- quoted strings to select the sort order. For example sort_stats('time',\r
- 'name') sorts on the major key of 'internal function time', and on the\r
- minor key of 'the name of the function'. Look at the two tables in\r
- sort_stats() and get_sort_arg_defs(self) for more examples.\r
-\r
- All methods return self, so you can string together commands like:\r
- Stats('foo', 'goo').strip_dirs().sort_stats('calls').\\r
- print_stats(5).print_callers(5)\r
- """\r
-\r
- def __init__(self, *args, **kwds):\r
- # I can't figure out how to explictly specify a stream keyword arg\r
- # with *args:\r
- # def __init__(self, *args, stream=sys.stdout): ...\r
- # so I use **kwds and sqauwk if something unexpected is passed in.\r
- self.stream = sys.stdout\r
- if "stream" in kwds:\r
- self.stream = kwds["stream"]\r
- del kwds["stream"]\r
- if kwds:\r
- keys = kwds.keys()\r
- keys.sort()\r
- extras = ", ".join(["%s=%s" % (k, kwds[k]) for k in keys])\r
- raise ValueError, "unrecognized keyword args: %s" % extras\r
- if not len(args):\r
- arg = None\r
- else:\r
- arg = args[0]\r
- args = args[1:]\r
- self.init(arg)\r
- self.add(*args)\r
-\r
- def init(self, arg):\r
- self.all_callees = None # calc only if needed\r
- self.files = []\r
- self.fcn_list = None\r
- self.total_tt = 0\r
- self.total_calls = 0\r
- self.prim_calls = 0\r
- self.max_name_len = 0\r
- self.top_level = {}\r
- self.stats = {}\r
- self.sort_arg_dict = {}\r
- self.load_stats(arg)\r
- trouble = 1\r
- try:\r
- self.get_top_level_stats()\r
- trouble = 0\r
- finally:\r
- if trouble:\r
- print >> self.stream, "Invalid timing data",\r
- if self.files: print >> self.stream, self.files[-1],\r
- print >> self.stream\r
-\r
- def load_stats(self, arg):\r
- if not arg: self.stats = {}\r
- elif isinstance(arg, basestring):\r
- f = open(arg, 'rb')\r
- self.stats = marshal.load(f)\r
- f.close()\r
- try:\r
- file_stats = os.stat(arg)\r
- arg = time.ctime(file_stats.st_mtime) + " " + arg\r
- except: # in case this is not unix\r
- pass\r
- self.files = [ arg ]\r
- elif hasattr(arg, 'create_stats'):\r
- arg.create_stats()\r
- self.stats = arg.stats\r
- arg.stats = {}\r
- if not self.stats:\r
- raise TypeError, "Cannot create or construct a %r object from '%r''" % (\r
- self.__class__, arg)\r
- return\r
-\r
- def get_top_level_stats(self):\r
- for func, (cc, nc, tt, ct, callers) in self.stats.items():\r
- self.total_calls += nc\r
- self.prim_calls += cc\r
- self.total_tt += tt\r
- if ("jprofile", 0, "profiler") in callers:\r
- self.top_level[func] = None\r
- if len(func_std_string(func)) > self.max_name_len:\r
- self.max_name_len = len(func_std_string(func))\r
-\r
- def add(self, *arg_list):\r
- if not arg_list: return self\r
- if len(arg_list) > 1: self.add(*arg_list[1:])\r
- other = arg_list[0]\r
- if type(self) != type(other) or self.__class__ != other.__class__:\r
- other = Stats(other)\r
- self.files += other.files\r
- self.total_calls += other.total_calls\r
- self.prim_calls += other.prim_calls\r
- self.total_tt += other.total_tt\r
- for func in other.top_level:\r
- self.top_level[func] = None\r
-\r
- if self.max_name_len < other.max_name_len:\r
- self.max_name_len = other.max_name_len\r
-\r
- self.fcn_list = None\r
-\r
- for func, stat in other.stats.iteritems():\r
- if func in self.stats:\r
- old_func_stat = self.stats[func]\r
- else:\r
- old_func_stat = (0, 0, 0, 0, {},)\r
- self.stats[func] = add_func_stats(old_func_stat, stat)\r
- return self\r
-\r
- def dump_stats(self, filename):\r
- """Write the profile data to a file we know how to load back."""\r
- f = file(filename, 'wb')\r
- try:\r
- marshal.dump(self.stats, f)\r
- finally:\r
- f.close()\r
-\r
- # list the tuple indices and directions for sorting,\r
- # along with some printable description\r
- sort_arg_dict_default = {\r
- "calls" : (((1,-1), ), "call count"),\r
- "cumulative": (((3,-1), ), "cumulative time"),\r
- "file" : (((4, 1), ), "file name"),\r
- "line" : (((5, 1), ), "line number"),\r
- "module" : (((4, 1), ), "file name"),\r
- "name" : (((6, 1), ), "function name"),\r
- "nfl" : (((6, 1),(4, 1),(5, 1),), "name/file/line"),\r
- "pcalls" : (((0,-1), ), "call count"),\r
- "stdname" : (((7, 1), ), "standard name"),\r
- "time" : (((2,-1), ), "internal time"),\r
- }\r
-\r
- def get_sort_arg_defs(self):\r
- """Expand all abbreviations that are unique."""\r
- if not self.sort_arg_dict:\r
- self.sort_arg_dict = dict = {}\r
- bad_list = {}\r
- for word, tup in self.sort_arg_dict_default.iteritems():\r
- fragment = word\r
- while fragment:\r
- if not fragment:\r
- break\r
- if fragment in dict:\r
- bad_list[fragment] = 0\r
- break\r
- dict[fragment] = tup\r
- fragment = fragment[:-1]\r
- for word in bad_list:\r
- del dict[word]\r
- return self.sort_arg_dict\r
-\r
- def sort_stats(self, *field):\r
- if not field:\r
- self.fcn_list = 0\r
- return self\r
- if len(field) == 1 and isinstance(field[0], (int, long)):\r
- # Be compatible with old profiler\r
- field = [ {-1: "stdname",\r
- 0: "calls",\r
- 1: "time",\r
- 2: "cumulative"}[field[0]] ]\r
-\r
- sort_arg_defs = self.get_sort_arg_defs()\r
- sort_tuple = ()\r
- self.sort_type = ""\r
- connector = ""\r
- for word in field:\r
- sort_tuple = sort_tuple + sort_arg_defs[word][0]\r
- self.sort_type += connector + sort_arg_defs[word][1]\r
- connector = ", "\r
-\r
- stats_list = []\r
- for func, (cc, nc, tt, ct, callers) in self.stats.iteritems():\r
- stats_list.append((cc, nc, tt, ct) + func +\r
- (func_std_string(func), func))\r
-\r
- stats_list.sort(key=cmp_to_key(TupleComp(sort_tuple).compare))\r
-\r
- self.fcn_list = fcn_list = []\r
- for tuple in stats_list:\r
- fcn_list.append(tuple[-1])\r
- return self\r
-\r
- def reverse_order(self):\r
- if self.fcn_list:\r
- self.fcn_list.reverse()\r
- return self\r
-\r
- def strip_dirs(self):\r
- oldstats = self.stats\r
- self.stats = newstats = {}\r
- max_name_len = 0\r
- for func, (cc, nc, tt, ct, callers) in oldstats.iteritems():\r
- newfunc = func_strip_path(func)\r
- if len(func_std_string(newfunc)) > max_name_len:\r
- max_name_len = len(func_std_string(newfunc))\r
- newcallers = {}\r
- for func2, caller in callers.iteritems():\r
- newcallers[func_strip_path(func2)] = caller\r
-\r
- if newfunc in newstats:\r
- newstats[newfunc] = add_func_stats(\r
- newstats[newfunc],\r
- (cc, nc, tt, ct, newcallers))\r
- else:\r
- newstats[newfunc] = (cc, nc, tt, ct, newcallers)\r
- old_top = self.top_level\r
- self.top_level = new_top = {}\r
- for func in old_top:\r
- new_top[func_strip_path(func)] = None\r
-\r
- self.max_name_len = max_name_len\r
-\r
- self.fcn_list = None\r
- self.all_callees = None\r
- return self\r
-\r
- def calc_callees(self):\r
- if self.all_callees: return\r
- self.all_callees = all_callees = {}\r
- for func, (cc, nc, tt, ct, callers) in self.stats.iteritems():\r
- if not func in all_callees:\r
- all_callees[func] = {}\r
- for func2, caller in callers.iteritems():\r
- if not func2 in all_callees:\r
- all_callees[func2] = {}\r
- all_callees[func2][func] = caller\r
- return\r
-\r
- #******************************************************************\r
- # The following functions support actual printing of reports\r
- #******************************************************************\r
-\r
- # Optional "amount" is either a line count, or a percentage of lines.\r
-\r
- def eval_print_amount(self, sel, list, msg):\r
- new_list = list\r
- if isinstance(sel, basestring):\r
- try:\r
- rex = re.compile(sel)\r
- except re.error:\r
- msg += " <Invalid regular expression %r>\n" % sel\r
- return new_list, msg\r
- new_list = []\r
- for func in list:\r
- if rex.search(func_std_string(func)):\r
- new_list.append(func)\r
- else:\r
- count = len(list)\r
- if isinstance(sel, float) and 0.0 <= sel < 1.0:\r
- count = int(count * sel + .5)\r
- new_list = list[:count]\r
- elif isinstance(sel, (int, long)) and 0 <= sel < count:\r
- count = sel\r
- new_list = list[:count]\r
- if len(list) != len(new_list):\r
- msg += " List reduced from %r to %r due to restriction <%r>\n" % (\r
- len(list), len(new_list), sel)\r
-\r
- return new_list, msg\r
-\r
- def get_print_list(self, sel_list):\r
- width = self.max_name_len\r
- if self.fcn_list:\r
- stat_list = self.fcn_list[:]\r
- msg = " Ordered by: " + self.sort_type + '\n'\r
- else:\r
- stat_list = self.stats.keys()\r
- msg = " Random listing order was used\n"\r
-\r
- for selection in sel_list:\r
- stat_list, msg = self.eval_print_amount(selection, stat_list, msg)\r
-\r
- count = len(stat_list)\r
-\r
- if not stat_list:\r
- return 0, stat_list\r
- print >> self.stream, msg\r
- if count < len(self.stats):\r
- width = 0\r
- for func in stat_list:\r
- if len(func_std_string(func)) > width:\r
- width = len(func_std_string(func))\r
- return width+2, stat_list\r
-\r
- def print_stats(self, *amount):\r
- for filename in self.files:\r
- print >> self.stream, filename\r
- if self.files: print >> self.stream\r
- indent = ' ' * 8\r
- for func in self.top_level:\r
- print >> self.stream, indent, func_get_function_name(func)\r
-\r
- print >> self.stream, indent, self.total_calls, "function calls",\r
- if self.total_calls != self.prim_calls:\r
- print >> self.stream, "(%d primitive calls)" % self.prim_calls,\r
- print >> self.stream, "in %.3f seconds" % self.total_tt\r
- print >> self.stream\r
- width, list = self.get_print_list(amount)\r
- if list:\r
- self.print_title()\r
- for func in list:\r
- self.print_line(func)\r
- print >> self.stream\r
- print >> self.stream\r
- return self\r
-\r
- def print_callees(self, *amount):\r
- width, list = self.get_print_list(amount)\r
- if list:\r
- self.calc_callees()\r
-\r
- self.print_call_heading(width, "called...")\r
- for func in list:\r
- if func in self.all_callees:\r
- self.print_call_line(width, func, self.all_callees[func])\r
- else:\r
- self.print_call_line(width, func, {})\r
- print >> self.stream\r
- print >> self.stream\r
- return self\r
-\r
- def print_callers(self, *amount):\r
- width, list = self.get_print_list(amount)\r
- if list:\r
- self.print_call_heading(width, "was called by...")\r
- for func in list:\r
- cc, nc, tt, ct, callers = self.stats[func]\r
- self.print_call_line(width, func, callers, "<-")\r
- print >> self.stream\r
- print >> self.stream\r
- return self\r
-\r
- def print_call_heading(self, name_size, column_title):\r
- print >> self.stream, "Function ".ljust(name_size) + column_title\r
- # print sub-header only if we have new-style callers\r
- subheader = False\r
- for cc, nc, tt, ct, callers in self.stats.itervalues():\r
- if callers:\r
- value = callers.itervalues().next()\r
- subheader = isinstance(value, tuple)\r
- break\r
- if subheader:\r
- print >> self.stream, " "*name_size + " ncalls tottime cumtime"\r
-\r
- def print_call_line(self, name_size, source, call_dict, arrow="->"):\r
- print >> self.stream, func_std_string(source).ljust(name_size) + arrow,\r
- if not call_dict:\r
- print >> self.stream\r
- return\r
- clist = call_dict.keys()\r
- clist.sort()\r
- indent = ""\r
- for func in clist:\r
- name = func_std_string(func)\r
- value = call_dict[func]\r
- if isinstance(value, tuple):\r
- nc, cc, tt, ct = value\r
- if nc != cc:\r
- substats = '%d/%d' % (nc, cc)\r
- else:\r
- substats = '%d' % (nc,)\r
- substats = '%s %s %s %s' % (substats.rjust(7+2*len(indent)),\r
- f8(tt), f8(ct), name)\r
- left_width = name_size + 1\r
- else:\r
- substats = '%s(%r) %s' % (name, value, f8(self.stats[func][3]))\r
- left_width = name_size + 3\r
- print >> self.stream, indent*left_width + substats\r
- indent = " "\r
-\r
- def print_title(self):\r
- print >> self.stream, ' ncalls tottime percall cumtime percall',\r
- print >> self.stream, 'filename:lineno(function)'\r
-\r
- def print_line(self, func): # hack : should print percentages\r
- cc, nc, tt, ct, callers = self.stats[func]\r
- c = str(nc)\r
- if nc != cc:\r
- c = c + '/' + str(cc)\r
- print >> self.stream, c.rjust(9),\r
- print >> self.stream, f8(tt),\r
- if nc == 0:\r
- print >> self.stream, ' '*8,\r
- else:\r
- print >> self.stream, f8(float(tt)/nc),\r
- print >> self.stream, f8(ct),\r
- if cc == 0:\r
- print >> self.stream, ' '*8,\r
- else:\r
- print >> self.stream, f8(float(ct)/cc),\r
- print >> self.stream, func_std_string(func)\r
-\r
-class TupleComp:\r
- """This class provides a generic function for comparing any two tuples.\r
- Each instance records a list of tuple-indices (from most significant\r
- to least significant), and sort direction (ascending or decending) for\r
- each tuple-index. The compare functions can then be used as the function\r
- argument to the system sort() function when a list of tuples need to be\r
- sorted in the instances order."""\r
-\r
- def __init__(self, comp_select_list):\r
- self.comp_select_list = comp_select_list\r
-\r
- def compare (self, left, right):\r
- for index, direction in self.comp_select_list:\r
- l = left[index]\r
- r = right[index]\r
- if l < r:\r
- return -direction\r
- if l > r:\r
- return direction\r
- return 0\r
-\r
-#**************************************************************************\r
-# func_name is a triple (file:string, line:int, name:string)\r
-\r
-def func_strip_path(func_name):\r
- filename, line, name = func_name\r
- return os.path.basename(filename), line, name\r
-\r
-def func_get_function_name(func):\r
- return func[2]\r
-\r
-def func_std_string(func_name): # match what old profile produced\r
- if func_name[:2] == ('~', 0):\r
- # special case for built-in functions\r
- name = func_name[2]\r
- if name.startswith('<') and name.endswith('>'):\r
- return '{%s}' % name[1:-1]\r
- else:\r
- return name\r
- else:\r
- return "%s:%d(%s)" % func_name\r
-\r
-#**************************************************************************\r
-# The following functions combine statists for pairs functions.\r
-# The bulk of the processing involves correctly handling "call" lists,\r
-# such as callers and callees.\r
-#**************************************************************************\r
-\r
-def add_func_stats(target, source):\r
- """Add together all the stats for two profile entries."""\r
- cc, nc, tt, ct, callers = source\r
- t_cc, t_nc, t_tt, t_ct, t_callers = target\r
- return (cc+t_cc, nc+t_nc, tt+t_tt, ct+t_ct,\r
- add_callers(t_callers, callers))\r
-\r
-def add_callers(target, source):\r
- """Combine two caller lists in a single list."""\r
- new_callers = {}\r
- for func, caller in target.iteritems():\r
- new_callers[func] = caller\r
- for func, caller in source.iteritems():\r
- if func in new_callers:\r
- if isinstance(caller, tuple):\r
- # format used by cProfile\r
- new_callers[func] = tuple([i[0] + i[1] for i in\r
- zip(caller, new_callers[func])])\r
- else:\r
- # format used by profile\r
- new_callers[func] += caller\r
- else:\r
- new_callers[func] = caller\r
- return new_callers\r
-\r
-def count_calls(callers):\r
- """Sum the caller statistics to get total number of calls received."""\r
- nc = 0\r
- for calls in callers.itervalues():\r
- nc += calls\r
- return nc\r
-\r
-#**************************************************************************\r
-# The following functions support printing of reports\r
-#**************************************************************************\r
-\r
-def f8(x):\r
- return "%8.3f" % x\r
-\r
-#**************************************************************************\r
-# Statistics browser added by ESR, April 2001\r
-#**************************************************************************\r
-\r
-if __name__ == '__main__':\r
- import cmd\r
- try:\r
- import readline\r
- except ImportError:\r
- pass\r
-\r
- class ProfileBrowser(cmd.Cmd):\r
- def __init__(self, profile=None):\r
- cmd.Cmd.__init__(self)\r
- self.prompt = "% "\r
- self.stats = None\r
- self.stream = sys.stdout\r
- if profile is not None:\r
- self.do_read(profile)\r
-\r
- def generic(self, fn, line):\r
- args = line.split()\r
- processed = []\r
- for term in args:\r
- try:\r
- processed.append(int(term))\r
- continue\r
- except ValueError:\r
- pass\r
- try:\r
- frac = float(term)\r
- if frac > 1 or frac < 0:\r
- print >> self.stream, "Fraction argument must be in [0, 1]"\r
- continue\r
- processed.append(frac)\r
- continue\r
- except ValueError:\r
- pass\r
- processed.append(term)\r
- if self.stats:\r
- getattr(self.stats, fn)(*processed)\r
- else:\r
- print >> self.stream, "No statistics object is loaded."\r
- return 0\r
- def generic_help(self):\r
- print >> self.stream, "Arguments may be:"\r
- print >> self.stream, "* An integer maximum number of entries to print."\r
- print >> self.stream, "* A decimal fractional number between 0 and 1, controlling"\r
- print >> self.stream, " what fraction of selected entries to print."\r
- print >> self.stream, "* A regular expression; only entries with function names"\r
- print >> self.stream, " that match it are printed."\r
-\r
- def do_add(self, line):\r
- if self.stats:\r
- self.stats.add(line)\r
- else:\r
- print >> self.stream, "No statistics object is loaded."\r
- return 0\r
- def help_add(self):\r
- print >> self.stream, "Add profile info from given file to current statistics object."\r
-\r
- def do_callees(self, line):\r
- return self.generic('print_callees', line)\r
- def help_callees(self):\r
- print >> self.stream, "Print callees statistics from the current stat object."\r
- self.generic_help()\r
-\r
- def do_callers(self, line):\r
- return self.generic('print_callers', line)\r
- def help_callers(self):\r
- print >> self.stream, "Print callers statistics from the current stat object."\r
- self.generic_help()\r
-\r
- def do_EOF(self, line):\r
- print >> self.stream, ""\r
- return 1\r
- def help_EOF(self):\r
- print >> self.stream, "Leave the profile brower."\r
-\r
- def do_quit(self, line):\r
- return 1\r
- def help_quit(self):\r
- print >> self.stream, "Leave the profile brower."\r
-\r
- def do_read(self, line):\r
- if line:\r
- try:\r
- self.stats = Stats(line)\r
- except IOError, args:\r
- print >> self.stream, args[1]\r
- return\r
- except Exception as err:\r
- print >> self.stream, err.__class__.__name__ + ':', err\r
- return\r
- self.prompt = line + "% "\r
- elif len(self.prompt) > 2:\r
- line = self.prompt[:-2]\r
- self.do_read(line)\r
- else:\r
- print >> self.stream, "No statistics object is current -- cannot reload."\r
- return 0\r
- def help_read(self):\r
- print >> self.stream, "Read in profile data from a specified file."\r
- print >> self.stream, "Without argument, reload the current file."\r
-\r
- def do_reverse(self, line):\r
- if self.stats:\r
- self.stats.reverse_order()\r
- else:\r
- print >> self.stream, "No statistics object is loaded."\r
- return 0\r
- def help_reverse(self):\r
- print >> self.stream, "Reverse the sort order of the profiling report."\r
-\r
- def do_sort(self, line):\r
- if not self.stats:\r
- print >> self.stream, "No statistics object is loaded."\r
- return\r
- abbrevs = self.stats.get_sort_arg_defs()\r
- if line and all((x in abbrevs) for x in line.split()):\r
- self.stats.sort_stats(*line.split())\r
- else:\r
- print >> self.stream, "Valid sort keys (unique prefixes are accepted):"\r
- for (key, value) in Stats.sort_arg_dict_default.iteritems():\r
- print >> self.stream, "%s -- %s" % (key, value[1])\r
- return 0\r
- def help_sort(self):\r
- print >> self.stream, "Sort profile data according to specified keys."\r
- print >> self.stream, "(Typing `sort' without arguments lists valid keys.)"\r
- def complete_sort(self, text, *args):\r
- return [a for a in Stats.sort_arg_dict_default if a.startswith(text)]\r
-\r
- def do_stats(self, line):\r
- return self.generic('print_stats', line)\r
- def help_stats(self):\r
- print >> self.stream, "Print statistics from the current stat object."\r
- self.generic_help()\r
-\r
- def do_strip(self, line):\r
- if self.stats:\r
- self.stats.strip_dirs()\r
- else:\r
- print >> self.stream, "No statistics object is loaded."\r
- def help_strip(self):\r
- print >> self.stream, "Strip leading path information from filenames in the report."\r
-\r
- def help_help(self):\r
- print >> self.stream, "Show help for a given command."\r
-\r
- def postcmd(self, stop, line):\r
- if stop:\r
- return stop\r
- return None\r
-\r
- import sys\r
- if len(sys.argv) > 1:\r
- initprofile = sys.argv[1]\r
- else:\r
- initprofile = None\r
- try:\r
- browser = ProfileBrowser(initprofile)\r
- print >> browser.stream, "Welcome to the profile statistics browser."\r
- browser.cmdloop()\r
- print >> browser.stream, "Goodbye."\r
- except KeyboardInterrupt:\r
- pass\r
-\r
-# That's all, folks.\r