# HG changeset patch # Parent 2f0716009132ad56aa1718aeaf072afd9c209076 Issue #27506: Make bytes.translate() table optional, support delete keyword diff -r 2f0716009132 Doc/library/stdtypes.rst --- a/Doc/library/stdtypes.rst Tue Jul 12 18:24:25 2016 -0400 +++ b/Doc/library/stdtypes.rst Sat Jul 23 13:04:27 2016 +0000 @@ -2631,8 +2631,8 @@ The prefix(es) to search for may be any :term:`bytes-like object`. -.. method:: bytes.translate(table[, delete]) - bytearray.translate(table[, delete]) +.. method:: bytes.translate([table, ]delete=b'') + bytearray.translate([table, ]delete=b'') Return a copy of the bytes or bytearray object where all bytes occurring in the optional argument *delete* are removed, and the remaining bytes have @@ -2642,12 +2642,16 @@ You can use the :func:`bytes.maketrans` method to create a translation table. - Set the *table* argument to ``None`` for translations that only delete - characters:: - - >>> b'read this short text'.translate(None, b'aeiou') + The *table* argument may be omitted (or set to ``None``) for + translations that only delete characters:: + + >>> b'read this short text'.translate(delete=b'aeiou') b'rd ths shrt txt' + .. versionadded:: 3.6 + *table* is now optional, and *delete* is now supported as a + keyword argument. + The following methods on bytes and bytearray objects have default behaviours that assume the use of ASCII compatible binary formats, but can still be used diff -r 2f0716009132 Lib/test/test_bytes.py --- a/Lib/test/test_bytes.py Tue Jul 12 18:24:25 2016 -0400 +++ b/Lib/test/test_bytes.py Sat Jul 23 13:04:27 2016 +0000 @@ -1424,6 +1424,7 @@ ba = bytearray(b) rosetta = bytearray(range(0, 256)) rosetta[ord('o')] = ord('e') + c = b.translate(rosetta, b'l') self.assertEqual(b, b'hello') self.assertEqual(c, b'hee') @@ -1437,6 +1438,35 @@ self.assertRaises(TypeError, b.translate, None, None) self.assertRaises(TypeError, ba.translate, None, None) + self.assertRaises(ValueError, b.translate, bytes(range(255))) + self.assertRaises(ValueError, ba.translate, bytes(range(255))) + c = b.translate(rosetta) + self.assertEqual(c, b'helle') + c = ba.translate(rosetta) + self.assertEqual(c, b'helle') + c = b.translate(rosetta, b'') + self.assertEqual(c, b'helle') + c = ba.translate(rosetta, b'') + self.assertEqual(c, b'helle') + c = b.translate() + self.assertEqual(c, b'hello') + c = ba.translate() + self.assertEqual(c, b'hello') + + # test delete as a keyword argument + c = b.translate(rosetta, delete=b'l') + self.assertEqual(c, b'hee') + c = ba.translate(rosetta, delete=b'l') + self.assertEqual(c, b'hee') + c = b.translate(None, delete=b'l') + self.assertEqual(c, b'heo') + c = ba.translate(None, delete=b'l') + self.assertEqual(c, b'heo') + c = b.translate(delete=b'l') + self.assertEqual(c, b'heo') + c = ba.translate(delete=b'l') + self.assertEqual(c, b'heo') + def test_split_bytearray(self): self.assertEqual(b'a b'.split(memoryview(b' ')), [b'a', b'b']) diff -r 2f0716009132 Objects/bytearrayobject.c --- a/Objects/bytearrayobject.c Tue Jul 12 18:24:25 2016 -0400 +++ b/Objects/bytearrayobject.c Sat Jul 23 13:04:27 2016 +0000 @@ -1175,23 +1175,21 @@ /*[clinic input] bytearray.translate - table: object + table: object = None Translation table, which must be a bytes object of length 256. - [ - deletechars: object - ] / + delete as deletechars: object(c_default="NULL") = b'' Return a copy with each character mapped by the given translation table. -All characters occurring in the optional argument deletechars are removed. +All characters occurring in the optional argument delete are removed. The remaining characters are mapped through the given translation table. [clinic start generated code]*/ static PyObject * bytearray_translate_impl(PyByteArrayObject *self, PyObject *table, - int group_right_1, PyObject *deletechars) -/*[clinic end generated code: output=2bebc86a9a1ff083 input=846a01671bccc1c5]*/ + PyObject *deletechars) +/*[clinic end generated code: output=b6a8f01c2a74e446 input=3579ac52f2e92b5e]*/ { char *input, *output; const char *table_chars; @@ -1260,8 +1258,7 @@ for (i = inlen; --i >= 0; ) { c = Py_CHARMASK(*input++); if (trans_table[c] != -1) - if (Py_CHARMASK(*output++ = (char)trans_table[c]) == c) - continue; + *output++ = (char)trans_table[c]; } /* Fix the size of the resulting string */ if (inlen > 0) diff -r 2f0716009132 Objects/bytesobject.c --- a/Objects/bytesobject.c Tue Jul 12 18:24:25 2016 -0400 +++ b/Objects/bytesobject.c Sat Jul 23 13:04:27 2016 +0000 @@ -2043,23 +2043,21 @@ /*[clinic input] bytes.translate - table: object + table: object = None Translation table, which must be a bytes object of length 256. - [ - deletechars: object - ] / + delete as deletechars: object(c_default="NULL") = b'' Return a copy with each character mapped by the given translation table. -All characters occurring in the optional argument deletechars are removed. +All characters occurring in the optional argument delete are removed. The remaining characters are mapped through the given translation table. [clinic start generated code]*/ static PyObject * -bytes_translate_impl(PyBytesObject *self, PyObject *table, int group_right_1, +bytes_translate_impl(PyBytesObject *self, PyObject *table, PyObject *deletechars) -/*[clinic end generated code: output=233df850eb50bf8d input=ca20edf39d780d49]*/ +/*[clinic end generated code: output=43be3437f1956211 input=63cf44ea510e147d]*/ { char *input, *output; Py_buffer table_view = {NULL, NULL}; @@ -2123,12 +2121,14 @@ output_start = output = PyBytes_AS_STRING(result); input = PyBytes_AS_STRING(input_obj); - if (dellen == 0 && table_chars != NULL) { + if (dellen == 0) { /* If no deletions are required, use faster code */ - for (i = inlen; --i >= 0; ) { - c = Py_CHARMASK(*input++); - if (Py_CHARMASK((*output++ = table_chars[c])) != c) - changed = 1; + if (table_chars != NULL) { + for (i = inlen; --i >= 0; ) { + c = Py_CHARMASK(*input++); + if (Py_CHARMASK((*output++ = table_chars[c])) != c) + changed = 1; + } } if (!changed && PyBytes_CheckExact(input_obj)) { Py_INCREF(input_obj); diff -r 2f0716009132 Objects/clinic/bytearrayobject.c.h --- a/Objects/clinic/bytearrayobject.c.h Tue Jul 12 18:24:25 2016 -0400 +++ b/Objects/clinic/bytearrayobject.c.h Sat Jul 23 13:04:27 2016 +0000 @@ -39,47 +39,37 @@ } PyDoc_STRVAR(bytearray_translate__doc__, -"translate(table, [deletechars])\n" +"translate($self, table=None, /, delete=b\'\')\n" +"--\n" +"\n" "Return a copy with each character mapped by the given translation table.\n" "\n" " table\n" " Translation table, which must be a bytes object of length 256.\n" "\n" -"All characters occurring in the optional argument deletechars are removed.\n" +"All characters occurring in the optional argument delete are removed.\n" "The remaining characters are mapped through the given translation table."); #define BYTEARRAY_TRANSLATE_METHODDEF \ - {"translate", (PyCFunction)bytearray_translate, METH_VARARGS, bytearray_translate__doc__}, + {"translate", (PyCFunction)bytearray_translate, METH_VARARGS|METH_KEYWORDS, bytearray_translate__doc__}, static PyObject * bytearray_translate_impl(PyByteArrayObject *self, PyObject *table, - int group_right_1, PyObject *deletechars); + PyObject *deletechars); static PyObject * -bytearray_translate(PyByteArrayObject *self, PyObject *args) +bytearray_translate(PyByteArrayObject *self, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; - PyObject *table; - int group_right_1 = 0; + static char *_keywords[] = {"", "delete", NULL}; + PyObject *table = Py_None; PyObject *deletechars = NULL; - switch (PyTuple_GET_SIZE(args)) { - case 1: - if (!PyArg_ParseTuple(args, "O:translate", &table)) { - goto exit; - } - break; - case 2: - if (!PyArg_ParseTuple(args, "OO:translate", &table, &deletechars)) { - goto exit; - } - group_right_1 = 1; - break; - default: - PyErr_SetString(PyExc_TypeError, "bytearray.translate requires 1 to 2 arguments"); - goto exit; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OO:translate", _keywords, + &table, &deletechars)) { + goto exit; } - return_value = bytearray_translate_impl(self, table, group_right_1, deletechars); + return_value = bytearray_translate_impl(self, table, deletechars); exit: return return_value; @@ -716,4 +706,4 @@ { return bytearray_sizeof_impl(self); } -/*[clinic end generated code: output=a32f183ebef159cc input=a9049054013a1b77]*/ +/*[clinic end generated code: output=440ca89f3c48384e input=a9049054013a1b77]*/ diff -r 2f0716009132 Objects/clinic/bytesobject.c.h --- a/Objects/clinic/bytesobject.c.h Tue Jul 12 18:24:25 2016 -0400 +++ b/Objects/clinic/bytesobject.c.h Sat Jul 23 13:04:27 2016 +0000 @@ -267,47 +267,37 @@ } PyDoc_STRVAR(bytes_translate__doc__, -"translate(table, [deletechars])\n" +"translate($self, table=None, /, delete=b\'\')\n" +"--\n" +"\n" "Return a copy with each character mapped by the given translation table.\n" "\n" " table\n" " Translation table, which must be a bytes object of length 256.\n" "\n" -"All characters occurring in the optional argument deletechars are removed.\n" +"All characters occurring in the optional argument delete are removed.\n" "The remaining characters are mapped through the given translation table."); #define BYTES_TRANSLATE_METHODDEF \ - {"translate", (PyCFunction)bytes_translate, METH_VARARGS, bytes_translate__doc__}, + {"translate", (PyCFunction)bytes_translate, METH_VARARGS|METH_KEYWORDS, bytes_translate__doc__}, static PyObject * -bytes_translate_impl(PyBytesObject *self, PyObject *table, int group_right_1, +bytes_translate_impl(PyBytesObject *self, PyObject *table, PyObject *deletechars); static PyObject * -bytes_translate(PyBytesObject *self, PyObject *args) +bytes_translate(PyBytesObject *self, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; - PyObject *table; - int group_right_1 = 0; + static char *_keywords[] = {"", "delete", NULL}; + PyObject *table = Py_None; PyObject *deletechars = NULL; - switch (PyTuple_GET_SIZE(args)) { - case 1: - if (!PyArg_ParseTuple(args, "O:translate", &table)) { - goto exit; - } - break; - case 2: - if (!PyArg_ParseTuple(args, "OO:translate", &table, &deletechars)) { - goto exit; - } - group_right_1 = 1; - break; - default: - PyErr_SetString(PyExc_TypeError, "bytes.translate requires 1 to 2 arguments"); - goto exit; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OO:translate", _keywords, + &table, &deletechars)) { + goto exit; } - return_value = bytes_translate_impl(self, table, group_right_1, deletechars); + return_value = bytes_translate_impl(self, table, deletechars); exit: return return_value; @@ -504,4 +494,4 @@ exit: return return_value; } -/*[clinic end generated code: output=6fe884a74e7d49cf input=a9049054013a1b77]*/ +/*[clinic end generated code: output=596a9cb71b613c7e input=a9049054013a1b77]*/