@@ -39,7 +39,8 @@ def __init__(
3939 fileHandle : Union [ctypes .wintypes .HANDLE ],
4040 onReceive : Callable [[bytes ], None ],
4141 writeFileHandle : Optional [ctypes .wintypes .HANDLE ] = None ,
42- onReceiveSize : int = 1
42+ onReceiveSize : int = 1 ,
43+ onReadError : Optional [Callable [[int ], bool ]] = None
4344 ):
4445 """Constructor.
4546 @param fileHandle: A handle to an open I/O device opened for overlapped I/O.
@@ -48,11 +49,15 @@ def __init__(
4849 @param onReceive: A callable taking the received data as its only argument.
4950 @param writeFileHandle: A handle to an open output device opened for overlapped I/O.
5051 @param onReceiveSize: The size (in bytes) of the data with which to call C{onReceive}.
52+ @param onReadError: If provided, a callback that takes the error code for a failed read
53+ and returns True if the I/O loop should exit cleanly or False if an
54+ exception should be thrown
5155 """
5256 self ._file = fileHandle
5357 self ._onReceive = onReceive
5458 self ._writeFile = writeFileHandle if writeFileHandle is not None else fileHandle
5559 self ._readSize = onReceiveSize
60+ self ._onReadError = onReadError
5661 self ._readBuf = ctypes .create_string_buffer (onReceiveSize )
5762 self ._readOl = OVERLAPPED ()
5863 self ._recvEvt = winKernel .createEvent ()
@@ -141,7 +146,11 @@ def _ioDone(self, error, numberOfBytes: int, overlapped):
141146 self ._ioDone = None
142147 return
143148 elif error != 0 :
144- raise ctypes .WinError (error )
149+ if not self ._onReadError or not self ._onReadError (error ):
150+ raise ctypes .WinError (error )
151+ else :
152+ self ._ioDone = None
153+ return
145154 self ._notifyReceive (self ._readBuf [:numberOfBytes ])
146155 winKernel .kernel32 .SetEvent (self ._recvEvt )
147156 self ._asyncRead ()
@@ -244,13 +253,17 @@ class Bulk(IoBase):
244253 def __init__ (
245254 self , path : str , epIn : int , epOut : int ,
246255 onReceive : Callable [[bytes ], None ],
247- onReceiveSize : int = 1
256+ onReceiveSize : int = 1 ,
257+ onReadError : Optional [Callable [[int ], bool ]] = None
248258 ):
249259 """Constructor.
250260 @param path: The device path.
251261 @param epIn: The endpoint to read data from.
252262 @param epOut: The endpoint to write data to.
253263 @param onReceive: A callable taking a received input report as its only argument.
264+ @param onReadError: An optional callable that handles read errors.
265+ It takes an error code and returns True if the error has been handled,
266+ allowing the read loop to exit cleanly, or False if an exception should be thrown.
254267 """
255268 if _isDebug ():
256269 log .debug ("Opening device %s" % path )
@@ -269,7 +282,8 @@ def __init__(
269282 log .debug ("Open write handle failed: %s" % ctypes .WinError ())
270283 raise ctypes .WinError ()
271284 super (Bulk , self ).__init__ (readHandle , onReceive ,
272- writeFileHandle = writeHandle , onReceiveSize = onReceiveSize )
285+ writeFileHandle = writeHandle , onReceiveSize = onReceiveSize ,
286+ onReadError = onReadError )
273287
274288 def close (self ):
275289 super (Bulk , self ).close ()
0 commit comments