diff --git a/bson/__init__.py b/bson/__init__.py index 07999c085..605ea5833 100644 --- a/bson/__init__.py +++ b/bson/__init__.py @@ -93,7 +93,15 @@ BSONMAX = b("\x7F") # Max key _CODEC_OPTIONS_TYPE_ERROR = TypeError( "codec_options must be an instance of bson.codec_options.CodecOptions") -def _get_int(data, position, as_class=None, + +def _raise_unknown_type(element_type, element_name): + """Unknown type helper.""" + raise InvalidBSON("Detected unknown BSON type %r for fieldname %r. Are " + "you using the latest driver version?" % ( + element_type, element_name)) + + +def _get_int(data, position, name, as_class=None, tz_aware=False, uuid_subtype=OLD_UUID_SUBTYPE, compile_re=True, unsigned=False): format = unsigned and "I" or "i" @@ -137,13 +145,15 @@ def _make_c_string(string, check_null=False): "UTF-8: %r" % string) -def _get_number(data, position, as_class, tz_aware, uuid_subtype, compile_re): +def _get_number( + data, position, name, as_class, tz_aware, uuid_subtype, compile_re): num = struct.unpack("= 3 + PyObject* type_obj = PyBytes_FromFormat("%c", type); +#else + PyObject* type_obj = PyString_FromFormat("%c", type); +#endif + if (type_obj) { + PyObject* type_repr = PyObject_Repr(type_obj); + Py_DECREF(type_obj); + if (type_repr) { + PyObject* errmsg = NULL; +#if PY_MAJOR_VERSION >= 3 + PyObject* left = PyUnicode_FromString( + "Detected unknown BSON type "); + if (left) { + PyObject* lmsg = PyUnicode_Concat(left, type_repr); + Py_DECREF(left); + if (lmsg) { + errmsg = PyUnicode_FromFormat( + "%U for fieldname '%U'. Are you using the " + "latest driver version?", lmsg, name); + Py_DECREF(lmsg); + } + } +#else + PyObject* name_repr = PyObject_Repr(name); + if (name_repr) { + errmsg = PyString_FromFormat( + "Detected unknown BSON type %s for fieldname %s." + " Are you using the latest driver version?", + PyString_AS_STRING(type_repr), + PyString_AS_STRING(name_repr)); + Py_DECREF(name_repr); + } +#endif + Py_DECREF(type_repr); + if (errmsg) { + PyErr_SetObject(InvalidBSON, errmsg); + Py_DECREF(errmsg); + } + } + } + Py_DECREF(InvalidBSON); } goto invalid; } @@ -2173,7 +2212,7 @@ static PyObject* _elements_to_dict(PyObject* self, const char* string, return NULL; } position += (unsigned)name_length + 1; - value = get_value(self, string, &position, type, + value = get_value(self, name, string, &position, type, max - position, as_class, tz_aware, uuid_subtype, compile_re); if (!value) { diff --git a/test/test_bson.py b/test/test_bson.py index 1985b5fd6..d87faf5ba 100644 --- a/test/test_bson.py +++ b/test/test_bson.py @@ -358,6 +358,24 @@ class TestBSON(unittest.TestCase): qcheck.check_unittest(self, encode_then_decode_backport_precedence, qcheck.gen_mongo_dict(3)) + def test_unknown_type(self): + # Repr value differs with major python version + part = "type %r for fieldname %r" % (b('\x13'), u"foo") + docs = [ + b('\x0e\x00\x00\x00\x13foo\x00\x01\x00\x00\x00\x00'), + b('\x16\x00\x00\x00\x04foo\x00\x0c\x00\x00\x00\x130' + '\x00\x01\x00\x00\x00\x00\x00'), + b(' \x00\x00\x00\x04bar\x00\x16\x00\x00\x00\x030\x00\x0e\x00\x00' + '\x00\x13foo\x00\x01\x00\x00\x00\x00\x00\x00')] + for bs in docs: + try: + bson.BSON(bs).decode() + except Exception, exc: + self.assertTrue(isinstance(exc, InvalidBSON)) + self.assertTrue(part in str(exc)) + else: + self.fail("Failed to raise an exception.") + def test_dbpointer(self): # *Note* - DBPointer and DBRef are *not* the same thing. DBPointer # is a deprecated BSON type. DBRef is a convention that does not