From 5ff3f73d94305900c0773e67ebdd9701d856a0fe Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Wed, 19 Dec 2012 15:27:41 -0600 Subject: [PATCH] try to call __bytes__ before __index__ (closes #16722) --- Lib/test/test_bytes.py | 6 ++++++ Misc/NEWS | 3 +++ Objects/bytesobject.c | 33 ++++++++++++++++++++++++++++----- 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py index fe6e93935bc..f88c2429228 100644 --- a/Lib/test/test_bytes.py +++ b/Lib/test/test_bytes.py @@ -701,6 +701,12 @@ class BytesTest(BaseBytesTest): def __bytes__(self): return None self.assertRaises(TypeError, bytes, A()) + class A: + def __bytes__(self): + return b'a' + def __index__(self): + return 42 + self.assertEqual(bytes(A()), b'a') # Test PyBytes_FromFormat() def test_from_format(self): diff --git a/Misc/NEWS b/Misc/NEWS index 7ba832e9ba9..509ea667526 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -12,6 +12,9 @@ What's New in Python 3.3.1? Core and Builtins ----------------- +- Issue #16722: In the bytes() constructor, try to call __bytes__ on the + argument before __index__. + - Issue #16602: When a weakref's target was part of a long deallocation chain, the object could remain reachable through its weakref even though its refcount had dropped to zero. diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c index bf9259f187a..52db15d6c26 100644 --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -2505,8 +2505,10 @@ bytes_new(PyTypeObject *type, PyObject *args, PyObject *kwds) const char *encoding = NULL; const char *errors = NULL; PyObject *new = NULL; + PyObject *func; Py_ssize_t size; static char *kwlist[] = {"source", "encoding", "errors", 0}; + _Py_IDENTIFIER(__bytes__); if (type != &PyBytes_Type) return str_subtype_new(type, args, kwds); @@ -2536,6 +2538,28 @@ bytes_new(PyTypeObject *type, PyObject *args, PyObject *kwds) assert(PyBytes_Check(new)); return new; } + + /* We'd like to call PyObject_Bytes here, but we need to check for an + integer argument before deferring to PyBytes_FromObject, something + PyObject_Bytes doesn't do. */ + func = _PyObject_LookupSpecial(x, &PyId___bytes__); + if (func != NULL) { + new = PyObject_CallFunctionObjArgs(func, NULL); + Py_DECREF(func); + if (new == NULL) + return NULL; + if (!PyBytes_Check(new)) { + PyErr_Format(PyExc_TypeError, + "__bytes__ returned non-bytes (type %.200s)", + Py_TYPE(new)->tp_name); + Py_DECREF(new); + return NULL; + } + return new; + } + else if (PyErr_Occurred()) + return NULL; + /* Is it an integer? */ size = PyNumber_AsSsize_t(x, PyExc_OverflowError); if (size == -1 && PyErr_Occurred()) { @@ -2549,12 +2573,10 @@ bytes_new(PyTypeObject *type, PyObject *args, PyObject *kwds) } else { new = PyBytes_FromStringAndSize(NULL, size); - if (new == NULL) { + if (new == NULL) return NULL; - } - if (size > 0) { + if (size > 0) memset(((PyBytesObject*)new)->ob_sval, 0, size); - } return new; } @@ -2564,7 +2586,8 @@ bytes_new(PyTypeObject *type, PyObject *args, PyObject *kwds) "encoding or errors without a string argument"); return NULL; } - return PyObject_Bytes(x); + + return PyBytes_FromObject(x); } PyObject *