diff --git a/Lib/cProfile.py b/Lib/cProfile.py index 2e449cc576cebd..20eabf741a8980 100755 --- a/Lib/cProfile.py +++ b/Lib/cProfile.py @@ -12,12 +12,14 @@ # ____________________________________________________________ # Simple interface -def run(statement, filename=None, sort=-1): - return _pyprofile._Utils(Profile).run(statement, filename, sort) +def run(statement, filename=None, sort=-1, numresults=None): + return _pyprofile._Utils(Profile).run(statement, filename, sort, + numresults) -def runctx(statement, globals, locals, filename=None, sort=-1): +def runctx(statement, globals, locals, filename=None, sort=-1, + numresults=None): return _pyprofile._Utils(Profile).runctx(statement, globals, locals, - filename, sort) + filename, sort, numresults) run.__doc__ = _pyprofile.run.__doc__ runctx.__doc__ = _pyprofile.runctx.__doc__ @@ -37,9 +39,10 @@ class Profile(_lsprof.Profiler): # Most of the functionality is in the base class. # This subclass only adds convenient and backward-compatible methods. - def print_stats(self, sort=-1): + def print_stats(self, sort=-1, numresults=None): import pstats - pstats.Stats(self).strip_dirs().sort_stats(sort).print_stats() + pstats.Stats(self).strip_dirs().sort_stats(sort).print_stats( + numresults) def dump_stats(self, file): import marshal @@ -155,8 +158,10 @@ def main(): help="Save stats to ", default=None) parser.add_option('-s', '--sort', dest="sort", help="Sort order when printing to stdout, based on pstats.Stats class", - default=-1, + default='time', choices=sorted(pstats.Stats.sort_arg_dict_default)) + parser.add_option('-n', '--numresults', dest="numresults", type="int", + help="Number of results to show", default=20) parser.add_option('-m', dest="module", action="store_true", help="Profile a library module", default=False) @@ -185,7 +190,8 @@ def main(): '__package__': None, '__cached__': None, } - runctx(code, globs, None, options.outfile, options.sort) + runctx(code, globs, None, options.outfile, options.sort, + options.numresults) else: parser.print_usage() return parser diff --git a/Lib/profile.py b/Lib/profile.py index 9a865d3f6f6ed7..c12b9670f6bce3 100755 --- a/Lib/profile.py +++ b/Lib/profile.py @@ -47,29 +47,29 @@ class _Utils: def __init__(self, profiler): self.profiler = profiler - def run(self, statement, filename, sort): + def run(self, statement, filename, sort, numresults): prof = self.profiler() try: prof.run(statement) except SystemExit: pass finally: - self._show(prof, filename, sort) + self._show(prof, filename, sort, numresults) - def runctx(self, statement, globals, locals, filename, sort): + def runctx(self, statement, globals, locals, filename, sort, numresults): prof = self.profiler() try: prof.runctx(statement, globals, locals) except SystemExit: pass finally: - self._show(prof, filename, sort) + self._show(prof, filename, sort, numresults) - def _show(self, prof, filename, sort): + def _show(self, prof, filename, sort, numresults): if filename is not None: prof.dump_stats(filename) else: - prof.print_stats(sort) + prof.print_stats(sort, numresults) #************************************************************************** @@ -77,7 +77,7 @@ def _show(self, prof, filename, sort): # Note that an instance of Profile() is *not* needed to call them. #************************************************************************** -def run(statement, filename=None, sort=-1): +def run(statement, filename=None, sort=-1, numresults=None): """Run statement under profiler optionally saving results in filename This function takes a single argument that can be passed to the @@ -88,15 +88,17 @@ def run(statement, filename=None, sort=-1): standard name string (file/line/function-name) that is presented in each line. """ - return _Utils(Profile).run(statement, filename, sort) + return _Utils(Profile).run(statement, filename, sort, numresults) -def runctx(statement, globals, locals, filename=None, sort=-1): +def runctx(statement, globals, locals, filename=None, sort=-1, + numresults=None): """Run statement under profiler, supplying your own globals and locals, optionally saving results in filename. statement and filename have the same semantics as profile.run """ - return _Utils(Profile).runctx(statement, globals, locals, filename, sort) + return _Utils(Profile).runctx(statement, globals, locals, filename, sort, + numresults) class Profile: @@ -383,10 +385,10 @@ def simulate_cmd_complete(self): self.t = get_time() - t - def print_stats(self, sort=-1): + def print_stats(self, sort=-1, numresults=None): import pstats - pstats.Stats(self).strip_dirs().sort_stats(sort). \ - print_stats() + pstats.Stats(self).strip_dirs().sort_stats(sort).print_stats( + numresults) def dump_stats(self, file): with open(file, 'wb') as f: @@ -566,18 +568,21 @@ def f(m, f1=f1): def main(): import os + import pstats from optparse import OptionParser - usage = "profile.py [-o output_file_path] [-s sort] [-m module | scriptfile] [arg] ..." + usage = "profile.py [-o output_file_path] [-s sort] [-n limit] [-m module | scriptfile] [arg] ..." parser = OptionParser(usage=usage) parser.allow_interspersed_args = False parser.add_option('-o', '--outfile', dest="outfile", help="Save stats to ", default=None) parser.add_option('-m', dest="module", action="store_true", help="Profile a library module.", default=False) + parser.add_option('-n', '--numresults', dest="numresults", type="int", + help="Number of results to show", default=20) parser.add_option('-s', '--sort', dest="sort", help="Sort order when printing to stdout, based on pstats.Stats class", - default=-1) + default='time', choices=sorted(pstats.Stats.sort_arg_dict_default)) if not sys.argv[1:]: parser.print_usage() @@ -605,7 +610,8 @@ def main(): '__package__': None, '__cached__': None, } - runctx(code, globs, None, options.outfile, options.sort) + runctx(code, globs, None, options.outfile, options.sort, + options.numresults) else: parser.print_usage() return parser diff --git a/Lib/pstats.py b/Lib/pstats.py index ded5ae59f7da21..01ebb090451a9c 100644 --- a/Lib/pstats.py +++ b/Lib/pstats.py @@ -492,7 +492,10 @@ def compare (self, left, right): def func_strip_path(func_name): filename, line, name = func_name - return os.path.basename(filename), line, name + path, base_name = os.path.split(filename) + if base_name.startswith('__'): + base_name = os.path.join(os.path.basename(path), base_name) + return base_name, line, name def func_get_function_name(func): return func[2] @@ -509,7 +512,7 @@ def func_std_string(func_name): # match what old profile produced return "%s:%d(%s)" % func_name #************************************************************************** -# The following functions combine statists for pairs functions. +# The following functions combine statistics for pairs functions. # The bulk of the processing involves correctly handling "call" lists, # such as callers and callees. #************************************************************************** diff --git a/Misc/NEWS.d/next/Library/2019-05-04-19-31-42.bpo-34861.4Clo6Y.rst b/Misc/NEWS.d/next/Library/2019-05-04-19-31-42.bpo-34861.4Clo6Y.rst new file mode 100644 index 00000000000000..cb70280d298228 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-05-04-19-31-42.bpo-34861.4Clo6Y.rst @@ -0,0 +1 @@ +Better cProfile CLI defaults. Add option -n to restrict to top n lines (defaults to 20 for CLI); set default of -s to sort by time (instead of unsorted). \ No newline at end of file