简体   繁体   English

强制subprocess.Popen使用write()函数而不是fileno()将stdout / stderr写入python中的文件状对象

[英]Forcing subprocess.Popen to write stdout/stderr to file-like object in python using write() function and not fileno()

My goal is to open a process using subprocess.Popen in python, and have this process pipe its stdout and stderr to a custom RingBuffer class that I've written, allowing me to periodically inspect the contents of the buffer from the same space I instantiated the subprocess from. 我的目标是使用python中的subprocess.Popen打开一个进程,并使该进程将其stdout和stderr传递给我编写的自定义RingBuffer类,从而使我能够从实例化的同一空间中定期检查缓冲区的内容来自的子流程。 This is important, I know there are ways to make a separate program, pipe the output of the subprocess to the stdin of that ringbuffer program, but then I have to go and manually inspect some underlying file that contains the ring buffer contents, etc, etc. The ideal thing would be to connect the output of the subprocess to some object that I have access to. 这很重要,我知道有一些方法可以制作一个单独的程序,将子进程的输出通过管道传送到该环形缓冲区程序的stdin ,但是然后我必须手动检查一些包含环形缓冲区内容的底层文件,等等,理想的情况是将子流程的输出连接到我可以访问的某个对象。

First, from the documenation for subprocess (python 2.X) ( https://docs.python.org/2/library/subprocess.html ) 首先,从subprocess文档(python 2.X)( https://docs.python.org/2/library/subprocess.html

stdin, stdout and stderr specify the executed program's standard input, standard output and standard error file handles, respectively. stdin,stdout和stderr分别指定执行程序的标准输入,标准输出和标准错误文件句柄。 Valid values are PIPE, an existing file descriptor (a positive integer), an existing file object, and None. 有效值为PIPE,现有文件描述符(正整数),现有文件对象和无。 PIPE indicates that a new pipe to the child should be created. PIPE指示应创​​建到子级的新管道。 With the default settings of None, no redirection will occur; 使用默认设置无,将不会发生重定向。 the child's file handles will be inherited from the parent. 子级的文件句柄将从父级继承。 Additionally, stderr can be STDOUT, which indicates that the stderr data from the child process should be captured into the same file handle as for stdout 此外,stderr可以是STDOUT,这表示子进程的stderr数据应捕获到与stdout相同的文件句柄中。

"an existing file object", so I assume if I make a class that conforms to the file interface it should work, right? “现有的文件对象”,因此我假设如果我创建一个符合file接口的类,它应该可以工作,对吗?

Let's say I've made a class like this 假设我上了这样的课

class RingBuffer(object):

    def __init__(max_size=1024*1024):
      self.max_size = max_size
      self.current_size = 0


    def write(self, data):
        self.current_size += len(data)
        self.data.append(data)
        if self.current_size >= self.max_size_bytes:
            while self.current_size >= self.trim_size_bytes:
                try:
                    popped = self.data.pop()
                    self.current_size -= len(popped)
                except IndexError as e:
                    break

def writelines(self, sequence):
    for item in sequence:
        self.write(item)

def dumps(self):
    ret = [line for line in self.data]
    return '\n'.join(ret)

def clear(self):
    self.data.clear()
    self.current_size = 0

granted there are likely bugs in this program but you get the gist, it exposes a write() function and writes data to a circular buffer, trimming the buffer to a certain size when it gets too bug, and letting the user recover the data when they want with the dumps() function. 可以肯定的是,该程序中可能存在错误,但是您可以理解,它公开了write()函数,并将数据写入循环缓冲区,如果出现错误,则将缓冲区调整为一定大小,并在出现以下情况时让用户恢复数据他们想要使用dumps()函数。

Now, if I try something like this 现在,如果我尝试这样的事情

r = RingBuffer()
pr = subprocess.Popen(["timeout", "15", "yes"], stdout=r, stderr=subprocess.STDOUT)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/Cellar/python/2.7.13/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 382, in __init__
    errread, errwrite), to_close = self._get_handles(stdin, stdout, stderr)
  File "/usr/local/Cellar/python/2.7.13/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 818, in _get_handles
    c2pwrite = stdout.fileno()
AttributeError: 'RingBuffer' object has no attribute 'fileno'

okay, so my "file-like" object is missing the fileno() function to conform to the file interface. 好的,所以我的“类似于文件”的对象缺少符合文件接口的fileno()函数。 This is where the issue lays.. why does it need a fileno? 这就是问题所在。.为什么它需要文件号? Why can't it just use my supplied write() function? 为什么不能仅使用提供的write()函数? I'm assuming that it is going to by-pass my write function and instead use the fileno to write directly to the file? 我假设它会绕过我的write功能,而不是使用fileno直接写入文件?

Let's say I add in a stub of that function 假设我添加了该函数的存根

def fileno()
    return None

then this happens 然后这发生

r = RingBuffer()
pr = subprocess.Popen(["timeout", "15", "yes"], stdout=r, stderr=subprocess.STDOUT)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/Cellar/python/2.7.13/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 390, in __init__
    errread, errwrite)
  File "/usr/local/Cellar/python/2.7.13/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 1024, in _execute_child
    raise child_exception
OSError: [Errno 2] No such file or directory

so my question is: how can I force subprocess.Popen to use my write() function for my file -like object instead of trying to write directly to the file handle returned from a non-existent fileno() function? 所以我的问题是:如何强制subprocess.Popen对类似file的对象使用我的write()函数,而不是尝试直接写入从不存在的fileno()函数返回的文件句柄? If there is no way to do this.. is there any way to accomplish what I want here? 如果没有办法做到这一点..有什么办法可以完成我想要的吗?

I know that theoretically I could make some file, /tmp/ringlog.txt , and open that file on instantiation of the class, then have the program write to that file, and have my program periodically look at the file and keep it under the max_size using a similar ringbuffer algorithm, but that's a mess. 我知道理论上我可以制作一些文件/tmp/ringlog.txt ,并在类实例化时打开该文件,然后让程序写入该文件,并让我的程序定期查看该文件并将其保存在使用类似的环形缓冲区算法的max_size ,但是很混乱。

Another option is to make a program that reads stdin, writes to a file, and ringbuffers the content to keep the file under a certain size, but then I'm still dealing with actual file, I just want to keep the contents in memory and accessible from the calling python environment. 另一个选择是制作一个程序,该程序可以读取stdin,写入文件并对内容进行环形缓冲,以将文件保持在一定的大小以下,但是我仍在处理实际文件,我只想将内容保留在内存中,可从调用python环境访问。

The child process is going to write to its stdout using standard OS-level file writing calls, which means it needs something compatible with those calls. 子进程将使用标准OS级别的文件写入调用写入其stdout,这意味着它需要与这些调用兼容的东西。 The child process can't see into Python's memory or call methods on Python objects. 子进程无法查看Python的内存或对Python对象的调用方法。

If you want to write the subprocess's output to a file-like object that doesn't represent something the OS can treat as a file, you're going to have to receive the output through a pipe and write it to the file-like object yourself. 如果您想将子进程的输出写入一个文件状的对象,而该对象不代表操作系统可以将其视为文件,那么您将必须通过管道接收输出并将其写入该文件状的对象你自己 You could spawn a worker thread for that (and make sure to synchronize access to the object, if you're planning to read from it before the worker terminates), but it might be simpler to interact with the pipe directly. 您可以为此生成一个工作线程(并且,如果您打算在工作线程终止之前从中读取对象,请确保同步对该对象的访问),但是直接与管道进行交互可能更简单。

If you are going to work with subprocess.Popen then I suggest understanding the problems that come with using pipes, which is generally in the area of deadlocks. 如果您要使用subprocess.Popen,那么我建议您了解使用管道带来的问题,通常在死锁方面。

See: https://thraxil.org/users/anders/posts/2008/03/13/Subprocess-Hanging-PIPE-is-your-enemy/ 参见: https : //thraxil.org/users/anders/posts/2008/03/13/Subprocess-Hanging-PIPE-is-your-enemy/

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM