]> granicus.if.org Git - python/commitdiff
try to call __bytes__ before __index__ (closes #16722)
authorBenjamin Peterson <benjamin@python.org>
Wed, 19 Dec 2012 21:27:41 +0000 (15:27 -0600)
committerBenjamin Peterson <benjamin@python.org>
Wed, 19 Dec 2012 21:27:41 +0000 (15:27 -0600)
Lib/test/test_bytes.py
Misc/NEWS
Objects/bytesobject.c

index fe6e93935bc4553d2044a56fef5bddfc1de3fbfe..f88c24292285c8a5d771ff5b16ea4b5cc584a5c4 100644 (file)
@@ -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):
index 7ba832e9ba9af88be23f78adaa3e35ef97b0621d..509ea66752615071f3119b1af9f639f668e7ea0f 100644 (file)
--- 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.
index bf9259f187ace137b03b2a9bc5903ab9f301d1f6..52db15d6c268375d0e8cbe0f905db96581d7c031 100644 (file)
@@ -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 *