From 865c3b257fe38154a4320c7ee6afb416f665b9c2 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 30 Oct 2019 12:03:53 +0200 Subject: [PATCH] bpo-28029: Make "".replace("", s, n) returning s for any n != 0. (GH-16981) --- Doc/whatsnew/3.9.rst | 5 +++++ Lib/test/string_tests.py | 1 + .../2019-10-29-09-38-54.bpo-28029.AmRMEF.rst | 3 +++ Objects/stringlib/transmogrify.h | 13 +++++-------- Objects/unicodeobject.c | 5 ++++- 5 files changed, 18 insertions(+), 9 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2019-10-29-09-38-54.bpo-28029.AmRMEF.rst diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst index bca22295566..d41e70808d6 100644 --- a/Doc/whatsnew/3.9.rst +++ b/Doc/whatsnew/3.9.rst @@ -99,6 +99,11 @@ Other Language Changes ignored for empty strings. (Contributed by Victor Stinner in :issue:`37388`.) +* ``"".replace("", s, n)`` now returns ``s`` instead of an empty string for + all non-zero ``n``. It is now consistent with ``"".replace("", s)``. + There are similar changes for :class:`bytes` and :class:`bytearray` objects. + (Contributed by Serhiy Storchaka in :issue:`28029`.) + New Modules =========== diff --git a/Lib/test/string_tests.py b/Lib/test/string_tests.py index 38da941f7f5..948e2a3c3c5 100644 --- a/Lib/test/string_tests.py +++ b/Lib/test/string_tests.py @@ -505,6 +505,7 @@ class BaseTest: EQ("", "", "replace", "A", "") EQ("", "", "replace", "A", "A") EQ("", "", "replace", "", "", 100) + EQ("A", "", "replace", "", "A", 100) EQ("", "", "replace", "", "", sys.maxsize) # interleave (from=="", 'to' gets inserted everywhere) diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-10-29-09-38-54.bpo-28029.AmRMEF.rst b/Misc/NEWS.d/next/Core and Builtins/2019-10-29-09-38-54.bpo-28029.AmRMEF.rst new file mode 100644 index 00000000000..420e53aec9b --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2019-10-29-09-38-54.bpo-28029.AmRMEF.rst @@ -0,0 +1,3 @@ +``"".replace("", s, n)`` now returns ``s`` instead of an empty string for +all non-zero ``n``. There are similar changes for :class:`bytes` and +:class:`bytearray` objects. diff --git a/Objects/stringlib/transmogrify.h b/Objects/stringlib/transmogrify.h index 9506019d5af..e1165ea38e8 100644 --- a/Objects/stringlib/transmogrify.h +++ b/Objects/stringlib/transmogrify.h @@ -680,9 +680,13 @@ stringlib_replace(PyObject *self, const char *to_s, Py_ssize_t to_len, Py_ssize_t maxcount) { + if (STRINGLIB_LEN(self) < from_len) { + /* nothing to do; return the original bytes */ + return return_self(self); + } if (maxcount < 0) { maxcount = PY_SSIZE_T_MAX; - } else if (maxcount == 0 || STRINGLIB_LEN(self) == 0) { + } else if (maxcount == 0) { /* nothing to do; return the original bytes */ return return_self(self); } @@ -699,13 +703,6 @@ stringlib_replace(PyObject *self, return stringlib_replace_interleave(self, to_s, to_len, maxcount); } - /* Except for b"".replace(b"", b"A") == b"A" there is no way beyond this */ - /* point for an empty self bytes to generate a non-empty bytes */ - /* Special case so the remaining code always gets a non-empty bytes */ - if (STRINGLIB_LEN(self) == 0) { - return return_self(self); - } - if (to_len == 0) { /* delete all occurrences of 'from' bytes */ if (from_len == 1) { diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 2d60627b191..5ae0af8ea33 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -10572,9 +10572,12 @@ replace(PyObject *self, PyObject *str1, int mayshrink; Py_UCS4 maxchar, maxchar_str1, maxchar_str2; + if (slen < len1) + goto nothing; + if (maxcount < 0) maxcount = PY_SSIZE_T_MAX; - else if (maxcount == 0 || slen == 0) + else if (maxcount == 0) goto nothing; if (str1 == str2)