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