2013-01-22 28 views
6

有没有任何方法可以对使用pyinstaller构建的可执行文件运行覆盖率?我试图运行它像一个Python脚本,它不喜欢可执行文件作为输入(我没有真正期望它的工作),我怀疑答案是没有没有简单的方法来运行覆盖对一个可执行文件......(这是Windows的exe)冻结的可执行文件覆盖率

我使用的覆盖包只是正常范围包,您从nedbatchelder.com(http://nedbatchelder.com/code/coverage/

+0

什么代码覆盖率工具,你用于未被烘焙到可执行文件的Python脚本? – selbie

+0

编辑包含该信息 –

+1

你想要测试的覆盖范围是什么?你不仅可以访问可以运行覆盖范围的解冻脚本吗? –

回答

6

这ISN与“easy_install的覆盖”得到”一个完全公式化的答案,但我迄今为止发现的。

从我如何pyinstaller作品的理解是,二进制是从嵌入了Python解释器和引导程序加载脚本一个小的C程序构成。 PyInstaller构建的EXE在包含Python代码资源的实际二进制文件结束后包含一个归档文件。这在这里解释http://www.pyinstaller.org/export/develop/project/doc/Manual.html#pyinstaller-archives

从Pyinstaller/loader/iu.py Docs有iu.py。您应该能够创建一个导入钩子以从二进制文件导入。谷歌搜索pyinstaller反汇编程序发现https://bitbucket.org/Trundle/exetractor/src/00df9ce00e1a/exetractor/pyinstaller.py,看起来像它可能会提取必要的部分。

另一部分是二进制档案中的所有资源都将被编译为python代码。最有可能的情况是,coverage.py会以与在正常条件下运行时击中任何其他编译模块相同的方式为您提供无用的输出。

3

亮点使用cover_pylib=True

我知道这是很长,你问的问题后,但我只是让各地需要的答案。 :)

使用当前bitbucket来源coverage.py我能够成功收集生成的EXE文件PyInstaller的覆盖率数据。

在主要来源为我的应用程序有条件地告诉覆盖率开始收集覆盖面是这样的:

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循环这里完全是为了确保所有将要结合正在清理覆盖文件。

注:仅有的覆盖数据上面的代码没有清理在默认情况下是不包括在他们的__file__属性_MEIXXX数据PyInstaller相关文件。

您现在可以成功生成HTML或XML(或其他)coverage.py报告的正常方式。

在我的情况是这样的:

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库签入我的版本控制树),并只关注我的应用程序代码。

我希望这会有所帮助。