mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	Issue #27506: Support bytes/bytearray.translate() delete as keyword argument
Patch by Xiang Zhang.
This commit is contained in:
		
							parent
							
								
									8c3c52b19f
								
							
						
					
					
						commit
						1b6c6da85d
					
				
					 7 changed files with 76 additions and 80 deletions
				
			
		|  | @ -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 | ||||||
|  |  | ||||||
|  | @ -689,6 +689,37 @@ def test_free_after_iterating(self): | ||||||
|         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 @@ def test_literal(self): | ||||||
|             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']) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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. | ||||||
|  |  | ||||||
|  | @ -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) | ||||||
|  |  | ||||||
|  | @ -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}; | ||||||
|  |  | ||||||
|  | @ -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]*/ | ||||||
|  |  | ||||||
|  | @ -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]*/ | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Martin Panter
						Martin Panter