diff -r fb93a04832df Doc/library/re.rst --- a/Doc/library/re.rst Sun Sep 21 22:59:06 2014 +0300 +++ b/Doc/library/re.rst Sun Sep 21 23:40:50 2014 +0300 @@ -255,6 +255,7 @@ The special characters are: | Context of reference to group "quote" | Ways to reference it | +=======================================+==================================+ | in the same pattern itself | * ``(?P=quote)`` (as shown) | + | | * ``(?P=1)`` | | | * ``\1`` | +---------------------------------------+----------------------------------+ | when processing match object ``m`` | * ``m.group('quote')`` | @@ -265,9 +266,12 @@ The special characters are: | | * ``\1`` | +---------------------------------------+----------------------------------+ -``(?P=name)`` - A backreference to a named group; it matches whatever text was matched by the - earlier group named *name*. +``(?P=name)``, ``(?P=number)`` + A backreference to a group; it matches whatever text was matched by the + earlier group named *name* or numbered *number*. + + .. versionchanged:: 3.5 + Added support of group numbers. ``(?#...)`` A comment; the contents of the parentheses are simply ignored. diff -r fb93a04832df Lib/sre_compile.py --- a/Lib/sre_compile.py Sun Sep 21 22:59:06 2014 +0300 +++ b/Lib/sre_compile.py Sun Sep 21 23:40:50 2014 +0300 @@ -470,12 +470,6 @@ def compile(p, flags=0): # print code - # XXX: get rid of this limitation! - if p.pattern.groups > 100: - raise AssertionError( - "sorry, but this version only supports 100 named groups" - ) - # map in either direction groupindex = p.pattern.groupdict indexgroup = [None] * p.pattern.groups diff -r fb93a04832df Lib/sre_constants.py --- a/Lib/sre_constants.py Sun Sep 21 22:59:06 2014 +0300 +++ b/Lib/sre_constants.py Sun Sep 21 23:40:50 2014 +0300 @@ -15,7 +15,7 @@ MAGIC = 20031017 -from _sre import MAXREPEAT +from _sre import MAXREPEAT, MAXGROUPS # SRE standard exception (access as sre.error) # should this really be here? diff -r fb93a04832df Lib/sre_parse.py --- a/Lib/sre_parse.py Sun Sep 21 22:59:06 2014 +0300 +++ b/Lib/sre_parse.py Sun Sep 21 23:40:50 2014 +0300 @@ -72,6 +72,8 @@ class Pattern: def opengroup(self, name=None): gid = self.groups self.groups = gid + 1 + if self.groups > MAXGROUPS: + raise error("groups number is too large") if name is not None: ogid = self.groupdict.get(name, None) if ogid is not None: @@ -623,13 +625,23 @@ def _parse(source, state): name = name + char if not name: raise error("missing group name") - if not name.isidentifier(): - raise error("bad character in backref group name " - "%r" % name) - gid = state.groupdict.get(name) - if gid is None: - msg = "unknown group name: {0!r}".format(name) - raise error(msg) + if name.isidentifier(): + gid = state.groupdict.get(name) + if gid is None: + msg = "unknown group name: {0!r}".format(name) + raise error(msg) + else: + try: + gid = int(name) + if gid < 0: + raise ValueError + except ValueError: + raise error("bad character in backref group name " + "%r" % name) from None + if not gid: + raise error("bad group number") + if gid >= MAXGROUPS: + raise error("the group number is too large") subpatternappend((GROUPREF, gid)) continue else: @@ -687,8 +699,14 @@ def _parse(source, state): else: try: condgroup = int(condname) + if condgroup < 0: + raise ValueError except ValueError: raise error("bad character in group name") + if not condgroup: + raise error("bad group number") + if condgroup >= MAXGROUPS: + raise error("the group number is too large") else: # flags if not source.next in FLAGS: @@ -814,6 +832,8 @@ def parse_template(source, pattern): index = int(name) if index < 0: raise error("negative group number") + if index >= MAXGROUPS: + raise error("the group number is too large") except ValueError: if not name.isidentifier(): raise error("bad character in group name") diff -r fb93a04832df Lib/test/test_re.py --- a/Lib/test/test_re.py Sun Sep 21 22:59:06 2014 +0300 +++ b/Lib/test/test_re.py Sun Sep 21 23:40:50 2014 +0300 @@ -193,10 +193,12 @@ class ReTests(unittest.TestCase): def test_symbolic_groups(self): re.compile('(?Px)(?P=a)(?(a)y)') re.compile('(?Px)(?P=a1)(?(a1)y)') + re.compile('(?Px)(?P=1)(?(1)y)') self.assertRaises(re.error, re.compile, '(?P)(?P)') self.assertRaises(re.error, re.compile, '(?Px)') self.assertRaises(re.error, re.compile, '(?P=)') - self.assertRaises(re.error, re.compile, '(?P=1)') + self.assertRaises(re.error, re.compile, '(?P=0)') + self.assertRaises(re.error, re.compile, '(?P=-1)') self.assertRaises(re.error, re.compile, '(?P=a)') self.assertRaises(re.error, re.compile, '(?P=a1)') self.assertRaises(re.error, re.compile, '(?P=a.)') @@ -212,6 +214,10 @@ class ReTests(unittest.TestCase): re.compile('(?P<ยต>x)(?P=ยต)(?(ยต)y)') re.compile('(?P<๐”˜๐”ซ๐”ฆ๐” ๐”ฌ๐”ก๐”ข>x)(?P=๐”˜๐”ซ๐”ฆ๐” ๐”ฌ๐”ก๐”ข)(?(๐”˜๐”ซ๐”ฆ๐” ๐”ฌ๐”ก๐”ข)y)') self.assertRaises(re.error, re.compile, '(?P<ยฉ>x)') + # Support > 100 groups. + pat = '|'.join('x(?P%x)y' % (i, i) for i in range(1, 200 + 1)) + pat = '(?:%s)(?P=200)' % pat + self.assertEqual(re.match(pat, 'xc8yc8').span(), (0, 6)) def test_symbolic_refs(self): self.assertRaises(re.error, re.sub, '(?Px)', '\gx)', r'\g<ยต>', 'xx'), 'xx') self.assertEqual(re.sub('(?P<๐”˜๐”ซ๐”ฆ๐” ๐”ฌ๐”ก๐”ข>x)', r'\g<๐”˜๐”ซ๐”ฆ๐” ๐”ฌ๐”ก๐”ข>', 'xx'), 'xx') self.assertRaises(re.error, re.sub, '(?Px)', r'\g<ยฉ>', 'xx') + # Support > 100 groups. + pat = '|'.join('x(?P%x)y' % (i, i) for i in range(1, 200 + 1)) + self.assertEqual(re.sub(pat, '\g<200>', 'xc8yzxc8y'), 'c8zc8') def test_re_subn(self): self.assertEqual(re.subn("(?i)b+", "x", "bbbb BBBB"), ('x x', 2)) @@ -404,6 +413,10 @@ class ReTests(unittest.TestCase): self.assertIsNone(p.match('abd')) self.assertIsNone(p.match('ac')) + # Support > 100 groups. + pat = '|'.join('x(?P%x)y' % (i, i) for i in range(1, 200 + 1)) + pat = '(?:%s)(?(200)z)' % pat + self.assertEqual(re.match(pat, 'xc8yz').span(), (0, 5)) def test_re_groupref(self): self.assertEqual(re.match(r'^(\|)?([^()]+)\1$', '|a|').groups(), @@ -1068,8 +1081,10 @@ class ReTests(unittest.TestCase): # a RuntimeError is raised instead of OverflowError. long_overflow = 2**128 self.assertRaises(TypeError, re.finditer, "a", {}) - self.assertRaises(OverflowError, _sre.compile, "abc", 0, [long_overflow]) - self.assertRaises(TypeError, _sre.compile, {}, 0, []) + with self.assertRaises(OverflowError): + _sre.compile("abc", 0, [long_overflow], 0, [], []) + with self.assertRaises(TypeError): + _sre.compile({}, 0, [], 0, [], []) def test_search_dot_unicode(self): self.assertTrue(re.search("123.*-", '123abc-')) diff -r fb93a04832df Modules/_sre.c --- a/Modules/_sre.c Sun Sep 21 22:59:06 2014 +0300 +++ b/Modules/_sre.c Sun Sep 21 23:40:50 2014 +0300 @@ -357,6 +357,11 @@ state_init(SRE_STATE* state, PatternObje memset(state, 0, sizeof(SRE_STATE)); + state->mark = PyMem_New(void *, pattern->groups * 2); + if (!state->mark) { + PyErr_NoMemory(); + goto err; + } state->lastmark = -1; state->lastindex = -1; @@ -409,6 +414,8 @@ state_init(SRE_STATE* state, PatternObje return string; err: + PyMem_Del(state->mark); + state->mark = NULL; if (state->buffer.buf) PyBuffer_Release(&state->buffer); return NULL; @@ -421,6 +428,8 @@ state_fini(SRE_STATE* state) PyBuffer_Release(&state->buffer); Py_XDECREF(state->string); data_stack_dealloc(state); + PyMem_Del(state->mark); + state->mark = NULL; } /* calculate offset from start of string */ @@ -560,6 +569,7 @@ pattern_match(PatternObject *self, PyObj PyObject *pattern = NULL; SRE_STATE state; Py_ssize_t status; + PyObject *match; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|Onn$O:match", _keywords, @@ -579,12 +589,14 @@ pattern_match(PatternObject *self, PyObj status = sre_match(&state, PatternObject_GetCode(self), 0); TRACE(("|%p|%p|END\n", PatternObject_GetCode(self), state.ptr)); - if (PyErr_Occurred()) + if (PyErr_Occurred()) { + state_fini(&state); return NULL; - + } + + match = pattern_new_match(self, &state, status); state_fini(&state); - - return (PyObject *)pattern_new_match(self, &state, status); + return match; } static PyObject* @@ -592,6 +604,7 @@ pattern_fullmatch(PatternObject* self, P { SRE_STATE state; Py_ssize_t status; + PyObject *match; PyObject *string = NULL, *string2 = NULL; Py_ssize_t start = 0; @@ -616,12 +629,14 @@ pattern_fullmatch(PatternObject* self, P status = sre_match(&state, PatternObject_GetCode(self), 1); TRACE(("|%p|%p|END\n", PatternObject_GetCode(self), state.ptr)); - if (PyErr_Occurred()) + if (PyErr_Occurred()) { + state_fini(&state); return NULL; - + } + + match = pattern_new_match(self, &state, status); state_fini(&state); - - return pattern_new_match(self, &state, status); + return match; } static PyObject* @@ -629,6 +644,7 @@ pattern_search(PatternObject* self, PyOb { SRE_STATE state; Py_ssize_t status; + PyObject *match; PyObject *string = NULL, *string2 = NULL; Py_ssize_t start = 0; @@ -652,12 +668,14 @@ pattern_search(PatternObject* self, PyOb TRACE(("|%p|%p|END\n", PatternObject_GetCode(self), state.ptr)); + if (PyErr_Occurred()) { + state_fini(&state); + return NULL; + } + + match = pattern_new_match(self, &state, status); state_fini(&state); - - if (PyErr_Occurred()) - return NULL; - - return pattern_new_match(self, &state, status); + return match; } static PyObject* @@ -1417,7 +1435,7 @@ static PyObject * PyObject* groupindex = NULL; PyObject* indexgroup = NULL; - if (!PyArg_ParseTuple(args, "OiO!|nOO", &pattern, &flags, + if (!PyArg_ParseTuple(args, "OiO!nOO", &pattern, &flags, &PyList_Type, &code, &groups, &groupindex, &indexgroup)) return NULL; @@ -1933,10 +1951,9 @@ static int static int _validate_outer(SRE_CODE *code, SRE_CODE *end, Py_ssize_t groups) { - if (groups < 0 || groups > 100 || code >= end || end[-1] != SRE_OP_SUCCESS) + if (groups < 0 || (size_t)groups > SRE_MAXGROUPS || + code >= end || end[-1] != SRE_OP_SUCCESS) FAIL; - if (groups == 0) /* fix for simplejson */ - groups = 100; /* 100 groups should always be safe */ return _validate_inner(code, end-1, groups); } @@ -2747,6 +2764,12 @@ PyMODINIT_FUNC PyInit__sre(void) Py_DECREF(x); } + x = PyLong_FromUnsignedLong(SRE_MAXGROUPS); + if (x) { + PyDict_SetItemString(d, "MAXGROUPS", x); + Py_DECREF(x); + } + x = PyUnicode_FromString(copyright); if (x) { PyDict_SetItemString(d, "copyright", x); diff -r fb93a04832df Modules/sre.h --- a/Modules/sre.h Sun Sep 21 22:59:06 2014 +0300 +++ b/Modules/sre.h Sun Sep 21 23:40:50 2014 +0300 @@ -18,8 +18,10 @@ #define SRE_CODE Py_UCS4 #if SIZEOF_SIZE_T > 4 # define SRE_MAXREPEAT (~(SRE_CODE)0) +# define SRE_MAXGROUPS ((~(SRE_CODE)0) / 2) #else # define SRE_MAXREPEAT ((SRE_CODE)PY_SSIZE_T_MAX) +# define SRE_MAXGROUPS ((SRE_CODE)PY_SSIZE_T_MAX / SIZEOF_SIZE_T / 2) #endif typedef struct { @@ -52,9 +54,6 @@ typedef struct { typedef unsigned int (*SRE_TOLOWER_HOOK)(unsigned int ch); -/* FIXME: shouldn't be a constant, really... */ -#define SRE_MARK_SIZE 200 - typedef struct SRE_REPEAT_T { Py_ssize_t count; SRE_CODE* pattern; /* points to REPEAT operator arguments */ @@ -76,7 +75,7 @@ typedef struct { /* registers */ Py_ssize_t lastindex; Py_ssize_t lastmark; - void* mark[SRE_MARK_SIZE]; + void** mark; /* dynamically allocated stuff */ char* data_stack; size_t data_stack_size;