From 1ce3eb5c5b4830e69b21865e2d723e22749544e0 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Wed, 1 Sep 2010 20:29:34 +0000 Subject: Issue #8990: array.fromstring() and array.tostring() get renamed to frombytes() and tobytes(), respectively, to avoid confusion. Furthermore, array.frombytes(), array.extend() as well as the array.array() constructor now accept bytearray objects. Patch by Thomas Jollans. --- Doc/library/array.rst | 34 +++++++++++---- Lib/multiprocessing/managers.py | 2 +- Lib/sre_compile.py | 2 +- Lib/test/test_array.py | 58 ++++++++++++++++++++----- Lib/test/test_file.py | 4 +- Lib/test/test_io.py | 2 +- Lib/test/test_memoryio.py | 2 +- Lib/test/test_memoryview.py | 2 +- Lib/test/test_struct.py | 8 ++-- Lib/wave.py | 2 +- Misc/NEWS | 5 +++ Modules/arraymodule.c | 94 +++++++++++++++++++++++++++++++++-------- 12 files changed, 167 insertions(+), 48 deletions(-) diff --git a/Doc/library/array.rst b/Doc/library/array.rst index 2235f08..e24a98b 100644 --- a/Doc/library/array.rst +++ b/Doc/library/array.rst @@ -60,7 +60,7 @@ The module defines the following type: appropriate type. If given a list or string, the initializer is passed to the new array's - :meth:`fromlist`, :meth:`fromstring`, or :meth:`fromunicode` method (see below) + :meth:`fromlist`, :meth:`frombytes`, or :meth:`fromunicode` method (see below) to add initial items to the array. Otherwise, the iterable initializer is passed to the :meth:`extend` method. @@ -136,6 +136,15 @@ The following data items and methods are also supported: must be the right type to be appended to the array. +.. method:: array.frombytes(s) + + Appends items from the string, interpreting the string as an array of machine + values (as if it had been read from a file using the :meth:`fromfile` method). + + .. versionadded:: 3.2 + :meth:`fromstring` is renamed to :meth:`frombytes` for clarity. + + .. method:: array.fromfile(f, n) Read *n* items (as machine values) from the file object *f* and append them to @@ -151,17 +160,16 @@ The following data items and methods are also supported: a.append(x)`` except that if there is a type error, the array is unchanged. -.. method:: array.fromstring(s) +.. method:: array.fromstring() - Appends items from the string, interpreting the string as an array of machine - values (as if it had been read from a file using the :meth:`fromfile` method). + Deprecated alias for :meth:`frombytes`. .. method:: array.fromunicode(s) Extends this array with data from the given unicode string. The array must be a type ``'u'`` array; otherwise a :exc:`ValueError` is raised. Use - ``array.fromstring(unicodestring.encode(enc))`` to append Unicode data to an + ``array.frombytes(unicodestring.encode(enc))`` to append Unicode data to an array of some other type. @@ -194,6 +202,16 @@ The following data items and methods are also supported: Reverse the order of the items in the array. +.. method:: array.tobytes() + + Convert the array to an array of machine values and return the bytes + representation (the same sequence of bytes that would be written to a file by + the :meth:`tofile` method.) + + .. versionadded:: 3.2 + :meth:`tostring` is renamed to :meth:`tobytes` for clarity. + + .. method:: array.tofile(f) Write all items (as machine values) to the file object *f*. @@ -206,15 +224,13 @@ The following data items and methods are also supported: .. method:: array.tostring() - Convert the array to an array of machine values and return the string - representation (the same sequence of bytes that would be written to a file by - the :meth:`tofile` method.) + Deprecated alias for :meth:`tobytes`. .. method:: array.tounicode() Convert the array to a unicode string. The array must be a type ``'u'`` array; - otherwise a :exc:`ValueError` is raised. Use ``array.tostring().decode(enc)`` to + otherwise a :exc:`ValueError` is raised. Use ``array.tobytes().decode(enc)`` to obtain a unicode string from an array of some other type. diff --git a/Lib/multiprocessing/managers.py b/Lib/multiprocessing/managers.py index 8faf34e..44f749b 100644 --- a/Lib/multiprocessing/managers.py +++ b/Lib/multiprocessing/managers.py @@ -32,7 +32,7 @@ from multiprocessing.util import Finalize, info # def reduce_array(a): - return array.array, (a.typecode, a.tostring()) + return array.array, (a.typecode, a.tobytes()) ForkingPickler.register(array.array, reduce_array) view_types = [type(getattr({}, name)()) for name in ('items','keys','values')] diff --git a/Lib/sre_compile.py b/Lib/sre_compile.py index 47e1701..f52ea01 100644 --- a/Lib/sre_compile.py +++ b/Lib/sre_compile.py @@ -343,7 +343,7 @@ def _optimize_unicode(charset, fixup): else: code = 'I' # Convert block indices to byte array of 256 bytes - mapping = array.array('b', mapping).tostring() + mapping = array.array('b', mapping).tobytes() # Convert byte array to word array mapping = array.array(code, mapping) assert mapping.itemsize == _sre.CODESIZE diff --git a/Lib/test/test_array.py b/Lib/test/test_array.py index d8d4ea7..d7b4fa8 100755 --- a/Lib/test/test_array.py +++ b/Lib/test/test_array.py @@ -11,6 +11,7 @@ import operator import io import math import struct +import warnings import array from array import _array_reconstructor as array_reconstructor @@ -367,15 +368,35 @@ class BaseTest(unittest.TestCase): self.assertEqual(a, b) def test_tofromstring(self): + nb_warnings = 4 + with warnings.catch_warnings(record=True) as r: + warnings.filterwarnings("always", + message=r"(to|from)string\(\) is deprecated", + category=DeprecationWarning) + a = array.array(self.typecode, 2*self.example) + b = array.array(self.typecode) + self.assertRaises(TypeError, a.tostring, 42) + self.assertRaises(TypeError, b.fromstring) + self.assertRaises(TypeError, b.fromstring, 42) + b.fromstring(a.tostring()) + self.assertEqual(a, b) + if a.itemsize>1: + self.assertRaises(ValueError, b.fromstring, "x") + nb_warnings += 1 + self.assertEqual(len(r), nb_warnings) + + def test_tofrombytes(self): a = array.array(self.typecode, 2*self.example) b = array.array(self.typecode) - self.assertRaises(TypeError, a.tostring, 42) - self.assertRaises(TypeError, b.fromstring) - self.assertRaises(TypeError, b.fromstring, 42) - b.fromstring(a.tostring()) + self.assertRaises(TypeError, a.tobytes, 42) + self.assertRaises(TypeError, b.frombytes) + self.assertRaises(TypeError, b.frombytes, 42) + b.frombytes(a.tobytes()) + c = array.array(self.typecode, bytearray(a.tobytes())) self.assertEqual(a, b) + self.assertEqual(a, c) if a.itemsize>1: - self.assertRaises(ValueError, b.fromstring, "x") + self.assertRaises(ValueError, b.frombytes, b"x") def test_repr(self): a = array.array(self.typecode, 2*self.example) @@ -898,8 +919,8 @@ class BaseTest(unittest.TestCase): a = array.array(self.typecode, self.example) m = memoryview(a) expected = m.tobytes() - self.assertEqual(a.tostring(), expected) - self.assertEqual(a.tostring()[0], expected[0]) + self.assertEqual(a.tobytes(), expected) + self.assertEqual(a.tobytes()[0], expected[0]) # Resizing is forbidden when there are buffer exports. # For issue 4509, we also check after each error that # the array was not modified. @@ -913,7 +934,7 @@ class BaseTest(unittest.TestCase): self.assertEqual(m.tobytes(), expected) self.assertRaises(BufferError, a.fromlist, a.tolist()) self.assertEqual(m.tobytes(), expected) - self.assertRaises(BufferError, a.fromstring, a.tostring()) + self.assertRaises(BufferError, a.frombytes, a.tobytes()) self.assertEqual(m.tobytes(), expected) if self.typecode == 'u': self.assertRaises(BufferError, a.fromunicode, a.tounicode()) @@ -932,7 +953,7 @@ class BaseTest(unittest.TestCase): def test_weakref(self): s = array.array(self.typecode, self.example) p = weakref.proxy(s) - self.assertEqual(p.tostring(), s.tostring()) + self.assertEqual(p.tobytes(), s.tobytes()) s = None self.assertRaises(ReferenceError, len, p) @@ -1110,6 +1131,23 @@ class UnsignedNumberTest(NumberTest): upper = int(pow(2, a.itemsize * 8)) - 1 self.check_overflow(lower, upper) + def test_bytes_extend(self): + s = bytes(self.example) + + a = array.array(self.typecode, self.example) + a.extend(s) + self.assertEqual( + a, + array.array(self.typecode, self.example+self.example) + ) + + a = array.array(self.typecode, self.example) + a.extend(bytearray(reversed(s))) + self.assertEqual( + a, + array.array(self.typecode, self.example+self.example[::-1]) + ) + class ByteTest(SignedNumberTest): typecode = 'b' @@ -1172,7 +1210,7 @@ class FPTest(NumberTest): # On alphas treating the byte swapped bit patters as # floats/doubles results in floating point exceptions # => compare the 8bit string values instead - self.assertNotEqual(a.tostring(), b.tostring()) + self.assertNotEqual(a.tobytes(), b.tobytes()) b.byteswap() self.assertEqual(a, b) diff --git a/Lib/test/test_file.py b/Lib/test/test_file.py index 6d51fd5..ebaa38b 100644 --- a/Lib/test/test_file.py +++ b/Lib/test/test_file.py @@ -44,7 +44,7 @@ class AutoFileTests(unittest.TestCase): a = array('b', b'x'*10) self.f = self.open(TESTFN, 'rb') n = self.f.readinto(a) - self.assertEquals(b'12', a.tostring()[:n]) + self.assertEquals(b'12', a.tobytes()[:n]) def testReadinto_text(self): # verify readinto refuses text files @@ -281,7 +281,7 @@ class OtherFileTests(unittest.TestCase): except ValueError: self.fail("readinto() after next() with supposedly empty " "iteration-buffer failed anyway") - line = buf.tostring() + line = buf.tobytes() if line != testline: self.fail("readinto() after next() with empty buffer " "failed. Got %r, expected %r" % (line, testline)) diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index fc109f3..76c8536 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -480,7 +480,7 @@ class IOTest(unittest.TestCase): def test_array_writes(self): a = array.array('i', range(10)) - n = len(a.tostring()) + n = len(a.tobytes()) with self.open(support.TESTFN, "wb", 0) as f: self.assertEqual(f.write(a), n) with self.open(support.TESTFN, "wb") as f: diff --git a/Lib/test/test_memoryio.py b/Lib/test/test_memoryio.py index 1e7d351..0decda5 100644 --- a/Lib/test/test_memoryio.py +++ b/Lib/test/test_memoryio.py @@ -425,7 +425,7 @@ class PyBytesIOTest(MemoryTestMixin, MemorySeekTestMixin, unittest.TestCase): a = array.array('b', b"hello world") memio = self.ioclass(buf) memio.readinto(a) - self.assertEqual(a.tostring(), b"1234567890d") + self.assertEqual(a.tobytes(), b"1234567890d") memio.close() self.assertRaises(ValueError, memio.readinto, b) diff --git a/Lib/test/test_memoryview.py b/Lib/test/test_memoryview.py index 95071b5..6ca23fc 100644 --- a/Lib/test/test_memoryview.py +++ b/Lib/test/test_memoryview.py @@ -231,7 +231,7 @@ class BaseBytesMemoryTests(AbstractMemoryTests): class BaseArrayMemoryTests(AbstractMemoryTests): ro_type = None rw_type = lambda self, b: array.array('i', list(b)) - getitem_type = lambda self, b: array.array('i', list(b)).tostring() + getitem_type = lambda self, b: array.array('i', list(b)).tobytes() itemsize = array.array('i').itemsize format = 'i' diff --git a/Lib/test/test_struct.py b/Lib/test/test_struct.py index 6ac8fdc..1151662 100644 --- a/Lib/test/test_struct.py +++ b/Lib/test/test_struct.py @@ -430,12 +430,12 @@ class StructTest(unittest.TestCase): # Test without offset s.pack_into(writable_buf, 0, test_string) - from_buf = writable_buf.tostring()[:len(test_string)] + from_buf = writable_buf.tobytes()[:len(test_string)] self.assertEqual(from_buf, test_string) # Test with offset. s.pack_into(writable_buf, 10, test_string) - from_buf = writable_buf.tostring()[:len(test_string)+10] + from_buf = writable_buf.tobytes()[:len(test_string)+10] self.assertEqual(from_buf, test_string[:10] + test_string) # Go beyond boundaries. @@ -458,12 +458,12 @@ class StructTest(unittest.TestCase): # Test without offset. pack_into(writable_buf, 0, test_string) - from_buf = writable_buf.tostring()[:len(test_string)] + from_buf = writable_buf.tobytes()[:len(test_string)] self.assertEqual(from_buf, test_string) # Test with offset. pack_into(writable_buf, 10, test_string) - from_buf = writable_buf.tostring()[:len(test_string)+10] + from_buf = writable_buf.tobytes()[:len(test_string)+10] self.assertEqual(from_buf, test_string[:10] + test_string) # Go beyond boundaries. diff --git a/Lib/wave.py b/Lib/wave.py index 950d8e2..57f9d17 100644 --- a/Lib/wave.py +++ b/Lib/wave.py @@ -248,7 +248,7 @@ class Wave_read: chunk = chunk.file chunk.size_read = chunk.size_read + nitems * self._sampwidth data.byteswap() - data = data.tostring() + data = data.tobytes() else: data = self._data_chunk.read(nframes * self._framesize) if self._convert and data: diff --git a/Misc/NEWS b/Misc/NEWS index 11b5913..2a959a4 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -149,6 +149,11 @@ Extensions Library ------- +- Issue #8990: array.fromstring() and array.tostring() get renamed to + frombytes() and tobytes(), respectively, to avoid confusion. Furthermore, + array.frombytes(), array.extend() as well as the array.array() + constructor now accept bytearray objects. Patch by Thomas Jollans. + - Issue #808164: Fixed socket.close to avoid references to globals, to avoid issues when socket.close is called from a __del__ method. diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c index 548f303..6ece49f 100644 --- a/Modules/arraymodule.c +++ b/Modules/arraymodule.c @@ -1175,7 +1175,7 @@ Reverse the order of the items in the array."); /* Forward */ -static PyObject *array_fromstring(arrayobject *self, PyObject *args); +static PyObject *array_frombytes(arrayobject *self, PyObject *args); static PyObject * array_fromfile(arrayobject *self, PyObject *args) @@ -1212,7 +1212,7 @@ array_fromfile(arrayobject *self, PyObject *args) if (args == NULL) return NULL; - res = array_fromstring(self, args); + res = array_frombytes(self, args); Py_DECREF(args); if (res == NULL) return NULL; @@ -1331,45 +1331,84 @@ PyDoc_STRVAR(tolist_doc, \n\ Convert array to an ordinary list with the same items."); - static PyObject * -array_fromstring(arrayobject *self, PyObject *args) +frombytes(arrayobject *self, Py_buffer *buffer) { - char *str; - Py_ssize_t n; int itemsize = self->ob_descr->itemsize; - if (!PyArg_ParseTuple(args, "s#:fromstring", &str, &n)) + Py_ssize_t n; + if (buffer->itemsize != 1) { + PyBuffer_Release(buffer); + PyErr_SetString(PyExc_TypeError, "string/buffer of bytes required."); return NULL; + } + n = buffer->len; if (n % itemsize != 0) { + PyBuffer_Release(buffer); PyErr_SetString(PyExc_ValueError, "string length not a multiple of item size"); return NULL; } n = n / itemsize; if (n > 0) { - Py_ssize_t old_size = Py_SIZE(self); + Py_ssize_t old_size = Py_SIZE(self); if ((n > PY_SSIZE_T_MAX - old_size) || ((old_size + n) > PY_SSIZE_T_MAX / itemsize)) { + PyBuffer_Release(buffer); return PyErr_NoMemory(); } - if (array_resize(self, old_size + n) == -1) + if (array_resize(self, old_size + n) == -1) { + PyBuffer_Release(buffer); return NULL; + } memcpy(self->ob_item + old_size * itemsize, - str, n * itemsize); + buffer->buf, n * itemsize); } + PyBuffer_Release(buffer); Py_INCREF(Py_None); return Py_None; } +static PyObject * +array_fromstring(arrayobject *self, PyObject *args) +{ + Py_buffer buffer; + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "fromstring() is deprecated. Use frombytes() instead.", 2) != 0) + return NULL; + if (!PyArg_ParseTuple(args, "s*:fromstring", &buffer)) + return NULL; + else + return frombytes(self, &buffer); +} + PyDoc_STRVAR(fromstring_doc, "fromstring(string)\n\ \n\ Appends items from the string, interpreting it as an array of machine\n\ +values, as if it had been read from a file using the fromfile() method).\n\ +\n\ +This method is deprecated. Use frombytes instead."); + + +static PyObject * +array_frombytes(arrayobject *self, PyObject *args) +{ + Py_buffer buffer; + if (!PyArg_ParseTuple(args, "y*:frombytes", &buffer)) + return NULL; + else + return frombytes(self, &buffer); +} + +PyDoc_STRVAR(frombytes_doc, +"frombytes(bytestring)\n\ +\n\ +Appends items from the string, interpreting it as an array of machine\n\ values, as if it had been read from a file using the fromfile() method)."); static PyObject * -array_tostring(arrayobject *self, PyObject *unused) +array_tobytes(arrayobject *self, PyObject *unused) { if (Py_SIZE(self) <= PY_SSIZE_T_MAX / self->ob_descr->itemsize) { return PyBytes_FromStringAndSize(self->ob_item, @@ -1379,13 +1418,30 @@ array_tostring(arrayobject *self, PyObject *unused) } } -PyDoc_STRVAR(tostring_doc, -"tostring() -> string\n\ +PyDoc_STRVAR(tobytes_doc, +"tobytes() -> bytes\n\ \n\ -Convert the array to an array of machine values and return the string\n\ +Convert the array to an array of machine values and return the bytes\n\ representation."); +static PyObject * +array_tostring(arrayobject *self, PyObject *unused) +{ + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "tostring() is deprecated. Use tobytes() instead.", 2) != 0) + return NULL; + return array_tobytes(self, unused); +} + +PyDoc_STRVAR(tostring_doc, +"tostring() -> bytes\n\ +\n\ +Convert the array to an array of machine values and return the bytes\n\ +representation.\n\ +\n\ +This method is deprecated. Use tobytes instead."); + static PyObject * array_fromunicode(arrayobject *self, PyObject *args) @@ -1420,7 +1476,7 @@ PyDoc_STRVAR(fromunicode_doc, \n\ Extends this array with data from the unicode string ustr.\n\ The array must be a unicode type array; otherwise a ValueError\n\ -is raised. Use array.fromstring(ustr.decode(...)) to\n\ +is raised. Use array.frombytes(ustr.decode(...)) to\n\ append Unicode data to an array of some other type."); @@ -1927,7 +1983,7 @@ array_reduce_ex(arrayobject *array, PyObject *value) return result; } - array_str = array_tostring(array, NULL); + array_str = array_tobytes(array, NULL); if (array_str == NULL) { Py_DECREF(dict); return NULL; @@ -1983,6 +2039,8 @@ static PyMethodDef array_methods[] = { fromlist_doc}, {"fromstring", (PyCFunction)array_fromstring, METH_VARARGS, fromstring_doc}, + {"frombytes", (PyCFunction)array_frombytes, METH_VARARGS, + frombytes_doc}, {"fromunicode", (PyCFunction)array_fromunicode, METH_VARARGS, fromunicode_doc}, {"index", (PyCFunction)array_index, METH_O, @@ -2005,6 +2063,8 @@ static PyMethodDef array_methods[] = { tolist_doc}, {"tostring", (PyCFunction)array_tostring, METH_NOARGS, tostring_doc}, + {"tobytes", (PyCFunction)array_tobytes, METH_NOARGS, + tobytes_doc}, {"tounicode", (PyCFunction)array_tounicode, METH_NOARGS, tounicode_doc}, {NULL, NULL} /* sentinel */ @@ -2386,7 +2446,7 @@ array_new(PyTypeObject *type, PyObject *args, PyObject *kwds) Py_DECREF(a); return NULL; } - v = array_fromstring((arrayobject *)a, + v = array_frombytes((arrayobject *)a, t_initial); Py_DECREF(t_initial); if (v == NULL) { -- cgit v0.12