В C++ приложение встраивается Python чтобы исполнять Python-вский скрипт прямо внутри приложения. Потребовалось сделать так, чтобы все, что Python-овский скрипт печатает в sys.stdout, отправлялось в C++ную часть.
Стал разбираться с Python-овским io.
Несколько раз проштудировал описание io в стандартной документации.
Мало что понял.
Что удивило. Т.к. пока читаешь, то вроде бы и буквы все понятные, и в слова понятные они складываются. А вот что к чему и почему... Все никак.
Гуглил, читал Stackoverflow, много думал :)
Но просветление не приходило.
Даже в исходники CPython заглянул, чтобы понять, как же вся эта кухня с IOBase, RawIOBase, BufferedIOBase и TextIOBase работает.
В общем, пришел к выводу, что мне нужен стандартный io.TextIOWrapper. В конструктор ему передается экземпляр стандартного io.BufferedWriter. А уже в конструктор io.BufferedWriter в качестве параметра raw передается экземпляр моего класса, наследника io.RawIOBase. Что-то вроде:
import io import sys class MyRawIOBase(io.RawIOBase): def __init__(self): self.dest = open(sys.stdout.fileno(), 'wb', 0) def write(self, b, /): self.dest.write(b) def writable(self): return True sys.stdout = io.TextIOWrapper(io.BufferedWriter(MyRawIOBase()), write_through=True) print('Hello, World!') |
Оно лично для меня выглядело складно и логично. А вот результат запуска, мягко говоря, озадачил:
Hello, World!
Hello, World!
Exception ignored in: <_io.TextIOWrapper encoding='UTF-8'>
BlockingIOError: [Errno 0] write could not complete without blocking
Hello, World!
Hello, World!
Могу предположить, что все это происки io.BufferedWriter. Но почему это происходит и как поправить -- вот непонятно совершенно.
В этот момент я уже откровенно впал в ступор.
Прежде чем отправиться в Гугл за следующей порцией информации решил проверить, а что будет, если в io.TextIOWrapper отдать мой MyRawIOBase напрямую, без промежуточных io.BufferedWriter:
import io import sys class MyRawIOBase(io.RawIOBase): def __init__(self): self.dest = open(sys.stdout.fileno(), 'wb', 0) def write(self, b, /): self.dest.write(b) def writable(self): return True sys.stdout = io.TextIOWrapper(MyRawIOBase(), write_through=True) print('Hello, World!') |
И, о чудо, оно заработало!
На чем-то подобном и остановился.
Хотя есть подозрение, что все-таки делаю что-то не так, и что io.BufferedWriter таки нужен. Но вот куда копать не имею совершенно никакого понятия.
PS. А стандартная документация по Python-у таки производит странное впечатление. Как уже и говорил, пока читаешь, то вроде как все понятно. А как пытаешься применить прочитанное, то выясняется, что ничего полезного-то и не вычитал. Возможно, это все следствие того, что в Python-е я совершенный новичок.
> io.BufferedWriter таки нужен
ОтветитьУдалитьА зачем?
У меня вот есть идея, но если что, я то же не "питонист", а только учусь ;)
> open(sys.stdout.fileno(), 'wb', 0)
Вот здесь вместо 0 можно поставить размер буфера и получите буфер "нахаляву".
> А зачем?
ОтветитьУдалитьПотому что в официальной документации к TextIOWrapper говорится: "A buffered text stream providing higher-level access to a BufferedIOBase buffered binary stream. It inherits TextIOBase."
Так что я предполагаю, что первым параметром к TextIOWrapper ожидается какой-то из BufferedIOBase. Но, возможно, ошибаюсь.
> Вот здесь вместо 0 можно поставить размер буфера и получите буфер "нахаляву".
Это здесь используется просто для того, чтобы был минимальный самодостаточный пример.