Issue #27506: Support bytes/bytearray.translate() delete as keyword argument

Patch by Xiang Zhang.
This commit is contained in:
Martin Panter 2016-08-27 08:35:02 +00:00
parent 8c3c52b19f
commit 1b6c6da85d
7 changed files with 76 additions and 80 deletions

View File

@ -2631,8 +2631,8 @@ arbitrary binary data.
The prefix(es) to search for may be any :term:`bytes-like object`. The prefix(es) to search for may be any :term:`bytes-like object`.
.. method:: bytes.translate(table[, delete]) .. method:: bytes.translate(table, delete=b'')
bytearray.translate(table[, delete]) bytearray.translate(table, delete=b'')
Return a copy of the bytes or bytearray object where all bytes occurring in 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 the optional argument *delete* are removed, and the remaining bytes have
@ -2648,6 +2648,9 @@ arbitrary binary data.
>>> b'read this short text'.translate(None, b'aeiou') >>> b'read this short text'.translate(None, b'aeiou')
b'rd ths shrt txt' b'rd ths shrt txt'
.. versionchanged:: 3.6
*delete* is now supported as a keyword argument.
The following methods on bytes and bytearray objects have default behaviours 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 that assume the use of ASCII compatible binary formats, but can still be used

View File

@ -689,6 +689,37 @@ class BaseBytesTest:
test.support.check_free_after_iterating(self, iter, self.type2test) test.support.check_free_after_iterating(self, iter, self.type2test)
test.support.check_free_after_iterating(self, reversed, self.type2test) test.support.check_free_after_iterating(self, reversed, self.type2test)
def test_translate(self):
b = self.type2test(b'hello')
rosetta = bytearray(range(256))
rosetta[ord('o')] = ord('e')
self.assertRaises(TypeError, b.translate)
self.assertRaises(TypeError, b.translate, None, None)
self.assertRaises(ValueError, b.translate, bytes(range(255)))
c = b.translate(rosetta, b'hello')
self.assertEqual(b, b'hello')
self.assertIsInstance(c, self.type2test)
c = b.translate(rosetta)
d = b.translate(rosetta, b'')
self.assertEqual(c, d)
self.assertEqual(c, b'helle')
c = b.translate(rosetta, b'l')
self.assertEqual(c, b'hee')
c = b.translate(None, b'e')
self.assertEqual(c, b'hllo')
# test delete as a keyword argument
c = b.translate(rosetta, delete=b'')
self.assertEqual(c, b'helle')
c = b.translate(rosetta, delete=b'l')
self.assertEqual(c, b'hee')
c = b.translate(None, delete=b'e')
self.assertEqual(c, b'hllo')
class BytesTest(BaseBytesTest, unittest.TestCase): class BytesTest(BaseBytesTest, unittest.TestCase):
type2test = bytes type2test = bytes
@ -1449,24 +1480,6 @@ class AssortedBytesTest(unittest.TestCase):
self.assertRaises(SyntaxError, eval, self.assertRaises(SyntaxError, eval,
'b"%s"' % chr(c)) 'b"%s"' % chr(c))
def test_translate(self):
b = b'hello'
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')
c = ba.translate(rosetta, b'l')
self.assertEqual(ba, b'hello')
self.assertEqual(c, b'hee')
c = b.translate(None, b'e')
self.assertEqual(c, b'hllo')
c = ba.translate(None, b'e')
self.assertEqual(c, b'hllo')
self.assertRaises(TypeError, b.translate, None, None)
self.assertRaises(TypeError, ba.translate, None, None)
def test_split_bytearray(self): def test_split_bytearray(self):
self.assertEqual(b'a b'.split(memoryview(b' ')), [b'a', b'b']) self.assertEqual(b'a b'.split(memoryview(b' ')), [b'a', b'b'])

View File

@ -10,6 +10,9 @@ What's New in Python 3.6.0 beta 1
Core and Builtins Core and Builtins
----------------- -----------------
- Issue #27506: Support passing the bytes/bytearray.translate() "delete"
argument by keyword.
- Issue #27587: Fix another issue found by PVS-Studio: Null pointer check - Issue #27587: Fix another issue found by PVS-Studio: Null pointer check
after use of 'def' in _PyState_AddModule(). after use of 'def' in _PyState_AddModule().
Initial patch by Christian Heimes. Initial patch by Christian Heimes.

View File

@ -1175,21 +1175,19 @@ bytearray.translate
table: object table: object
Translation table, which must be a bytes object of length 256. 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. 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. The remaining characters are mapped through the given translation table.
[clinic start generated code]*/ [clinic start generated code]*/
static PyObject * static PyObject *
bytearray_translate_impl(PyByteArrayObject *self, PyObject *table, bytearray_translate_impl(PyByteArrayObject *self, PyObject *table,
int group_right_1, PyObject *deletechars) PyObject *deletechars)
/*[clinic end generated code: output=2bebc86a9a1ff083 input=846a01671bccc1c5]*/ /*[clinic end generated code: output=b6a8f01c2a74e446 input=cfff956d4d127a9b]*/
{ {
char *input, *output; char *input, *output;
const char *table_chars; const char *table_chars;
@ -1258,8 +1256,7 @@ bytearray_translate_impl(PyByteArrayObject *self, PyObject *table,
for (i = inlen; --i >= 0; ) { for (i = inlen; --i >= 0; ) {
c = Py_CHARMASK(*input++); c = Py_CHARMASK(*input++);
if (trans_table[c] != -1) if (trans_table[c] != -1)
if (Py_CHARMASK(*output++ = (char)trans_table[c]) == c) *output++ = (char)trans_table[c];
continue;
} }
/* Fix the size of the resulting string */ /* Fix the size of the resulting string */
if (inlen > 0) if (inlen > 0)

View File

@ -2045,21 +2045,19 @@ bytes.translate
table: object table: object
Translation table, which must be a bytes object of length 256. 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. 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. The remaining characters are mapped through the given translation table.
[clinic start generated code]*/ [clinic start generated code]*/
static PyObject * static PyObject *
bytes_translate_impl(PyBytesObject *self, PyObject *table, int group_right_1, bytes_translate_impl(PyBytesObject *self, PyObject *table,
PyObject *deletechars) PyObject *deletechars)
/*[clinic end generated code: output=233df850eb50bf8d input=ca20edf39d780d49]*/ /*[clinic end generated code: output=43be3437f1956211 input=0ecdf159f654233c]*/
{ {
char *input, *output; char *input, *output;
Py_buffer table_view = {NULL, NULL}; Py_buffer table_view = {NULL, NULL};

View File

@ -39,47 +39,38 @@ bytearray_copy(PyByteArrayObject *self, PyObject *Py_UNUSED(ignored))
} }
PyDoc_STRVAR(bytearray_translate__doc__, PyDoc_STRVAR(bytearray_translate__doc__,
"translate(table, [deletechars])\n" "translate($self, table, /, delete=b\'\')\n"
"--\n"
"\n"
"Return a copy with each character mapped by the given translation table.\n" "Return a copy with each character mapped by the given translation table.\n"
"\n" "\n"
" table\n" " table\n"
" Translation table, which must be a bytes object of length 256.\n" " Translation table, which must be a bytes object of length 256.\n"
"\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."); "The remaining characters are mapped through the given translation table.");
#define BYTEARRAY_TRANSLATE_METHODDEF \ #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 * static PyObject *
bytearray_translate_impl(PyByteArrayObject *self, PyObject *table, bytearray_translate_impl(PyByteArrayObject *self, PyObject *table,
int group_right_1, PyObject *deletechars); PyObject *deletechars);
static PyObject * static PyObject *
bytearray_translate(PyByteArrayObject *self, PyObject *args) bytearray_translate(PyByteArrayObject *self, PyObject *args, PyObject *kwargs)
{ {
PyObject *return_value = NULL; PyObject *return_value = NULL;
static const char * const _keywords[] = {"", "delete", NULL};
static _PyArg_Parser _parser = {"O|O:translate", _keywords, 0};
PyObject *table; PyObject *table;
int group_right_1 = 0;
PyObject *deletechars = NULL; PyObject *deletechars = NULL;
switch (PyTuple_GET_SIZE(args)) { if (!_PyArg_ParseTupleAndKeywordsFast(args, kwargs, &_parser,
case 1: &table, &deletechars)) {
if (!PyArg_ParseTuple(args, "O:translate", &table)) {
goto exit; goto exit;
} }
break; return_value = bytearray_translate_impl(self, table, deletechars);
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;
}
return_value = bytearray_translate_impl(self, table, group_right_1, deletechars);
exit: exit:
return return_value; return return_value;
@ -720,4 +711,4 @@ bytearray_sizeof(PyByteArrayObject *self, PyObject *Py_UNUSED(ignored))
{ {
return bytearray_sizeof_impl(self); return bytearray_sizeof_impl(self);
} }
/*[clinic end generated code: output=0af30f8c0b1ecd76 input=a9049054013a1b77]*/ /*[clinic end generated code: output=59a0c86b29ff06d1 input=a9049054013a1b77]*/

View File

@ -269,47 +269,38 @@ exit:
} }
PyDoc_STRVAR(bytes_translate__doc__, PyDoc_STRVAR(bytes_translate__doc__,
"translate(table, [deletechars])\n" "translate($self, table, /, delete=b\'\')\n"
"--\n"
"\n"
"Return a copy with each character mapped by the given translation table.\n" "Return a copy with each character mapped by the given translation table.\n"
"\n" "\n"
" table\n" " table\n"
" Translation table, which must be a bytes object of length 256.\n" " Translation table, which must be a bytes object of length 256.\n"
"\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."); "The remaining characters are mapped through the given translation table.");
#define BYTES_TRANSLATE_METHODDEF \ #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 * static PyObject *
bytes_translate_impl(PyBytesObject *self, PyObject *table, int group_right_1, bytes_translate_impl(PyBytesObject *self, PyObject *table,
PyObject *deletechars); PyObject *deletechars);
static PyObject * static PyObject *
bytes_translate(PyBytesObject *self, PyObject *args) bytes_translate(PyBytesObject *self, PyObject *args, PyObject *kwargs)
{ {
PyObject *return_value = NULL; PyObject *return_value = NULL;
static const char * const _keywords[] = {"", "delete", NULL};
static _PyArg_Parser _parser = {"O|O:translate", _keywords, 0};
PyObject *table; PyObject *table;
int group_right_1 = 0;
PyObject *deletechars = NULL; PyObject *deletechars = NULL;
switch (PyTuple_GET_SIZE(args)) { if (!_PyArg_ParseTupleAndKeywordsFast(args, kwargs, &_parser,
case 1: &table, &deletechars)) {
if (!PyArg_ParseTuple(args, "O:translate", &table)) {
goto exit; goto exit;
} }
break; return_value = bytes_translate_impl(self, table, deletechars);
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;
}
return_value = bytes_translate_impl(self, table, group_right_1, deletechars);
exit: exit:
return return_value; return return_value;
@ -508,4 +499,4 @@ bytes_fromhex(PyTypeObject *type, PyObject *arg)
exit: exit:
return return_value; return return_value;
} }
/*[clinic end generated code: output=637c2c14610d3c8d input=a9049054013a1b77]*/ /*[clinic end generated code: output=5618c05c24c1e617 input=a9049054013a1b77]*/