Skip to content

Commit e4d3392

Browse files
Luke Lovettbehackett
authored andcommitted
PYTHON-472 - Add a RawBSONDocument class that decodes its comprising bytes only on-demand.
This provides an API for inserting and returning raw BSON.
1 parent 504d4b2 commit e4d3392

File tree

12 files changed

+875
-356
lines changed

12 files changed

+875
-356
lines changed

bson/__init__.py

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@
3131
JAVA_LEGACY, CSHARP_LEGACY,
3232
UUIDLegacy)
3333
from bson.code import Code
34-
from bson.codec_options import CodecOptions, DEFAULT_CODEC_OPTIONS
34+
from bson.codec_options import (
35+
CodecOptions, DEFAULT_CODEC_OPTIONS, _raw_document_class)
3536
from bson.dbref import DBRef
3637
from bson.errors import (InvalidBSON,
3738
InvalidDocument,
@@ -132,6 +133,10 @@ def _get_object(data, position, obj_end, opts):
132133
raise InvalidBSON("bad eoo")
133134
if end >= obj_end:
134135
raise InvalidBSON("invalid object length")
136+
if _raw_document_class(opts.document_class):
137+
return (opts.document_class(data[position:end + 1], opts),
138+
position + obj_size)
139+
135140
obj = _elements_to_dict(data, position + 4, end, opts)
136141

137142
position += obj_size
@@ -147,6 +152,7 @@ def _get_array(data, position, obj_end, opts):
147152
end = position + size - 1
148153
if data[end:end + 1] != b"\x00":
149154
raise InvalidBSON("bad eoo")
155+
150156
position += 4
151157
end -= 1
152158
result = []
@@ -304,14 +310,21 @@ def _element_to_dict(data, position, obj_end, opts):
304310
value, position = _ELEMENT_GETTER[element_type](data,
305311
position, obj_end, opts)
306312
return element_name, value, position
313+
if _USE_C:
314+
_element_to_dict = _cbson._element_to_dict
307315

308316

309-
def _elements_to_dict(data, position, obj_end, opts):
310-
"""Decode a BSON document."""
311-
result = opts.document_class()
317+
def _iterate_elements(data, position, obj_end, opts):
312318
end = obj_end - 1
313319
while position < end:
314320
(key, value, position) = _element_to_dict(data, position, obj_end, opts)
321+
yield key, value
322+
323+
324+
def _elements_to_dict(data, position, obj_end, opts):
325+
"""Decode a BSON document."""
326+
result = opts.document_class()
327+
for key, value in _iterate_elements(data, position, obj_end, opts):
315328
result[key] = value
316329
return result
317330

@@ -327,6 +340,8 @@ def _bson_to_dict(data, opts):
327340
if data[obj_size - 1:obj_size] != b"\x00":
328341
raise InvalidBSON("bad eoo")
329342
try:
343+
if _raw_document_class(opts.document_class):
344+
return opts.document_class(data, opts)
330345
return _elements_to_dict(data, 4, obj_size - 1, opts)
331346
except InvalidBSON:
332347
raise
@@ -429,6 +444,8 @@ def _encode_bytes(name, value, dummy0, dummy1):
429444

430445
def _encode_mapping(name, value, check_keys, opts):
431446
"""Encode a mapping type."""
447+
if _raw_document_class(value):
448+
return b'\x03' + name + value.raw
432449
data = b"".join([_element_to_bson(key, val, check_keys, opts)
433450
for key, val in iteritems(value)])
434451
return b"\x03" + name + _PACK_INT(len(data) + 5) + data + b"\x00"
@@ -694,6 +711,8 @@ def _element_to_bson(key, value, check_keys, opts):
694711

695712
def _dict_to_bson(doc, check_keys, opts, top_level=True):
696713
"""Encode a document to BSON."""
714+
if _raw_document_class(doc):
715+
return doc.raw
697716
try:
698717
elements = []
699718
if top_level and "_id" in doc:
@@ -751,6 +770,7 @@ def decode_all(data, codec_options=DEFAULT_CODEC_OPTIONS):
751770
docs = []
752771
position = 0
753772
end = len(data) - 1
773+
use_raw = _raw_document_class(codec_options.document_class)
754774
try:
755775
while position < end:
756776
obj_size = _UNPACK_INT(data[position:position + 4])[0]
@@ -759,10 +779,15 @@ def decode_all(data, codec_options=DEFAULT_CODEC_OPTIONS):
759779
obj_end = position + obj_size - 1
760780
if data[obj_end:position + obj_size] != b"\x00":
761781
raise InvalidBSON("bad eoo")
762-
docs.append(_elements_to_dict(data,
763-
position + 4,
764-
obj_end,
765-
codec_options))
782+
if use_raw:
783+
docs.append(
784+
codec_options.document_class(
785+
data[position:obj_end + 1], codec_options))
786+
else:
787+
docs.append(_elements_to_dict(data,
788+
position + 4,
789+
obj_end,
790+
codec_options))
766791
position += obj_size
767792
return docs
768793
except InvalidBSON:

0 commit comments

Comments
 (0)