Skip to content

Commit 1457c5d

Browse files
emmatypingmsullivan
authored andcommitted
Fixed overlapped I/O when more data needs to be read (#6272)
For some reason the tests didn't capture this, but ReadFile, not GetOverlappedResult returns _winapi.ERROR_MORE_DATA, which means that we would not do another read. This handles that and correctly peeks then reads the remaining data.
1 parent 4be41e4 commit 1457c5d

File tree

2 files changed

+16
-12
lines changed

2 files changed

+16
-12
lines changed

mypy/ipc.py

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -48,25 +48,24 @@ class IPCBase:
4848
connection = None # type: _IPCHandle
4949

5050
def __init__(self, name: str, timeout: Optional[float]) -> None:
51-
self.READ_SIZE = 100000
5251
self.name = name
5352
self.timeout = timeout
5453

55-
def read(self) -> bytes:
54+
def read(self, size: int = 100000) -> bytes:
5655
"""Read bytes from an IPC connection until its empty."""
5756
bdata = bytearray()
5857
if sys.platform == 'win32':
5958
while True:
60-
ov, err = _winapi.ReadFile(self.connection, self.READ_SIZE, overlapped=True)
59+
ov, err = _winapi.ReadFile(self.connection, size, overlapped=True)
6160
# TODO: remove once typeshed supports Literal types
6261
assert isinstance(ov, _winapi.Overlapped)
6362
assert isinstance(err, int)
6463
try:
65-
if err != 0:
66-
assert err == _winapi.ERROR_IO_PENDING
64+
if err == _winapi.ERROR_IO_PENDING:
6765
timeout = int(self.timeout * 1000) if self.timeout else _winapi.INFINITE
6866
res = _winapi.WaitForSingleObject(ov.event, timeout)
69-
assert res == _winapi.WAIT_OBJECT_0
67+
if res != _winapi.WAIT_OBJECT_0:
68+
raise IPCException("Bad result from I/O wait: {}".format(res))
7069
except BaseException:
7170
ov.cancel()
7271
raise
@@ -77,11 +76,14 @@ def read(self) -> bytes:
7776
if err == 0:
7877
# we are done!
7978
break
79+
elif err == _winapi.ERROR_MORE_DATA:
80+
# read again
81+
continue
8082
elif err == _winapi.ERROR_OPERATION_ABORTED:
8183
raise IPCException("ReadFile operation aborted.")
8284
else:
8385
while True:
84-
more = self.connection.recv(self.READ_SIZE)
86+
more = self.connection.recv(size)
8587
if not more:
8688
break
8789
bdata.extend(more)
@@ -96,16 +98,18 @@ def write(self, data: bytes) -> None:
9698
assert isinstance(ov, _winapi.Overlapped)
9799
assert isinstance(err, int)
98100
try:
99-
if err != 0:
100-
assert err == _winapi.ERROR_IO_PENDING
101+
if err == _winapi.ERROR_IO_PENDING:
101102
timeout = int(self.timeout * 1000) if self.timeout else _winapi.INFINITE
102103
res = _winapi.WaitForSingleObject(ov.event, timeout)
103-
assert res == _winapi.WAIT_OBJECT_0
104+
if res != _winapi.WAIT_OBJECT_0:
105+
raise IPCException("Bad result from I/O wait: {}".format(res))
106+
elif err != 0:
107+
raise IPCException("Failed writing to pipe with error: {}".format(err))
104108
except BaseException:
105109
ov.cancel()
106110
raise
107111
bytes_written, err = ov.GetOverlappedResult(True)
108-
assert err == 0
112+
assert err == 0, err
109113
assert bytes_written == len(data)
110114
except WindowsError as e:
111115
raise IPCException("Failed to write with error: {}".format(e.winerror))

mypy/test/testipc.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def server(msg: str, q: 'Queue[str]') -> None:
2424
class IPCTests(TestCase):
2525
def test_transaction_large(self) -> None:
2626
queue = Queue() # type: Queue[str]
27-
msg = 't' * 100001 # longer than the max read size of 100_000
27+
msg = 't' * 200000 # longer than the max read size of 100_000
2828
p = Process(target=server, args=(msg, queue), daemon=True)
2929
p.start()
3030
connection_name = queue.get()

0 commit comments

Comments
 (0)