Моя цель — открыть процесс, использующий subprocess.Popen
в python, и заставить этот процесс передавать свои stdout и stderr в пользовательский класс RingBuffer, который я написал, что позволяет мне периодически проверять содержимое буфера из того же пространства, в котором я создал экземпляр подпроцесса. из. Это важно, я знаю, что есть способы сделать отдельную программу, направить вывод подпроцесса в stdin
этой программы кольцевого буфера, но тогда мне придется пойти и вручную проверить какой-то базовый файл, содержащий содержимое кольцевого буфера и т. д. и т.д. Идеальным было бы связать вывод подпроцесса с каким-то объектом, к которому у меня есть доступ.
Во-первых, из документации для subprocess
(python 2.X) (https://docs.python.org/2/library/subprocess.html)
stdin, stdout и stderr определяют стандартный ввод, стандартный вывод и стандартный дескриптор файла ошибки исполняемой программы соответственно. Допустимые значения: PIPE, существующий файловый дескриптор (положительное целое число), существующий файловый объект и None. PIPE указывает, что необходимо создать новый канал для дочернего элемента. При настройках по умолчанию None перенаправление не происходит; дескрипторы дочерних файлов будут унаследованы от родителя. Кроме того, stderr может быть STDOUT, что указывает на то, что данные stderr из дочернего процесса должны быть записаны в тот же дескриптор файла, что и для stdout.
«существующий файловый объект», поэтому я предполагаю, что если я создам класс, соответствующий интерфейсу file
, он должен работать, верно?
Скажем, я сделал такой класс
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
конечно, в этой программе есть вероятные ошибки, но вы поняли суть, она предоставляет функцию write()
и записывает данные в кольцевой буфер, обрезая буфер до определенного размера, когда он становится слишком ошибочным, и позволяя пользователю восстановить данные, когда они хотят с функцией dumps()
.
Теперь, если я попробую что-то вроде этого
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'
Итак, в моем «файлоподобном» объекте отсутствует функция fileno()
для соответствия файловому интерфейсу. Вот в чем проблема. Зачем нужен файл? Почему он не может просто использовать предоставленную мне функцию write()
? Я предполагаю, что он обойдет мою функцию write
и вместо этого будет использовать fileno
для записи непосредственно в файл?
Скажем, я добавляю заглушку этой функции
def fileno()
return None
тогда это происходит
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
поэтому мой вопрос: как я могу заставить subprocess.Popen
использовать мою функцию write()
для моего объекта, подобного file
, вместо того, чтобы пытаться писать непосредственно в дескриптор файла, возвращаемый из несуществующей функции fileno()
? Если нет способа сделать это... есть ли способ выполнить то, что я хочу здесь?
Я знаю, что теоретически я мог бы создать некоторый файл /tmp/ringlog.txt
и открыть этот файл при создании экземпляра класса, а затем заставить программу писать в этот файл, а моя программа периодически просматривать файл и хранить его под max_size
, используя аналогичный кольцевой буфер. алгоритм, но это беспорядок.
Другой вариант — создать программу, которая читает стандартный ввод, записывает в файл и буферизует содержимое, чтобы сохранить файл меньше определенного размера, но тогда я все еще имею дело с фактическим файлом, я просто хочу сохранить содержимое в памяти и доступный из вызывающей среды Python.
subprocess.PIPE
- это именно то, что вам нужно здесь: другой процесс записывает в (предоставленный ОС) конвейер, ваш процесс читает с вашего конца конвейера. - person jasonharper   schedule 28.07.2018