Skip to content

Commit 5ff3f73

Browse files
committed
try to call __bytes__ before __index__ (closes #16722)
1 parent 1f415cf commit 5ff3f73

3 files changed

Lines changed: 37 additions & 5 deletions

File tree

‎Lib/test/test_bytes.py‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,12 @@ class A:
701701
def __bytes__(self):
702702
return None
703703
self.assertRaises(TypeError, bytes, A())
704+
class A:
705+
def __bytes__(self):
706+
return b'a'
707+
def __index__(self):
708+
return 42
709+
self.assertEqual(bytes(A()), b'a')
704710

705711
# Test PyBytes_FromFormat()
706712
def test_from_format(self):

‎Misc/NEWS‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ What's New in Python 3.3.1?
1212
Core and Builtins
1313
-----------------
1414

15+
- Issue #16722: In the bytes() constructor, try to call __bytes__ on the
16+
argument before __index__.
17+
1518
- Issue #16602: When a weakref's target was part of a long deallocation
1619
chain, the object could remain reachable through its weakref even though
1720
its refcount had dropped to zero.

‎Objects/bytesobject.c‎

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2505,8 +2505,10 @@ bytes_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
25052505
const char *encoding = NULL;
25062506
const char *errors = NULL;
25072507
PyObject *new = NULL;
2508+
PyObject *func;
25082509
Py_ssize_t size;
25092510
static char *kwlist[] = {"source", "encoding", "errors", 0};
2511+
_Py_IDENTIFIER(__bytes__);
25102512

25112513
if (type != &PyBytes_Type)
25122514
return str_subtype_new(type, args, kwds);
@@ -2536,6 +2538,28 @@ bytes_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
25362538
assert(PyBytes_Check(new));
25372539
return new;
25382540
}
2541+
2542+
/* We'd like to call PyObject_Bytes here, but we need to check for an
2543+
integer argument before deferring to PyBytes_FromObject, something
2544+
PyObject_Bytes doesn't do. */
2545+
func = _PyObject_LookupSpecial(x, &PyId___bytes__);
2546+
if (func != NULL) {
2547+
new = PyObject_CallFunctionObjArgs(func, NULL);
2548+
Py_DECREF(func);
2549+
if (new == NULL)
2550+
return NULL;
2551+
if (!PyBytes_Check(new)) {
2552+
PyErr_Format(PyExc_TypeError,
2553+
"__bytes__ returned non-bytes (type %.200s)",
2554+
Py_TYPE(new)->tp_name);
2555+
Py_DECREF(new);
2556+
return NULL;
2557+
}
2558+
return new;
2559+
}
2560+
else if (PyErr_Occurred())
2561+
return NULL;
2562+
25392563
/* Is it an integer? */
25402564
size = PyNumber_AsSsize_t(x, PyExc_OverflowError);
25412565
if (size == -1 && PyErr_Occurred()) {
@@ -2549,12 +2573,10 @@ bytes_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
25492573
}
25502574
else {
25512575
new = PyBytes_FromStringAndSize(NULL, size);
2552-
if (new == NULL) {
2576+
if (new == NULL)
25532577
return NULL;
2554-
}
2555-
if (size > 0) {
2578+
if (size > 0)
25562579
memset(((PyBytesObject*)new)->ob_sval, 0, size);
2557-
}
25582580
return new;
25592581
}
25602582

@@ -2564,7 +2586,8 @@ bytes_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
25642586
"encoding or errors without a string argument");
25652587
return NULL;
25662588
}
2567-
return PyObject_Bytes(x);
2589+
2590+
return PyBytes_FromObject(x);
25682591
}
25692592

25702593
PyObject *

0 commit comments

Comments
 (0)