Как открыть специальный файл устройства символов именованного канала для чтения и записи на Python

у меня есть служба, работающая на Linux box, которая создает именованный канал символьное устройство-специальный файл, и я хочу написать программу Python3, которая взаимодействует с сервисом, записывая текстовые команды и читая текстовые ответы из труба устройства. У меня нет исходного кода для службы.

я могу использовать os.open(named_pipe_pathname, os.O_RDWR), и я могу использовать os.read(...) и os.write(...) читать и писать, но это боль, потому что я должен написать свой собственный код для преобразования между байтами и строками я должен написать свой собственный readline(...) функция, etc.

Я бы предпочел использовать Python3 io объект для чтения и записи труба устройства, но каждый способ я могу думать, чтобы создать один возвращает ту же ошибку:

io.UnsupportedOperation: File or stream is not seekable.

например, я получаю это сообщение, если я попробую open(pathname, "r+"), и я получаю то же самое сообщение, если я попытаюсь fd=os.open(...) следовал по os.fdopen(fd, "r+", ...).

Q: каков предпочтительный способ для программы Python3 писать и читать текст в именованный канал символьное устройство?


Edit:

Упс! Я предположил, что имею дело с именованным каналом, потому что документация для службы описывает его как "канал" и, поскольку он не появляется в файловой системе до запуска службы пользовательского режима. Но, Linux file утилита говорит, что это на самом деле, персонаж специальный файл устройства.

3 ответов


проблема возникает из-за попытки использовать io.open в режиме чтения-записи неявно пытается обернуть исходный файл в io.BufferedRandom (который затем заворачивают в io.TextIOWrapper Если в текстовом режиме), который предполагает, что базовый файл не только чтение/запись, но и произвольный доступ, и он берет на себя вольности (поиск неявно) на основе этого. Существует отдельный класс io.BufferedRWPair, предназначенный для использования с трубами чтения/записи (docstring специально упоминает, что он используется для сокетов и двухсторонних труб).

вы можете имитировать эффекты io.open вручную оборачивая слой за слоем, чтобы получить тот же конечный результат. В частности, для обертки текстового режима вы должны сделать что-то вроде:

rawf = io.FileIO(named_pipe_pathname, mode="rb+")
with io.TextIOWrapper(io.BufferedRWPair(rawf, rawf), encoding='utf-8', write_through=True) as txtf:
    del rawf   # Remove separate reference to rawf; txtf manages lifetime now
    # Example use that works (but is terrible form, since communicating with
    # oneself without threading, select module, etc., is highly likely to deadlock)
    # It works for this super-simple case; presumably you have some parallel real code
    txtf.write("abcé\n")
    txtf.flush()
    print(txtf.readline(), flush=True)

Я считаю, что это будет близко rawf два раза, когда txtf закрыт, но, к счастью, двойное закрытие здесь безвредно (второе close ничего не делает, понимая, что он уже закрыт).


решение

можно использовать pexpect. Вот пример использования двух модулей python:

caller.py

import pexpect

proc = pexpect.spawn('python3 backwards.py')
proc.expect(' > ')

while True:

    n = proc.sendline(input('Feed me - '))
    proc.expect(' > ')
    print(proc.before[n+1:].decode())

backwards.py

x = ''

while True:
    x = input(x[::-1] + ' > ')

объяснение

caller.py использует "псевдо-TTY устройство", чтобы поговорить с backwards.py. Мы предоставляем ввод с sendline и захват ввода с expect (и ).


похоже, вам нужно создать отдельные дескрипторы для чтения и записи: для открытия чтения/записи просто требуется метод поиска. Я не мог понять, как тайм-аут чтения, поэтому приятно добавить открыватель (см. docstring для io.open), который открывает считыватель в неблокирующем режиме. Я создал простой Эхо-сервис на именованной трубе под названием /tmp/test_pipe:

In [1]: import io
In [2]: import os
In [3]: nonblockingOpener = lambda name, flags:os.open(name, flags|os.O_NONBLOCK)
In [4]: reader = io.open('/tmp/test_pipe', 'r', opener = nonblockingOpener)
In [5]: writer = io.open('/tmp/test_pipe', 'w')
In [6]: writer.write('Hi have a line\n')
In [7]: writer.flush()
In [8]: reader.readline()
Out[8]: 'You said: Hi have a line\n'
In [9]: reader.readline()
''