@@ -52,6 +52,14 @@ def _remove_error_label(self, label):
5252 """Remove the given label from this error."""
5353 self ._error_labels .discard (label )
5454
55+ @property
56+ def timeout (self ) -> bool :
57+ """True if this error was caused by a timeout.
58+
59+ .. versionadded:: 4.2
60+ """
61+ return False
62+
5563
5664class ProtocolError (PyMongoError ):
5765 """Raised for failures related to the wire protocol."""
@@ -69,6 +77,10 @@ class WaitQueueTimeoutError(ConnectionFailure):
6977 .. versionadded:: 4.2
7078 """
7179
80+ @property
81+ def timeout (self ) -> bool :
82+ return True
83+
7284
7385class AutoReconnect (ConnectionFailure ):
7486 """Raised when a connection to the database is lost and an attempt to
@@ -106,6 +118,10 @@ class NetworkTimeout(AutoReconnect):
106118 Subclass of :exc:`~pymongo.errors.AutoReconnect`.
107119 """
108120
121+ @property
122+ def timeout (self ) -> bool :
123+ return True
124+
109125
110126def _format_detailed_error (message , details ):
111127 if details is not None :
@@ -149,6 +165,10 @@ class ServerSelectionTimeoutError(AutoReconnect):
149165 Preference that the replica set cannot satisfy.
150166 """
151167
168+ @property
169+ def timeout (self ) -> bool :
170+ return True
171+
152172
153173class ConfigurationError (PyMongoError ):
154174 """Raised when something is incorrectly configured."""
@@ -199,6 +219,10 @@ def details(self) -> Optional[Mapping[str, Any]]:
199219 """
200220 return self .__details
201221
222+ @property
223+ def timeout (self ) -> bool :
224+ return self .__code in (50 ,)
225+
202226
203227class CursorNotFound (OperationFailure ):
204228 """Raised while iterating query results if the cursor is
@@ -217,6 +241,10 @@ class ExecutionTimeout(OperationFailure):
217241 .. versionadded:: 2.7
218242 """
219243
244+ @property
245+ def timeout (self ) -> bool :
246+ return True
247+
220248
221249class WriteConcernError (OperationFailure ):
222250 """Base exception type for errors raised due to write concern.
@@ -242,11 +270,20 @@ class WTimeoutError(WriteConcernError):
242270 .. versionadded:: 2.7
243271 """
244272
273+ @property
274+ def timeout (self ) -> bool :
275+ return True
276+
245277
246278class DuplicateKeyError (WriteError ):
247279 """Raised when an insert or update fails due to a duplicate key error."""
248280
249281
282+ def _wtimeout_error (error : Any ) -> bool :
283+ """Return True if this writeConcernError doc is a caused by a timeout."""
284+ return error .get ("code" ) == 50 or ("errInfo" in error and error ["errInfo" ].get ("wtimeout" ))
285+
286+
250287class BulkWriteError (OperationFailure ):
251288 """Exception class for bulk write errors.
252289
@@ -261,6 +298,19 @@ def __init__(self, results: Mapping[str, Any]) -> None:
261298 def __reduce__ (self ) -> Tuple [Any , Any ]:
262299 return self .__class__ , (self .details ,)
263300
301+ @property
302+ def timeout (self ) -> bool :
303+ # Check the last writeConcernError and last writeError to determine if this
304+ # BulkWriteError was caused by a timeout.
305+ wces = self .details .get ("writeConcernErrors" , [])
306+ if wces and _wtimeout_error (wces [- 1 ]):
307+ return True
308+
309+ werrs = self .details .get ("writeErrors" , [])
310+ if werrs and werrs [- 1 ].get ("code" ) == 50 :
311+ return True
312+ return False
313+
264314
265315class InvalidOperation (PyMongoError ):
266316 """Raised when a client attempts to perform an invalid operation."""
@@ -302,6 +352,12 @@ def cause(self) -> Exception:
302352 """The exception that caused this encryption or decryption error."""
303353 return self .__cause
304354
355+ @property
356+ def timeout (self ) -> bool :
357+ if isinstance (self .__cause , PyMongoError ):
358+ return self .__cause .timeout
359+ return False
360+
305361
306362class _OperationCancelled (AutoReconnect ):
307363 """Internal error raised when a socket operation is cancelled."""
0 commit comments