make bytes(o) respect __bytes__ #2415
This adds two new C-API functions: PyObject_Bytes and PyBytes_FromObject. Reviewer: Barry
This commit is contained in:
parent
a786b026c9
commit
c15a07333e
|
@ -118,6 +118,12 @@ called with a non-bytes parameter.
|
||||||
arguments.
|
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)
|
.. cfunction:: Py_ssize_t PyBytes_Size(PyObject *o)
|
||||||
|
|
||||||
Return the length of the bytes in bytes object *o*.
|
Return the length of the bytes in bytes object *o*.
|
||||||
|
|
|
@ -139,6 +139,14 @@ Object Protocol
|
||||||
Python expression ``str(o)``. Called by the :func:`str` built-in function
|
Python expression ``str(o)``. Called by the :func:`str` built-in function
|
||||||
and, therefore, by the :func:`print` 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)
|
.. cfunction:: int PyObject_IsInstance(PyObject *inst, PyObject *cls)
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,7 @@ PyAPI_DATA(PyTypeObject) PyBytesIter_Type;
|
||||||
|
|
||||||
PyAPI_FUNC(PyObject *) PyBytes_FromStringAndSize(const char *, Py_ssize_t);
|
PyAPI_FUNC(PyObject *) PyBytes_FromStringAndSize(const char *, Py_ssize_t);
|
||||||
PyAPI_FUNC(PyObject *) PyBytes_FromString(const char *);
|
PyAPI_FUNC(PyObject *) PyBytes_FromString(const char *);
|
||||||
|
PyAPI_FUNC(PyObject *) PyBytes_FromObject(PyObject *);
|
||||||
PyAPI_FUNC(PyObject *) PyBytes_FromFormatV(const char*, va_list)
|
PyAPI_FUNC(PyObject *) PyBytes_FromFormatV(const char*, va_list)
|
||||||
Py_GCC_ATTRIBUTE((format(printf, 1, 0)));
|
Py_GCC_ATTRIBUTE((format(printf, 1, 0)));
|
||||||
PyAPI_FUNC(PyObject *) PyBytes_FromFormat(const char*, ...)
|
PyAPI_FUNC(PyObject *) PyBytes_FromFormat(const char*, ...)
|
||||||
|
|
|
@ -423,6 +423,7 @@ PyAPI_FUNC(void) _PyObject_Dump(PyObject *);
|
||||||
PyAPI_FUNC(PyObject *) PyObject_Repr(PyObject *);
|
PyAPI_FUNC(PyObject *) PyObject_Repr(PyObject *);
|
||||||
PyAPI_FUNC(PyObject *) PyObject_Str(PyObject *);
|
PyAPI_FUNC(PyObject *) PyObject_Str(PyObject *);
|
||||||
PyAPI_FUNC(PyObject *) PyObject_ASCII(PyObject *);
|
PyAPI_FUNC(PyObject *) PyObject_ASCII(PyObject *);
|
||||||
|
PyAPI_FUNC(PyObject *) PyObject_Bytes(PyObject *);
|
||||||
PyAPI_FUNC(int) PyObject_Compare(PyObject *, PyObject *);
|
PyAPI_FUNC(int) PyObject_Compare(PyObject *, PyObject *);
|
||||||
PyAPI_FUNC(PyObject *) PyObject_RichCompare(PyObject *, PyObject *, int);
|
PyAPI_FUNC(PyObject *) PyObject_RichCompare(PyObject *, PyObject *, int);
|
||||||
PyAPI_FUNC(int) PyObject_RichCompareBool(PyObject *, PyObject *, int);
|
PyAPI_FUNC(int) PyObject_RichCompareBool(PyObject *, PyObject *, int);
|
||||||
|
|
|
@ -458,6 +458,18 @@ class BytesTest(BaseBytesTest):
|
||||||
with open(fd, "rb", buffering=0) as f:
|
with open(fd, "rb", buffering=0) as f:
|
||||||
self.assertRaises(TypeError, f.readinto, b"")
|
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):
|
class ByteArrayTest(BaseBytesTest):
|
||||||
type2test = bytearray
|
type2test = bytearray
|
||||||
|
|
|
@ -27,6 +27,13 @@ Core and Builtins
|
||||||
|
|
||||||
- Issue #3650: Fixed a reference leak in bytes.split('x').
|
- 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
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
|
|
@ -2882,11 +2882,10 @@ str_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
|
||||||
static PyObject *
|
static PyObject *
|
||||||
string_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
string_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
{
|
{
|
||||||
PyObject *x = NULL, *it;
|
PyObject *x = NULL;
|
||||||
const char *encoding = NULL;
|
const char *encoding = NULL;
|
||||||
const char *errors = NULL;
|
const char *errors = NULL;
|
||||||
PyObject *new = NULL;
|
PyObject *new = NULL;
|
||||||
Py_ssize_t i, size;
|
|
||||||
static char *kwlist[] = {"source", "encoding", "errors", 0};
|
static char *kwlist[] = {"source", "encoding", "errors", 0};
|
||||||
|
|
||||||
if (type != &PyBytes_Type)
|
if (type != &PyBytes_Type)
|
||||||
|
@ -2924,6 +2923,14 @@ string_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
"encoding or errors without a string argument");
|
"encoding or errors without a string argument");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
return PyObject_Bytes(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject *
|
||||||
|
PyBytes_FromObject(PyObject *x)
|
||||||
|
{
|
||||||
|
PyObject *new, *it;
|
||||||
|
Py_ssize_t i, size;
|
||||||
|
|
||||||
/* Is it an int? */
|
/* Is it an int? */
|
||||||
size = PyNumber_AsSsize_t(x, PyExc_ValueError);
|
size = PyNumber_AsSsize_t(x, PyExc_ValueError);
|
||||||
|
|
|
@ -453,6 +453,45 @@ PyObject_ASCII(PyObject *v)
|
||||||
return res;
|
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
|
/* The new comparison philosophy is: we completely separate three-way
|
||||||
comparison from rich comparison. That is, PyObject_Compare() and
|
comparison from rich comparison. That is, PyObject_Compare() and
|
||||||
PyObject_Cmp() *just* use the tp_compare slot. And PyObject_RichCompare()
|
PyObject_Cmp() *just* use the tp_compare slot. And PyObject_RichCompare()
|
||||||
|
|
Loading…
Reference in New Issue