| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 02c04f2 commit 4a7f44a
5 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -371,6 +371,8 @@ The special characters are: | |||
| 371 | 371 | ``(?#...)`` | |
| 372 | 372 | A comment; the contents of the parentheses are simply ignored. | |
| 373 | 373 | ||
| 374 | + .. index:: single: (?=; in regular expressions | ||
| 375 | + | ||
| 374 | 376 | ``(?=...)`` | |
| 375 | 377 | Matches if ``...`` matches next, but doesn't consume any of the string. This is | |
| 376 | 378 | called a :dfn:`lookahead assertion`. For example, ``Isaac (?=Asimov)`` will match | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2067,6 +2067,40 @@ def test_bug_29444(self): | |||
| 2067 | 2067 | self.assertEqual(m.group(), b'xyz') | |
| 2068 | 2068 | self.assertEqual(m2.group(), b'') | |
| 2069 | 2069 | ||
| 2070 | + def test_bug_34294(self): | ||
| 2071 | + # Issue 34294: wrong capturing groups | ||
| 2072 | + | ||
| 2073 | + # exists since Python 2 | ||
| 2074 | + s = "a\tx" | ||
| 2075 | + p = r"\b(?=(\t)|(x))x" | ||
| 2076 | + self.assertEqual(re.search(p, s).groups(), (None, 'x')) | ||
| 2077 | + | ||
| 2078 | + # introduced in Python 3.7.0 | ||
| 2079 | + s = "ab" | ||
| 2080 | + p = r"(?=(.)(.)?)" | ||
| 2081 | + self.assertEqual(re.findall(p, s), | ||
| 2082 | + [('a', 'b'), ('b', '')]) | ||
| 2083 | + self.assertEqual([m.groups() for m in re.finditer(p, s)], | ||
| 2084 | + [('a', 'b'), ('b', None)]) | ||
| 2085 | + | ||
| 2086 | + # test-cases provided by issue34294, introduced in Python 3.7.0 | ||
| 2087 | + p = r"(?=<(?P<tag>\w+)/?>(?:(?P<text>.+?)</(?P=tag)>)?)" | ||
| 2088 | + s = "<test><foo2/></test>" | ||
| 2089 | + self.assertEqual(re.findall(p, s), | ||
| 2090 | + [('test', '<foo2/>'), ('foo2', '')]) | ||
| 2091 | + self.assertEqual([m.groupdict() for m in re.finditer(p, s)], | ||
| 2092 | + [{'tag': 'test', 'text': '<foo2/>'}, | ||
| 2093 | + {'tag': 'foo2', 'text': None}]) | ||
| 2094 | + s = "<test>Hello</test><foo/>" | ||
| 2095 | + self.assertEqual([m.groupdict() for m in re.finditer(p, s)], | ||
| 2096 | + [{'tag': 'test', 'text': 'Hello'}, | ||
| 2097 | + {'tag': 'foo', 'text': None}]) | ||
| 2098 | + s = "<test>Hello</test><foo/><foo/>" | ||
| 2099 | + self.assertEqual([m.groupdict() for m in re.finditer(p, s)], | ||
| 2100 | + [{'tag': 'test', 'text': 'Hello'}, | ||
| 2101 | + {'tag': 'foo', 'text': None}, | ||
| 2102 | + {'tag': 'foo', 'text': None}]) | ||
| 2103 | + | ||
| 2070 | 2104 | ||
| 2071 | 2105 | class PatternReprTests(unittest.TestCase): | |
| 2072 | 2106 | def check(self, pattern, expected): | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,4 @@ | |||
| 1 | + re module, fix wrong capturing groups in rare cases. :func:`re.search`, | ||
| 2 | + :func:`re.findall`, :func:`re.sub` and other functions that scan through | ||
| 3 | + string looking for a match, should reset capturing groups between two match | ||
| 4 | + attempts. Patch by Ma Lin. | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -340,7 +340,7 @@ _sre_unicode_tolower_impl(PyObject *module, int character) | |||
| 340 | 340 | LOCAL(void) | |
| 341 | 341 | state_reset(SRE_STATE* state) | |
| 342 | 342 | { | |
| 343 | - /* FIXME: dynamic! */ | ||
| 343 | + /* state->mark will be set to 0 in SRE_OP_MARK dynamically. */ | ||
| 344 | 344 | /*memset(state->mark, 0, sizeof(*state->mark) * SRE_MARK_SIZE);*/ | |
| 345 | 345 | ||
| 346 | 346 | state->lastmark = -1; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1363,6 +1363,10 @@ SRE(match)(SRE_STATE* state, SRE_CODE* pattern, int toplevel) | |||
| 1363 | 1363 | return ret; /* should never get here */ | |
| 1364 | 1364 | } | |
| 1365 | 1365 | ||
| 1366 | + /* need to reset capturing groups between two SRE(match) callings in loops */ | ||
| 1367 | + #define RESET_CAPTURE_GROUP() \ | ||
| 1368 | + do { state->lastmark = state->lastindex = -1; } while (0) | ||
| 1369 | + | ||
| 1366 | 1370 | LOCAL(Py_ssize_t) | |
| 1367 | 1371 | SRE(search)(SRE_STATE* state, SRE_CODE* pattern) | |
| 1368 | 1372 | { | |
@@ -1440,6 +1444,7 @@ SRE(search)(SRE_STATE* state, SRE_CODE* pattern) | |||
| 1440 | 1444 | if (status != 0) | |
| 1441 | 1445 | return status; | |
| 1442 | 1446 | ++ptr; | |
| 1447 | + RESET_CAPTURE_GROUP(); | ||
| 1443 | 1448 | } | |
| 1444 | 1449 | return 0; | |
| 1445 | 1450 | } | |
@@ -1487,6 +1492,7 @@ SRE(search)(SRE_STATE* state, SRE_CODE* pattern) | |||
| 1487 | 1492 | /* close but no cigar -- try again */ | |
| 1488 | 1493 | if (++ptr >= end) | |
| 1489 | 1494 | return 0; | |
| 1495 | + RESET_CAPTURE_GROUP(); | ||
| 1490 | 1496 | } | |
| 1491 | 1497 | i = overlap[i]; | |
| 1492 | 1498 | } while (i != 0); | |
@@ -1510,6 +1516,7 @@ SRE(search)(SRE_STATE* state, SRE_CODE* pattern) | |||
| 1510 | 1516 | if (status != 0) | |
| 1511 | 1517 | break; | |
| 1512 | 1518 | ptr++; | |
| 1519 | + RESET_CAPTURE_GROUP(); | ||
| 1513 | 1520 | } | |
| 1514 | 1521 | } else { | |
| 1515 | 1522 | /* general case */ | |
@@ -1520,6 +1527,7 @@ SRE(search)(SRE_STATE* state, SRE_CODE* pattern) | |||
| 1520 | 1527 | state->must_advance = 0; | |
| 1521 | 1528 | while (status == 0 && ptr < end) { | |
| 1522 | 1529 | ptr++; | |
| 1530 | + RESET_CAPTURE_GROUP(); | ||
| 1523 | 1531 | TRACE(("|%p|%p|SEARCH\n", pattern, ptr)); | |
| 1524 | 1532 | state->start = state->ptr = ptr; | |
| 1525 | 1533 | status = SRE(match)(state, pattern, 0); | |
| Back | FazBrowse Home | New Git URL |
0 commit comments