Skip to content

Commit faa2cc6

Browse files
authored
bpo-28856: Let %b format for bytes support objects that follow the buffer protocol (GH-664)
1 parent 388e256 commit faa2cc6

File tree

3 files changed

+23
-3
lines changed

3 files changed

+23
-3
lines changed

Lib/test/test_format.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -315,10 +315,12 @@ def __bytes__(self):
315315
testcommon(b"%b", b"abc", b"abc")
316316
testcommon(b"%b", bytearray(b"def"), b"def")
317317
testcommon(b"%b", fb, b"123")
318+
testcommon(b"%b", memoryview(b"abc"), b"abc")
318319
# # %s is an alias for %b -- should only be used for Py2/3 code
319320
testcommon(b"%s", b"abc", b"abc")
320321
testcommon(b"%s", bytearray(b"def"), b"def")
321322
testcommon(b"%s", fb, b"123")
323+
testcommon(b"%s", memoryview(b"abc"), b"abc")
322324
# %a will give the equivalent of
323325
# repr(some_obj).encode('ascii', 'backslashreplace')
324326
testcommon(b"%a", 3.14, b"3.14")
@@ -377,9 +379,11 @@ def test_exc(formatstr, args, exception, excmsg):
377379
test_exc(b"%c", 3.14, TypeError,
378380
"%c requires an integer in range(256) or a single byte")
379381
test_exc(b"%b", "Xc", TypeError,
380-
"%b requires bytes, or an object that implements __bytes__, not 'str'")
382+
"%b requires a bytes-like object, "
383+
"or an object that implements __bytes__, not 'str'")
381384
test_exc(b"%s", "Wd", TypeError,
382-
"%b requires bytes, or an object that implements __bytes__, not 'str'")
385+
"%b requires a bytes-like object, "
386+
"or an object that implements __bytes__, not 'str'")
383387

384388
if maxsize == 2**31-1:
385389
# crashes 2.2.1 and earlier:

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ Core and Builtins
1212

1313
- bpo-29600: Fix wrapping coroutine return values in StopIteration.
1414

15+
- bpo-28856: Fix an oversight that %b format for bytes should support objects
16+
follow the buffer protocol.
17+
1518
- bpo-29723: The ``sys.path[0]`` initialization change for bpo-29139 caused a
1619
regression by revealing an inconsistency in how sys.path is initialized when
1720
executing ``__main__`` from a zipfile, directory, or other import location.

Objects/bytesobject.c

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,8 @@ byte_converter(PyObject *arg, char *p)
528528
return 0;
529529
}
530530

531+
static PyObject *_PyBytes_FromBuffer(PyObject *x);
532+
531533
static PyObject *
532534
format_obj(PyObject *v, const char **pbuf, Py_ssize_t *plen)
533535
{
@@ -564,8 +566,19 @@ format_obj(PyObject *v, const char **pbuf, Py_ssize_t *plen)
564566
*plen = PyBytes_GET_SIZE(result);
565567
return result;
566568
}
569+
/* does it support buffer protocol? */
570+
if (PyObject_CheckBuffer(v)) {
571+
/* maybe we can avoid making a copy of the buffer object here? */
572+
result = _PyBytes_FromBuffer(v);
573+
if (result == NULL)
574+
return NULL;
575+
*pbuf = PyBytes_AS_STRING(result);
576+
*plen = PyBytes_GET_SIZE(result);
577+
return result;
578+
}
567579
PyErr_Format(PyExc_TypeError,
568-
"%%b requires bytes, or an object that implements __bytes__, not '%.100s'",
580+
"%%b requires a bytes-like object, "
581+
"or an object that implements __bytes__, not '%.100s'",
569582
Py_TYPE(v)->tp_name);
570583
return NULL;
571584
}

0 commit comments

Comments
 (0)