def _check_command_response(response, max_wire_version, allowable_errors=None, parse_write_concern_error=False): """Check the response to a command for errors. """ if "ok" not in response: # Server didn't recognize our message as a command. raise OperationFailure(response.get("$err"), response.get("code"), response, max_wire_version) if parse_write_concern_error and 'writeConcernError' in response: _error = response["writeConcernError"] _labels = response.get("errorLabels") if _labels: _error.update({'errorLabels': _labels}) _raise_write_concern_error(_error) if response["ok"]: return details = response # Mongos returns the error details in a 'raw' object # for some errors. if "raw" in response: for shard in response["raw"].values(): # Grab the first non-empty raw error from a shard. if shard.get("errmsg") and not shard.get("ok"): details = shard break errmsg = details["errmsg"] code = details.get("code") # For allowable errors, only check for error messages when the code is not # included. if allowable_errors: if code is not None: if code in allowable_errors: return elif errmsg in allowable_errors: return # Server is "not primary" or "recovering" if code is not None: if code in _NOT_MASTER_CODES: raise NotPrimaryError(errmsg, response) elif "not master" in errmsg or "node is recovering" in errmsg: raise NotPrimaryError(errmsg, response) # Other errors # findAndModify with upsert can raise duplicate key error if code in (11000, 11001, 12582): raise DuplicateKeyError(errmsg, code, response, max_wire_version) elif code == 50: raise ExecutionTimeout(errmsg, code, response, max_wire_version) elif code == 43: raise CursorNotFound(errmsg, code, response, max_wire_version) raise OperationFailure(errmsg, code, response, max_wire_version)
def test_not_primary_error(self): exc = NotPrimaryError("not primary test", {"errmsg": "error"}) self.assertIn("full error", str(exc)) try: raise exc except NotPrimaryError: self.assertIn("full error", traceback.format_exc())
def _check_gle_response(result, max_wire_version): """Return getlasterror response as a dict, or raise OperationFailure.""" # Did getlasterror itself fail? _check_command_response(result, max_wire_version) if result.get("wtimeout", False): # MongoDB versions before 1.8.0 return the error message in an "errmsg" # field. If "errmsg" exists "err" will also exist set to None, so we # have to check for "errmsg" first. raise WTimeoutError(result.get("errmsg", result.get("err")), result.get("code"), result) error_msg = result.get("err", "") if error_msg is None: return result if error_msg.startswith("not master"): raise NotPrimaryError(error_msg, result) details = result # mongos returns the error code in an error object for some errors. if "errObjects" in result: for errobj in result["errObjects"]: if errobj.get("err") == error_msg: details = errobj break code = details.get("code") if code in (11000, 11001, 12582): raise DuplicateKeyError(details["err"], code, result) raise OperationFailure(details["err"], code, result)
def handle_getlasterror(self, address, error_msg): """Clear our pool for a server, mark it Unknown, and check it soon.""" error = NotPrimaryError(error_msg, { 'code': 10107, 'errmsg': error_msg }) with self._lock: server = self._servers.get(address) if server: self._process_change(ServerDescription(address, error=error), True) server.request_check()
def raw_response(self, cursor_id=None, user_fields=None): """Check the response header from the database, without decoding BSON. Check the response for errors and unpack. Can raise CursorNotFound, NotPrimaryError, ExecutionTimeout, or OperationFailure. :Parameters: - `cursor_id` (optional): cursor_id we sent to get this response - used for raising an informative exception when we get cursor id not valid at server response. """ if self.flags & 1: # Shouldn't get this response if we aren't doing a getMore if cursor_id is None: raise ProtocolError("No cursor id for getMore operation") # Fake a getMore command response. OP_GET_MORE provides no # document. msg = "Cursor not found, cursor id: %d" % (cursor_id, ) errobj = {"ok": 0, "errmsg": msg, "code": 43} raise CursorNotFound(msg, 43, errobj) elif self.flags & 2: error_object: dict = bson.BSON(self.documents).decode() # Fake the ok field if it doesn't exist. error_object.setdefault("ok", 0) if error_object["$err"].startswith(HelloCompat.LEGACY_ERROR): raise NotPrimaryError(error_object["$err"], error_object) elif error_object.get("code") == 50: default_msg = "operation exceeded time limit" raise ExecutionTimeout(error_object.get("$err", default_msg), error_object.get("code"), error_object) raise OperationFailure( "database error: %s" % error_object.get("$err"), error_object.get("code"), error_object, ) if self.documents: return [self.documents] return []
def test_pickle_NotPrimaryError(self): exc = NotPrimaryError("not primary test", {"errmsg": "error"}) self.assertPyMongoErrorEqual(exc, pickle.loads(pickle.dumps(exc)))
def test_unicode_strs_not_master_error(self): exc = NotPrimaryError('unicode \U0001f40d', {"errmsg": 'unicode \U0001f40d'}) self._test_unicode_strs(exc)