1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
|
"""This plugin will run tests using the hotshot profiler, which is part
of the standard library. To turn it on, use the ``--with-profile`` option
or set the NOSE_WITH_PROFILE environment variable. Profiler output can be
controlled with the ``--profile-sort`` and ``--profile-restrict`` options,
and the profiler output file may be changed with ``--profile-stats-file``.
See the `hotshot documentation`_ in the standard library documentation for
more details on the various output options.
.. _hotshot documentation: http://docs.python.org/library/hotshot.html
"""
try:
import hotshot
from hotshot import stats
except ImportError:
hotshot, stats = None, None
import logging
import os
import sys
import tempfile
from nose.plugins.base import Plugin
from nose.util import tolist
log = logging.getLogger('nose.plugins')
class Profile(Plugin):
"""
Use this plugin to run tests using the hotshot profiler.
"""
pfile = None
clean_stats_file = False
def options(self, parser, env):
"""Register commandline options.
"""
if not self.available():
return
Plugin.options(self, parser, env)
parser.add_option('--profile-sort', action='store', dest='profile_sort',
default=env.get('NOSE_PROFILE_SORT', 'cumulative'),
metavar="SORT",
help="Set sort order for profiler output")
parser.add_option('--profile-stats-file', action='store',
dest='profile_stats_file',
metavar="FILE",
default=env.get('NOSE_PROFILE_STATS_FILE'),
help='Profiler stats file; default is a new '
'temp file on each run')
parser.add_option('--profile-restrict', action='append',
dest='profile_restrict',
metavar="RESTRICT",
default=env.get('NOSE_PROFILE_RESTRICT'),
help="Restrict profiler output. See help for "
"pstats.Stats for details")
def available(cls):
return hotshot is not None
available = classmethod(available)
def begin(self):
"""Create profile stats file and load profiler.
"""
if not self.available():
return
self._create_pfile()
self.prof = hotshot.Profile(self.pfile)
def configure(self, options, conf):
"""Configure plugin.
"""
if not self.available():
self.enabled = False
return
Plugin.configure(self, options, conf)
self.conf = conf
if options.profile_stats_file:
self.pfile = options.profile_stats_file
self.clean_stats_file = False
else:
self.pfile = None
self.clean_stats_file = True
self.fileno = None
self.sort = options.profile_sort
self.restrict = tolist(options.profile_restrict)
def prepareTest(self, test):
"""Wrap entire test run in :func:`prof.runcall`.
"""
if not self.available():
return
log.debug('preparing test %s' % test)
def run_and_profile(result, prof=self.prof, test=test):
self._create_pfile()
prof.runcall(test, result)
return run_and_profile
def report(self, stream):
"""Output profiler report.
"""
log.debug('printing profiler report')
self.prof.close()
prof_stats = stats.load(self.pfile)
prof_stats.sort_stats(self.sort)
# 2.5 has completely different stream handling from 2.4 and earlier.
# Before 2.5, stats objects have no stream attribute; in 2.5 and later
# a reference sys.stdout is stored before we can tweak it.
compat_25 = hasattr(prof_stats, 'stream')
if compat_25:
tmp = prof_stats.stream
prof_stats.stream = stream
else:
tmp = sys.stdout
sys.stdout = stream
try:
if self.restrict:
log.debug('setting profiler restriction to %s', self.restrict)
prof_stats.print_stats(*self.restrict)
else:
prof_stats.print_stats()
finally:
if compat_25:
prof_stats.stream = tmp
else:
sys.stdout = tmp
def finalize(self, result):
"""Clean up stats file, if configured to do so.
"""
if not self.available():
return
try:
self.prof.close()
except AttributeError:
# TODO: is this trying to catch just the case where not
# hasattr(self.prof, "close")? If so, the function call should be
# moved out of the try: suite.
pass
if self.clean_stats_file:
if self.fileno:
try:
os.close(self.fileno)
except OSError:
pass
try:
os.unlink(self.pfile)
except OSError:
pass
return None
def _create_pfile(self):
if not self.pfile:
self.fileno, self.pfile = tempfile.mkstemp()
self.clean_stats_file = True
|