如何判断我的python程序是否已在sys.stdout中写入任何内容(在同一程序中)?

时间:2019-01-01 09:44:46

标签: python io stdout file-descriptor

我正在制作一个具有不同控制流的程序,具体取决于是否已将任何内容写入sys.stdout。我首先在Mac上制作了该程序,然后可以做到

pos = sys.stdout.tell()
exec(some_code, globals)
if pos == sys.stdout.tell():
    # ...

但是事实证明,我无法在Linux上使用tell()方法,这对我来说是个问题。在这种情况下,如何判断sys.stdout是否被触摸?

我认为我可以想到的是为sys.stdoutsys.stdout.buffer都创建一个包装器类,以监视所有书写调用,尽管我不确定这样做是否可靠。

或者我想我可以尝试contextlib.redirect_stdout。但是我不确定如何使用它。.

编辑: 我正在使用此实用程序https://github.com/bombs-kim/pythonp。我要实现的功能是在没有输出到stdout的情况下自动将some_codeexec(some_code, globals)的最后一个表达式写到stdout。

2 个答案:

答案 0 :(得分:2)

  

在这种情况下,如何判断sys.stdout是否已被触摸?

您不能,因为stdout可能是redirectedpipeline中的(通过shell中用于运行Python脚本的命令)。例如,您的用户可以通过将script.py运行为script.py > /dev/null来将标准输出重定向到/dev/null(请参阅null(4)),然后您的问题甚至没有感。而且您的stdout甚至可能是套接字,fifo,设备等。此外,some_code的{​​{3}}-ution可能会更改stdout(例如,将其关闭,重定向使用exec,重新定义stdout Python对象等))

此外,您的程序甚至可能不会在终端中运行(例如os.dup2作业,或者使用atssh运行程序)。另请参见t crontab(5)页。

stdout he tty demystified(通常)是由调用过程(通常但并非总是某些shell)设置的,该过程具有standard stream-ed和fork(2)-ed您的脚本。

那不是Linux特有的,它是execve(2)。您可能需要阅读一些Unix或Linux编程书籍(例如Unix philosophy)。

您可以通过使用ALP来检测 stdout 是终端的情况。但这当然不能涵盖所有可能的用例。

Linux具有/proc/(有关更多信息,请参见isatty(3)),并且stdout也是/dev/stdout,也可以是/proc/self/fd/1,您可以在其上执行proc(5)(以及当然您可以直接对fstatSTDOUT_FILENO,即1)。但是,即使那样也无法涵盖所有​​可能的用例。

您是否考虑过程序监视自身的情况(例如pythonp pythonp ...)?

我不确定您的真实目标是什么,但是它们使我心中的stat(2)响了起来,您的问题可能无法确定。

答案 1 :(得分:1)

我通过将sys.stdout.writesys.stdout.buffer.write都包装在python3中来自行解决。

import sys
write = sys.stdout.write

write_called = [False]


def make_write_hook(old_writer, flag: list):
    self = old_writer.__self__

    def new_writer(value):
        self.write = old_writer
        flag[0] = True
        old_writer(value)
    return new_writer


sys.stdout.write = make_write_hook(sys.stdout.write, write_called)
sys.stdout.buffer.write = make_write_hook(
    sys.stdout.buffer.write, write_called)

history = []
history.append(write_called[0])
print("hello world")
history.append(write_called[0])
print(history)

# ### output
# hello world
# [False, True]

根据我对cpython的标准库代码的观察,python的约定是,对标准输出的所有写入均应通过调用sys.stdout.writesys.stdout.buffer.write来完成,而不是直接调用os.fdopen(1, 'w').write之类。这是我的临时结论,我希望比我更了解python的人确认这一点。谢谢大家!