@@ -52,6 +52,14 @@ def _remove_error_label(self, label):
52
52
"""Remove the given label from this error."""
53
53
self ._error_labels .discard (label )
54
54
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
+
55
63
56
64
class ProtocolError (PyMongoError ):
57
65
"""Raised for failures related to the wire protocol."""
@@ -69,6 +77,10 @@ class WaitQueueTimeoutError(ConnectionFailure):
69
77
.. versionadded:: 4.2
70
78
"""
71
79
80
+ @property
81
+ def timeout (self ) -> bool :
82
+ return True
83
+
72
84
73
85
class AutoReconnect (ConnectionFailure ):
74
86
"""Raised when a connection to the database is lost and an attempt to
@@ -106,6 +118,10 @@ class NetworkTimeout(AutoReconnect):
106
118
Subclass of :exc:`~pymongo.errors.AutoReconnect`.
107
119
"""
108
120
121
+ @property
122
+ def timeout (self ) -> bool :
123
+ return True
124
+
109
125
110
126
def _format_detailed_error (message , details ):
111
127
if details is not None :
@@ -149,6 +165,10 @@ class ServerSelectionTimeoutError(AutoReconnect):
149
165
Preference that the replica set cannot satisfy.
150
166
"""
151
167
168
+ @property
169
+ def timeout (self ) -> bool :
170
+ return True
171
+
152
172
153
173
class ConfigurationError (PyMongoError ):
154
174
"""Raised when something is incorrectly configured."""
@@ -199,6 +219,10 @@ def details(self) -> Optional[Mapping[str, Any]]:
199
219
"""
200
220
return self .__details
201
221
222
+ @property
223
+ def timeout (self ) -> bool :
224
+ return self .__code in (50 ,)
225
+
202
226
203
227
class CursorNotFound (OperationFailure ):
204
228
"""Raised while iterating query results if the cursor is
@@ -217,6 +241,10 @@ class ExecutionTimeout(OperationFailure):
217
241
.. versionadded:: 2.7
218
242
"""
219
243
244
+ @property
245
+ def timeout (self ) -> bool :
246
+ return True
247
+
220
248
221
249
class WriteConcernError (OperationFailure ):
222
250
"""Base exception type for errors raised due to write concern.
@@ -242,11 +270,20 @@ class WTimeoutError(WriteConcernError):
242
270
.. versionadded:: 2.7
243
271
"""
244
272
273
+ @property
274
+ def timeout (self ) -> bool :
275
+ return True
276
+
245
277
246
278
class DuplicateKeyError (WriteError ):
247
279
"""Raised when an insert or update fails due to a duplicate key error."""
248
280
249
281
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
+
250
287
class BulkWriteError (OperationFailure ):
251
288
"""Exception class for bulk write errors.
252
289
@@ -261,6 +298,19 @@ def __init__(self, results: Mapping[str, Any]) -> None:
261
298
def __reduce__ (self ) -> Tuple [Any , Any ]:
262
299
return self .__class__ , (self .details ,)
263
300
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
+
264
314
265
315
class InvalidOperation (PyMongoError ):
266
316
"""Raised when a client attempts to perform an invalid operation."""
@@ -302,6 +352,12 @@ def cause(self) -> Exception:
302
352
"""The exception that caused this encryption or decryption error."""
303
353
return self .__cause
304
354
355
+ @property
356
+ def timeout (self ) -> bool :
357
+ if isinstance (self .__cause , PyMongoError ):
358
+ return self .__cause .timeout
359
+ return False
360
+
305
361
306
362
class _OperationCancelled (AutoReconnect ):
307
363
"""Internal error raised when a socket operation is cancelled."""
0 commit comments