功能打印生成器

时间:2016-09-01 09:56:57

标签: python python-3.x flask subprocess generator

目前我有一个调用另一个python文件的flask项目。我完全清楚这种方式有点糟糕,因此,我希望将其换成函数调用,同时保持打印到网站上的打印

def get_Checks():
    root = request.url_root

    def func():
        yield ("Inicio <br>")
        with subprocess.Popen(r"python somefile.py", stdout=subprocess.PIPE, bufsize=1,
                              universal_newlines=True) as p:
            for line in p.stdout:
                yield (line + "<br>")

    return Response(func())

我尝试直接用函数替换文件调用,但它只是将它打印到控制台。

我非常感谢您提供的任何帮助。

2 个答案:

答案 0 :(得分:1)

一种简单的方法是暂时将sys.stdout更改为类似文件的对象,调用该函数,然后恢复sys.stdout。输出将在类文件对象中可用。

这是一个工作的Flask应用程序,演示了该方法:

import sys
from io import StringIO
from flask import Flask, request, Response
import somefile

app = Flask(__name__)

@app.route("/")
def hello():
    def func():
        yield ("Inicio <br>")

        try:
            _stdout = sys.stdout
            sys.stdout = output = StringIO()
            somefile.main()
            output.seek(0)
            for line in output:
                sys.stdout = _stdout
                yield '{}<br>'.format(line.rstrip())
                sys.stdout = output
        finally:
            sys.stdout.close()    # close the StringIO object
            sys.stdout = _stdout  # restore sys.stdout

    return Response(func())

if __name__ == "__main__":
    app.run()

这里使用io.StringIO对象来收集函数产生的标准输出,然后从该对象产生线。 finally确保之后恢复原始sys.stdoutyield语句有一些额外的复杂性,因为yield将控制返回到调用代码,如果调用者也想打印到stdout,则必须恢复stdout。

假设somefile.py中的函数是“main”函数,并且它的调用由if __name__ == '__main__':测试保护,如下所示:

def main():
    for i in range(10):
        print(i)

if __name__ == '__main__':
    main()

答案 1 :(得分:0)

假设您要抓取的所有打印都在同一模块中完成,您可以修补其他模块的print功能。在下面的示例中,我使用上下文管理器在抓取完成后恢复原始打印功能。

这是mod1,具有行为不当功能的模块。

def bogus_function():
    print('Hello World!')
    print('Line 2')

这是mod2,模块使用mod1.bogus_function()

import io
import functools
import contextlib

import mod1

@contextlib.contextmanager
def grab_stdout(module, fd):
    def monkey_print(*args, **kwargs):
        kwargs['file'] = fd
        print(*args, **kwargs)

    setattr(module, 'print', monkey_print)
    try:
        yield
    finally:
        setattr(module, 'print', print)

def line_generator():
    fd = io.StringIO()
    with grab_stdout(mod1, fd):
        mod1.bogus_function()
    fd.seek(0)

    for line in fd:
        yield line.rstrip('\r\n') + '<br>'

for t in enumerate(line_generator()):
    print('line %d: %r' % t)

grab_stdout()上下文管理器将module的打印调用重定向到类文件对象fd。在函数line_generator()中,grab_stdout()用于在bogus_function对象StringIO中存储fd的打印输出。其余的应该是不言自明的。

如果您不确切知道是否在相关函数的调用树中的其他模块中调用了print,则可以按如下方式修改grab_stdout

import builtins
print_orig = builtins.print

@contextlib.contextmanager
def grab_stdout_global(fd):
    def monkey_print(*args, **kwargs):
        kwargs['file'] = fd
        print_orig(*args, **kwargs)

    builtins.print = monkey_print
    try:
        yield
    finally:
        builtins.print = print_orig