Skip to content

Commit f4be5a7

Browse files
committed
Make the pstats browser recognise SampledStats
The SampledStats results are stored in the exact same format as Stats, but since the results don't represent call counts but sample counts, the column headers are different to account for this. This ensure that using the pstats browser instantiates the right object to handle the correct columns, add a factory function which can instantiate the correct class. As the Stats class can only handle either a filename or an object which provides the 'stats' attribute in a pre-parsed format, this provides a StatsLoaderShim to avoid marshalling the data twice (once to check the marker and once in the Stats module if we were to pass the file name).
1 parent 38ab68c commit f4be5a7

File tree

2 files changed

+35
-3
lines changed

2 files changed

+35
-3
lines changed

Lib/profile/sample.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,10 @@ def print_stats(self, sort=-1):
6262
pstats.SampledStats(self).strip_dirs().sort_stats(*sort).print_stats()
6363

6464
def dump_stats(self, file):
65+
stats_with_marker = dict(self.stats)
66+
stats_with_marker[("__sampled__",)] = True
6567
with open(file, "wb") as f:
66-
marshal.dump(self.stats, f)
68+
marshal.dump(stats_with_marker, f)
6769

6870
# Needed for compatibility with pstats.Stats
6971
def create_stats(self):

Lib/pstats.py

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,11 @@ def load_stats(self, arg):
139139
return
140140
elif isinstance(arg, str):
141141
with open(arg, 'rb') as f:
142-
self.stats = marshal.load(f)
142+
stats = marshal.load(f)
143+
if (('__sampled__',)) in stats:
144+
stats.pop((('__sampled__',)))
145+
self.__class__ = SampledStats
146+
self.stats = stats
143147
try:
144148
file_stats = os.stat(arg)
145149
arg = time.ctime(file_stats.st_mtime) + " " + arg
@@ -639,6 +643,24 @@ def f8(x):
639643
# Statistics browser added by ESR, April 2001
640644
#**************************************************************************
641645

646+
class StatsLoaderShim:
647+
"""Compatibility shim implementing 'create_stats' needed by Stats classes
648+
to handle already unmarshalled data."""
649+
def __init__(self, raw_stats):
650+
self.stats = raw_stats
651+
652+
def create_stats(self):
653+
pass
654+
655+
def stats_factory(raw_stats):
656+
"""Return a Stats or SampledStats instance based on the marker in raw_stats."""
657+
if (('__sampled__',)) in raw_stats:
658+
raw_stats = dict(raw_stats) # avoid mutating caller's dict
659+
raw_stats.pop((('__sampled__',)))
660+
return SampledStats(StatsLoaderShim(raw_stats))
661+
else:
662+
return Stats(StatsLoaderShim(raw_stats))
663+
642664
if __name__ == '__main__':
643665
import cmd
644666
try:
@@ -725,7 +747,15 @@ def help_quit(self):
725747
def do_read(self, line):
726748
if line:
727749
try:
728-
self.stats = Stats(line)
750+
with open(line, 'rb') as f:
751+
raw_stats = marshal.load(f)
752+
self.stats = stats_factory(raw_stats)
753+
try:
754+
file_stats = os.stat(line)
755+
arg = time.ctime(file_stats.st_mtime) + " " + line
756+
except Exception:
757+
arg = line
758+
self.stats.files = [arg]
729759
except OSError as err:
730760
print(err.args[1], file=self.stream)
731761
return

0 commit comments

Comments
 (0)