Repository navigation
bpo-25054, bpo-1647489: Added support of splitting on zerowidth patterns. #4471
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
09ac71a
a445ea3
33e8373
f162bb3
1ad1e67
599d54f
37055b2
00cf4b8
9c2eb1f
8dda513
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
…rns. Fixed searching patterns that could match an empty string.
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -358,6 +358,10 @@ The flags :const:`re.ASCII`, :const:`re.LOCALE` and :const:`re.UNICODE` | |
| can be set within the scope of a group. | ||
| (Contributed by Serhiy Storchaka in :issue:`31690`.) | ||
|
|
||
| :func:`re.split` now supports splitting on a pattern that matches an empty | ||
| string like ``r'\b'``, ``'^$'`` or ``(?=-)``. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. a pattern like . . . that matches an empty string.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
| (Contributed by Serhiy Storchaka in :issue:`25054`.) | ||
|
|
||
| string | ||
| ------ | ||
|
|
||
|
|
@@ -725,6 +729,15 @@ Changes in the Python API | |
| avoid a warning escape them with a backslash. | ||
| (Contributed by Serhiy Storchaka in :issue:`30349`.) | ||
|
|
||
| * The result of splitting a string on a :mod:`regular expression <re>` | ||
| that could match an empty string like has been changed. For example | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Drop like
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
| splitting on ``r'\s*'`` will now split not only on whitespaces as it | ||
| did previously, but also between any pair of non-whitespace | ||
| characters. The previous behavior can be restored by changing the pattern | ||
| to ``r'\s+'``. A :exc:`FutureWarning` was emitted for such patterns since | ||
| Python 3.5. | ||
| (Contributed by Serhiy Storchaka in :issue:`25054`.) | ||
|
|
||
| .. _Unicode Technical Standard #18: https://unicode.org/reports/tr18/ | ||
|
|
||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -331,21 +331,21 @@ def test_re_split(self): | |
| ['', 'a', '', '', 'c']) | ||
|
|
||
| for sep, expected in [ | ||
| (':*', ['', 'a', 'b', 'c']), | ||
| ('(?::*)', ['', 'a', 'b', 'c']), | ||
| ('(:*)', ['', ':', 'a', ':', 'b', '::', 'c']), | ||
| ('(:)*', ['', ':', 'a', ':', 'b', ':', 'c']), | ||
| (':*', ['', 'a', 'b', 'c', '']), | ||
| ('(?::*)', ['', 'a', 'b', 'c', '']), | ||
| ('(:*)', ['', ':', 'a', ':', 'b', '::', 'c', '', '']), | ||
| ('(:)*', ['', ':', 'a', ':', 'b', ':', 'c', None, '']), | ||
| ]: | ||
| with self.subTest(sep=sep), self.assertWarns(FutureWarning): | ||
| with self.subTest(sep=sep): | ||
| self.assertTypedEqual(re.split(sep, ':a:b::c'), expected) | ||
|
|
||
| for sep, expected in [ | ||
| ('', [':a:b::c']), | ||
| (r'\b', [':a:b::c']), | ||
| (r'(?=:)', [':a:b::c']), | ||
| (r'(?<=:)', [':a:b::c']), | ||
| ('', ['', ':', 'a', ':', 'b', ':', ':', 'c', '']), | ||
| (r'\b', [':', 'a', ':', 'b', '::', 'c', '']), | ||
| (r'(?=:)', ['', ':a', ':b', ':', ':c']), | ||
| (r'(?<=:)', [':', 'a:', 'b:', ':', 'c']), | ||
| ]: | ||
| with self.subTest(sep=sep), self.assertRaises(ValueError): | ||
| with self.subTest(sep=sep): | ||
| self.assertTypedEqual(re.split(sep, ':a:b::c'), expected) | ||
|
|
||
| def test_qualified_re_split(self): | ||
|
|
@@ -356,9 +356,8 @@ def test_qualified_re_split(self): | |
| ['', ':', 'a', ':', 'b::c']) | ||
| self.assertEqual(re.split("(:+)", ":a:b::c", maxsplit=2), | ||
| ['', ':', 'a', ':', 'b::c']) | ||
| with self.assertWarns(FutureWarning): | ||
| self.assertEqual(re.split("(:*)", ":a:b::c", maxsplit=2), | ||
| ['', ':', 'a', ':', 'b::c']) | ||
| self.assertEqual(re.split("(:*)", ":a:b::c", maxsplit=2), | ||
| ['', ':', 'a', ':', 'b::c']) | ||
|
|
||
| def test_re_findall(self): | ||
| self.assertEqual(re.findall(":+", "abc"), []) | ||
|
|
@@ -1751,6 +1750,19 @@ def test_match_repr(self): | |
| "span=(3, 5), match='bb'>" % | ||
| (type(second).__module__, type(second).__qualname__)) | ||
|
|
||
| def test_zerowidth(self): | ||
| # Issues 852532, 1647489, 3262, 25054. | ||
| self.assertEqual(re.split(r"\b", "a::bc"), ['', 'a', '::', 'bc', '']) | ||
| self.assertEqual(re.split(r"\b|:+", "a::bc"), ['', 'a', '', 'bc', '']) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps break this down so I can infer what is going on here :) re.split(r"\b|:", "a:") # How many matches after "a"?
re.split(r"\b|:", ":b") # Is there an empty match before "b"?
re.split(r":??", ":") # Does it match the colon?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
But the main purpose of this test is testing that the new behavior differs from the old one. In older Python (2.7 and 3.4) |
||
| self.assertEqual(re.sub(r"\b", "-", "a::bc"), '-a-::-bc-') | ||
| self.assertEqual(re.sub(r"\b|:+", "-", "a::bc"), '-a--bc-') | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps use a callback to verify that the second match is empty, not the third.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll use the replacement template that includes the matched string. |
||
| self.assertEqual(re.findall(r"\b|:+", "a::bc"), ['', '', '::', '', '']) | ||
| self.assertEqual(re.findall(r"\b|\w+", "a::bc"), | ||
| ['', 'a', '', '', 'bc', '']) | ||
| self.assertEqual([m.span() for m in re.finditer(r"\b|:+", "a::bc")], | ||
| [(0, 0), (1, 1), (1, 3), (3, 3), (5, 5)]) | ||
| self.assertEqual([m.span() for m in re.finditer(r"\b|\w+", "a::bc")], | ||
| [(0, 0), (0, 1), (1, 1), (3, 3), (3, 5), (5, 5)]) | ||
|
|
||
| def test_bug_2537(self): | ||
| # issue 2537: empty submatches | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| Added support of splitting on a pattern that could match an empty string. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| Fixed searching regular expression patterns that could match an empty | ||
| string. Non-empty string can now be correctly found after matching an empty | ||
| string. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An example with a shorter result may be easier to understand:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wanted to use an example similar to the above examples (but with
*instead of+) for taking the example of the incorrect usage which "worked" in previous versions due to a bug. I'll simplify the example.