Неблокирование чтения на os.pipe в Windows

Этот вопрос - Как читать из os.pipe() без блокировки? - показывает, как проверить, есть ли os.pipe какие-либо данные для Linux, и для этого вам нужно перевести трубу в неблокирующий режим:

import os, fcntl
fcntl.fcntl(thePipe, fcntl.F_SETFL, os.O_NONBLOCK)

В Windows это:

ImportError: No module named fcntl

Но os.pipe есть:

>>> os.pipe()
(3, 4)

Итак, можно ли неблокировать чтение или просмотр содержимого os.pipe в Windows?

Ответы

Ответ 1

Отвечая на мой собственный вопрос после копания в течение некоторого времени через StackOverflow.

ОБНОВЛЕНИЕ. Вещи меняются благодаря @HarryJohnston.

Сначала ответ был нет, невозможно сделать неблокирующее чтение на os.pipe в Windows. От этот ответ У меня есть это:

Термин для неблокирующего/асинхронного ввода-вывода в Windows "перекрывается" - это то, что вы должны смотреть.

os.pipe в Windows реализован через CreatePipe API (см. здесь и... ну, я не смог найти код os.pipe в Источники Python). CreatePipe делает анонимные каналы, а анонимные каналы не поддерживают асинхронный ввод-вывод.

Но затем @HarryJohnston прокомментировал, что SetNamedPipeHandleState doc позволяет помещать анонимный канал в неблокирующий режим. Я написал тест, и он не прошел с помощью OSError: [Errno 22] Invalid argument. Сообщение об ошибке показалось неправильным, поэтому я попытался проверить, что должно быть результатом возврата при неблокирующей операции чтения, когда данные недоступны, и после чтения Заметка MSDN на именованные режимы pipe. Я обнаружил, что это должно быть ERROR_NO_DATA, которое имеет значение int 232. Добавление ctypes.WinError() вызов обработчику исключений показал ожидаемый [Error 232] The pipe is being closed.

Итак, ответ да, можно сделать неблокирующее чтение на os.pipe в Windows, и вот доказательство:

import msvcrt
import os

from ctypes import windll, byref, wintypes, GetLastError, WinError
from ctypes.wintypes import HANDLE, DWORD, POINTER, BOOL

LPDWORD = POINTER(DWORD)

PIPE_NOWAIT = wintypes.DWORD(0x00000001)

ERROR_NO_DATA = 232

def pipe_no_wait(pipefd):
  """ pipefd is a integer as returned by os.pipe """

  SetNamedPipeHandleState = windll.kernel32.SetNamedPipeHandleState
  SetNamedPipeHandleState.argtypes = [HANDLE, LPDWORD, LPDWORD, LPDWORD]
  SetNamedPipeHandleState.restype = BOOL

  h = msvcrt.get_osfhandle(pipefd)

  res = windll.kernel32.SetNamedPipeHandleState(h, byref(PIPE_NOWAIT), None, None)
  if res == 0:
      print(WinError())
      return False
  return True


if __name__  == '__main__':
  # CreatePipe
  r, w = os.pipe()

  pipe_no_wait(r)

  print os.write(w, 'xxx')
  print os.read(r, 1024)
  try:
    print os.write(w, 'yyy')
    print os.read(r, 1024)
    print os.read(r, 1024)
  except OSError as e:
    print dir(e), e.errno, GetLastError()
    print(WinError())
    if GetLastError() != ERROR_NO_DATA:
        raise