]> granicus.if.org Git - python/commitdiff
bpo-28856: Let %b format for bytes support objects that follow the buffer protocol...
authorXiang Zhang <angwerzx@126.com>
Tue, 14 Mar 2017 07:27:01 +0000 (15:27 +0800)
committerGitHub <noreply@github.com>
Tue, 14 Mar 2017 07:27:01 +0000 (15:27 +0800)
Lib/test/test_format.py
Misc/NEWS
Objects/bytesobject.c

index 83eb29faf88d6cd73f5dc8a17156c2b65f3f1c74..b6ba2e566b295f6bfc26446fd357ca939ae3f809 100644 (file)
@@ -315,10 +315,12 @@ class FormatTest(unittest.TestCase):
         testcommon(b"%b", b"abc", b"abc")
         testcommon(b"%b", bytearray(b"def"), b"def")
         testcommon(b"%b", fb, b"123")
+        testcommon(b"%b", memoryview(b"abc"), b"abc")
         # # %s is an alias for %b -- should only be used for Py2/3 code
         testcommon(b"%s", b"abc", b"abc")
         testcommon(b"%s", bytearray(b"def"), b"def")
         testcommon(b"%s", fb, b"123")
+        testcommon(b"%s", memoryview(b"abc"), b"abc")
         # %a will give the equivalent of
         # repr(some_obj).encode('ascii', 'backslashreplace')
         testcommon(b"%a", 3.14, b"3.14")
@@ -377,9 +379,11 @@ class FormatTest(unittest.TestCase):
         test_exc(b"%c", 3.14, TypeError,
                 "%c requires an integer in range(256) or a single byte")
         test_exc(b"%b", "Xc", TypeError,
-                "%b requires bytes, or an object that implements __bytes__, not 'str'")
+                "%b requires a bytes-like object, "
+                 "or an object that implements __bytes__, not 'str'")
         test_exc(b"%s", "Wd", TypeError,
-                "%b requires bytes, or an object that implements __bytes__, not 'str'")
+                "%b requires a bytes-like object, "
+                 "or an object that implements __bytes__, not 'str'")
 
         if maxsize == 2**31-1:
             # crashes 2.2.1 and earlier:
index 25a24f52008a6fda3d79770a7f091119962646c5..284a2031eeafc8cd127ee0931068d3723a8b73ef 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -12,6 +12,9 @@ Core and Builtins
 
 - bpo-29600: Fix wrapping coroutine return values in StopIteration.
 
+- bpo-28856: Fix an oversight that %b format for bytes should support objects
+  follow the buffer protocol.
+
 - bpo-29723: The ``sys.path[0]`` initialization change for bpo-29139 caused a
   regression by revealing an inconsistency in how sys.path is initialized when
   executing ``__main__`` from a zipfile, directory, or other import location.
index 801711f7e6ccaae8a0620617a2fefbea0a118d7f..30c1a7e24532da26f148d7141a5ee1c1202c7d6a 100644 (file)
@@ -528,6 +528,8 @@ byte_converter(PyObject *arg, char *p)
     return 0;
 }
 
+static PyObject *_PyBytes_FromBuffer(PyObject *x);
+
 static PyObject *
 format_obj(PyObject *v, const char **pbuf, Py_ssize_t *plen)
 {
@@ -564,8 +566,19 @@ format_obj(PyObject *v, const char **pbuf, Py_ssize_t *plen)
         *plen = PyBytes_GET_SIZE(result);
         return result;
     }
+    /* does it support buffer protocol? */
+    if (PyObject_CheckBuffer(v)) {
+        /* maybe we can avoid making a copy of the buffer object here? */
+        result = _PyBytes_FromBuffer(v);
+        if (result == NULL)
+            return NULL;
+        *pbuf = PyBytes_AS_STRING(result);
+        *plen = PyBytes_GET_SIZE(result);
+        return result;
+    }
     PyErr_Format(PyExc_TypeError,
-                 "%%b requires bytes, or an object that implements __bytes__, not '%.100s'",
+                 "%%b requires a bytes-like object, "
+                 "or an object that implements __bytes__, not '%.100s'",
                  Py_TYPE(v)->tp_name);
     return NULL;
 }