summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/library/profile.rst11
-rw-r--r--Lib/pstats.py57
-rw-r--r--Lib/test/test_pstats.py24
-rw-r--r--Misc/NEWS.d/next/Library/2019-08-27-03-57-25.bpo-37958.lRORI3.rst2
4 files changed, 90 insertions, 4 deletions
diff --git a/Doc/library/profile.rst b/Doc/library/profile.rst
index 8d589d2..34525a9 100644
--- a/Doc/library/profile.rst
+++ b/Doc/library/profile.rst
@@ -525,6 +525,17 @@ Analysis of the profiler data is done using the :class:`~pstats.Stats` class.
ordering are identical to the :meth:`~pstats.Stats.print_callers` method.
+ .. method:: get_stats_profile()
+
+ This method returns an instance of StatsProfile, which contains a mapping
+ of function names to instances of FunctionProfile. Each FunctionProfile
+ instance holds information related to the function's profile such as how
+ long the function took to run, how many times it was called, etc...
+
+ .. versionadded:: 3.9
+ Added the following dataclasses: StatsProfile, FunctionProfile.
+ Added the following function: get_stats_profile.
+
.. _deterministic-profiling:
What Is Deterministic Profiling?
diff --git a/Lib/pstats.py b/Lib/pstats.py
index 4b419a8..e781b91 100644
--- a/Lib/pstats.py
+++ b/Lib/pstats.py
@@ -25,11 +25,13 @@ import os
import time
import marshal
import re
+
from enum import Enum
from functools import cmp_to_key
+from dataclasses import dataclass
+from typing import Dict
-__all__ = ["Stats", "SortKey"]
-
+__all__ = ["Stats", "SortKey", "FunctionProfile", "StatsProfile"]
class SortKey(str, Enum):
CALLS = 'calls', 'ncalls'
@@ -52,6 +54,22 @@ class SortKey(str, Enum):
return obj
+@dataclass(unsafe_hash=True)
+class FunctionProfile:
+ ncalls: int
+ tottime: float
+ percall_tottime: float
+ cumtime: float
+ percall_cumtime: float
+ file_name: str
+ line_number: int
+
+@dataclass(unsafe_hash=True)
+class StatsProfile:
+ '''Class for keeping track of an item in inventory.'''
+ total_tt: float
+ func_profiles: Dict[str, FunctionProfile]
+
class Stats:
"""This class is used for creating reports from data generated by the
Profile class. It is a "friend" of that class, and imports data either
@@ -333,6 +351,41 @@ class Stats:
return new_list, msg
+ def get_stats_profile(self):
+ """This method returns an instance of StatsProfile, which contains a mapping
+ of function names to instances of FunctionProfile. Each FunctionProfile
+ instance holds information related to the function's profile such as how
+ long the function took to run, how many times it was called, etc...
+ """
+ func_list = self.fcn_list[:] if self.fcn_list else list(self.stats.keys())
+ if not func_list:
+ return StatsProfile(0, {})
+
+ total_tt = float(f8(self.total_tt))
+ func_profiles = {}
+ stats_profile = StatsProfile(total_tt, func_profiles)
+
+ for func in func_list:
+ cc, nc, tt, ct, callers = self.stats[func]
+ file_name, line_number, func_name = func
+ ncalls = str(nc) if nc == cc else (str(nc) + '/' + str(cc))
+ tottime = float(f8(tt))
+ percall_tottime = -1 if nc == 0 else float(f8(tt/nc))
+ cumtime = float(f8(ct))
+ percall_cumtime = -1 if cc == 0 else float(f8(ct/cc))
+ func_profile = FunctionProfile(
+ ncalls,
+ tottime, # time spent in this function alone
+ percall_tottime,
+ cumtime, # time spent in the function plus all functions that this function called,
+ percall_cumtime,
+ file_name,
+ line_number
+ )
+ func_profiles[func_name] = func_profile
+
+ return stats_profile
+
def get_print_list(self, sel_list):
width = self.max_name_len
if self.fcn_list:
diff --git a/Lib/test/test_pstats.py b/Lib/test/test_pstats.py
index f835ce3..f3a6e58 100644
--- a/Lib/test/test_pstats.py
+++ b/Lib/test/test_pstats.py
@@ -1,10 +1,12 @@
import unittest
+
from test import support
from io import StringIO
-import pstats
from pstats import SortKey
-
+import pstats
+import time
+import cProfile
class AddCallersTestCase(unittest.TestCase):
"""Tests for pstats.add_callers helper."""
@@ -75,6 +77,24 @@ class StatsTestCase(unittest.TestCase):
SortKey.TIME,
'calls')
+ def test_get_stats_profile(self):
+ def pass1(): pass
+ def pass2(): pass
+ def pass3(): pass
+
+ pr = cProfile.Profile()
+ pr.enable()
+ pass1()
+ pass2()
+ pass3()
+ pr.create_stats()
+ ps = pstats.Stats(pr)
+
+ stats_profile = ps.get_stats_profile()
+ funcs_called = set(stats_profile.func_profiles.keys())
+ self.assertIn('pass1', funcs_called)
+ self.assertIn('pass2', funcs_called)
+ self.assertIn('pass3', funcs_called)
if __name__ == "__main__":
unittest.main()
diff --git a/Misc/NEWS.d/next/Library/2019-08-27-03-57-25.bpo-37958.lRORI3.rst b/Misc/NEWS.d/next/Library/2019-08-27-03-57-25.bpo-37958.lRORI3.rst
new file mode 100644
index 0000000..d0b4d6a
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2019-08-27-03-57-25.bpo-37958.lRORI3.rst
@@ -0,0 +1,2 @@
+Added the pstats.Stats.get_profile_dict() method to return the profile
+data as a StatsProfile instance.