diff -r 24f86799e9d3 Lib/sre_parse.py --- a/Lib/sre_parse.py Sat Nov 29 23:06:22 2014 -0500 +++ b/Lib/sre_parse.py Sun Nov 30 17:29:07 2014 +0200 @@ -68,6 +68,7 @@ class Pattern: self.flags = 0 self.groupdict = {} self.subpatterns = [None] # group 0 + self.lookbehindgroup = 0 @property def groups(self): return len(self.subpatterns) @@ -86,6 +87,14 @@ class Pattern: def checkgroup(self, gid): return gid < self.groups and self.subpatterns[gid] is not None + def checklookbehindgroup(self, gid): + if self.lookbehindgroup: + if not self.checkgroup(gid): + raise error('cannot refer to open group') + if gid >= self.lookbehindgroup: + raise error('cannot refer to group defined in the same ' + 'lookbehind subpattern') + class SubPattern: # a subpattern, in intermediate form def __init__(self, pattern, data=None): @@ -314,6 +323,7 @@ def _escape(source, escape, state): if group < state.groups: if not state.checkgroup(group): raise error, "cannot refer to open group" + state.checklookbehindgroup(group) return GROUPREF, group raise ValueError if len(escape) == 2: @@ -593,6 +603,7 @@ def _parse(source, state): if gid is None: msg = "unknown group name: {0!r}".format(name) raise error(msg) + state.checklookbehindgroup(gid) subpatternappend((GROUPREF, gid)) continue else: @@ -621,7 +632,11 @@ def _parse(source, state): raise error, "syntax error" dir = -1 # lookbehind char = sourceget() + lookbehindgroup = state.lookbehindgroup + state.lookbehindgroup = state.groups p = _parse_sub(source, state) + if dir < 0: + state.lookbehindgroup = lookbehindgroup if not sourcematch(")"): raise error, "unbalanced parenthesis" if char == "=": @@ -652,6 +667,7 @@ def _parse(source, state): condgroup = int(condname) except ValueError: raise error, "bad character in group name" + state.checklookbehindgroup(condgroup) else: # flags if not source.next in FLAGS: diff -r 24f86799e9d3 Lib/test/test_re.py --- a/Lib/test/test_re.py Sat Nov 29 23:06:22 2014 -0500 +++ b/Lib/test/test_re.py Sun Nov 30 17:29:07 2014 +0200 @@ -494,10 +494,13 @@ class ReTests(unittest.TestCase): self.assertIsNone(re.match('(?:(a)|(x))b(?<=(?(1)c|x))c', 'abc')) self.assertTrue(re.match('(?:(a)|(x))b(?<=(?(1)b|x))c', 'abc')) # Group used before defined. - self.assertIsNone(re.match('(a)b(?<=(?(2)x|c))(c)', 'abc')) - self.assertIsNone(re.match('(a)b(?<=(?(2)b|x))(c)', 'abc')) + self.assertRaises(re.error, re.compile, '(a)b(?<=(?(2)b|x))(c)') self.assertIsNone(re.match('(a)b(?<=(?(1)c|x))(c)', 'abc')) self.assertTrue(re.match('(a)b(?<=(?(1)b|x))(c)', 'abc')) + # Group defined in the same lookbehind pattern + self.assertRaises(re.error, re.compile, r'(a)b(?<=(.)\2)(c)') + self.assertRaises(re.error, re.compile, '(a)b(?<=(?P.)(?P=a))(c)') + self.assertRaises(re.error, re.compile, '(a)b(?<=(a)(?(2)b|x))(c)') def test_ignore_case(self): self.assertEqual(re.match("abc", "ABC", re.I).group(0), "ABC")