Issue 1519638: Now unmatched groups are replaced with empty strings in re.sub()

and re.subn().
This commit is contained in:
Serhiy Storchaka 2014-10-10 11:06:31 +03:00
parent 365e28238f
commit 7438e4b56f
5 changed files with 26 additions and 8 deletions

View File

@ -701,6 +701,9 @@ form.
.. versionchanged:: 3.1 .. versionchanged:: 3.1
Added the optional flags argument. Added the optional flags argument.
.. versionchanged:: 3.5
Unmatched groups are replaced with an empty string.
.. function:: subn(pattern, repl, string, count=0, flags=0) .. function:: subn(pattern, repl, string, count=0, flags=0)
@ -710,6 +713,9 @@ form.
.. versionchanged:: 3.1 .. versionchanged:: 3.1
Added the optional flags argument. Added the optional flags argument.
.. versionchanged:: 3.5
Unmatched groups are replaced with an empty string.
.. function:: escape(string) .. function:: escape(string)
@ -885,6 +891,8 @@ Match objects support the following methods and attributes:
(``\g<1>``, ``\g<name>``) are replaced by the contents of the (``\g<1>``, ``\g<name>``) are replaced by the contents of the
corresponding group. corresponding group.
.. versionchanged:: 3.5
Unmatched groups are replaced with an empty string.
.. method:: match.group([group1, ...]) .. method:: match.group([group1, ...])

View File

@ -223,6 +223,9 @@ re
* Number of capturing groups in regular expression is no longer limited by 100. * Number of capturing groups in regular expression is no longer limited by 100.
(Contributed by Serhiy Storchaka in :issue:`22437`.) (Contributed by Serhiy Storchaka in :issue:`22437`.)
* Now unmatched groups are replaced with empty strings in :func:`re.sub`
and :func:`re.subn`. (Contributed by Serhiy Storchaka in :issue:`1519638`.)
shutil shutil
------ ------

View File

@ -880,14 +880,12 @@ def parse_template(source, pattern):
def expand_template(template, match): def expand_template(template, match):
g = match.group g = match.group
sep = match.string[:0] empty = match.string[:0]
groups, literals = template groups, literals = template
literals = literals[:] literals = literals[:]
try: try:
for index, group in groups: for index, group in groups:
literals[index] = s = g(group) literals[index] = g(group) or empty
if s is None:
raise error("unmatched group")
except IndexError: except IndexError:
raise error("invalid group reference") raise error("invalid group reference")
return sep.join(literals) return empty.join(literals)

View File

@ -225,9 +225,11 @@ class ReTests(unittest.TestCase):
self.assertRaises(re.error, re.sub, '(?P<a>x)', '\g<a a>', 'xx') self.assertRaises(re.error, re.sub, '(?P<a>x)', '\g<a a>', 'xx')
self.assertRaises(re.error, re.sub, '(?P<a>x)', '\g<>', 'xx') self.assertRaises(re.error, re.sub, '(?P<a>x)', '\g<>', 'xx')
self.assertRaises(re.error, re.sub, '(?P<a>x)', '\g<1a1>', 'xx') self.assertRaises(re.error, re.sub, '(?P<a>x)', '\g<1a1>', 'xx')
self.assertRaises(re.error, re.sub, '(?P<a>x)', r'\g<2>', 'xx')
self.assertRaises(re.error, re.sub, '(?P<a>x)', r'\2', 'xx')
self.assertRaises(IndexError, re.sub, '(?P<a>x)', '\g<ab>', 'xx') self.assertRaises(IndexError, re.sub, '(?P<a>x)', '\g<ab>', 'xx')
self.assertRaises(re.error, re.sub, '(?P<a>x)|(?P<b>y)', '\g<b>', 'xx') self.assertEqual(re.sub('(?P<a>x)|(?P<b>y)', r'\g<b>', 'xx'), '')
self.assertRaises(re.error, re.sub, '(?P<a>x)|(?P<b>y)', '\\2', 'xx') self.assertEqual(re.sub('(?P<a>x)|(?P<b>y)', r'\2', 'xx'), '')
self.assertRaises(re.error, re.sub, '(?P<a>x)', '\g<-1>', 'xx') self.assertRaises(re.error, re.sub, '(?P<a>x)', '\g<-1>', 'xx')
# New valid/invalid identifiers in Python 3 # New valid/invalid identifiers in Python 3
self.assertEqual(re.sub('(?P<µ>x)', r'\g<µ>', 'xx'), 'xx') self.assertEqual(re.sub('(?P<µ>x)', r'\g<µ>', 'xx'), 'xx')
@ -439,6 +441,10 @@ class ReTests(unittest.TestCase):
"first second") "first second")
.expand(r"\2 \1 \g<second> \g<first>"), .expand(r"\2 \1 \g<second> \g<first>"),
"second first second first") "second first second first")
self.assertEqual(re.match("(?P<first>first)|(?P<second>second)",
"first")
.expand(r"\2 \g<second>"),
" ")
def test_repeat_minmax(self): def test_repeat_minmax(self):
self.assertIsNone(re.match("^(\w){1}$", "abc")) self.assertIsNone(re.match("^(\w){1}$", "abc"))

View File

@ -166,7 +166,10 @@ Core and Builtins
Library Library
------- -------
- Issue $18615: sndhdr.what/whathdr now return a namedtuple. - Issue 1519638: Now unmatched groups are replaced with empty strings in re.sub()
and re.subn().
- Issue #18615: sndhdr.what/whathdr now return a namedtuple.
- Issue #22462: Fix pyexpat's creation of a dummy frame to make it - Issue #22462: Fix pyexpat's creation of a dummy frame to make it
appear in exception tracebacks. appear in exception tracebacks.