EndermanAPM EndermanAPM - 3 months ago 10
Python Question

Generator from function prints

At the moment I have a little

flask
project that calls another python file. I'm fully aware that this way is kinda awful, and so, I want to swap it for a function call while maintaining the prints getting yelded to the website.

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())


I've tryed to replace the file call with the function directly but it just prints it to the console.

I really appreciate any help you can provide.

Answer

Assuming that all the printing you want to grab is done within the same module, You can monkey-patch the print function of the other module. In the example below, I use a context manager to revert the original print function after the grabbing is done.

This is mod1, the module with the misbehaving function.

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

This is mod2, the module using 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)

The grab_stdout() context manager redirects print calls of module to the file-like object fd. In the function line_generator(), grab_stdout() is used to store the print output of bogus_function in the StringIO object fd. The rest should be self-explanatory.

Comments