diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 04d01f4660a..8c11c1b14eb 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -1523,7 +1523,7 @@ expression support in the :mod:`re` module). at that position. -.. method:: str.expandtabs([tabsize]) +.. method:: str.expandtabs(tabsize=8) Return a copy of the string where all tab characters are replaced by one or more spaces, depending on the current column and the given tab size. Tab diff --git a/Lib/test/buffer_tests.py b/Lib/test/buffer_tests.py index cf54c287595..0a629400fd9 100644 --- a/Lib/test/buffer_tests.py +++ b/Lib/test/buffer_tests.py @@ -160,14 +160,20 @@ class MixinBytesBufferCommonTests(object): self.marshal(b'abc\rab\tdef\ng\thi').expandtabs(8)) self.assertEqual(b'abc\rab def\ng hi', self.marshal(b'abc\rab\tdef\ng\thi').expandtabs(4)) + self.assertEqual(b'abc\r\nab def\ng hi', + self.marshal(b'abc\r\nab\tdef\ng\thi').expandtabs()) + self.assertEqual(b'abc\r\nab def\ng hi', + self.marshal(b'abc\r\nab\tdef\ng\thi').expandtabs(8)) self.assertEqual(b'abc\r\nab def\ng hi', self.marshal(b'abc\r\nab\tdef\ng\thi').expandtabs(4)) - self.assertEqual(b'abc\rab def\ng hi', - self.marshal(b'abc\rab\tdef\ng\thi').expandtabs()) - self.assertEqual(b'abc\rab def\ng hi', - self.marshal(b'abc\rab\tdef\ng\thi').expandtabs(8)) self.assertEqual(b'abc\r\nab\r\ndef\ng\r\nhi', - self.marshal(b'abc\r\nab\r\ndef\ng\r\nhi').expandtabs(4)) + self.marshal(b'abc\r\nab\r\ndef\ng\r\nhi').expandtabs(4)) + # check keyword args + self.assertEqual(b'abc\rab def\ng hi', + self.marshal(b'abc\rab\tdef\ng\thi').expandtabs(tabsize=8)) + self.assertEqual(b'abc\rab def\ng hi', + self.marshal(b'abc\rab\tdef\ng\thi').expandtabs(tabsize=4)) + self.assertEqual(b' a\n b', self.marshal(b' \ta\n\tb').expandtabs(1)) self.assertRaises(TypeError, self.marshal(b'hello').expandtabs, 42, 42) diff --git a/Lib/test/string_tests.py b/Lib/test/string_tests.py index 1d70385d055..30784d4ec5b 100644 --- a/Lib/test/string_tests.py +++ b/Lib/test/string_tests.py @@ -328,13 +328,26 @@ class BaseTest: self.checkraises(TypeError, 'hello', 'upper', 42) def test_expandtabs(self): - self.checkequal('abc\rab def\ng hi', 'abc\rab\tdef\ng\thi', 'expandtabs') - self.checkequal('abc\rab def\ng hi', 'abc\rab\tdef\ng\thi', 'expandtabs', 8) - self.checkequal('abc\rab def\ng hi', 'abc\rab\tdef\ng\thi', 'expandtabs', 4) - self.checkequal('abc\r\nab def\ng hi', 'abc\r\nab\tdef\ng\thi', 'expandtabs', 4) - self.checkequal('abc\rab def\ng hi', 'abc\rab\tdef\ng\thi', 'expandtabs') - self.checkequal('abc\rab def\ng hi', 'abc\rab\tdef\ng\thi', 'expandtabs', 8) - self.checkequal('abc\r\nab\r\ndef\ng\r\nhi', 'abc\r\nab\r\ndef\ng\r\nhi', 'expandtabs', 4) + self.checkequal('abc\rab def\ng hi', 'abc\rab\tdef\ng\thi', + 'expandtabs') + self.checkequal('abc\rab def\ng hi', 'abc\rab\tdef\ng\thi', + 'expandtabs', 8) + self.checkequal('abc\rab def\ng hi', 'abc\rab\tdef\ng\thi', + 'expandtabs', 4) + self.checkequal('abc\r\nab def\ng hi', 'abc\r\nab\tdef\ng\thi', + 'expandtabs') + self.checkequal('abc\r\nab def\ng hi', 'abc\r\nab\tdef\ng\thi', + 'expandtabs', 8) + self.checkequal('abc\r\nab def\ng hi', 'abc\r\nab\tdef\ng\thi', + 'expandtabs', 4) + self.checkequal('abc\r\nab\r\ndef\ng\r\nhi', 'abc\r\nab\r\ndef\ng\r\nhi', + 'expandtabs', 4) + # check keyword args + self.checkequal('abc\rab def\ng hi', 'abc\rab\tdef\ng\thi', + 'expandtabs', tabsize=8) + self.checkequal('abc\rab def\ng hi', 'abc\rab\tdef\ng\thi', + 'expandtabs', tabsize=4) + self.checkequal(' a\n b', ' \ta\n\tb', 'expandtabs', 1) self.checkraises(TypeError, 'hello', 'expandtabs', 42, 42) diff --git a/Misc/NEWS b/Misc/NEWS index 2c0508c8afe..89c37d9ba18 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,9 @@ Projected release date: 2013-11-24 Core and Builtins ----------------- +- Issue #17806: Added keyword-argument support for "tabsize" to + str/bytes.expandtabs(). + - Issue #17828: Output type errors in str.encode(), bytes.decode() and bytearray.decode() now direct users to codecs.encode() or codecs.decode() as appropriate. diff --git a/Objects/bytearrayobject.c b/Objects/bytearrayobject.c index 2358e05d251..400da1c1aca 100644 --- a/Objects/bytearrayobject.c +++ b/Objects/bytearrayobject.c @@ -2805,7 +2805,7 @@ bytearray_methods[] = { {"count", (PyCFunction)bytearray_count, METH_VARARGS, count__doc__}, {"decode", (PyCFunction)bytearray_decode, METH_VARARGS | METH_KEYWORDS, decode_doc}, {"endswith", (PyCFunction)bytearray_endswith, METH_VARARGS, endswith__doc__}, - {"expandtabs", (PyCFunction)stringlib_expandtabs, METH_VARARGS, + {"expandtabs", (PyCFunction)stringlib_expandtabs, METH_VARARGS | METH_KEYWORDS, expandtabs__doc__}, {"extend", (PyCFunction)bytearray_extend, METH_O, extend__doc__}, {"find", (PyCFunction)bytearray_find, METH_VARARGS, find__doc__}, diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c index 0a9d04d6db7..efa0192a01d 100644 --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -2389,7 +2389,7 @@ bytes_methods[] = { {"decode", (PyCFunction)bytes_decode, METH_VARARGS | METH_KEYWORDS, decode__doc__}, {"endswith", (PyCFunction)bytes_endswith, METH_VARARGS, endswith__doc__}, - {"expandtabs", (PyCFunction)stringlib_expandtabs, METH_VARARGS, + {"expandtabs", (PyCFunction)stringlib_expandtabs, METH_VARARGS | METH_KEYWORDS, expandtabs__doc__}, {"find", (PyCFunction)bytes_find, METH_VARARGS, find__doc__}, {"fromhex", (PyCFunction)bytes_fromhex, METH_VARARGS|METH_CLASS, diff --git a/Objects/stringlib/transmogrify.h b/Objects/stringlib/transmogrify.h index 90fa129b32b..dd00976eaca 100644 --- a/Objects/stringlib/transmogrify.h +++ b/Objects/stringlib/transmogrify.h @@ -5,21 +5,23 @@ shared code in bytes_methods.c to cut down on duplicate code bloat. */ PyDoc_STRVAR(expandtabs__doc__, -"B.expandtabs([tabsize]) -> copy of B\n\ +"B.expandtabs(tabsize=8) -> copy of B\n\ \n\ Return a copy of B where all tab characters are expanded using spaces.\n\ If tabsize is not given, a tab size of 8 characters is assumed."); static PyObject* -stringlib_expandtabs(PyObject *self, PyObject *args) +stringlib_expandtabs(PyObject *self, PyObject *args, PyObject *kwds) { const char *e, *p; char *q; size_t i, j; PyObject *u; + static char *kwlist[] = {"tabsize", 0}; int tabsize = 8; - if (!PyArg_ParseTuple(args, "|i:expandtabs", &tabsize)) + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|i:expandtabs", + kwlist, &tabsize)) return NULL; /* First pass: determine size of output string */ diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 77898165a71..925d86c6cf5 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -11010,23 +11010,25 @@ unicode_encode(PyObject *self, PyObject *args, PyObject *kwargs) } PyDoc_STRVAR(expandtabs__doc__, - "S.expandtabs([tabsize]) -> str\n\ + "S.expandtabs(tabsize=8) -> str\n\ \n\ Return a copy of S where all tab characters are expanded using spaces.\n\ If tabsize is not given, a tab size of 8 characters is assumed."); static PyObject* -unicode_expandtabs(PyObject *self, PyObject *args) +unicode_expandtabs(PyObject *self, PyObject *args, PyObject *kwds) { Py_ssize_t i, j, line_pos, src_len, incr; Py_UCS4 ch; PyObject *u; void *src_data, *dest_data; + static char *kwlist[] = {"tabsize", 0}; int tabsize = 8; int kind; int found; - if (!PyArg_ParseTuple(args, "|i:expandtabs", &tabsize)) + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|i:expandtabs", + kwlist, &tabsize)) return NULL; if (PyUnicode_READY(self) == -1) @@ -13394,7 +13396,8 @@ static PyMethodDef unicode_methods[] = { {"title", (PyCFunction) unicode_title, METH_NOARGS, title__doc__}, {"center", (PyCFunction) unicode_center, METH_VARARGS, center__doc__}, {"count", (PyCFunction) unicode_count, METH_VARARGS, count__doc__}, - {"expandtabs", (PyCFunction) unicode_expandtabs, METH_VARARGS, expandtabs__doc__}, + {"expandtabs", (PyCFunction) unicode_expandtabs, + METH_VARARGS | METH_KEYWORDS, expandtabs__doc__}, {"find", (PyCFunction) unicode_find, METH_VARARGS, find__doc__}, {"partition", (PyCFunction) unicode_partition, METH_O, partition__doc__}, {"index", (PyCFunction) unicode_index, METH_VARARGS, index__doc__}, @@ -13406,7 +13409,8 @@ static PyMethodDef unicode_methods[] = { {"rjust", (PyCFunction) unicode_rjust, METH_VARARGS, rjust__doc__}, {"rstrip", (PyCFunction) unicode_rstrip, METH_VARARGS, rstrip__doc__}, {"rpartition", (PyCFunction) unicode_rpartition, METH_O, rpartition__doc__}, - {"splitlines", (PyCFunction) unicode_splitlines, METH_VARARGS | METH_KEYWORDS, splitlines__doc__}, + {"splitlines", (PyCFunction) unicode_splitlines, + METH_VARARGS | METH_KEYWORDS, splitlines__doc__}, {"strip", (PyCFunction) unicode_strip, METH_VARARGS, strip__doc__}, {"swapcase", (PyCFunction) unicode_swapcase, METH_NOARGS, swapcase__doc__}, {"translate", (PyCFunction) unicode_translate, METH_O, translate__doc__},