Skip to content

Commit 6b6efd9

Browse files
committed
PYTHON-1664 Include type in InvalidDocument error
1 parent 9cca2a7 commit 6b6efd9

File tree

4 files changed

+59
-29
lines changed

4 files changed

+59
-29
lines changed

bson/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -812,7 +812,7 @@ def _name_value_to_bson(name, value, check_keys, opts,
812812
in_fallback_call=True)
813813

814814
raise InvalidDocument(
815-
"cannot convert value of type %s to bson" % type(value))
815+
"cannot encode object: %r, of type: %r" % (value, type(value)))
816816

817817

818818
def _element_to_bson(key, value, check_keys, opts):

bson/_cbsonmodule.c

Lines changed: 42 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -593,37 +593,53 @@ _fix_java(const char* in, char* out) {
593593

594594
static void
595595
_set_cannot_encode(PyObject* value) {
596+
PyObject* type = NULL;
596597
PyObject* InvalidDocument = _error("InvalidDocument");
597-
if (InvalidDocument) {
598-
PyObject* repr = PyObject_Repr(value);
599-
if (repr) {
600-
#if PY_MAJOR_VERSION >= 3
601-
PyObject* errmsg = PyUnicode_FromString("Cannot encode object: ");
602-
#else
603-
PyObject* errmsg = PyString_FromString("Cannot encode object: ");
604-
#endif
605-
if (errmsg) {
598+
if (InvalidDocument == NULL) {
599+
goto error;
600+
}
601+
602+
type = PyObject_Type(value);
603+
if (type == NULL) {
604+
goto error;
605+
}
606606
#if PY_MAJOR_VERSION >= 3
607-
PyObject* error = PyUnicode_Concat(errmsg, repr);
608-
if (error) {
609-
PyErr_SetObject(InvalidDocument, error);
610-
Py_DECREF(error);
611-
}
612-
Py_DECREF(errmsg);
613-
Py_DECREF(repr);
607+
PyErr_Format(InvalidDocument, "cannot encode object: %R, of type: %R",
608+
value, type);
614609
#else
615-
PyString_ConcatAndDel(&errmsg, repr);
616-
if (errmsg) {
617-
PyErr_SetObject(InvalidDocument, errmsg);
618-
Py_DECREF(errmsg);
619-
}
620-
#endif
621-
} else {
622-
Py_DECREF(repr);
623-
}
610+
else {
611+
PyObject* value_repr = NULL;
612+
PyObject* type_repr = NULL;
613+
char* value_str = NULL;
614+
char* type_str = NULL;
615+
616+
value_repr = PyObject_Repr(value);
617+
if (value_repr == NULL) {
618+
goto py2error;
624619
}
625-
Py_DECREF(InvalidDocument);
620+
value_str = PyString_AsString(value_repr);
621+
if (value_str == NULL) {
622+
goto py2error;
623+
}
624+
type_repr = PyObject_Repr(type);
625+
if (type_repr == NULL) {
626+
goto py2error;
627+
}
628+
type_str = PyString_AsString(type_repr);
629+
if (type_str == NULL) {
630+
goto py2error;
631+
}
632+
633+
PyErr_Format(InvalidDocument, "cannot encode object: %s, of type: %s",
634+
value_str, type_str);
635+
py2error:
636+
Py_XDECREF(type_repr);
637+
Py_XDECREF(value_repr);
626638
}
639+
#endif
640+
error:
641+
Py_XDECREF(type);
642+
Py_XDECREF(InvalidDocument);
627643
}
628644

629645
/*

doc/examples/custom_type.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ to save an instance of ``Decimal`` with PyMongo, results in an
3939
>>> db.test.insert_one({'num': num})
4040
Traceback (most recent call last):
4141
...
42-
bson.errors.InvalidDocument: Cannot encode object: <__main__.Decimal object at ...>
42+
bson.errors.InvalidDocument: cannot encode object: Decimal('45.321'), of type: <class 'decimal.Decimal'>
4343

4444

4545
.. _custom-type-type-codec:
@@ -179,7 +179,7 @@ codec for it, we get an error:
179179
>>> collection.insert_one({'num': DecimalInt("45.321")})
180180
Traceback (most recent call last):
181181
...
182-
bson.errors.InvalidDocument: Cannot encode object: Decimal('45.321')
182+
bson.errors.InvalidDocument: cannot encode object: Decimal('45.321'), of type: <class 'decimal.Decimal'>
183183

184184
In order to proceed further, we must define a type codec for ``DecimalInt``.
185185
This is trivial to do since the same transformation as the one used for

test/test_bson.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -923,6 +923,20 @@ def target(i):
923923
for t in threads:
924924
self.assertIsNone(t.exc)
925925

926+
def test_raise_invalid_document(self):
927+
class Wrapper(object):
928+
def __init__(self, val):
929+
self.val = val
930+
931+
def __repr__(self):
932+
return repr(self.val)
933+
934+
self.assertEqual('1', repr(Wrapper(1)))
935+
with self.assertRaisesRegex(
936+
InvalidDocument,
937+
"cannot encode object: 1, of type: " + repr(Wrapper)):
938+
BSON.encode({'t': Wrapper(1)})
939+
926940

927941
class TestCodecOptions(unittest.TestCase):
928942
def test_document_class(self):

0 commit comments

Comments
 (0)