PYTHON-1648 Binary buffer protocol support

This commit is contained in:
Bernie Hackett 2019-07-18 08:41:40 -07:00
parent 77913c7d36
commit e92e2b0efb
3 changed files with 32 additions and 5 deletions

View File

@ -133,22 +133,25 @@ class Binary(bytes):
directly to :class:`bytes`.
:Parameters:
- `data`: the binary data to represent
- `data`: the binary data to represent. Can be any bytes-like type
that implements the buffer protocol.
- `subtype` (optional): the `binary subtype
<http://bsonspec.org/#/specification>`_
to use
.. versionchanged:: 3.9
Support any bytes-like type that implements the buffer protocol.
"""
_type_marker = 5
def __new__(cls, data, subtype=BINARY_SUBTYPE):
if not isinstance(data, bytes):
raise TypeError("data must be an instance of bytes")
if not isinstance(subtype, int):
raise TypeError("subtype must be an instance of int")
if subtype >= 256 or subtype < 0:
raise ValueError("subtype must be contained in [0, 256)")
self = bytes.__new__(cls, data)
# Support any type that implements the buffer protocol.
self = bytes.__new__(cls, memoryview(data).tobytes())
self.__subtype = subtype
return self

View File

@ -60,6 +60,8 @@ Version 3.9 adds support for MongoDB 4.2. Highlights include:
:meth:`~pymongo.collection.Collection.find_one_and_update`,
:meth:`~pymongo.operations.UpdateOne`, and
:meth:`~pymongo.operations.UpdateMany`.
- :class:`~bson.binary.Binary` now supports any bytes-like type that implements
the buffer protocol.
.. _URI options specification: https://github.com/mongodb/specifications/blob/master/source/uri-options/uri-options.rst

View File

@ -14,9 +14,11 @@
"""Tests for the Binary wrapper."""
import array
import base64
import copy
import pickle
import platform
import sys
import uuid
@ -26,6 +28,7 @@ import bson
from bson.binary import *
from bson.codec_options import CodecOptions
from bson.py3compat import PY3
from bson.son import SON
from pymongo.mongo_client import MongoClient
from test import client_context, unittest
@ -81,7 +84,6 @@ class TestBinary(unittest.TestCase):
def test_exceptions(self):
self.assertRaises(TypeError, Binary, None)
self.assertRaises(TypeError, Binary, u"hello")
self.assertRaises(TypeError, Binary, 5)
self.assertRaises(TypeError, Binary, 10.2)
self.assertRaises(TypeError, Binary, b"hello", None)
@ -90,6 +92,10 @@ class TestBinary(unittest.TestCase):
self.assertRaises(ValueError, Binary, b"hello", 256)
self.assertTrue(Binary(b"hello", 0))
self.assertTrue(Binary(b"hello", 255))
if platform.python_implementation() != "Jython":
# Jython's memoryview accepts unicode strings...
# https://bugs.jython.org/issue2784
self.assertRaises(TypeError, Binary, u"hello")
def test_subtype(self):
one = Binary(b"hello")
@ -356,6 +362,22 @@ class TestBinary(unittest.TestCase):
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
self.assertEqual(uul, pickle.loads(pickle.dumps(uul, proto)))
def test_buffer_protocol(self):
b0 = Binary(b'123', 2)
self.assertEqual(b0, Binary(memoryview(b'123'), 2))
self.assertEqual(b0, Binary(bytearray(b'123'), 2))
# mmap.mmap and array.array only expose the
# buffer interface in python 3.x
if PY3:
# No mmap module in Jython
import mmap
with mmap.mmap(-1, len(b'123')) as mm:
mm.write(b'123')
mm.seek(0)
self.assertEqual(b0, Binary(mm, 2))
self.assertEqual(b0, Binary(array.array('B', b'123'), 2))
if __name__ == "__main__":
unittest.main()