Skip to content

Commit 9bbeea4

Browse files
author
Mike Dirolf
committed
raise InvalidDocument when trying to encode a document with non-string keys
1 parent 00b375f commit 9bbeea4

File tree

3 files changed

+41
-34
lines changed

3 files changed

+41
-34
lines changed

pymongo/_cbsonmodule.c

Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -846,7 +846,7 @@ static int write_pair(bson_buffer* buffer, const char* name, Py_ssize_t name_len
846846
int type_byte;
847847

848848
/* Don't write any _id elements unless we're explicitly told to -
849-
* _id has to be written first so we write do so, but don't bother
849+
* _id has to be written first so we do so, but don't bother
850850
* deleting it from the dictionary being written. */
851851
if (!allow_id && strcmp(name, "_id") == 0) {
852852
return 1;
@@ -868,6 +868,37 @@ static int write_pair(bson_buffer* buffer, const char* name, Py_ssize_t name_len
868868
return 1;
869869
}
870870

871+
static int decode_and_write_pair(bson_buffer* buffer, PyObject* key,
872+
PyObject* value, unsigned char check_keys) {
873+
PyObject* encoded;
874+
if (PyUnicode_Check(key)) {
875+
encoded = PyUnicode_AsUTF8String(key);
876+
if (!encoded) {
877+
return 0;
878+
}
879+
} else if (PyString_Check(key)) {
880+
encoded = key;
881+
Py_INCREF(encoded);
882+
} else {
883+
PyObject* errmsg = PyString_FromString("documents must have only string keys, key was ");
884+
PyObject* repr = PyObject_Repr(key);
885+
PyString_ConcatAndDel(&errmsg, repr);
886+
PyErr_SetString(InvalidDocument, PyString_AsString(errmsg));
887+
Py_DECREF(errmsg);
888+
return 0;
889+
}
890+
891+
/* Don't allow writing _id here - it was already written. */
892+
if (!write_pair(buffer, PyString_AsString(encoded),
893+
PyString_Size(encoded), value, check_keys, 0)) {
894+
Py_DECREF(encoded);
895+
return 0;
896+
}
897+
898+
Py_DECREF(encoded);
899+
return 1;
900+
}
901+
871902
static int write_son(bson_buffer* buffer, PyObject* dict, int start_position,
872903
int length_location, unsigned char check_keys) {
873904
PyObject* keys = PyObject_CallMethod(dict, "keys", NULL);
@@ -880,33 +911,16 @@ static int write_son(bson_buffer* buffer, PyObject* dict, int start_position,
880911
for(i = 0; i < items; i++) {
881912
PyObject* key;
882913
PyObject* value;
883-
PyObject* encoded;
884914

885915
key = PyList_GetItem(keys, i);
886916
if (!key) {
887917
Py_DECREF(keys);
888918
return 0;
889919
}
890920
value = PyDict_GetItem(dict, key);
891-
if (!value) {
892-
Py_DECREF(keys);
893-
return 0;
894-
}
895-
if (PyUnicode_CheckExact(key)) {
896-
encoded = PyUnicode_AsUTF8String(key);
897-
if (!encoded) {
898-
Py_DECREF(keys);
899-
return 0;
900-
}
901-
} else {
902-
encoded = key;
903-
Py_INCREF(encoded);
904-
}
905-
/* Don't allow writing _id here - it was written above. */
906-
if (!write_pair(buffer, PyString_AsString(encoded),
907-
PyString_Size(encoded), value, check_keys, 0)) {
921+
if (!value ||
922+
!decode_and_write_pair(buffer, key, value, check_keys)) {
908923
Py_DECREF(keys);
909-
Py_DECREF(encoded);
910924
return 0;
911925
}
912926
}
@@ -950,20 +964,7 @@ static int write_dict(bson_buffer* buffer, PyObject* dict, unsigned char check_k
950964
Py_ssize_t pos = 0;
951965

952966
while (PyDict_Next(dict, &pos, &key, &value)) {
953-
PyObject* encoded;
954-
if (PyUnicode_CheckExact(key)) {
955-
encoded = PyUnicode_AsUTF8String(key);
956-
if (!encoded) {
957-
return 0;
958-
}
959-
} else {
960-
encoded = key;
961-
Py_INCREF(encoded);
962-
}
963-
/* Don't allow writing _id here - it was written above. */
964-
if (!write_pair(buffer, PyString_AsString(encoded),
965-
PyString_Size(encoded), value, check_keys, 0)) {
966-
Py_DECREF(encoded);
967+
if (!decode_and_write_pair(buffer, key, value, check_keys)) {
967968
return 0;
968969
}
969970
}

pymongo/bson.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,9 @@ def _bson_to_dict(data):
373373

374374

375375
def _element_to_bson(key, value, check_keys):
376+
if not isinstance(key, (str, unicode)):
377+
raise InvalidDocument("documents must have only string keys, key was %r" % key)
378+
376379
if check_keys:
377380
if key.startswith("$"):
378381
raise InvalidName("key %r must not start with '$'" % key)

test/test_bson.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,9 @@ def test_unicode_regex(self):
238238
regex = re.compile(u'revisi\xf3n')
239239
BSON.from_dict({"regex": regex}).to_dict()
240240

241+
def test_non_string_keys(self):
242+
self.assertRaises(InvalidDocument, BSON.from_dict, {8.9: "test"})
243+
241244
# TODO this test doesn't pass w/ C extension
242245
#
243246
# timegm doesn't handle years < 1900 (negative), at least on OS X

0 commit comments

Comments
 (0)