diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 2fce851e2e0cf28..88809e3bcc4be59 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -2303,8 +2303,11 @@ data and are closely related to string objects in a variety of other ways. other ways: * A zero-filled bytes object of a specified length: ``bytes(10)`` + (deprecated in version 3.7) * From an iterable of integers: ``bytes(range(20))`` * Copying existing binary data via the buffer protocol: ``bytes(obj)`` + * Using the :meth:`~bytes.zeros` constructor: ``bytes.zeros(10)`` + * Using the :meth:`~bytes.byte` single-byte constructor: ``bytes.byte(65)`` Also see the :ref:`bytes ` built-in. @@ -2375,8 +2378,11 @@ objects. * Creating an empty instance: ``bytearray()`` * Creating a zero-filled instance with a given length: ``bytearray(10)`` + (deprecated in version 3.7) * From an iterable of integers: ``bytearray(range(20))`` * Copying existing binary data via the buffer protocol: ``bytearray(b'Hi!')`` + * Using the :meth:`~bytearray.zeros` constructor: ``bytearray.zeros(10)`` + * Using the :meth:`~bytearray.byte` single-byte constructor: ``bytearray.byte(65)`` As bytearray objects are mutable, they support the :ref:`mutable ` sequence operations in addition to the @@ -2547,6 +2553,14 @@ arbitrary binary data. Also accept an integer in the range 0 to 255 as the subsequence. +.. classmethod:: bytes.iterbytes() + bytearray.iterbytes() + + Return an iterator that produce :class:`bytes` objects of length 1. + + .. versionadded:: 3.7 + + .. method:: bytes.join(iterable) bytearray.join(iterable) @@ -3681,6 +3695,12 @@ copying. .. versionchanged:: 3.5 The source format is no longer restricted when casting to a byte view. + .. classmethod:: memoryview.iterbytes() + + Return an iterator that produce :class:`bytes` objects of length 1. + + .. versionadded:: 3.7 + There are also several readonly attributes available: .. attribute:: obj diff --git a/Doc/whatsnew/3.7.rst b/Doc/whatsnew/3.7.rst index 8547660e1881a49..412516acfe90f2f 100644 --- a/Doc/whatsnew/3.7.rst +++ b/Doc/whatsnew/3.7.rst @@ -108,6 +108,23 @@ locale remains active when the core interpreter is initialized. PEP written and implemented by Nick Coghlan. +.. _whatsnew37-pep467: + +PEP 467: Minor API improvements for binary sequences +---------------------------------------------------- + +:pep:`467` adds four small adjustments to the APIs of the :class:`bytes`, :class:`bytearray` and :class:`memoryview` types to make it easier to operate entirely in the binary domain: + +* Deprecate passing single integer values to :class:`bytes` and :class:`bytearray` +* Add :meth:`bytes.zeros` and :meth:`bytearray.zeros` alternative constructors +* Add :meth:`bytes.byte` and :meth:`bytearray.byte` alternative constructors +* Add :meth:`bytes.iterbytes`, :meth:`bytearray.iterbytes` and :meth:`memoryview.iterbytes` alternative iterators + +.. seealso:: + + :pep:`467` -- Minor API improvements for binary sequences + + Other Language Changes ====================== diff --git a/Include/bytes_methods.h b/Include/bytes_methods.h index 7fa7540c38b7313..c8a8ee6fb0cf1f1 100644 --- a/Include/bytes_methods.h +++ b/Include/bytes_methods.h @@ -41,6 +41,7 @@ extern const char _Py_isdigit__doc__[]; extern const char _Py_islower__doc__[]; extern const char _Py_isupper__doc__[]; extern const char _Py_istitle__doc__[]; +extern const char _Py_iterbytes__doc__[]; extern const char _Py_lower__doc__[]; extern const char _Py_upper__doc__[]; extern const char _Py_title__doc__[]; diff --git a/Lib/sre_compile.py b/Lib/sre_compile.py index 144620c6d1bc3fe..01c911535bdff70 100644 --- a/Lib/sre_compile.py +++ b/Lib/sre_compile.py @@ -262,7 +262,7 @@ def _optimize_charset(charset, iscased=None, fixup=None, fixes=None): # internal: optimize character set out = [] tail = [] - charmap = bytearray(256) + charmap = bytearray.zeros(256) hascased = False for op, av in charset: while True: @@ -373,7 +373,7 @@ def _optimize_charset(charset, iscased=None, fixup=None, fixes=None): charmap = bytes(charmap) # should be hashable comps = {} - mapping = bytearray(256) + mapping = bytearray.zeros(256) block = 0 data = bytearray() for i in range(0, 65536, 256): diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py index 18091c1cfd2f191..6b914f14b749557 100644 --- a/Lib/test/test_bytes.py +++ b/Lib/test/test_bytes.py @@ -95,15 +95,38 @@ def __index__(self): self.assertEqual(self.type2test(B(b"foobar")), b"foobar") def test_from_ssize(self): - self.assertEqual(self.type2test(0), b'') - self.assertEqual(self.type2test(1), b'\x00') - self.assertEqual(self.type2test(5), b'\x00\x00\x00\x00\x00') + with self.assertWarns(DeprecationWarning): + self.assertEqual(self.type2test(0), b'') + with self.assertWarns(DeprecationWarning): + self.assertEqual(self.type2test(1), b'\x00') + with self.assertWarns(DeprecationWarning): + self.assertEqual(self.type2test(5), b'\x00\x00\x00\x00\x00') + with self.assertWarns(DeprecationWarning): + self.assertEqual(self.type2test(10), self.type2test([0]*10)) + with self.assertWarns(DeprecationWarning): + self.assertEqual(self.type2test(10000), self.type2test([0]*10000)) + self.assertRaises(ValueError, self.type2test, -1) self.assertEqual(self.type2test('0', 'ascii'), b'0') self.assertEqual(self.type2test(b'0'), b'0') self.assertRaises(OverflowError, self.type2test, sys.maxsize + 1) + def test_zeros(self): + self.assertEqual(self.type2test.zeros(0), b'') + self.assertEqual(self.type2test.zeros(1), b'\x00') + self.assertEqual(self.type2test.zeros(5), b'\x00\x00\x00\x00\x00') + self.assertRaises(ValueError, self.type2test.zeros, -1) + + def test_single_byte(self): + self.assertEqual(self.type2test.byte(0), b'\x00') + self.assertEqual(self.type2test.byte(65), b'A') + self.assertEqual(self.type2test.byte(255), b'\xff') + self.assertEqual(self.type2test.byte(False), b'\x00') + self.assertEqual(self.type2test.byte(True), b'\x01') + self.assertRaises(ValueError, self.type2test.byte, 256) + self.assertRaises(ValueError, self.type2test.byte, -1) + def test_constructor_type_errors(self): self.assertRaises(TypeError, self.type2test, 0.0) class C: @@ -242,14 +265,6 @@ def test_decode(self): # Default encoding is utf-8 self.assertEqual(self.type2test(b'\xe2\x98\x83').decode(), '\u2603') - def test_from_int(self): - b = self.type2test(0) - self.assertEqual(b, self.type2test()) - b = self.type2test(10) - self.assertEqual(b, self.type2test([0]*10)) - b = self.type2test(10000) - self.assertEqual(b, self.type2test([0]*10000)) - def test_concat(self): b1 = self.type2test(b"abc") b2 = self.type2test(b"def") @@ -504,6 +519,14 @@ def test_rindex(self): self.assertEqual(b.rindex(i, 3, 9), 7) self.assertRaises(ValueError, b.rindex, w, 1, 3) + def test_iterbytes(self): + b = self.type2test(b'foo') + it = b.iterbytes() + self.assertEqual(next(it), b'f') + self.assertEqual(next(it), b'o') + self.assertEqual(next(it), b'o') + self.assertRaises(StopIteration, next, it) + def test_mod(self): b = self.type2test(b'hello, %b!') orig = b diff --git a/Lib/test/test_doctest.py b/Lib/test/test_doctest.py index 2258c6b1baed26b..d60b706a0ef82cf 100644 --- a/Lib/test/test_doctest.py +++ b/Lib/test/test_doctest.py @@ -659,7 +659,7 @@ def non_Python_modules(): r""" >>> import builtins >>> tests = doctest.DocTestFinder().find(builtins) - >>> 790 < len(tests) < 810 # approximate number of objects with docstrings + >>> 800 < len(tests) < 850 # approximate number of objects with docstrings True >>> real_tests = [t for t in tests if len(t.examples) > 0] >>> len(real_tests) # objects that actually have doctests diff --git a/Lib/test/test_memoryview.py b/Lib/test/test_memoryview.py index ddd5b9a8e2f2505..64f03a7bd7e69e2 100644 --- a/Lib/test/test_memoryview.py +++ b/Lib/test/test_memoryview.py @@ -383,6 +383,15 @@ def test_issue22668(self): self.assertEqual(c.format, "H") self.assertEqual(d.format, "H") + def test_iterbytes(self): + for tp in self._types: + b = tp(self._source) + m = self._view(b) + it = m.iterbytes() + for byte in m: + self.assertEqual(next(it), bytes([byte])) + self.assertRaises(StopIteration, next, it) + # Variations on source objects for the buffer: bytes-like objects, then arrays # with itemsize > 1. diff --git a/Misc/ACKS b/Misc/ACKS index 91e04246f73b3b6..c6c62abc90859da 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1759,6 +1759,7 @@ Masazumi Yoshikawa Arnaud Ysmal Bernard Yue Moshe Zadka +Elias Zamaria Milan Zamazal Artur Zaprzala Mike Zarnstorff diff --git a/Misc/NEWS.d/next/Core and Builtins/2017-08-29-11-51-08.bpo-27923.y1IldO.rst b/Misc/NEWS.d/next/Core and Builtins/2017-08-29-11-51-08.bpo-27923.y1IldO.rst new file mode 100644 index 000000000000000..596d4515a81c4c2 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2017-08-29-11-51-08.bpo-27923.y1IldO.rst @@ -0,0 +1 @@ +Minor API improvements for binary sequences (PEP 467) diff --git a/Objects/bytearrayobject.c b/Objects/bytearrayobject.c index 857a1aa070eeb08..671c668f3d7c0e8 100644 --- a/Objects/bytearrayobject.c +++ b/Objects/bytearrayobject.c @@ -807,6 +807,13 @@ bytearray_init(PyByteArrayObject *self, PyObject *args, PyObject *kwds) PyErr_SetString(PyExc_ValueError, "negative count"); return -1; } + if (count >= 0) { + if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, + "Passing an integer to the bytearray " + "constructor is deprecated. Use " + "bytearray.zeros(%zd) instead.\n", count) < 0) + return NULL; + } if (count > 0) { if (PyByteArray_Resize((PyObject *)self, count)) return -1; @@ -1995,6 +2002,67 @@ bytearray_fromhex_impl(PyTypeObject *type, PyObject *string) return result; } +/*[clinic input] +@classmethod +bytearray.zeros + + size: int + / + +Create a bytearray object of size given by the parameter initialized with null bytes. + +Parameter must be 0 or a positive integer. +Example: bytearray.zeros(3) -> bytearray(b\'\\x00\\x00\\x00') +[clinic start generated code]*/ + +static PyObject * +bytearray_zeros_impl(PyTypeObject *type, int size) +/*[clinic end generated code: output=483e961ce69e50dc input=b6e4556bd3095de2]*/ +{ + if (size == -1 && PyErr_Occurred()) { + return NULL; + } + if (size < 0) { + PyErr_SetString(PyExc_ValueError, "negative count"); + return NULL; + } + if (size >= 0) { + PyObject *result = PyByteArray_FromStringAndSize(NULL, size); + if (PyByteArray_Resize((PyObject *)result, size)) + return NULL; + memset(PyByteArray_AS_STRING(result), 0, size); + return result; + } +} + +/*[clinic input] +@classmethod +bytearray.byte + + x: int + / + +Create a bytearray object, consisting of a single byte. + +Parameter must be in range(0, 256) +bytearray.byte(x) is equivalent to bytearray([x]) +Example: bytearray.byte(3) -> bytearray(b'\x03') +[clinic start generated code]*/ + +static PyObject * +bytearray_byte_impl(PyTypeObject *type, int x) +/*[clinic end generated code: output=756fefaafb00a523 input=d36148c99214b551]*/ +{ + char byte; + if (x < 0 || x > 255) { + PyErr_Format(PyExc_ValueError, "bytes must be in range(0, 256)"); + return NULL; + } + byte = (char)x; + return PyByteArray_FromStringAndSize(&byte, 1); +} + + PyDoc_STRVAR(hex__doc__, "B.hex() -> string\n\ \n\ @@ -2113,6 +2181,8 @@ static PyBufferProcs bytearray_as_buffer = { (releasebufferproc)bytearray_releasebuffer, }; +static PyObject *bytearray_iterbytes(PyObject *seq); + static PyMethodDef bytearray_methods[] = { {"__alloc__", (PyCFunction)bytearray_alloc, METH_NOARGS, alloc_doc}, @@ -2120,6 +2190,7 @@ bytearray_methods[] = { BYTEARRAY_REDUCE_EX_METHODDEF BYTEARRAY_SIZEOF_METHODDEF BYTEARRAY_APPEND_METHODDEF + BYTEARRAY_BYTE_METHODDEF {"capitalize", (PyCFunction)stringlib_capitalize, METH_NOARGS, _Py_capitalize__doc__}, {"center", (PyCFunction)stringlib_center, METH_VARARGS, _Py_center__doc__}, @@ -2153,6 +2224,8 @@ bytearray_methods[] = { _Py_istitle__doc__}, {"isupper", (PyCFunction)stringlib_isupper, METH_NOARGS, _Py_isupper__doc__}, + {"iterbytes", (PyCFunction)bytearray_iterbytes, METH_NOARGS, + _Py_iterbytes__doc__}, BYTEARRAY_JOIN_METHODDEF {"ljust", (PyCFunction)stringlib_ljust, METH_VARARGS, _Py_ljust__doc__}, {"lower", (PyCFunction)stringlib_lower, METH_NOARGS, _Py_lower__doc__}, @@ -2179,6 +2252,7 @@ bytearray_methods[] = { {"title", (PyCFunction)stringlib_title, METH_NOARGS, _Py_title__doc__}, BYTEARRAY_TRANSLATE_METHODDEF {"upper", (PyCFunction)stringlib_upper, METH_NOARGS, _Py_upper__doc__}, + BYTEARRAY_ZEROS_METHODDEF {"zfill", (PyCFunction)stringlib_zfill, METH_VARARGS, _Py_zfill__doc__}, {NULL} }; @@ -2411,3 +2485,67 @@ bytearray_iter(PyObject *seq) _PyObject_GC_TRACK(it); return (PyObject *)it; } + +/****************** bytearray_iterbytes object ***********************/ + +static PyObject * +bytearray_iterbytes_next(bytesiterobject *it) +{ + PyObject *item = bytearrayiter_next(it); + if (item != NULL) { + return Py_BuildValue("c", (unsigned char)PyLong_AsLong(item)); + } + return item; +} + +PyTypeObject PyByteArrayIterBytes_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "bytearray_iterator", /* tp_name */ + sizeof(bytesiterobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)bytearrayiter_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ + 0, /* tp_doc */ + (traverseproc)bytearrayiter_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)bytearray_iterbytes_next, /* tp_iternext */ + bytearrayiter_methods, /* tp_methods */ + 0, +}; + +static PyObject * +bytearray_iterbytes(PyObject *seq) +{ + bytesiterobject *it; + + if (!PyByteArray_Check(seq)) { + PyErr_BadInternalCall(); + return NULL; + } + it = PyObject_GC_New(bytesiterobject, &PyByteArrayIterBytes_Type); + if (it == NULL) + return NULL; + it->it_index = 0; + Py_INCREF(seq); + it->it_seq = (PyByteArrayObject *)seq; + _PyObject_GC_TRACK(it); + return (PyObject *)it; +} diff --git a/Objects/bytes_methods.c b/Objects/bytes_methods.c index bd79773a54eabff..ebd03d79640014f 100644 --- a/Objects/bytes_methods.c +++ b/Objects/bytes_methods.c @@ -239,6 +239,10 @@ _Py_bytes_istitle(const char *cptr, Py_ssize_t len) } +PyDoc_STRVAR_shared(_Py_iterbytes__doc__, +"B.iterbytes() -> iterator that produces length 1 bytes objects instead of integers"); + + PyDoc_STRVAR_shared(_Py_lower__doc__, "B.lower() -> copy of B\n\ \n\ diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c index e95ab9c63b1b8d6..b46ab00d71ab9de 100644 --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -2426,6 +2426,58 @@ _PyBytes_FromHex(PyObject *string, int use_bytearray) return NULL; } +/*[clinic input] +@classmethod +bytes.zeros + + size: int + / + +Create a bytes object of size given by the parameter initialized with null bytes. + +Parameter must be 0 or a positive integer. +Example: bytes.zeros(3) -> b\'\\x00\\x00\\x00' +[clinic start generated code]*/ + +static PyObject * +bytes_zeros_impl(PyTypeObject *type, int size) +/*[clinic end generated code: output=ae545bb17865f2a1 input=12e0adfbb086d92d]*/ +{ + if (size == -1 && PyErr_Occurred()) { + return NULL; + } + if (size < 0) { + PyErr_SetString(PyExc_ValueError, "negative count"); + return NULL; + } + return _PyBytes_FromSize(size, 1); +} + +/*[clinic input] +@classmethod +bytes.byte + + x: int + / + +Create a bytes object, consisting of a single byte. + +Parameter must be in range(0, 256) +bytes.byte(x) is equivalent to bytes([x]) +Example: bytes.byte(3) -> b'\x03' +[clinic start generated code]*/ + +static PyObject * +bytes_byte_impl(PyTypeObject *type, int x) +/*[clinic end generated code: output=9045f894ce311daa input=eb583448446d0edd]*/ +{ + if (x < 0 || x > 255) { + PyErr_Format(PyExc_ValueError, "bytes must be in range(0, 256)"); + return NULL; + } + return Py_BuildValue("c", x); +} + PyDoc_STRVAR(hex__doc__, "B.hex() -> string\n\ \n\ @@ -2446,10 +2498,13 @@ bytes_getnewargs(PyBytesObject *v) return Py_BuildValue("(y#)", v->ob_sval, Py_SIZE(v)); } +static PyObject *bytes_iterbytes(PyObject *seq); + static PyMethodDef bytes_methods[] = { {"__getnewargs__", (PyCFunction)bytes_getnewargs, METH_NOARGS}, + BYTES_BYTE_METHODDEF {"capitalize", (PyCFunction)stringlib_capitalize, METH_NOARGS, _Py_capitalize__doc__}, {"center", (PyCFunction)stringlib_center, METH_VARARGS, @@ -2480,6 +2535,8 @@ bytes_methods[] = { _Py_istitle__doc__}, {"isupper", (PyCFunction)stringlib_isupper, METH_NOARGS, _Py_isupper__doc__}, + {"iterbytes", (PyCFunction)bytes_iterbytes, METH_NOARGS, + _Py_iterbytes__doc__}, BYTES_JOIN_METHODDEF {"ljust", (PyCFunction)stringlib_ljust, METH_VARARGS, _Py_ljust__doc__}, {"lower", (PyCFunction)stringlib_lower, METH_NOARGS, _Py_lower__doc__}, @@ -2503,6 +2560,7 @@ bytes_methods[] = { {"title", (PyCFunction)stringlib_title, METH_NOARGS, _Py_title__doc__}, BYTES_TRANSLATE_METHODDEF {"upper", (PyCFunction)stringlib_upper, METH_NOARGS, _Py_upper__doc__}, + BYTES_ZEROS_METHODDEF {"zfill", (PyCFunction)stringlib_zfill, METH_VARARGS, _Py_zfill__doc__}, {NULL, NULL} /* sentinel */ }; @@ -2618,6 +2676,11 @@ bytes_new(PyTypeObject *type, PyObject *args, PyObject *kwds) new = _PyBytes_FromSize(size, 1); if (new == NULL) return NULL; + if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, + "Passing an integer to the bytes constructor is " + "deprecated. Use bytes.zeros(%zd) instead.\n", + size) < 0) + return NULL; return new; } } @@ -3151,6 +3214,71 @@ bytes_iter(PyObject *seq) } +/*********************** iterbytes object ****************************/ + +static PyObject * +iterbytes_next(striterobject *it) +{ + PyObject *item = striter_next(it); + if (item != NULL) { + return Py_BuildValue("c", (unsigned char)PyLong_AsLong(item)); + } + return item; +} + +PyTypeObject PyIterBytes_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "bytes_iterator", /* tp_name */ + sizeof(striterobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)striter_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ + 0, /* tp_doc */ + (traverseproc)striter_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)iterbytes_next, /* tp_iternext */ + striter_methods, /* tp_methods */ + 0, +}; + +static PyObject * +bytes_iterbytes(PyObject *seq) +{ + striterobject *it; + + if (!PyBytes_Check(seq)) { + PyErr_BadInternalCall(); + return NULL; + } + it = PyObject_GC_New(striterobject, &PyIterBytes_Type); + if (it == NULL) + return NULL; + it->it_index = 0; + Py_INCREF(seq); + it->it_seq = (PyBytesObject *)seq; + _PyObject_GC_TRACK(it); + return (PyObject *)it; +} + + /* _PyBytesWriter API */ #ifdef MS_WINDOWS diff --git a/Objects/clinic/bytearrayobject.c.h b/Objects/clinic/bytearrayobject.c.h index 319c86e73d864fd..b6b9cae12542375 100644 --- a/Objects/clinic/bytearrayobject.c.h +++ b/Objects/clinic/bytearrayobject.c.h @@ -648,6 +648,67 @@ bytearray_fromhex(PyTypeObject *type, PyObject *arg) return return_value; } +PyDoc_STRVAR(bytearray_zeros__doc__, +"zeros($type, size, /)\n" +"--\n" +"\n" +"Create a bytearray object of size given by the parameter initialized with null bytes.\n" +"\n" +"Parameter must be 0 or a positive integer.\n" +"Example: bytearray.zeros(3) -> bytearray(b\\\'\\\\x00\\\\x00\\\\x00\')"); + +#define BYTEARRAY_ZEROS_METHODDEF \ + {"zeros", (PyCFunction)bytearray_zeros, METH_O|METH_CLASS, bytearray_zeros__doc__}, + +static PyObject * +bytearray_zeros_impl(PyTypeObject *type, int size); + +static PyObject * +bytearray_zeros(PyTypeObject *type, PyObject *arg) +{ + PyObject *return_value = NULL; + int size; + + if (!PyArg_Parse(arg, "i:zeros", &size)) { + goto exit; + } + return_value = bytearray_zeros_impl(type, size); + +exit: + return return_value; +} + +PyDoc_STRVAR(bytearray_byte__doc__, +"byte($type, x, /)\n" +"--\n" +"\n" +"Create a bytearray object, consisting of a single byte.\n" +"\n" +"Parameter must be in range(0, 256)\n" +"bytearray.byte(x) is equivalent to bytearray([x])\n" +"Example: bytearray.byte(3) -> bytearray(b\'\\x03\')"); + +#define BYTEARRAY_BYTE_METHODDEF \ + {"byte", (PyCFunction)bytearray_byte, METH_O|METH_CLASS, bytearray_byte__doc__}, + +static PyObject * +bytearray_byte_impl(PyTypeObject *type, int x); + +static PyObject * +bytearray_byte(PyTypeObject *type, PyObject *arg) +{ + PyObject *return_value = NULL; + int x; + + if (!PyArg_Parse(arg, "i:byte", &x)) { + goto exit; + } + return_value = bytearray_byte_impl(type, x); + +exit: + return return_value; +} + PyDoc_STRVAR(bytearray_reduce__doc__, "__reduce__($self, /)\n" "--\n" @@ -711,4 +772,4 @@ bytearray_sizeof(PyByteArrayObject *self, PyObject *Py_UNUSED(ignored)) { return bytearray_sizeof_impl(self); } -/*[clinic end generated code: output=e53f10084457a46b input=a9049054013a1b77]*/ +/*[clinic end generated code: output=26f8911786111e2b input=a9049054013a1b77]*/ diff --git a/Objects/clinic/bytesobject.c.h b/Objects/clinic/bytesobject.c.h index 315f6f250981a94..35e509d11da87e2 100644 --- a/Objects/clinic/bytesobject.c.h +++ b/Objects/clinic/bytesobject.c.h @@ -499,4 +499,65 @@ bytes_fromhex(PyTypeObject *type, PyObject *arg) exit: return return_value; } -/*[clinic end generated code: output=9e3374bd7d04c163 input=a9049054013a1b77]*/ + +PyDoc_STRVAR(bytes_zeros__doc__, +"zeros($type, size, /)\n" +"--\n" +"\n" +"Create a bytes object of size given by the parameter initialized with null bytes.\n" +"\n" +"Parameter must be 0 or a positive integer.\n" +"Example: bytes.zeros(3) -> b\\\'\\\\x00\\\\x00\\\\x00\'"); + +#define BYTES_ZEROS_METHODDEF \ + {"zeros", (PyCFunction)bytes_zeros, METH_O|METH_CLASS, bytes_zeros__doc__}, + +static PyObject * +bytes_zeros_impl(PyTypeObject *type, int size); + +static PyObject * +bytes_zeros(PyTypeObject *type, PyObject *arg) +{ + PyObject *return_value = NULL; + int size; + + if (!PyArg_Parse(arg, "i:zeros", &size)) { + goto exit; + } + return_value = bytes_zeros_impl(type, size); + +exit: + return return_value; +} + +PyDoc_STRVAR(bytes_byte__doc__, +"byte($type, x, /)\n" +"--\n" +"\n" +"Create a bytes object, consisting of a single byte.\n" +"\n" +"Parameter must be in range(0, 256)\n" +"bytes.byte(x) is equivalent to bytes([x])\n" +"Example: bytes.byte(3) -> b\'\\x03\'"); + +#define BYTES_BYTE_METHODDEF \ + {"byte", (PyCFunction)bytes_byte, METH_O|METH_CLASS, bytes_byte__doc__}, + +static PyObject * +bytes_byte_impl(PyTypeObject *type, int x); + +static PyObject * +bytes_byte(PyTypeObject *type, PyObject *arg) +{ + PyObject *return_value = NULL; + int x; + + if (!PyArg_Parse(arg, "i:byte", &x)) { + goto exit; + } + return_value = bytes_byte_impl(type, x); + +exit: + return return_value; +} +/*[clinic end generated code: output=13231af5ad06dec3 input=a9049054013a1b77]*/ diff --git a/Objects/memoryobject.c b/Objects/memoryobject.c index 1b95af2d4a8e8a7..06357ec76f605e9 100644 --- a/Objects/memoryobject.c +++ b/Objects/memoryobject.c @@ -2537,6 +2537,167 @@ static PySequenceMethods memory_as_sequence = { }; +/**************************************************************************/ +/* iterbytes object */ +/**************************************************************************/ + +typedef struct { + PyObject_HEAD + Py_ssize_t it_index; + PyMemoryViewObject *it_seq; /* Set to NULL when iterator is exhausted */ +} memoryview_iterobject; + +static void +memoryview_iter_dealloc(memoryview_iterobject *it) +{ + _PyObject_GC_UNTRACK(it); + Py_XDECREF(it->it_seq); + PyObject_GC_Del(it); +} + +static int +memoryview_iter_traverse(memoryview_iterobject *it, visitproc visit, void *arg) +{ + Py_VISIT(it->it_seq); + return 0; +} + +static PyObject * +memoryview_iterbytes_next(memoryview_iterobject *it) +{ + PyMemoryViewObject *seq; + PyObject *item; + Py_buffer *view; + + assert(it != NULL); + seq = it->it_seq; + if (seq == NULL) + return NULL; + assert(PyMemoryView_Check(seq)); + view = &(seq->view); + + if (it->it_index < view->shape[0]) { + char *ptr = ptr_from_index(view, it->it_index); + item = Py_BuildValue("c", (unsigned char)*ptr); + if (item != NULL) + ++it->it_index; + return item; + } + + it->it_seq = NULL; + Py_DECREF(seq); + return NULL; +} + +static PyObject * +memoryview_iter_len(memoryview_iterobject *it) +{ + Py_ssize_t len = 0; + if (it->it_seq) + len = Py_SIZE(it->it_seq) - it->it_index; + return PyLong_FromSsize_t(len); +} + +PyDoc_STRVAR(length_hint_doc, + "Private method returning an estimate of len(list(it))."); + +static PyObject * +memoryview_iter_reduce(memoryview_iterobject *it) +{ + if (it->it_seq != NULL) { + return Py_BuildValue("N(O)n", _PyObject_GetBuiltin("iter"), + it->it_seq, it->it_index); + } else { + PyObject *u = PyUnicode_FromUnicode(NULL, 0); + if (u == NULL) + return NULL; + return Py_BuildValue("N(N)", _PyObject_GetBuiltin("iter"), u); + } +} + +PyDoc_STRVAR(reduce_doc, "Return state information for pickling."); + +static PyObject * +memoryview_iter_setstate(memoryview_iterobject *it, PyObject *state) +{ + Py_ssize_t index = PyLong_AsSsize_t(state); + if (index == -1 && PyErr_Occurred()) + return NULL; + if (it->it_seq != NULL) { + if (index < 0) + index = 0; + else if (index > Py_SIZE(it->it_seq)) + index = Py_SIZE(it->it_seq); /* iterator exhausted */ + it->it_index = index; + } + Py_RETURN_NONE; +} + +PyDoc_STRVAR(setstate_doc, "Set state information for unpickling."); + +static PyMethodDef memoryview_iter_methods[] = { + {"__length_hint__", (PyCFunction)memoryview_iter_len, METH_NOARGS, + length_hint_doc}, + {"__reduce__", (PyCFunction)memoryview_iter_reduce, METH_NOARGS, + reduce_doc}, + {"__setstate__", (PyCFunction)memoryview_iter_setstate, METH_O, + setstate_doc}, + {NULL, NULL} /* sentinel */ +}; + +PyTypeObject PyMemoryViewIterBytes_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "memoryview_iterator", /* tp_name */ + sizeof(memoryview_iterobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)memoryview_iter_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ + 0, /* tp_doc */ + (traverseproc)memoryview_iter_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)memoryview_iterbytes_next, /* tp_iternext */ + memoryview_iter_methods, /* tp_methods */ + 0, +}; + +static PyObject * +memoryview_iterbytes(PyObject *seq) +{ + memoryview_iterobject *it; + + if (!PyMemoryView_Check(seq)) { + PyErr_BadInternalCall(); + return NULL; + } + it = PyObject_GC_New(memoryview_iterobject, &PyMemoryViewIterBytes_Type); + if (it == NULL) + return NULL; + it->it_index = 0; + Py_INCREF(seq); + it->it_seq = (PyMemoryViewObject *)seq; + _PyObject_GC_TRACK(it); + return (PyObject *)it; +} + + /**************************************************************************/ /* Comparisons */ /**************************************************************************/ @@ -3059,6 +3220,12 @@ PyDoc_STRVAR(memory_cast_doc, "cast($self, /, format, *, shape)\n--\n\ \n\ Cast a memoryview to a new format or shape."); +PyDoc_STRVAR(memory_iterbytes_doc, +"iterbytes($self, /)\n--\n\ +\n\ +Iterator that produces length 1 bytes objects instead of integers"); + +static PyObject *memoryview_iterbytes(PyObject *seq); static PyMethodDef memory_methods[] = { {"release", (PyCFunction)memory_release, METH_NOARGS, memory_release_doc}, @@ -3066,6 +3233,7 @@ static PyMethodDef memory_methods[] = { {"hex", (PyCFunction)memory_hex, METH_NOARGS, memory_hex_doc}, {"tolist", (PyCFunction)memory_tolist, METH_NOARGS, memory_tolist_doc}, {"cast", (PyCFunction)memory_cast, METH_VARARGS|METH_KEYWORDS, memory_cast_doc}, + {"iterbytes", (PyCFunction)memoryview_iterbytes, METH_NOARGS, memory_iterbytes_doc}, {"__enter__", memory_enter, METH_NOARGS, NULL}, {"__exit__", memory_exit, METH_VARARGS, NULL}, {NULL, NULL}