Skip to content

bpo-34861 Make cProfile default output more useful #9655

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

boxed
Copy link
Contributor

@boxed boxed commented Oct 1, 2018

  • Display one folder level for pstats.stripdirs() when filename starts with __ (__init__.py, __main__.py primarily)

For a test script:

from json import dumps

for _ in range(100):
    dumps(dict(a=1))

The old output is:

         3666 function calls (3556 primitive calls) in 0.005 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        2    0.000    0.000    0.002    0.001 <frozen importlib._bootstrap>:1009(_handle_fromlist)
        7    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:103(release)
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:143(__init__)
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:147(__enter__)
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:151(__exit__)
        7    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:157(_get_module_lock)
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:176(cb)
        2    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:194(_lock_unlock_module)
      7/1    0.000    0.000    0.003    0.003 <frozen importlib._bootstrap>:211(_call_with_frames_removed)
       53    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:222(_verbose_message)
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:307(__init__)
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:311(__enter__)
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:318(__exit__)
       20    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:321(<genexpr>)
        4    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:35(_new_module)
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:369(__init__)
        9    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:403(cached)
        7    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:416(parent)
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:424(has_location)
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:504(_init_module_attrs)
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:576(module_from_spec)
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:58(__init__)
      5/1    0.000    0.000    0.004    0.004 <frozen importlib._bootstrap>:663(_load_unlocked)
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:719(find_spec)
        7    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:78(acquire)
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:792(find_spec)
       15    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:855(__enter__)
       15    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:859(__exit__)
        5    0.000    0.000    0.001    0.000 <frozen importlib._bootstrap>:882(_find_spec)
      5/1    0.000    0.000    0.004    0.004 <frozen importlib._bootstrap>:948(_find_and_load_unlocked)
      5/1    0.000    0.000    0.004    0.004 <frozen importlib._bootstrap>:978(_find_and_load)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:1072(__init__)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:1083(create_module)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:1091(exec_module)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:1233(_path_hooks)
       12    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:1246(_path_importer_cache)
        5    0.000    0.000    0.001    0.000 <frozen importlib._bootstrap_external>:1283(_get_spec)
        5    0.000    0.000    0.001    0.000 <frozen importlib._bootstrap_external>:1315(find_spec)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:1362(__init__)
        8    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:1368(<genexpr>)
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:1394(_get_spec)
       10    0.000    0.000    0.001    0.000 <frozen importlib._bootstrap_external>:1399(find_spec)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:1447(_fill_cache)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:1476(<setcomp>)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:1488(path_hook_for_FileFinder)
        8    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:282(cache_from_source)
       10    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:36(_relax_case)
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:412(_get_cached)
        4    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:444(_check_name_wrapper)
        4    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:481(_classify_pyc)
       12    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:51(_r_long)
        4    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:514(_validate_timestamp_pyc)
       51    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:56(_path_join)
        4    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:566(_compile_bytecode)
       51    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:58(<listcomp>)
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:617(spec_from_file_location)
        8    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:62(_path_split)
       23    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:74(_path_stat)
        4    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:762(create_module)
      4/1    0.000    0.000    0.004    0.004 <frozen importlib._bootstrap_external>:765(exec_module)
        4    0.000    0.000    0.001    0.000 <frozen importlib._bootstrap_external>:836(get_code)
        9    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:84(_path_is_mode_type)
        4    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:927(__init__)
        8    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:93(_path_isfile)
        4    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:952(get_filename)
        4    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:957(get_data)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:98(_path_isdir)
        4    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:994(path_stats)
      100    0.000    0.000    0.001    0.000 __init__.py:183(dumps)
        1    0.000    0.000    0.003    0.003 __init__.py:97(<module>)
        1    0.000    0.000    0.002    0.002 decoder.py:2(<module>)
        1    0.000    0.000    0.000    0.000 decoder.py:20(JSONDecodeError)
        1    0.000    0.000    0.000    0.000 decoder.py:254(JSONDecoder)
        1    0.000    0.000    0.000    0.000 decoder.py:284(__init__)
        1    0.000    0.000    0.000    0.000 encoder.py:104(__init__)
      100    0.000    0.000    0.000    0.000 encoder.py:182(encode)
        1    0.000    0.000    0.001    0.001 encoder.py:2(<module>)
      100    0.000    0.000    0.000    0.000 encoder.py:204(iterencode)
        1    0.000    0.000    0.000    0.000 encoder.py:73(JSONEncoder)
       20    0.000    0.000    0.000    0.000 enum.py:275(__call__)
       20    0.000    0.000    0.000    0.000 enum.py:525(__new__)
       23    0.000    0.000    0.000    0.000 enum.py:602(name)
        5    0.000    0.000    0.000    0.000 enum.py:607(value)
        2    0.000    0.000    0.000    0.000 enum.py:765(_missing_)
        2    0.000    0.000    0.000    0.000 enum.py:772(_create_pseudo_member_)
        4    0.000    0.000    0.000    0.000 enum.py:802(__or__)
        6    0.000    0.000    0.000    0.000 enum.py:808(__and__)
        3    0.000    0.000    0.000    0.000 enum.py:827(_high_bit)
        2    0.000    0.000    0.000    0.000 enum.py:844(_decompose)
        2    0.000    0.000    0.000    0.000 enum.py:862(<listcomp>)
        5    0.000    0.000    0.000    0.000 enum.py:873(<lambda>)
        5    0.000    0.000    0.000    0.000 enum.py:879(_power_of_two)
        6    0.000    0.000    0.001    0.000 re.py:232(compile)
        6    0.000    0.000    0.001    0.000 re.py:271(_compile)
        1    0.000    0.000    0.001    0.001 scanner.py:2(<module>)
       14    0.000    0.000    0.000    0.000 sre_compile.py:249(_compile_charset)
       14    0.000    0.000    0.000    0.000 sre_compile.py:276(_optimize_charset)
        4    0.000    0.000    0.000    0.000 sre_compile.py:411(_mk_bitmap)
        4    0.000    0.000    0.000    0.000 sre_compile.py:413(<listcomp>)
        9    0.000    0.000    0.000    0.000 sre_compile.py:423(_simple)
       13    0.000    0.000    0.000    0.000 sre_compile.py:453(_get_iscased)
      8/5    0.000    0.000    0.000    0.000 sre_compile.py:461(_get_literal_prefix)
        5    0.000    0.000    0.000    0.000 sre_compile.py:492(_get_charset_prefix)
        6    0.000    0.000    0.000    0.000 sre_compile.py:536(_compile_info)
       12    0.000    0.000    0.000    0.000 sre_compile.py:595(isstring)
        6    0.000    0.000    0.001    0.000 sre_compile.py:598(_code)
       12    0.000    0.000    0.000    0.000 sre_compile.py:65(_combine_flags)
     25/6    0.000    0.000    0.000    0.000 sre_compile.py:71(_compile)
        6    0.000    0.000    0.001    0.000 sre_compile.py:759(compile)
       26    0.000    0.000    0.000    0.000 sre_parse.py:111(__init__)
       52    0.000    0.000    0.000    0.000 sre_parse.py:160(__len__)
      123    0.000    0.000    0.000    0.000 sre_parse.py:164(__getitem__)
       10    0.000    0.000    0.000    0.000 sre_parse.py:168(__setitem__)
       25    0.000    0.000    0.000    0.000 sre_parse.py:172(append)
    31/12    0.000    0.000    0.000    0.000 sre_parse.py:174(getwidth)
        6    0.000    0.000    0.000    0.000 sre_parse.py:224(__init__)
      102    0.000    0.000    0.000    0.000 sre_parse.py:233(__next)
       71    0.000    0.000    0.000    0.000 sre_parse.py:249(match)
       71    0.000    0.000    0.000    0.000 sre_parse.py:254(get)
        4    0.000    0.000    0.000    0.000 sre_parse.py:258(getwhile)
       38    0.000    0.000    0.000    0.000 sre_parse.py:286(tell)
       16    0.000    0.000    0.000    0.000 sre_parse.py:295(_class_escape)
        4    0.000    0.000    0.000    0.000 sre_parse.py:355(_escape)
        9    0.000    0.000    0.000    0.000 sre_parse.py:432(_uniq)
     13/6    0.000    0.000    0.001    0.000 sre_parse.py:441(_parse_sub)
     15/6    0.000    0.000    0.001    0.000 sre_parse.py:499(_parse)
        6    0.000    0.000    0.000    0.000 sre_parse.py:76(__init__)
       24    0.000    0.000    0.000    0.000 sre_parse.py:81(groups)
        6    0.000    0.000    0.000    0.000 sre_parse.py:84(opengroup)
        6    0.000    0.000    0.000    0.000 sre_parse.py:927(fix_flags)
        6    0.000    0.000    0.001    0.000 sre_parse.py:943(parse)
        6    0.000    0.000    0.000    0.000 sre_parse.py:96(closegroup)
        1    0.000    0.000    0.005    0.005 stats_test.py:1(<module>)
       28    0.000    0.000    0.000    0.000 types.py:164(__get__)
        2    0.000    0.000    0.000    0.000 {built-in method __new__ of type object at 0x10402e370}
        4    0.000    0.000    0.000    0.000 {built-in method _imp._fix_co_filename}
       27    0.000    0.000    0.000    0.000 {built-in method _imp.acquire_lock}
        1    0.000    0.000    0.000    0.000 {built-in method _imp.create_dynamic}
        1    0.000    0.000    0.000    0.000 {built-in method _imp.exec_dynamic}
        2    0.000    0.000    0.000    0.000 {built-in method _imp.is_builtin}
        5    0.000    0.000    0.000    0.000 {built-in method _imp.is_frozen}
       27    0.000    0.000    0.000    0.000 {built-in method _imp.release_lock}
        6    0.000    0.000    0.000    0.000 {built-in method _sre.compile}
       10    0.000    0.000    0.000    0.000 {built-in method _thread.allocate_lock}
       14    0.000    0.000    0.000    0.000 {built-in method _thread.get_ident}
        3    0.000    0.000    0.000    0.000 {built-in method builtins.__build_class__}
        1    0.000    0.000    0.002    0.002 {built-in method builtins.__import__}
        5    0.000    0.000    0.000    0.000 {built-in method builtins.any}
       32    0.000    0.000    0.000    0.000 {built-in method builtins.chr}
      5/1    0.000    0.000    0.005    0.005 {built-in method builtins.exec}
       30    0.000    0.000    0.000    0.000 {built-in method builtins.getattr}
       22    0.000    0.000    0.000    0.000 {built-in method builtins.hasattr}
      389    0.000    0.000    0.000    0.000 {built-in method builtins.isinstance}
  331/303    0.000    0.000    0.000    0.000 {built-in method builtins.len}
        4    0.000    0.000    0.000    0.000 {built-in method builtins.max}
       59    0.000    0.000    0.000    0.000 {built-in method builtins.min}
       17    0.000    0.000    0.000    0.000 {built-in method builtins.ord}
        3    0.000    0.000    0.000    0.000 {built-in method builtins.setattr}
       12    0.000    0.000    0.000    0.000 {built-in method from_bytes}
        4    0.000    0.000    0.000    0.000 {built-in method marshal.loads}
       13    0.000    0.000    0.000    0.000 {built-in method posix.fspath}
        2    0.000    0.000    0.000    0.000 {built-in method posix.getcwd}
        1    0.000    0.000    0.000    0.000 {built-in method posix.listdir}
       23    0.000    0.000    0.000    0.000 {built-in method posix.stat}
      312    0.000    0.000    0.000    0.000 {method 'append' of 'list' objects}
        3    0.000    0.000    0.000    0.000 {method 'bit_length' of 'int' objects}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        6    0.000    0.000    0.000    0.000 {method 'endswith' of 'str' objects}
       12    0.000    0.000    0.000    0.000 {method 'extend' of 'list' objects}
       48    0.000    0.000    0.000    0.000 {method 'find' of 'bytearray' objects}
       33    0.000    0.000    0.000    0.000 {method 'format' of 'str' objects}
       38    0.000    0.000    0.000    0.000 {method 'get' of 'dict' objects}
        1    0.000    0.000    0.000    0.000 {method 'insert' of 'list' objects}
        8    0.000    0.000    0.000    0.000 {method 'items' of 'dict' objects}
      159    0.000    0.000    0.000    0.000 {method 'join' of 'str' objects}
        6    0.000    0.000    0.000    0.000 {method 'lower' of 'str' objects}
        4    0.000    0.000    0.000    0.000 {method 'read' of '_io.FileIO' objects}
       38    0.000    0.000    0.000    0.000 {method 'rpartition' of 'str' objects}
      110    0.000    0.000    0.000    0.000 {method 'rstrip' of 'str' objects}
       34    0.000    0.000    0.000    0.000 {method 'setdefault' of 'dict' objects}
        2    0.000    0.000    0.000    0.000 {method 'sort' of 'list' objects}
        2    0.000    0.000    0.000    0.000 {method 'startswith' of 'str' objects}
        4    0.000    0.000    0.000    0.000 {method 'translate' of 'bytearray' objects}

after this change the output is:

         3666 function calls (3556 primitive calls) in 0.005 seconds

   Ordered by: cumulative time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
      5/1    0.000    0.000    0.005    0.005 {built-in method builtins.exec}
        1    0.000    0.000    0.005    0.005 stats_test.py:1(<module>)
      5/1    0.000    0.000    0.004    0.004 <frozen importlib._bootstrap>:978(_find_and_load)
      5/1    0.000    0.000    0.004    0.004 <frozen importlib._bootstrap>:948(_find_and_load_unlocked)
      5/1    0.000    0.000    0.004    0.004 <frozen importlib._bootstrap>:663(_load_unlocked)
      4/1    0.000    0.000    0.004    0.004 <frozen importlib._bootstrap_external>:765(exec_module)
      7/1    0.000    0.000    0.004    0.004 <frozen importlib._bootstrap>:211(_call_with_frames_removed)
        1    0.000    0.000    0.004    0.004 json/__init__.py:97(<module>)
        1    0.000    0.000    0.002    0.002 decoder.py:2(<module>)
        2    0.000    0.000    0.002    0.001 <frozen importlib._bootstrap>:1009(_handle_fromlist)
        1    0.000    0.000    0.002    0.002 {built-in method builtins.__import__}
        6    0.000    0.000    0.001    0.000 re.py:232(compile)
        6    0.000    0.000    0.001    0.000 re.py:271(_compile)
        6    0.000    0.000    0.001    0.000 sre_compile.py:759(compile)
        1    0.000    0.000    0.001    0.001 scanner.py:2(<module>)
        6    0.000    0.000    0.001    0.000 sre_parse.py:943(parse)
        5    0.000    0.000    0.001    0.000 <frozen importlib._bootstrap>:882(_find_spec)
        5    0.000    0.000    0.001    0.000 <frozen importlib._bootstrap_external>:1315(find_spec)
        5    0.000    0.000    0.001    0.000 <frozen importlib._bootstrap_external>:1283(_get_spec)
     13/6    0.000    0.000    0.001    0.000 sre_parse.py:441(_parse_sub)
        4    0.000    0.000    0.001    0.000 <frozen importlib._bootstrap_external>:836(get_code)
     15/6    0.000    0.000    0.001    0.000 sre_parse.py:499(_parse)
        1    0.000    0.000    0.001    0.001 encoder.py:2(<module>)
      100    0.000    0.000    0.001    0.000 json/__init__.py:183(dumps)
       10    0.000    0.000    0.001    0.000 <frozen importlib._bootstrap_external>:1399(find_spec)
      100    0.000    0.000    0.000    0.000 encoder.py:182(encode)
        6    0.000    0.000    0.000    0.000 sre_compile.py:598(_code)
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:576(module_from_spec)
     25/6    0.000    0.000    0.000    0.000 sre_compile.py:71(_compile)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:1083(create_module)
        1    0.000    0.000    0.000    0.000 {built-in method _imp.create_dynamic}
      100    0.000    0.000    0.000    0.000 encoder.py:204(iterencode)
        4    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:566(_compile_bytecode)
       23    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:74(_path_stat)
        4    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:957(get_data)
       23    0.000    0.000    0.000    0.000 {built-in method posix.stat}
        4    0.000    0.000    0.000    0.000 {built-in method marshal.loads}
       14    0.000    0.000    0.000    0.000 sre_compile.py:276(_optimize_charset)
        6    0.000    0.000    0.000    0.000 sre_compile.py:536(_compile_info)
       20    0.000    0.000    0.000    0.000 enum.py:275(__call__)
       51    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:56(_path_join)
        4    0.000    0.000    0.000    0.000 enum.py:802(__or__)
       12    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:1246(_path_importer_cache)
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:504(_init_module_attrs)
        9    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:84(_path_is_mode_type)
       20    0.000    0.000    0.000    0.000 enum.py:525(__new__)
        8    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:93(_path_isfile)
        4    0.000    0.000    0.000    0.000 {method 'read' of '_io.FileIO' objects}
    31/12    0.000    0.000    0.000    0.000 sre_parse.py:174(getwidth)
        2    0.000    0.000    0.000    0.000 enum.py:765(_missing_)
        2    0.000    0.000    0.000    0.000 enum.py:772(_create_pseudo_member_)
      123    0.000    0.000    0.000    0.000 sre_parse.py:164(__getitem__)
      389    0.000    0.000    0.000    0.000 {built-in method builtins.isinstance}
        8    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:282(cache_from_source)
       71    0.000    0.000    0.000    0.000 sre_parse.py:254(get)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:1447(_fill_cache)
      102    0.000    0.000    0.000    0.000 sre_parse.py:233(__next)
        3    0.000    0.000    0.000    0.000 {built-in method builtins.__build_class__}
        2    0.000    0.000    0.000    0.000 enum.py:844(_decompose)
        6    0.000    0.000    0.000    0.000 sre_parse.py:96(closegroup)
        9    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:403(cached)
        1    0.000    0.000    0.000    0.000 {built-in method posix.listdir}
       51    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:58(<listcomp>)
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:147(__enter__)
        4    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:994(path_stats)
        2    0.000    0.000    0.000    0.000 {built-in method posix.getcwd}
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:412(_get_cached)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:1233(_path_hooks)
  331/303    0.000    0.000    0.000    0.000 {built-in method builtins.len}
      312    0.000    0.000    0.000    0.000 {method 'append' of 'list' objects}
       33    0.000    0.000    0.000    0.000 {method 'format' of 'str' objects}
        6    0.000    0.000    0.000    0.000 enum.py:808(__and__)
        7    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:157(_get_module_lock)
       16    0.000    0.000    0.000    0.000 sre_parse.py:295(_class_escape)
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:1394(_get_spec)
        4    0.000    0.000    0.000    0.000 sre_compile.py:411(_mk_bitmap)
        2    0.000    0.000    0.000    0.000 enum.py:862(<listcomp>)
       71    0.000    0.000    0.000    0.000 sre_parse.py:249(match)
        6    0.000    0.000    0.000    0.000 sre_parse.py:927(fix_flags)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:1488(path_hook_for_FileFinder)
       14    0.000    0.000    0.000    0.000 sre_compile.py:249(_compile_charset)
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:318(__exit__)
       25    0.000    0.000    0.000    0.000 sre_parse.py:172(append)
        9    0.000    0.000    0.000    0.000 sre_compile.py:423(_simple)
      159    0.000    0.000    0.000    0.000 {method 'join' of 'str' objects}
       30    0.000    0.000    0.000    0.000 {built-in method builtins.getattr}
       59    0.000    0.000    0.000    0.000 {built-in method builtins.min}
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:151(__exit__)
       53    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:222(_verbose_message)
        4    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:481(_classify_pyc)
        7    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:78(acquire)
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:617(spec_from_file_location)
       38    0.000    0.000    0.000    0.000 sre_parse.py:286(tell)
        6    0.000    0.000    0.000    0.000 sre_parse.py:224(__init__)
       48    0.000    0.000    0.000    0.000 {method 'find' of 'bytearray' objects}
        7    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:103(release)
      8/5    0.000    0.000    0.000    0.000 sre_compile.py:461(_get_literal_prefix)
       26    0.000    0.000    0.000    0.000 sre_parse.py:111(__init__)
        8    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:62(_path_split)
        4    0.000    0.000    0.000    0.000 sre_compile.py:413(<listcomp>)
       38    0.000    0.000    0.000    0.000 {method 'rpartition' of 'str' objects}
       22    0.000    0.000    0.000    0.000 {built-in method builtins.hasattr}
        2    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:194(_lock_unlock_module)
        9    0.000    0.000    0.000    0.000 sre_parse.py:432(_uniq)
        6    0.000    0.000    0.000    0.000 {built-in method _sre.compile}
       12    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:51(_r_long)
        6    0.000    0.000    0.000    0.000 sre_parse.py:84(opengroup)
       52    0.000    0.000    0.000    0.000 sre_parse.py:160(__len__)
       38    0.000    0.000    0.000    0.000 {method 'get' of 'dict' objects}
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:98(_path_isdir)
       28    0.000    0.000    0.000    0.000 types.py:164(__get__)
        5    0.000    0.000    0.000    0.000 sre_compile.py:492(_get_charset_prefix)
        4    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:514(_validate_timestamp_pyc)
        1    0.000    0.000    0.000    0.000 decoder.py:284(__init__)
      110    0.000    0.000    0.000    0.000 {method 'rstrip' of 'str' objects}
       32    0.000    0.000    0.000    0.000 {built-in method builtins.chr}
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:1362(__init__)
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:58(__init__)
        4    0.000    0.000    0.000    0.000 sre_parse.py:258(getwhile)
       34    0.000    0.000    0.000    0.000 {method 'setdefault' of 'dict' objects}
        5    0.000    0.000    0.000    0.000 {built-in method builtins.any}
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:176(cb)
       15    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:855(__enter__)
       12    0.000    0.000    0.000    0.000 {method 'extend' of 'list' objects}
       12    0.000    0.000    0.000    0.000 sre_compile.py:595(isstring)
       24    0.000    0.000    0.000    0.000 sre_parse.py:81(groups)
       12    0.000    0.000    0.000    0.000 {built-in method from_bytes}
       27    0.000    0.000    0.000    0.000 {built-in method _imp.acquire_lock}
       15    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:859(__exit__)
       12    0.000    0.000    0.000    0.000 sre_compile.py:65(_combine_flags)
       10    0.000    0.000    0.000    0.000 sre_parse.py:168(__setitem__)
        6    0.000    0.000    0.000    0.000 {method 'endswith' of 'str' objects}
       27    0.000    0.000    0.000    0.000 {built-in method _imp.release_lock}
        4    0.000    0.000    0.000    0.000 {built-in method _imp._fix_co_filename}
        4    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:35(_new_module)
       10    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:36(_relax_case)
       20    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:321(<genexpr>)
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:792(find_spec)
        4    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:444(_check_name_wrapper)
        5    0.000    0.000    0.000    0.000 enum.py:879(_power_of_two)
        2    0.000    0.000    0.000    0.000 {method 'sort' of 'list' objects}
        5    0.000    0.000    0.000    0.000 {built-in method _imp.is_frozen}
       10    0.000    0.000    0.000    0.000 {built-in method _thread.allocate_lock}
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:311(__enter__)
        7    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:416(parent)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:1091(exec_module)
        6    0.000    0.000    0.000    0.000 sre_parse.py:76(__init__)
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:369(__init__)
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:719(find_spec)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:1476(<setcomp>)
        4    0.000    0.000    0.000    0.000 sre_parse.py:355(_escape)
        4    0.000    0.000    0.000    0.000 {method 'translate' of 'bytearray' objects}
       17    0.000    0.000    0.000    0.000 {built-in method builtins.ord}
        3    0.000    0.000    0.000    0.000 {built-in method builtins.setattr}
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:143(__init__)
        4    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:927(__init__)
       23    0.000    0.000    0.000    0.000 enum.py:602(name)
        3    0.000    0.000    0.000    0.000 enum.py:827(_high_bit)
        2    0.000    0.000    0.000    0.000 {built-in method __new__ of type object at 0x10fe1c370}
        2    0.000    0.000    0.000    0.000 {method 'startswith' of 'str' objects}
        2    0.000    0.000    0.000    0.000 {built-in method _imp.is_builtin}
       13    0.000    0.000    0.000    0.000 {built-in method posix.fspath}
       14    0.000    0.000    0.000    0.000 {built-in method _thread.get_ident}
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:307(__init__)
        8    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:1368(<genexpr>)
        1    0.000    0.000    0.000    0.000 decoder.py:254(JSONDecoder)
        5    0.000    0.000    0.000    0.000 enum.py:607(value)
       13    0.000    0.000    0.000    0.000 sre_compile.py:453(_get_iscased)
        1    0.000    0.000    0.000    0.000 encoder.py:104(__init__)
        1    0.000    0.000    0.000    0.000 encoder.py:73(JSONEncoder)
        1    0.000    0.000    0.000    0.000 {method 'insert' of 'list' objects}
        3    0.000    0.000    0.000    0.000 {method 'bit_length' of 'int' objects}
        8    0.000    0.000    0.000    0.000 {method 'items' of 'dict' objects}
        4    0.000    0.000    0.000    0.000 {built-in method builtins.max}
        4    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:762(create_module)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:1072(__init__)
        1    0.000    0.000    0.000    0.000 decoder.py:20(JSONDecodeError)
        5    0.000    0.000    0.000    0.000 enum.py:873(<lambda>)
        6    0.000    0.000    0.000    0.000 {method 'lower' of 'str' objects}
        1    0.000    0.000    0.000    0.000 {built-in method _imp.exec_dynamic}
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:424(has_location)
        4    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:952(get_filename)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

https://bugs.python.org/issue34861

@boxed boxed force-pushed the nicer-cprofile-command-line branch 2 times, most recently from ce14a9f to 4bce0c3 Compare October 1, 2018 11:21
@boxed
Copy link
Contributor Author

boxed commented May 19, 2020

Excuse the ping here, but what can I do to move this forward? This is a very simple change that make a HUGE difference in usability, and it has been reviewed already. How can this be merged?

@boxed boxed changed the title bpo-34861 Make cProfile default output useful bpo-34861 Make cProfile default output more useful Jun 2, 2020
Copy link
Member

@pablogsal pablogsal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @boxed and thanks for the PR. I would suggest splitting this PR into the one changing the default and the one changing the path because I have some doubts regarding the path approach but I don't want to block on the default, as that is indeed a good improvement.

Lib/pstats.py Outdated
@@ -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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although I agree that this happens substantially more with __init__ files, I don't think this is an elegant solution as there may be many files with the same file name in different directories or packages. I think the correct solution will be incorporating a way for the user to get the full path (or how many levels of the path maybe). Notice as well that the docs refer to this field as filename. Including the folder instead in some cases is technically backwards incompatible.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is an elegant solution as there may be many files with the same file name in different directories or packages

Absolutely. The correct solution is to cut the paths until they are all unique but also the shortest possible. But this change is going to do that almost always at a much smaller implementation and maintenance cost.

I think the correct solution will be incorporating a way for the user to get the full path (or how many levels of the path maybe).

I disagree. The full path is way too long in almost all cases, which is the reason the default is to drop all of the path all of the time. That just happens to be slightly too much to cover even the 80-20 rule. This change will get us well within 80% with a super small and understandable change to the behavior.

Notice as well that the docs refer to this field as filename. Including the folder instead in some cases is technically backwards incompatible.

It would surprise me greatly if "filename" is never used for the full filename with path anywhere in the docs :) And yes, this is technically incompatible. The change to the sort order is also incompatible if someone has built tools to parse this output and depend on the alphabetical output.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would surprise me greatly if "filename" is never used for the full filename with path anywhere in the docs :) And yes, this is technically incompatible. The change to the sort order is also incompatible if someone has built tools to parse this output and depend on the alphabetical output.

I understand you points, but we care greatly about backwards compatibility and IMHO this does not justify breaking it. Take into account that pstats can be used as a library and therefore people may be relying programmatically upon (without parsing any output) that filename is what the name suggests: a filename. I am referring uniquely to the change in the filename, the change in the default on the command line is fine.

I disagree. The full path is way too long in almost all cases, which is the reason the default is to drop all of the path all of the time. That just happens to be slightly too much to cover even the 80-20 rule. This change will get us well within 80% with a super small and understandable change to the behavior.

I understand what you say, but I still think that making special cases for files that start with double underscores makes the working of the tool inconsistent to work with. This is the standard library and therefore we need to target correctness, not something that works with 80% of the cases.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Take into account that pstats can be used as a library and therefore people may be relying programmatically upon (without parsing any output) that filename is what the name suggests: a filename. I am referring uniquely to the change in the filename, the change in the default on the command line is fine.

Oh, sorry, I misremembered my change. Yes, I see it clearly now. My change should have been to introduce another func_strip_path() that is used by the command line tool by default.

I understand what you say, but I still think that making special cases for files that start with double underscores makes the working of the tool inconsistent to work with.

Inconsistent can be good, if consistent is not usable though, which I think is the current situation in many cases.

This is the standard library and therefore we need to target correctness, not something that works with 80% of the cases.

I don't agree, but I do see your point. If I were to take your position I think the correct way to do this would be to introduce a func_shortest_unique_path function that is called by the command line by default that does the full job to make filenames minimally unique.

If you agree with this, I can get started right away, it doesn't seem like it would be too hard, it just unfortunately requires the function to have access to all paths instead of just the one at a time. A mutation tested test suite for such a function seems pretty straight forward to produce too.

Copy link
Member

@pablogsal pablogsal Oct 14, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I were to take your position I think the correct way to do this would be to introduce a func_shortest_unique_path function that is called by the command line by default that does the full job to make filenames minimally unique.

If this were something that only affects the command line output, I would be ok with such change.

Copy link
Member

@pablogsal pablogsal Oct 14, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent can be good, if consistent is not usable though, which I think is the current situation in many cases.

I am referring to the following scenario: imagine we do the change you propose. Then users start complaining that sometimes they see a filename and sometimes a path and they don't understand why is that. Then, when they understand that is for files with __, they immediately start complaining (rightfully) that there are plenty of other stuff that is not being normalized and why we chose only files with __ and not include namespace packages or other common prefixes to disambiguate the output. Then other users will come in and ask why we are only showing one folder because they have an implicit namespace with 10 different directories with a init.py file that has the same parent folder. And so on and so forth.

Inconsistent here is that (1) the output may be strange to users (why is doing that?) and (2) is not solving the generic problem, just making a bandaid.

Copy link
Member

@pablogsal pablogsal Oct 14, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the other hand, giving an option to receive the full path (not in the command line, that can be too big, we would need a different solution for that) will make the tool consistent and will cover 100% of all cases.

I am not suggesting we do that here, I am just explaining my point further :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bandaids stop bleeding, they are a very good thing :)

But yes ok, I'll do the full thing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I implemented this suggested change. It's much more complex obviously, but actually using it, I think it's worse. The problem is that we've been thinking about this wrong. Unique filenames isn't the only relevant thing. If the filename is __init__.py, this is pretty much identical to the empty string in usefulness unless your entire project has only one__init__.py with code in it. That the profiler only hit one of those files doesn't actually mean that just showing __init__.py gives any useful information. So I think a special case for __init__.py is still very much warranted.

I have committed an updated piece of code with the changes you suggested plus a special case for __init__.py. There's also a test suite with only one surviving mutant for mutmut and that is a <= -> < mutation for the step loop. I think this is fine.

@bedevere-bot
Copy link

A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated.

Once you have made the requested changes, please leave a comment on this pull request containing the phrase I have made the requested changes; please review again. I will then notify any core developers who have left a review that you're ready for them to take another look at this pull request.

@boxed
Copy link
Contributor Author

boxed commented Oct 14, 2020

I clicked on the "resolve conversation" button to let you know I've answered the questions. I hope that was the right thing to do and doesn't appear rude or something. I'm unsure what the etiquette is...

@boxed
Copy link
Contributor Author

boxed commented Oct 14, 2020

Ah, I need to do this too:

I have made the requested changes; please review again.

I haven't made all the requested changes, but I have replied to the discussion at least.

@bedevere-bot
Copy link

Thanks for making the requested changes!

@pablogsal: please review the changes made to this pull request.

Copy link
Member

@pablogsal pablogsal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check out this comment: https://github.com/python/cpython/pull/9655/files#r504605933

I kindly insist on what I said before: I think is easier to split the PR.

@bedevere-bot
Copy link

A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated.

Once you have made the requested changes, please leave a comment on this pull request containing the phrase I have made the requested changes; please review again. I will then notify any core developers who have left a review that you're ready for them to take another look at this pull request.

@boxed
Copy link
Contributor Author

boxed commented Oct 16, 2020

I have made the requested changes; please review again.

@bedevere-bot
Copy link

Thanks for making the requested changes!

@pablogsal: please review the changes made to this pull request.

@pablogsal
Copy link
Member

@boxed Did you forgot to push your changes? The last commit here was 3 days ago

@boxed
Copy link
Contributor Author

boxed commented Oct 17, 2020

@pablogsal Yes I did :( I have pushed now and resolved a merge conflict I got now too.

@boxed boxed force-pushed the nicer-cprofile-command-line branch from 99d996d to f13da29 Compare October 18, 2020 11:22
@boxed
Copy link
Contributor Author

boxed commented Oct 18, 2020

I rebased and squashed everything, and fixed the docs. I was so far out of date with master that this was needed (due to this PR started its life in 2018).

Copy link

This PR is stale because it has been open for 30 days with no activity.

@github-actions github-actions bot added the stale Stale PR or inactive for long period of time. label Feb 27, 2025
Display one folder level for pstats.stripdirs() when filename starts with __ (__init__.py, __main__.py primarily)
@boxed boxed force-pushed the nicer-cprofile-command-line branch from 32bde8f to f6da3b6 Compare February 27, 2025 06:56
@boxed
Copy link
Contributor Author

boxed commented Feb 27, 2025

I have reapplied the change on top of current main, and removed the change for setting sorting to cumtime as that was added in another PR.

@boxed
Copy link
Contributor Author

boxed commented Feb 27, 2025

How do I remove the stale tag? I don't think it's correct.

@github-actions github-actions bot removed the stale Stale PR or inactive for long period of time. label Apr 13, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants