[英]Non blocking read on os.pipe on Windows
This question - How to read from an os.pipe() without getting blocked?这个问题 - 如何从 os.pipe() 读取而不会被阻塞? - shows a solution how to check if os.pipe
has any data for Linux, and for this you need to put the pipe into non-blocking mode: - 展示了如何检查os.pipe
是否有任何适用于 Linux 的数据的解决方案,为此您需要将管道置于非阻塞模式:
import os, fcntl
fcntl.fcntl(thePipe, fcntl.F_SETFL, os.O_NONBLOCK)
On Windows we have this:在 Windows 上,我们有这个:
ImportError: No module named fcntl
But os.pipe
is there:但是os.pipe
在那里:
>>> os.pipe()
(3, 4)
So, is it possible to do non-blocking read or peek the contents of os.pipe
on Windows?那么,是否可以在 Windows 上进行非阻塞读取或查看os.pipe
的内容?
Answering my own question after digging for some time through StackOverflow.通过 StackOverflow 挖掘一段时间后回答我自己的问题。
UPDATE : Things changes thanks to @HarryJohnston.更新:由于@HarryJohnston,事情发生了变化。
At first the answer was no , it is not possible to do non-blocking read on os.pipe
on Windows.起初的答案是否定的,不可能在 Windows 上对os.pipe
进行非阻塞读取。 From this answer I've got that:从这个答案我得到了:
The term for non-blocking / asynchronous I/O in Windows is 'overlapped' - that's what you should be looking at. Windows 中非阻塞/异步 I/O 的术语是“重叠”——这就是你应该关注的。
os.pipe
on Windows is implemented through CreatePipe
API (see here and ... well, I couldn't find os.pipe
code in Python sources ). Windows 上的os.pipe
是通过CreatePipe
API 实现的(参见这里和...好吧,我在Python 源代码中找不到os.pipe
代码)。 CreatePipe
makes anonymous pipes, and anonymous pipes do not support asynchronous I/O . CreatePipe
制作匿名管道, 匿名管道不支持异步 I/O 。
But then @HarryJohnston commented that SetNamedPipeHandleState doc allows to put anonymous pipe to non-blocking mode.但随后@HarryJohnston评论说SetNamedPipeHandleState文档允许将匿名管道置于非阻塞模式。 I wrote the test and it failed with OSError: [Errno 22] Invalid argument
.我写了这个测试,它失败了OSError: [Errno 22] Invalid argument
。 The error message seemed wrong, so I tried to check what should be return result on non-blocking read operation when data is not available, and after reading MSDN note on named pipe modes I found that it should be ERROR_NO_DATA
that has a int value 232. Adding ctypes.WinError()
call to exception handler revealed the expected [Error 232] The pipe is being closed.
错误消息似乎是错误的,所以我试图检查当数据不可用时非阻塞读取操作的返回结果应该是什么,并且在阅读了关于命名管道模式的 MSDN 注释后,我发现它应该是具有 int 值 232 的ERROR_NO_DATA
. 向异常处理程序添加ctypes.WinError()
调用显示预期的[Error 232] The pipe is being closed.
So, the answer is yes , it is possible to do non-blocking read on os.pipe
on Windows, and here is the proof:所以,答案是肯定的,可以在 Windows 上对os.pipe
进行非阻塞读取,这是证明:
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
This answer is basically @anatolytechtonik's answer but with classes.这个答案基本上是@anatolytechtonik 的答案,但有课程。
import msvcrt
import os
# No idea what is going on here but if it works, it works.
from ctypes import windll, byref, wintypes, GetLastError, WinError, POINTER
from ctypes.wintypes import HANDLE, DWORD, BOOL
# ???
LPDWORD = POINTER(DWORD)
PIPE_NOWAIT = wintypes.DWORD(0x00000001)
ERROR_NO_DATA = 232
class AdvancedFD:
"""
A wrapper for a file descriptor so that we can call:
`<AdvancedFD>.read(number_of_bytes)` and
`<AdvancedFD>.write(data_as_bytes)`
It also makes the `read_fd` non blocking. When reading from a non-blocking
pipe with no data it returns b"".
Methods:
write(data: bytes) -> None
read(number_of_bytes: int) -> bytes
rawfd() -> int
close() -> None
"""
def __init__(self, fd: int):
self.fd = fd
self.closed = False
def __del__(self) -> None:
"""
When this object is garbage collected close the fd
"""
self.close()
def close(self) -> None:
"""
Closes the file descriptor.
Note: it cannot be reopened and might raise an error if it is
being used. You don't have to call this function. It is automatically
called when this object is being garbage collected.
"""
self.closed = True
def write(self, data: bytes) -> None:
"""
Writes a string of bytes to the file descriptor.
Note: Must be bytes.
"""
os.write(self.fd, data)
def read(self, x: int) -> bytes:
"""
Reads `x` bytes from the file descriptor.
Note: `x` must be an int
Returns the bytes. Use `<bytes>.decode()` to convert it to a str
"""
try:
return os.read(self.fd, x)
except OSError as error:
err_code = GetLastError()
# If the error code is `ERROR_NO_DATA`
if err_code == ERROR_NO_DATA:
# Return an empty string of bytes
return b""
else:
# Otherwise raise the error
website = "https://docs.microsoft.com/en-us/windows/win32/" +\
"debug/system-error-codes--0-499-"
raise OSError("An exception occured. Error code: %i Look up" +\
" the error code here: %s" % (err_code, website))
def config_non_blocking(self) -> bool:
"""
Makes the file descriptor non blocking.
Returns `True` if sucessfull, otherwise returns `False`
"""
# Please note that this is kindly plagiarised from:
# https://stackoverflow.com/a/34504971/11106801
SetNamedPipeHandleState = windll.kernel32.SetNamedPipeHandleState
SetNamedPipeHandleState.argtypes = [HANDLE, LPDWORD, LPDWORD, LPDWORD]
SetNamedPipeHandleState.restype = BOOL
handle = msvcrt.get_osfhandle(self.fd)
res = windll.kernel32.SetNamedPipeHandleState(handle,
byref(PIPE_NOWAIT), None,
None)
return not (res == 0)
def rawfd(self) -> int:
"""
Returns the raw fd as an int.
"""
return self.fd
class NonBlockingPipe:
"""
Creates 2 file descriptors and wrapps them in the `AdvancedFD` class
so that we can call:
`<AdvancedFD>.read(number_of_bytes)` and
`<AdvancedFD>.write(data_as_bytes)`
It also makes the `read_fd` non blocking. When reading from a non-blocking
pipe with no data it returns b"".
Methods:
write(data: bytes) -> None
read(number_of_bytes: int) -> bytes
rawfds() -> (int, int)
close() -> None
"""
def __init__(self):
self.read_fd, self.write_fd = self.create_pipes()
self.read_fd.config_non_blocking()
def __del__(self) -> None:
"""
When this object is garbage collected close the fds
"""
self.close()
def close(self) -> None:
"""
Note: it cannot be reopened and might raise an error if it is
being used. You don't have to call this function. It is automatically
called when this object is being garbage collected.
"""
self.read_fd.close()
self.write_fd.close()
def create_pipes(self) -> (AdvancedFD, AdvancedFD):
"""
Creates 2 file descriptors and wrapps them in the `Pipe` class so
that we can call:
`<Pipe>.read(number_of_bytes)` and
`<Pipe>.write(data_as_bytes)`
"""
read_fd, write_fd = os.pipe()
return AdvancedFD(read_fd), AdvancedFD(write_fd)
def write(self, data: bytes) -> None:
"""
Writes a string of bytes to the file descriptor.
Note: Must be bytes.
"""
self.write_fd.write(data)
def read(self, number_of_bytes: int) -> bytes:
"""
Reads `x` bytes from the file descriptor.
Note: `x` must be an int
Returns the bytes. Use `<bytes>.decode()` to convert it to a str
"""
return self.read_fd.read(number_of_bytes)
def rawfds(self) -> (int, int):
"""
Returns the raw file descriptors as ints in the form:
(read_fd, write_fd)
"""
return self.read_fd.rawfd(), self.write_fd.rawfd()
if __name__ == "__main__":
# Create the nonblocking pipe
pipe = NonBlockingPipe()
pipe.write(b"xxx")
print(pipe.read(1024)) # Check if it can still read properly
pipe.write(b"yyy")
print(pipe.read(1024)) # Read all of the data in the pipe
print(pipe.read(1024)) # Check if it is non blocking
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.