Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/three.js/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
python在永不结束的进程上运行覆盖率_Python_Multithreading_Python Multiprocessing_Coverage.py - Fatal编程技术网

python在永不结束的进程上运行覆盖率

python在永不结束的进程上运行覆盖率,python,multithreading,python-multiprocessing,coverage.py,Python,Multithreading,Python Multiprocessing,Coverage.py,我有一个多进程的web服务器,它的进程永远不会结束,我想在一个实时环境中检查整个项目的代码覆盖率(不仅仅是测试) 问题是,由于进程永远不会结束,我没有一个好位置来设置cov.start()cov.stop()cov.save()挂钩 因此,我考虑在无限循环中生成一个线程,该线程将保存并合并覆盖率数据,然后在一段时间内休眠,但是这种方法不起作用,覆盖率报告似乎是空的,除了休眠行 我很乐意收到关于如何覆盖我的代码的任何想法, 或者任何关于为什么我的想法行不通的建议。以下是我的代码片段: import

我有一个多进程的web服务器,它的进程永远不会结束,我想在一个实时环境中检查整个项目的代码覆盖率(不仅仅是测试)

问题是,由于进程永远不会结束,我没有一个好位置来设置
cov.start()cov.stop()cov.save()
挂钩

因此,我考虑在无限循环中生成一个线程,该线程将保存并合并覆盖率数据,然后在一段时间内休眠,但是这种方法不起作用,覆盖率报告似乎是空的,除了休眠行

我很乐意收到关于如何覆盖我的代码的任何想法, 或者任何关于为什么我的想法行不通的建议。以下是我的代码片段:

import coverage
cov = coverage.Coverage()
import time
import threading
import os

class CoverageThread(threading.Thread):
    _kill_now = False
    _sleep_time = 2

@classmethod
def exit_gracefully(cls):
    cls._kill_now = True

def sleep_some_time(self):
    time.sleep(CoverageThread._sleep_time)

def run(self):
    while True:
        cov.start()
        self.sleep_some_time()
        cov.stop()
        if os.path.exists('.coverage'):
            cov.combine()
        cov.save()
        if self._kill_now:
            break
    cov.stop()
    if os.path.exists('.coverage'):
        cov.combine()
    cov.save()
    cov.html_report(directory="coverage_report_data.html")
    print "End of the program. I was killed gracefully :)"

既然您愿意为测试以不同的方式运行代码,为什么不添加一种结束测试过程的方法呢?这似乎比尝试破解覆盖范围要简单。

显然,使用多个
线程
无法很好地控制
覆盖范围。
一旦启动了不同的线程,停止
覆盖
对象将停止所有覆盖,而
启动
将仅在“启动”线程中重新启动它。 因此,除了
CoverageThread
之外,对于所有
Thread
线程,您的代码基本上会在2秒钟后停止覆盖

我使用了一点API,可以在不停止
Coverage
对象的情况下访问测量值。 因此,您可以启动一个线程,使用API定期保存覆盖率数据。 第一个实现是这样的

import threading
from time import sleep
from coverage import Coverage
from coverage.data import CoverageData, CoverageDataFiles
from coverage.files import abs_file

cov = Coverage(config_file=True)
cov.start()


def get_data_dict(d):
    """Return a dict like d, but with keys modified by `abs_file` and
    remove the copied elements from d.
    """
    res = {}
    keys = list(d.keys())
    for k in keys:
        a = {}
        lines = list(d[k].keys())
        for l in lines:
            v = d[k].pop(l)
            a[l] = v
        res[abs_file(k)] = a
    return res


class CoverageLoggerThread(threading.Thread):
    _kill_now = False
    _delay = 2

    def __init__(self, main=True):
        self.main = main
        self._data = CoverageData()
        self._fname = cov.config.data_file
        self._suffix = None
        self._data_files = CoverageDataFiles(basename=self._fname,
                                             warn=cov._warn)
        self._pid = os.getpid()
        super(CoverageLoggerThread, self).__init__()

    def shutdown(self):
        self._kill_now = True

    def combine(self):
        aliases = None
        if cov.config.paths:
            from coverage.aliases import PathAliases
            aliases = PathAliases()
            for paths in self.config.paths.values():
                result = paths[0]
                for pattern in paths[1:]:
                    aliases.add(pattern, result)

        self._data_files.combine_parallel_data(self._data, aliases=aliases)

    def export(self, new=True):
        cov_report = cov
        if new:
            cov_report = Coverage(config_file=True)
            cov_report.load()
        self.combine()
        self._data_files.write(self._data)
        cov_report.data.update(self._data)
        cov_report.html_report(directory="coverage_report_data.html")
        cov_report.report(show_missing=True)

    def _collect_and_export(self):
        new_data = get_data_dict(cov.collector.data)
        if cov.collector.branch:
            self._data.add_arcs(new_data)
        else:
            self._data.add_lines(new_data)
        self._data.add_file_tracers(get_data_dict(cov.collector.file_tracers))
        self._data_files.write(self._data, self._suffix)

        if self.main:
            self.export()

    def run(self):
        while True:
            sleep(CoverageLoggerThread._delay)
            if self._kill_now:
                break

            self._collect_and_export()

        cov.stop()

        if not self.main:
            self._collect_and_export()
            return

        self.export(new=False)
        print("End of the program. I was killed gracefully :)")
更稳定的版本可以在这里找到。 这段代码基本上是在不停止的情况下获取收集器收集的信息。
get\u data\u dict
函数在
Coverage.collector
中获取字典并弹出可用数据。这应该足够安全,因此不会丢失任何测量值。
报告文件每延迟
秒更新一次

但是,如果有多个进程正在运行,则需要额外努力以确保所有进程都运行
CoverageLoggerThread
。这是
patch\u多处理
功能,从
覆盖范围
monkey patch…
代码在目录中。它基本上用自定义进程替换了原始进程,该进程在运行
run
方法之前启动
CoverageLoggerThread
,并在进程结束时加入线程。 脚本
main.py
允许使用线程和进程启动不同的测试

此代码有2/3的缺点,您需要小心:

  • 同时使用
    combine
    功能是个坏主意,因为它对
    .coverage.*
    文件执行当前的读/写/删除访问。这意味着功能
    export
    不是超级安全的。它应该是好的,因为数据被多次复制,但我会在生产中使用它之前做一些测试

  • 导出数据后,数据将保留在内存中。因此,如果代码库庞大,它可能会消耗一些资源。可以转储所有数据并重新加载,但我假设如果您希望每2秒记录一次日志,则不希望每次都重新加载所有数据。如果延迟几分钟,我每次都会创建一个新的
    \u数据
    ,使用
    coverage data.read\u file
    重新加载此进程以前的覆盖状态

  • 自定义进程将等待
    \u delay
    完成,因为我们在进程结束时加入了
    CoverageThreadLogger
    ,因此如果您有很多快速进程,您需要增加睡眠的粒度,以便能够更快地检测到进程的结束。它只需要一个自定义的睡眠循环,在
    \u kill\u now
    时中断

让我知道这是否在某种程度上对您有所帮助,或者是否有可能改进此要点


编辑: 似乎您不需要对多处理模块进行猴子补丁就可以自动启动记录器。在python安装中使用
.pth
可以使用环境变量在新进程上自动启动记录器:

# Content of coverage.pth in your site-package folder
import os
if "COVERAGE_LOGGER_START" in os.environ:
    import atexit
    from coverage_logger import CoverageLoggerThread
    thread_cov = CoverageLoggerThread(main=False)
    thread_cov.start()
    def close_cov()
        thread_cov.shutdown()
        thread_cov.join()
    atexit.register(close_cov)
然后,您可以通过以下两个程序直接使用
coverage\u logger\u start=1 python main.y

启动覆盖率记录器

# start.py
import sys
import coverage

sys.cov = cov = coverage.coverage()
cov.start()
这个呢

# stop.py
import sys

sys.cov.stop()
sys.cov.save()
sys.cov.html_report()

另一种方法是使用跟踪程序,即使它只打印调用,它也会很有用。

您到底想测量什么?“覆盖率”具体指测试。您是否正在尝试查看代码的哪些部分实际执行?是否要检查您的.py文件是否仍处于活动状态?是的,代码库非常庞大,我们希望查看哪些代码区域从未到达,如果这些区域存在,请将其删除,或检查其无法访问的原因。@Rizzit我知道这些文件处于活动状态,但我实际上想知道执行的是什么。Coverage使用解释器挂钩来通知代码中正在执行的每一行。这将显著降低性能,您愿意为此付出代价吗?因为数据应该以无缝的方式收集给最终用户,所以我们的想法是从公司的多个用户收集数据,并合并他们的结果,同时允许他们以正常的方式工作。此外,如果我将强制进程重新启动或停止,这将影响到将损害结果的行为。感谢您提出这一点。它在coverage.py代码库中使用了许多非公共接口。coverage.py可以提供什么样的公共API使其更受支持?还有,为什么要调用combine?为什么不将每个快照写入一个单独的文件,然后在以后合并它们呢?Coverage可以提出一个导出功能,将当前数据保存在一个文件中,并且不停止
Coverage
对象。这是一个非常复杂的问题