mirror of
https://github.com/python/cpython.git
synced 2025-12-04 08:34:25 +00:00
bpo-29237: Create enum for pstats sorting options (GH-5103)
This commit is contained in:
parent
4666ec597c
commit
863b1e4d0e
5 changed files with 142 additions and 59 deletions
|
|
@ -139,6 +139,7 @@ The :mod:`pstats` module's :class:`~pstats.Stats` class has a variety of methods
|
||||||
for manipulating and printing the data saved into a profile results file::
|
for manipulating and printing the data saved into a profile results file::
|
||||||
|
|
||||||
import pstats
|
import pstats
|
||||||
|
from pstats import SortKey
|
||||||
p = pstats.Stats('restats')
|
p = pstats.Stats('restats')
|
||||||
p.strip_dirs().sort_stats(-1).print_stats()
|
p.strip_dirs().sort_stats(-1).print_stats()
|
||||||
|
|
||||||
|
|
@ -148,14 +149,14 @@ entries according to the standard module/line/name string that is printed. The
|
||||||
:meth:`~pstats.Stats.print_stats` method printed out all the statistics. You
|
:meth:`~pstats.Stats.print_stats` method printed out all the statistics. You
|
||||||
might try the following sort calls::
|
might try the following sort calls::
|
||||||
|
|
||||||
p.sort_stats('name')
|
p.sort_stats(SortKey.NAME)
|
||||||
p.print_stats()
|
p.print_stats()
|
||||||
|
|
||||||
The first call will actually sort the list by function name, and the second call
|
The first call will actually sort the list by function name, and the second call
|
||||||
will print out the statistics. The following are some interesting calls to
|
will print out the statistics. The following are some interesting calls to
|
||||||
experiment with::
|
experiment with::
|
||||||
|
|
||||||
p.sort_stats('cumulative').print_stats(10)
|
p.sort_stats(SortKey.CUMULATIVE).print_stats(10)
|
||||||
|
|
||||||
This sorts the profile by cumulative time in a function, and then only prints
|
This sorts the profile by cumulative time in a function, and then only prints
|
||||||
the ten most significant lines. If you want to understand what algorithms are
|
the ten most significant lines. If you want to understand what algorithms are
|
||||||
|
|
@ -164,20 +165,20 @@ taking time, the above line is what you would use.
|
||||||
If you were looking to see what functions were looping a lot, and taking a lot
|
If you were looking to see what functions were looping a lot, and taking a lot
|
||||||
of time, you would do::
|
of time, you would do::
|
||||||
|
|
||||||
p.sort_stats('time').print_stats(10)
|
p.sort_stats(SortKey.TIME).print_stats(10)
|
||||||
|
|
||||||
to sort according to time spent within each function, and then print the
|
to sort according to time spent within each function, and then print the
|
||||||
statistics for the top ten functions.
|
statistics for the top ten functions.
|
||||||
|
|
||||||
You might also try::
|
You might also try::
|
||||||
|
|
||||||
p.sort_stats('file').print_stats('__init__')
|
p.sort_stats(SortKey.FILENAME).print_stats('__init__')
|
||||||
|
|
||||||
This will sort all the statistics by file name, and then print out statistics
|
This will sort all the statistics by file name, and then print out statistics
|
||||||
for only the class init methods (since they are spelled with ``__init__`` in
|
for only the class init methods (since they are spelled with ``__init__`` in
|
||||||
them). As one final example, you could try::
|
them). As one final example, you could try::
|
||||||
|
|
||||||
p.sort_stats('time', 'cumulative').print_stats(.5, 'init')
|
p.sort_stats(SortKey.TIME, SortKey.CUMULATIVE).print_stats(.5, 'init')
|
||||||
|
|
||||||
This line sorts statistics with a primary key of time, and a secondary key of
|
This line sorts statistics with a primary key of time, and a secondary key of
|
||||||
cumulative time, and then prints out some of the statistics. To be specific, the
|
cumulative time, and then prints out some of the statistics. To be specific, the
|
||||||
|
|
@ -250,12 +251,13 @@ functions:
|
||||||
without writing the profile data to a file::
|
without writing the profile data to a file::
|
||||||
|
|
||||||
import cProfile, pstats, io
|
import cProfile, pstats, io
|
||||||
|
from pstats import SortKey
|
||||||
pr = cProfile.Profile()
|
pr = cProfile.Profile()
|
||||||
pr.enable()
|
pr.enable()
|
||||||
# ... do something ...
|
# ... do something ...
|
||||||
pr.disable()
|
pr.disable()
|
||||||
s = io.StringIO()
|
s = io.StringIO()
|
||||||
sortby = 'cumulative'
|
sortby = SortKey.CUMULATIVE
|
||||||
ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
|
ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
|
||||||
ps.print_stats()
|
ps.print_stats()
|
||||||
print(s.getvalue())
|
print(s.getvalue())
|
||||||
|
|
@ -361,60 +363,65 @@ Analysis of the profiler data is done using the :class:`~pstats.Stats` class.
|
||||||
.. method:: sort_stats(*keys)
|
.. method:: sort_stats(*keys)
|
||||||
|
|
||||||
This method modifies the :class:`Stats` object by sorting it according to
|
This method modifies the :class:`Stats` object by sorting it according to
|
||||||
the supplied criteria. The argument is typically a string identifying the
|
the supplied criteria. The argument can be either a string or a SortKey
|
||||||
basis of a sort (example: ``'time'`` or ``'name'``).
|
enum identifying the basis of a sort (example: ``'time'``, ``'name'``,
|
||||||
|
``SortKey.TIME`` or ``SortKey.NAME``). The SortKey enums argument have
|
||||||
|
advantage over the string argument in that it is more robust and less
|
||||||
|
error prone.
|
||||||
|
|
||||||
When more than one key is provided, then additional keys are used as
|
When more than one key is provided, then additional keys are used as
|
||||||
secondary criteria when there is equality in all keys selected before
|
secondary criteria when there is equality in all keys selected before
|
||||||
them. For example, ``sort_stats('name', 'file')`` will sort all the
|
them. For example, ``sort_stats(SortKey.NAME, SortKey.FILE)`` will sort
|
||||||
entries according to their function name, and resolve all ties (identical
|
all the entries according to their function name, and resolve all ties
|
||||||
function names) by sorting by file name.
|
(identical function names) by sorting by file name.
|
||||||
|
|
||||||
Abbreviations can be used for any key names, as long as the abbreviation
|
For the string argument, abbreviations can be used for any key names, as
|
||||||
is unambiguous. The following are the keys currently defined:
|
long as the abbreviation is unambiguous.
|
||||||
|
|
||||||
+------------------+----------------------+
|
The following are the valid string and SortKey:
|
||||||
| Valid Arg | Meaning |
|
|
||||||
+==================+======================+
|
+------------------+---------------------+----------------------+
|
||||||
| ``'calls'`` | call count |
|
| Valid String Arg | Valid enum Arg | Meaning |
|
||||||
+------------------+----------------------+
|
+==================+=====================+======================+
|
||||||
| ``'cumulative'`` | cumulative time |
|
| ``'calls'`` | SortKey.CALLS | call count |
|
||||||
+------------------+----------------------+
|
+------------------+---------------------+----------------------+
|
||||||
| ``'cumtime'`` | cumulative time |
|
| ``'cumulative'`` | SortKey.CUMULATIVE | cumulative time |
|
||||||
+------------------+----------------------+
|
+------------------+---------------------+----------------------+
|
||||||
| ``'file'`` | file name |
|
| ``'cumtime'`` | N/A | cumulative time |
|
||||||
+------------------+----------------------+
|
+------------------+---------------------+----------------------+
|
||||||
| ``'filename'`` | file name |
|
| ``'file'`` | N/A | file name |
|
||||||
+------------------+----------------------+
|
+------------------+---------------------+----------------------+
|
||||||
| ``'module'`` | file name |
|
| ``'filename'`` | SortKey.FILENAME | file name |
|
||||||
+------------------+----------------------+
|
+------------------+---------------------+----------------------+
|
||||||
| ``'ncalls'`` | call count |
|
| ``'module'`` | N/A | file name |
|
||||||
+------------------+----------------------+
|
+------------------+---------------------+----------------------+
|
||||||
| ``'pcalls'`` | primitive call count |
|
| ``'ncalls'`` | N/A | call count |
|
||||||
+------------------+----------------------+
|
+------------------+---------------------+----------------------+
|
||||||
| ``'line'`` | line number |
|
| ``'pcalls'`` | SortKey.PCALLS | primitive call count |
|
||||||
+------------------+----------------------+
|
+------------------+---------------------+----------------------+
|
||||||
| ``'name'`` | function name |
|
| ``'line'`` | SortKey.LINE | line number |
|
||||||
+------------------+----------------------+
|
+------------------+---------------------+----------------------+
|
||||||
| ``'nfl'`` | name/file/line |
|
| ``'name'`` | SortKey.NAME | function name |
|
||||||
+------------------+----------------------+
|
+------------------+---------------------+----------------------+
|
||||||
| ``'stdname'`` | standard name |
|
| ``'nfl'`` | SortKey.NFL | name/file/line |
|
||||||
+------------------+----------------------+
|
+------------------+---------------------+----------------------+
|
||||||
| ``'time'`` | internal time |
|
| ``'stdname'`` | SortKey.STDNAME | standard name |
|
||||||
+------------------+----------------------+
|
+------------------+---------------------+----------------------+
|
||||||
| ``'tottime'`` | internal time |
|
| ``'time'`` | SortKey.TIME | internal time |
|
||||||
+------------------+----------------------+
|
+------------------+---------------------+----------------------+
|
||||||
|
| ``'tottime'`` | N/A | internal time |
|
||||||
|
+------------------+---------------------+----------------------+
|
||||||
|
|
||||||
Note that all sorts on statistics are in descending order (placing most
|
Note that all sorts on statistics are in descending order (placing most
|
||||||
time consuming items first), where as name, file, and line number searches
|
time consuming items first), where as name, file, and line number searches
|
||||||
are in ascending order (alphabetical). The subtle distinction between
|
are in ascending order (alphabetical). The subtle distinction between
|
||||||
``'nfl'`` and ``'stdname'`` is that the standard name is a sort of the
|
``SortKey.NFL`` and ``SortKey.STDNAME`` is that the standard name is a
|
||||||
name as printed, which means that the embedded line numbers get compared
|
sort of the name as printed, which means that the embedded line numbers
|
||||||
in an odd way. For example, lines 3, 20, and 40 would (if the file names
|
get compared in an odd way. For example, lines 3, 20, and 40 would (if
|
||||||
were the same) appear in the string order 20, 3 and 40. In contrast,
|
the file names were the same) appear in the string order 20, 3 and 40.
|
||||||
``'nfl'`` does a numeric compare of the line numbers. In fact,
|
In contrast, ``SortKey.NFL`` does a numeric compare of the line numbers.
|
||||||
``sort_stats('nfl')`` is the same as ``sort_stats('name', 'file',
|
In fact, ``sort_stats(SortKey.NFL)`` is the same as
|
||||||
'line')``.
|
``sort_stats(SortKey.NAME, SortKey.FILENAME, SortKey.LINE)``.
|
||||||
|
|
||||||
For backward-compatibility reasons, the numeric arguments ``-1``, ``0``,
|
For backward-compatibility reasons, the numeric arguments ``-1``, ``0``,
|
||||||
``1``, and ``2`` are permitted. They are interpreted as ``'stdname'``,
|
``1``, and ``2`` are permitted. They are interpreted as ``'stdname'``,
|
||||||
|
|
@ -424,6 +431,8 @@ Analysis of the profiler data is done using the :class:`~pstats.Stats` class.
|
||||||
|
|
||||||
.. For compatibility with the old profiler.
|
.. For compatibility with the old profiler.
|
||||||
|
|
||||||
|
.. versionadded:: 3.7
|
||||||
|
Added the SortKey enum.
|
||||||
|
|
||||||
.. method:: reverse_order()
|
.. method:: reverse_order()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,9 +25,32 @@ import os
|
||||||
import time
|
import time
|
||||||
import marshal
|
import marshal
|
||||||
import re
|
import re
|
||||||
|
from enum import Enum
|
||||||
from functools import cmp_to_key
|
from functools import cmp_to_key
|
||||||
|
|
||||||
__all__ = ["Stats"]
|
__all__ = ["Stats", "SortKey"]
|
||||||
|
|
||||||
|
|
||||||
|
class SortKey(str, Enum):
|
||||||
|
CALLS = 'calls', 'ncalls'
|
||||||
|
CUMULATIVE = 'cumulative', 'cumtime'
|
||||||
|
FILENAME = 'filename', 'module'
|
||||||
|
LINE = 'line'
|
||||||
|
NAME = 'name'
|
||||||
|
NFL = 'nfl'
|
||||||
|
PCALLS = 'pcalls'
|
||||||
|
STDNAME = 'stdname'
|
||||||
|
TIME = 'time', 'tottime'
|
||||||
|
|
||||||
|
def __new__(cls, *values):
|
||||||
|
obj = str.__new__(cls)
|
||||||
|
|
||||||
|
obj._value_ = values[0]
|
||||||
|
for other_value in values[1:]:
|
||||||
|
cls._value2member_map_[other_value] = obj
|
||||||
|
obj._all_values = values
|
||||||
|
return obj
|
||||||
|
|
||||||
|
|
||||||
class Stats:
|
class Stats:
|
||||||
"""This class is used for creating reports from data generated by the
|
"""This class is used for creating reports from data generated by the
|
||||||
|
|
@ -49,13 +72,14 @@ class Stats:
|
||||||
|
|
||||||
The sort_stats() method now processes some additional options (i.e., in
|
The sort_stats() method now processes some additional options (i.e., in
|
||||||
addition to the old -1, 0, 1, or 2 that are respectively interpreted as
|
addition to the old -1, 0, 1, or 2 that are respectively interpreted as
|
||||||
'stdname', 'calls', 'time', and 'cumulative'). It takes an arbitrary number
|
'stdname', 'calls', 'time', and 'cumulative'). It takes either an
|
||||||
of quoted strings to select the sort order.
|
arbitrary number of quoted strings or SortKey enum to select the sort
|
||||||
|
order.
|
||||||
|
|
||||||
For example sort_stats('time', 'name') sorts on the major key of 'internal
|
For example sort_stats('time', 'name') or sort_stats(SortKey.TIME,
|
||||||
function time', and on the minor key of 'the name of the function'. Look at
|
SortKey.NAME) sorts on the major key of 'internal function time', and on
|
||||||
the two tables in sort_stats() and get_sort_arg_defs(self) for more
|
the minor key of 'the name of the function'. Look at the two tables in
|
||||||
examples.
|
sort_stats() and get_sort_arg_defs(self) for more examples.
|
||||||
|
|
||||||
All methods return self, so you can string together commands like:
|
All methods return self, so you can string together commands like:
|
||||||
Stats('foo', 'goo').strip_dirs().sort_stats('calls').\
|
Stats('foo', 'goo').strip_dirs().sort_stats('calls').\
|
||||||
|
|
@ -161,7 +185,6 @@ class Stats:
|
||||||
"ncalls" : (((1,-1), ), "call count"),
|
"ncalls" : (((1,-1), ), "call count"),
|
||||||
"cumtime" : (((3,-1), ), "cumulative time"),
|
"cumtime" : (((3,-1), ), "cumulative time"),
|
||||||
"cumulative": (((3,-1), ), "cumulative time"),
|
"cumulative": (((3,-1), ), "cumulative time"),
|
||||||
"file" : (((4, 1), ), "file name"),
|
|
||||||
"filename" : (((4, 1), ), "file name"),
|
"filename" : (((4, 1), ), "file name"),
|
||||||
"line" : (((5, 1), ), "line number"),
|
"line" : (((5, 1), ), "line number"),
|
||||||
"module" : (((4, 1), ), "file name"),
|
"module" : (((4, 1), ), "file name"),
|
||||||
|
|
@ -202,12 +225,19 @@ class Stats:
|
||||||
0: "calls",
|
0: "calls",
|
||||||
1: "time",
|
1: "time",
|
||||||
2: "cumulative"}[field[0]] ]
|
2: "cumulative"}[field[0]] ]
|
||||||
|
elif len(field) >= 2:
|
||||||
|
for arg in field[1:]:
|
||||||
|
if type(arg) != type(field[0]):
|
||||||
|
raise TypeError("Can't have mixed argument type")
|
||||||
|
|
||||||
sort_arg_defs = self.get_sort_arg_defs()
|
sort_arg_defs = self.get_sort_arg_defs()
|
||||||
|
|
||||||
sort_tuple = ()
|
sort_tuple = ()
|
||||||
self.sort_type = ""
|
self.sort_type = ""
|
||||||
connector = ""
|
connector = ""
|
||||||
for word in field:
|
for word in field:
|
||||||
|
if isinstance(word, SortKey):
|
||||||
|
word = word.value
|
||||||
sort_tuple = sort_tuple + sort_arg_defs[word][0]
|
sort_tuple = sort_tuple + sort_arg_defs[word][0]
|
||||||
self.sort_type += connector + sort_arg_defs[word][1]
|
self.sort_type += connector + sort_arg_defs[word][1]
|
||||||
connector = ", "
|
connector = ", "
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import unittest
|
||||||
from test import support
|
from test import support
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
import pstats
|
import pstats
|
||||||
|
from pstats import SortKey
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -33,6 +34,47 @@ class StatsTestCase(unittest.TestCase):
|
||||||
stats = pstats.Stats(stream=stream)
|
stats = pstats.Stats(stream=stream)
|
||||||
stats.add(self.stats, self.stats)
|
stats.add(self.stats, self.stats)
|
||||||
|
|
||||||
|
def test_sort_stats_int(self):
|
||||||
|
valid_args = {-1: 'stdname',
|
||||||
|
0: 'calls',
|
||||||
|
1: 'time',
|
||||||
|
2: 'cumulative'}
|
||||||
|
for arg_int, arg_str in valid_args.items():
|
||||||
|
self.stats.sort_stats(arg_int)
|
||||||
|
self.assertEqual(self.stats.sort_type,
|
||||||
|
self.stats.sort_arg_dict_default[arg_str][-1])
|
||||||
|
|
||||||
|
def test_sort_stats_string(self):
|
||||||
|
for sort_name in ['calls', 'ncalls', 'cumtime', 'cumulative',
|
||||||
|
'filename', 'line', 'module', 'name', 'nfl', 'pcalls',
|
||||||
|
'stdname', 'time', 'tottime']:
|
||||||
|
self.stats.sort_stats(sort_name)
|
||||||
|
self.assertEqual(self.stats.sort_type,
|
||||||
|
self.stats.sort_arg_dict_default[sort_name][-1])
|
||||||
|
|
||||||
|
def test_sort_stats_partial(self):
|
||||||
|
sortkey = 'filename'
|
||||||
|
for sort_name in ['f', 'fi', 'fil', 'file', 'filen', 'filena',
|
||||||
|
'filenam', 'filename']:
|
||||||
|
self.stats.sort_stats(sort_name)
|
||||||
|
self.assertEqual(self.stats.sort_type,
|
||||||
|
self.stats.sort_arg_dict_default[sortkey][-1])
|
||||||
|
|
||||||
|
def test_sort_stats_enum(self):
|
||||||
|
for member in SortKey:
|
||||||
|
self.stats.sort_stats(member)
|
||||||
|
self.assertEqual(
|
||||||
|
self.stats.sort_type,
|
||||||
|
self.stats.sort_arg_dict_default[member.value][-1])
|
||||||
|
|
||||||
|
def test_sort_starts_mix(self):
|
||||||
|
self.assertRaises(TypeError, self.stats.sort_stats,
|
||||||
|
'calls',
|
||||||
|
SortKey.TIME)
|
||||||
|
self.assertRaises(TypeError, self.stats.sort_stats,
|
||||||
|
SortKey.TIME,
|
||||||
|
'calls')
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
||||||
|
|
@ -1706,6 +1706,7 @@ Jeff Wheeler
|
||||||
Christopher White
|
Christopher White
|
||||||
David White
|
David White
|
||||||
Mats Wichmann
|
Mats Wichmann
|
||||||
|
Marcel Widjaja
|
||||||
Truida Wiedijk
|
Truida Wiedijk
|
||||||
Felix Wiemann
|
Felix Wiemann
|
||||||
Gerry Wiener
|
Gerry Wiener
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
Create enum for pstats sorting options
|
||||||
Loading…
Add table
Add a link
Reference in a new issue