ATLAS Offline Software
Toggle main menu visibility
Loading...
Searching...
No Matches
Tools
PyUtils
python
coverage.py
Go to the documentation of this file.
1
# noqa: ATL902
2
#
3
# File: coverage.py
4
# Purpose: A coverage utility for component tests.
5
# Created: Dec 2008, sss from D0 code circa Jan, 2000.
6
#
7
# Derived from trace.py by Andrew Dalke and Skip Montanaro, mainly by
8
# cutting out a whole bunch of stuff.
9
#
10
# To do coverage testing for some component 'Foo', add the following
11
# to the start of Foo's test module:
12
#
13
# import python_util.coverage
14
# python_util.coverage.Coverage ('Foo')
15
#
16
# If all executable lines in Foo are covered by the test, there will be
17
# no output. Otherwise, a summary of the number of uncovered lines
18
# will be printed to stdout, and a file Foo.cover will be created
19
# with an annotated source listing for Foo, showing coverage counts
20
# and uncovered lines.
21
#
22
# You can also run coverage testing in conjunction with doctest-based
23
# regression tests. In general, doctests can be both in the source file
24
# itself, or in a separate test file. If you have a separate test file,
25
# you can run all the tests for a module DIR.MOD like this:
26
#
27
# from PyUtils import coverage
28
# c = coverage.Coverage ('DIR.MOD')
29
# c.doctest_cover ()
30
#
31
# This will run any doctests in the DIR.MOD source file, plus any
32
# additional doctests in the test file. If any lines in the DIR.MOD
33
# source file are uncovered, a coverage report will be generated.
34
#
35
# The coverage testing may be inhibited by setting the environment
36
# variable NOCOVER.
37
#
38
# Coverage testing for an individual line may be suppressed by
39
# adding '#pragma: NO COVER' to the end of the line.
40
#
41
# Original permission notice:
42
# Copyright 1999, 2025, Bioreason, Inc., all rights reserved.
43
# Author: Andrew Dalke
44
#
45
# Copyright 1995-1997, Automatrix, Inc., all rights reserved.
46
# Author: Skip Montanaro
47
#
48
# Copyright 1991-1995, Stichting Mathematisch Centrum, all rights reserved.
49
#
50
# Permission to use, copy, modify, and distribute this Python software
51
# and its associated documentation for any purpose without fee is
52
# hereby granted, provided that the above copyright notice appears in
53
# all copies, and that both that copyright notice and this permission
54
# notice appear in supporting documentation, and that the name of
55
# neither Automatrix nor Bioreason be used in advertising or publicity
56
# pertaining to distribution of the software without specific, written
57
# prior permission.
58
#
59
import
sys
60
61
import
re
62
import
os
63
import
dis
64
65
running_coverage = 0
66
67
68
# Given a code string, return the SET_LINENO information
69
# This works up to python 2.2
70
def
_find_LINENO_from_code_22
(code):
71
"""return all of the SET_LINENO information from a code block"""
72
co_code = code.co_code
73
linenos = {}
74
75
# This code was filched from the `dis' module then modified
76
n = len(co_code)
77
i = 0
78
prev_op =
None
79
prev_lineno = 0
80
while
i < n:
81
c = co_code[i]
82
op = ord(c)
83
if
op == dis.SET_LINENO:
84
if
prev_op == op:
85
# two SET_LINENO in a row, so the previous didn't
86
# indicate anything. This occurs with triple
87
# quoted strings (?). Remove the old one.
88
del linenos[prev_lineno]
89
prev_lineno = ord(co_code[i+1]) + ord(co_code[i+2])*256
90
linenos[prev_lineno] = 1
91
if
op >= dis.HAVE_ARGUMENT:
92
i = i + 3
93
else
:
94
i = i + 1
95
prev_op = op
96
return
linenos
97
98
99
# This works from python 2.3 on.
100
def
_find_LINENO_from_code_23
(code):
101
linenos = {}
102
for
(o, l)
in
dis.findlinestarts (code):
103
linenos[l] = 1
104
return
linenos
105
106
# Choose the appropriate version.
107
if
'SET_LINENO'
in
dis.__dict__:
108
_find_LINENO_from_code = _find_LINENO_from_code_22
109
else
:
110
_find_LINENO_from_code = _find_LINENO_from_code_23
111
112
def
_find_LINENO
(code):
113
"""return all of the SET_LINENO information from a code object"""
114
import
types
115
116
# get all of the lineno information from the code of this scope level
117
#linenos = _find_LINENO_from_string(code.co_code)
118
linenos =
_find_LINENO_from_code
(code)
119
120
# and check the constants for references to other code objects
121
for
c
in
code.co_consts:
122
if
isinstance(c, types.CodeType):
123
# find another code object, so recurse into it
124
linenos.update(
_find_LINENO
(c))
125
return
linenos
126
127
def
find_executable_linenos
(filename):
128
"""return a dict of the line numbers from executable statements in a file
129
130
Works by finding all of the code-like objects in the module then searching
131
the byte code for 'SET_LINENO' terms (so this won't work one -O files).
132
133
"""
134
135
prog = open(filename).
read
()
136
code = compile (prog, filename,
'exec'
)
137
138
# The only way I know to find line numbers is to look for the
139
# SET_LINENO instructions. Isn't there some way to get it from
140
# the AST?
141
142
return
_find_LINENO
(code)
143
144
class
Coverage
:
145
def
__init__
(self, modname, toplev_name = 'main'):
146
global
running_coverage
147
running_coverage = 1
148
self.
modname
= modname
149
self.
toplev_name
= toplev_name
150
self.
counts
= {}
# keys are linenumber
151
self.
mutex
=
None
152
if
'NOCOVER'
not
in
os.environ:
153
#self.save_import = __builtin__.__import__
154
#__builtin__.__import__ = self.import_hook
155
sys.settrace (self.
trace
)
156
return
157
158
159
#def import_hook (self, name, globals={}, locals={}, fromlist=[]):
160
# loaded = sys.modules.has_key (name)
161
# mod = self.save_import (name, globals, locals, fromlist)
162
# if not loaded:
163
# if name == 'thread_util':
164
# self.set_thread_hook (mod)
165
# elif name == 'itc' or name == 'd0me':
166
# self.set_itc_hook (mod)
167
# return mod
168
169
170
#def set_thread_hook (self, thread_util):
171
# if not self.mutex:
172
# self.mutex = thread_util.ACE_Thread_Mutex ()
173
# thread_util.Pythread._save_run = thread_util.Pythread._run
174
# def _run (self, coverage=self):
175
# sys.settrace (coverage.trace)
176
# return self._save_run ()
177
# thread_util.Pythread._run = _run
178
# return
179
180
#def set_itc_hook (self, itc):
181
# if not self.mutex:
182
# import thread_util
183
# assert self.mutex != None
184
# self.old_itc_hook = itc._callback_hook
185
# def itc_hook (self=self):
186
# sys.settrace (self.trace)
187
# if self.old_itc_hook: self.old_itc_hook ()
188
# return
189
# itc._callback_hook = itc_hook
190
# return
191
192
193
def
trace
(self, frame, why, arg):
194
# something is fishy about getting the file name
195
modulename = frame.f_globals.get (
"__name__"
)
196
if
why ==
'call'
:
197
# Don't bother tracing if we're not in one of these modules.
198
if
not
(modulename == self.
modname
or
199
(modulename ==
'__main__'
and
200
frame.f_code.co_name == self.
toplev_name
)):
201
return
None
202
if
why ==
'line'
:
203
if
modulename == self.
modname
:
204
lineno = frame.f_lineno
205
206
# record the file name and line number of every trace
207
if
self.
mutex
: self.
mutex
.acquire ()
208
self.
counts
[lineno] = self.
counts
.
get
(lineno, 0) + 1
209
if
self.
mutex
: self.
mutex
.release ()
210
211
elif
why ==
'return'
:
212
if
frame.f_code.co_name == self.
toplev_name
:
213
sys.settrace (
None
)
214
if
self.
mutex
: self.
mutex
.acquire ()
215
self.
analyze
()
216
m = self.
mutex
217
self.
mutex
=
None
218
if
m: m.release ()
219
220
return
self.
trace
221
222
def
analyze
(self):
223
filename = sys.modules[self.
modname
].__file__
224
if
filename[-4:] ==
".pyc"
or
filename[-4:] ==
".pyo"
:
225
orig_filename = filename[:-4] +
'.py'
226
else
:
227
orig_filename = filename
228
229
# Get the original lines from the .py file
230
try
:
231
lines = open(orig_filename,
'r'
).readlines()
232
except
IOError
as
err:
233
sys.stderr.write(
234
"%s: Could not open %s for reading because: %s - skipping\n"
%
235
\
236
(
"trace"
, repr(filename), err.strerror))
237
return
238
239
# there are many places where this is insufficient, like a blank
240
# line embedded in a multiline string.
241
blank = re.compile(
r'^\s*(#.*)?$'
)
242
243
executable_linenos =
find_executable_linenos
(orig_filename)
244
245
lines_hit = self.
counts
246
uncovered = 0
247
outlines = []
248
for
i
in
range(len(lines)):
249
line = lines[i]
250
251
# do the blank/comment match to try to mark more lines
252
# (help the reader find stuff that hasn't been covered)
253
if
(i+1)
in
lines_hit:
254
# count precedes the lines that we captured
255
prefix =
'%5d: '
% lines_hit[i+1]
256
elif
blank.match(line):
257
# blank lines and comments are preceded by dots
258
prefix =
' . '
259
else
:
260
# lines preceded by no marks weren't hit
261
# Highlight them if so indicated, unless the line contains
262
# '#pragma: NO COVER' (it is possible to embed this into
263
# the text as a non-comment; no easy fix)
264
if
(i+1)
in
executable_linenos
and
\
265
lines[i].
find
(
'#pragma: NO COVER'
) == -1:
266
prefix =
'>>>>>> '
267
uncovered = uncovered + 1
268
else
:
269
prefix =
' '
*7
270
outlines.append (prefix + line.expandtabs(8))
271
272
if
uncovered:
273
print
(
"*** There were %d uncovered lines."
% uncovered)
274
else
:
275
return
276
277
# build list file name by appending a ".cover" to the module name
278
# and sticking it into the specified directory
279
listfilename = self.
modname
+
".cover"
280
try
:
281
outfile = open(listfilename,
'w'
)
282
except
IOError
as
err:
283
sys.stderr.write(
284
'%s: Could not open %s for writing because: %s - skipping\n'
%
285
(
"trace"
, repr(listfilename), err.strerror))
286
return
287
288
for
l
in
outlines:
289
outfile.write (l)
290
291
outfile.close()
292
293
return
294
295
296
def
doctest_cover
(self, *args, **kw):
297
import
doctest
298
m = __import__ (self.
modname
)
299
300
# Note a peculiarity of __import__: if modname is like A.B,
301
# then it returns A not B...
302
mm = self.
modname
.split (
'.'
)
303
if
len(mm) > 1:
304
m = getattr (m, mm[-1])
305
306
oldrun = doctest.DocTestRunner.run
307
def
xrun (xself, *args, **kw):
308
sys.settrace (self.
trace
)
309
return
oldrun (xself, *args, **kw)
310
doctest.DocTestRunner.run = xrun
311
312
import
bdb
313
old_set_continue = bdb.Bdb.set_continue
314
def
xcontinue (xself):
315
old_set_continue (xself)
316
sys.settrace (self.
trace
)
317
return
318
bdb.Bdb.set_continue = xcontinue
319
320
doctest.testmod (m, *args, **kw)
321
322
main = sys.modules[
'__main__'
]
323
if
m != main:
324
doctest.testmod (main, *args, **kw)
325
326
self.
analyze
()
327
return
print
void print(char *figname, TCanvas *c1)
Definition
TRTCalib_StrawStatusPlots.cxx:26
python.coverage.Coverage
Definition
coverage.py:144
python.coverage.Coverage.toplev_name
toplev_name
Definition
coverage.py:149
python.coverage.Coverage.doctest_cover
doctest_cover(self, *args, **kw)
Definition
coverage.py:296
python.coverage.Coverage.modname
modname
Definition
coverage.py:148
python.coverage.Coverage.analyze
analyze(self)
Definition
coverage.py:222
python.coverage.Coverage.mutex
self.mutex.acquire() mutex
Definition
coverage.py:151
python.coverage.Coverage.trace
trace
Definition
coverage.py:155
python.coverage.Coverage.counts
dict counts
Definition
coverage.py:150
python.coverage.Coverage.__init__
__init__(self, modname, toplev_name='main')
Definition
coverage.py:145
get
T * get(TKey *tobj)
get a TObject* from a TKey* (why can't a TObject be a TKey?)
Definition
hcg.cxx:132
find
std::string find(const std::string &s)
return a remapped string
Definition
hcg.cxx:140
python.coverage.find_executable_linenos
find_executable_linenos(filename)
Definition
coverage.py:127
python.coverage._find_LINENO_from_code_22
_find_LINENO_from_code_22(code)
Definition
coverage.py:70
python.coverage._find_LINENO_from_code_23
_find_LINENO_from_code_23(code)
Definition
coverage.py:100
python.coverage._find_LINENO_from_code
_find_LINENO_from_code
Definition
coverage.py:108
python.coverage._find_LINENO
_find_LINENO(code)
Definition
coverage.py:112
read
IovVectorMap_t read(const Folder &theFolder, const SelectionCriterion &choice, const unsigned int limit=10)
Definition
openCoraCool.cxx:569
Generated on
for ATLAS Offline Software by
1.17.0