]> granicus.if.org Git - python/commitdiff
make bytes(o) respect __bytes__ #2415
authorBenjamin Peterson <benjamin@python.org>
Tue, 26 Aug 2008 16:46:47 +0000 (16:46 +0000)
committerBenjamin Peterson <benjamin@python.org>
Tue, 26 Aug 2008 16:46:47 +0000 (16:46 +0000)
This adds two new C-API functions: PyObject_Bytes and PyBytes_FromObject.

Reviewer: Barry

Doc/c-api/bytes.rst
Doc/c-api/object.rst
Include/bytesobject.h
Include/object.h
Lib/test/test_bytes.py
Misc/NEWS
Objects/bytesobject.c
Objects/object.c

index 916e20eaf8e97c18db15f9f53c35a80c86bd575c..c9a114a9382ab20c7e49d566a2472deccec62a2e 100644 (file)
@@ -118,6 +118,12 @@ called with a non-bytes parameter.
    arguments.
 
 
+.. cfunction:: PyObject* PyBytes_FromObject(PyObject *o)
+
+   Return the bytes representation of object *o* that implements the buffer
+   protocol.
+
+
 .. cfunction:: Py_ssize_t PyBytes_Size(PyObject *o)
 
    Return the length of the bytes in bytes object *o*.
index 193ab958f47a4d04e9e5dc24790fdc47bb6dee47..7b9682cb2b0874c6d8965dad6b45a099ac15cacf 100644 (file)
@@ -139,6 +139,14 @@ Object Protocol
    Python expression ``str(o)``.  Called by the :func:`str` built-in function
    and, therefore, by the :func:`print` function.
 
+.. cfunction:: PyObject* PyObject_Bytes(PyObject *o)
+
+   .. index:: builtin: bytes
+
+   Compute a bytes representation of object *o*.  *NULL* is returned on failure
+   and a bytes object on success.  This is equivalent to the Python expression
+   ``bytes(o)``.
+
 
 .. cfunction:: int PyObject_IsInstance(PyObject *inst, PyObject *cls)
 
index ece06d26675c9e6679c4b350d473b213a72e3637..3f275a86850fb11fdcbd965ac117d4f8abfbfd08 100644 (file)
@@ -48,6 +48,7 @@ PyAPI_DATA(PyTypeObject) PyBytesIter_Type;
 
 PyAPI_FUNC(PyObject *) PyBytes_FromStringAndSize(const char *, Py_ssize_t);
 PyAPI_FUNC(PyObject *) PyBytes_FromString(const char *);
+PyAPI_FUNC(PyObject *) PyBytes_FromObject(PyObject *);
 PyAPI_FUNC(PyObject *) PyBytes_FromFormatV(const char*, va_list)
                                Py_GCC_ATTRIBUTE((format(printf, 1, 0)));
 PyAPI_FUNC(PyObject *) PyBytes_FromFormat(const char*, ...)
index 372eada548da26f81f5691a40922aecf12197b99..60584a02c33615bab220b7a9898cbfa3605ba883 100644 (file)
@@ -423,6 +423,7 @@ PyAPI_FUNC(void) _PyObject_Dump(PyObject *);
 PyAPI_FUNC(PyObject *) PyObject_Repr(PyObject *);
 PyAPI_FUNC(PyObject *) PyObject_Str(PyObject *);
 PyAPI_FUNC(PyObject *) PyObject_ASCII(PyObject *);
+PyAPI_FUNC(PyObject *) PyObject_Bytes(PyObject *);
 PyAPI_FUNC(int) PyObject_Compare(PyObject *, PyObject *);
 PyAPI_FUNC(PyObject *) PyObject_RichCompare(PyObject *, PyObject *, int);
 PyAPI_FUNC(int) PyObject_RichCompareBool(PyObject *, PyObject *, int);
index 630f862a1513d07fcd20d8da1ae8ec201e40b395..81d2dad3d21bdb3b42a3b3ce2d9144939d54451f 100644 (file)
@@ -458,6 +458,18 @@ class BytesTest(BaseBytesTest):
         with open(fd, "rb", buffering=0) as f:
             self.assertRaises(TypeError, f.readinto, b"")
 
+    def test_custom(self):
+        class A:
+            def __bytes__(self):
+                return b'abc'
+        self.assertEqual(bytes(A()), b'abc')
+        class A: pass
+        self.assertRaises(TypeError, bytes, A())
+        class A:
+            def __bytes__(self):
+                return None
+        self.assertRaises(TypeError, bytes, A())
+
 
 class ByteArrayTest(BaseBytesTest):
     type2test = bytearray
index 8594c99e6f5b40cf0fc4845c90e0c16d4969721c..d77913242238977ad0fde32b8806f4d3e35e30a8 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -27,6 +27,13 @@ Core and Builtins
 
 - Issue #3650: Fixed a reference leak in bytes.split('x').
 
+- bytes(o) now tries to use o.__bytes__() before using fallbacks.
+
+C API
+-----
+
+- PyObject_Bytes and PyBytes_FromObject were added.
+
 Library
 -------
 
index d59e79a22a3126841c1eee3ab5823be9a0de73e0..3bda6d99459ca25a1dbf835bd25a2ae24ea5425d 100644 (file)
@@ -2882,11 +2882,10 @@ str_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
 static PyObject *
 string_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
 {
-       PyObject *x = NULL, *it;
+       PyObject *x = NULL;
        const char *encoding = NULL;
        const char *errors = NULL;
        PyObject *new = NULL;
-       Py_ssize_t i, size;
        static char *kwlist[] = {"source", "encoding", "errors", 0};
 
        if (type != &PyBytes_Type)
@@ -2924,6 +2923,14 @@ string_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
                        "encoding or errors without a string argument");
                return NULL;
        }
+        return PyObject_Bytes(x);
+}
+
+PyObject *
+PyBytes_FromObject(PyObject *x)
+{
+       PyObject *new, *it;
+       Py_ssize_t i, size;
 
        /* Is it an int? */
        size = PyNumber_AsSsize_t(x, PyExc_ValueError);
index 79f8288a4f5abdfd1414affd5c8e03c5a1a43b37..206bb88d5f8230a94b8ad6f0430f97ae53793996 100644 (file)
@@ -453,6 +453,45 @@ PyObject_ASCII(PyObject *v)
        return res;
 }
 
+PyObject *
+PyObject_Bytes(PyObject *v)
+{
+       PyObject *bytesmeth, *result, *func;
+       static PyObject *bytesstring = NULL;
+
+       if (bytesstring == NULL) {
+               bytesstring = PyUnicode_InternFromString("__bytes__");
+               if (bytesstring == NULL)
+                       return NULL;
+       }
+
+       if (v == NULL)
+               return PyBytes_FromString("<NULL>");
+
+       if (PyBytes_CheckExact(v)) {
+               Py_INCREF(v);
+               return v;
+       }
+
+        /* Doesn't create a reference */
+       func = _PyType_Lookup(Py_TYPE(v), bytesstring);
+       if (func != NULL) {
+            result = PyObject_CallFunctionObjArgs(func, v, NULL);
+            if (result == NULL)
+               return NULL;
+            if (!PyBytes_Check(result)) {
+               PyErr_Format(PyExc_TypeError,
+                            "__bytes__ returned non-bytes (type %.200s)",
+                            Py_TYPE(result)->tp_name);
+               Py_DECREF(result);
+               return NULL;
+            }
+            return result;
+       }
+        PyErr_Clear();
+       return PyBytes_FromObject(v);
+}
+
 /* The new comparison philosophy is: we completely separate three-way
    comparison from rich comparison.  That is, PyObject_Compare() and
    PyObject_Cmp() *just* use the tp_compare slot.  And PyObject_RichCompare()