突出用途cover_pylib=True
我知道这是在你问这个问题很久之后,但我只是想知道答案。:)
使用当前的 bitbucket 源,coverage.py
我能够成功地从PyInstaller
生成的 EXE 文件中收集覆盖率数据。
在我的应用程序的主要来源中,我有条件地告诉覆盖率开始收集覆盖率,如下所示:
if os.environ.has_key('COVERAGE') and len(os.environ['COVERAGE']) > 0:
usingCoverage = True
import coverage
import time
cov = coverage.coverage(data_file='.coverage.' + version.GetFullString(), data_suffix=time.strftime(".%Y_%m_%d_%H_%M.%S", time.localtime()), cover_pylib=True)
cov.start()
这仅在我需要时才开始覆盖收集。使用 data_suffix 可以让我更容易地cov.combine()
在以后合并覆盖文件。 version.GetFullString()
只是我的应用程序版本号。
cover_pylib
之所以设置为True
此处,是因为所有标准 Python 库模块__file__
属性看起来...\_MEIXXXXX\random.pyc
都像这样,因此与包内不存在的其他代码无法区分(基于路径)。
当应用程序准备退出时,我有这个小片段:
if usingCoverage:
cov.stop()
cov.save()
一旦我的应用程序运行,coverage.py 仍然不会自动为我生成它的 HTML 报告。需要清理覆盖数据,以便将...\_MEIXXXX\...
文件引用转换为真实源代码的绝对文件路径。
我通过运行这段代码来做到这一点:
import sys
import os.path
from coverage.data import CoverageData
from coverage import coverage
from glob import glob
def cleanupLines(data):
"""
The coverage data collected via PyInstaller coverage needs the data fixed up
so that coverage.py's report generation code can analyze the source code.
PyInstaller __file__ attributes on code objecters are all in subdirectories of the _MEIXXXX
temporary subdirectory. We need to replace the _MEIXXXX temp directory prefix with the correct
prefix for each source file.
"""
prefix = None
for file, lines in data.lines.iteritems():
origFile = file
if prefix is None:
index = file.find('_MEI')
if index >= 0:
pathSepIndex = file.find('\\', index)
if pathSepIndex >= 0:
prefix = file[:pathSepIndex + 1]
if prefix is not None and file.find(prefix) >= 0:
file = file.replace(prefix, "", 1)
for path in sys.path:
if os.path.exists(path) and os.path.isdir(path):
fileName = os.path.join(path, file)
if os.path.exists(fileName) and os.path.isfile(fileName):
file = fileName
if origFile != file:
del data.lines[origFile]
data.lines[file] = lines
for file in glob('.coverage.' + version.GetFullString() + '*'):
print "Cleaning up: ", file
data = CoverageData(file)
data.read()
cleanupLines(data)
data.write()
这里的 for 循环只是为了确保所有将被合并的覆盖文件都被清理干净。
注意:默认情况下,此代码不会清除的唯一覆盖数据是相关文件,它们的属性中PyInstaller
不包含_MEIXXX
数据。__file__
您现在可以coverage.py
正常方式成功生成 HTML 或 XML(或其他)报告。
就我而言,它看起来像这样:
cov = coverage(data_file='.coverage.' + version.GetFullString(), data_suffix='.combined')
cov.load()
cov.combine()
cov.save()
cov.load()
cov.html_report(ignore_errors=True,omit=[r'c:\python27\*', r'..\3rdParty\PythonPackages\*'])
在构造函数中使用 data_file 是为了确保加载/组合能够正确识别我所有清理过的覆盖文件。
该html_report
调用告诉coverage.py
忽略标准 Python 库(以及检查到我的版本控制树中的 Python 库)并只关注我的应用程序代码。
我希望这有帮助。