【问题标题】:coverage on a frozen executable覆盖冻结的可执行文件
【发布时间】:2013-01-05 06:26:51
【问题描述】:

有没有办法对使用 pyinstaller 构建的可执行文件进行覆盖?我试着像运行它一样运行它,它是一个 python 脚本,它不喜欢可执行文件作为输入(我真的没想到它会工作),我怀疑答案是否定的,没有简单的方法可以针对构建的可执行文件运行覆盖....(这是在 windows .exe 上)

我使用的覆盖包只是您通过 nedbatchelder.com (http://nedbatchelder.com/code/coverage/) 的“easy_install coverage”获得的普通覆盖包

【问题讨论】:

  • 对于未烘焙到可执行文件中的 python 脚本,您使用哪些代码覆盖工具?
  • 已编辑以包含该信息
  • 您希望覆盖率测试什么?您不仅可以访问可以运行覆盖范围的解冻脚本吗?
  • 我愿意...它有点像“我能做到这一点”类型的问题...(有时可执行文件无法通过脚本通过的测试...(不经常))
  • 我明白了。您能否在安装程序中不包含覆盖率包并使用运行覆盖率而不是实际脚本的开关?

标签: python windows pyinstaller coverage.py


【解决方案1】:

这不是一个完整的答案,而是我迄今为止发现的。

根据我对 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 disassembler 发现 https://bitbucket.org/Trundle/exetractor/src/00df9ce00e1a/exetractor/pyinstaller.py 看起来它可能会提取必要的部分。

另一部分是二进制存档中的所有资源都将编译为python代码。最有可能的是,coverage.py 会像在正常条件下运行任何其他已编译模块时一样给您无用的输出。

【讨论】:

    【解决方案2】:

    高亮使用cover_pylib=True

    我知道你问这个问题已经很久了,但我只是想知道答案。 :)

    使用coverage.py 的当前bitbucket 源,我能够从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 并且因此无法(在路径基础上)与内部不存在的其他代码区分开来一袋。

    当应用程序准备好退出时,我有这个小 sn-p:

    if usingCoverage:
       cov.stop()
       cov.save()
    

    一旦我的应用程序运行了,coverage.py 仍然不会自动为我生成它的 HTML 报告。需要清理覆盖数据,以便将...\_MEIXXXX\... 文件引用转换为真正源代码的绝对文件路径。

    我通过运行这段 sn-p 代码来做到这一点:

    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 相关文件,它们的 __file__ 属性中不包含 _MEIXXX 数据。

    您现在可以正常方式成功生成 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 是为了确保 load/combine 能够正确识别我清理的所有覆盖文件。

    html_report 调用告诉coverage.py 忽略标准 Python 库(以及检查到我的版本控制树中的 Python 库)并只关注我的应用程序代码。

    我希望这会有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-04
      • 2014-11-30
      • 1970-01-01
      • 1970-01-01
      • 2013-03-25
      相关资源
      最近更新 更多