Skip to content

Commit 5f67be0

Browse files
committed
Address feedback from @picnixz
1 parent d0aa6fa commit 5f67be0

File tree

3 files changed

+119
-59
lines changed

3 files changed

+119
-59
lines changed

Doc/library/re.rst

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1494,7 +1494,7 @@ You can also destructure match objects with python's ``match`` statement::
14941494

14951495
.. versionadded:: 3.6
14961496

1497-
.. versionchanged:: 3.14
1497+
.. versionchanged:: next
14981498

14991499
Negative indexing is now supported. This allows accessing match groups
15001500
from the end, starting from the last group defined in the pattern::
@@ -1523,7 +1523,7 @@ You can also destructure match objects with python's ``match`` statement::
15231523
>>> len(m)
15241524
3
15251525

1526-
.. versionadded:: 3.14
1526+
.. versionadded:: next
15271527

15281528

15291529
.. method:: Match.groups(default=None)
@@ -1596,13 +1596,13 @@ You can also destructure match objects with python's ``match`` statement::
15961596

15971597
Raises :exc:`ValueError` if the value is not present.
15981598

1599-
.. versionadded:: 3.14
1599+
.. versionadded:: next
16001600

16011601
.. method:: Match.count(value, /)
16021602

16031603
Return the number of occurrences of the value among the matched groups.
16041604

1605-
.. versionadded:: 3.14
1605+
.. versionadded:: next
16061606

16071607
.. attribute:: Match.pos
16081608

Lib/test/test_re.py

Lines changed: 101 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import sys
99
import unittest
1010
import warnings
11+
from collections.abc import Sequence
1112
from re import Scanner
1213
from weakref import proxy
1314

@@ -598,40 +599,47 @@ def test_match_getitem(self):
598599
self.assertEqual(m[1], 'a')
599600
self.assertEqual(m[2], None)
600601
self.assertEqual(m[3], 'c')
602+
self.assertEqual(m[-1], 'c')
603+
self.assertEqual(m[-2], None)
604+
self.assertEqual(m[-3], 'a')
605+
self.assertEqual(m[-4], 'ac')
601606

602607
# Cannot assign.
603608
with self.assertRaises(TypeError):
604609
m[0] = 1
605610

606-
def test_match_sequence(self):
607-
from collections.abc import Sequence
608-
611+
# Slices.
609612
m = re.match(r"(a)(b)(c)", "abc")
610-
self.assertIsInstance(m, Sequence)
611-
self.assertEqual(len(m), 4)
612-
613-
self.assertEqual(m[0], "abc")
614-
self.assertEqual(m[1], "a")
615-
self.assertEqual(m[2], "b")
616-
self.assertEqual(m[3], "c")
617-
with self.assertRaises(IndexError):
618-
_ = m[4]
619-
620-
self.assertEqual(m[-1], "c")
621-
self.assertEqual(m[-2], "b")
622-
self.assertEqual(m[-3], "a")
623-
self.assertEqual(m[-4], "abc")
624-
with self.assertRaises(IndexError):
625-
_ = m[-5]
626-
613+
self.assertEqual(m[:0], ())
614+
self.assertEqual(m[:1], ("abc",))
615+
self.assertEqual(m[:2], ("abc", "a"))
616+
self.assertEqual(m[:3], ("abc", "a", "b"))
617+
self.assertEqual(m[:4], ("abc", "a", "b", "c"))
618+
self.assertEqual(m[0:], ("abc", "a", "b", "c"))
619+
self.assertEqual(m[1:], ("a", "b", "c"))
620+
self.assertEqual(m[2:], ("b", "c"))
621+
self.assertEqual(m[3:], ("c",))
622+
self.assertEqual(m[4:], ())
623+
self.assertEqual(m[:-4], ())
624+
self.assertEqual(m[:-3], ("abc",))
625+
self.assertEqual(m[:-2], ("abc", "a"))
626+
self.assertEqual(m[:-1], ("abc", "a", "b"))
627+
self.assertEqual(m[-4:], ("abc", "a", "b", "c"))
628+
self.assertEqual(m[-3:], ("a", "b", "c"))
629+
self.assertEqual(m[-2:], ("b", "c"))
630+
self.assertEqual(m[-1:], ("c",))
627631
self.assertEqual(m[1:-1], ("a", "b"))
628632
self.assertEqual(m[::-1], ("c", "b", "a", "abc"))
633+
self.assertEqual(m[::4], ("abc",))
634+
self.assertEqual(m[2:2], ())
635+
self.assertEqual(m[3:1], ())
636+
self.assertEqual(m[1:3], ("a", "b"))
637+
self.assertEqual(m[-1::-2], ("c", "a"))
629638

630-
it = iter(m)
631-
self.assertEqual(next(it), "abc")
632-
self.assertEqual(next(it), "a")
633-
self.assertEqual(next(it), "b")
634-
self.assertEqual(next(it), "c")
639+
def test_match_sequence(self):
640+
m = re.match(r"(a)(b)(c)", "abc")
641+
self.assertIsInstance(m, Sequence)
642+
self.assertEqual(len(m), 4)
635643

636644
self.assertEqual(tuple(m), ("abc", "a", "b", "c"))
637645
self.assertEqual(list(m), ["abc", "a", "b", "c"])
@@ -650,36 +658,97 @@ def test_match_sequence(self):
650658

651659
self.assertEqual(list(reversed(m)), ["c", "b", "a", "abc"])
652660

661+
for s, k, v in re.finditer(r"(\w+):(\w+)", "abc:123"):
662+
self.assertEqual(s, "abc:123")
663+
self.assertEqual(k, "abc")
664+
self.assertEqual(v, "123")
665+
666+
def test_match_iter(self):
667+
m = re.match(r"(a)(b)(c)", "abc")
668+
it = iter(m)
669+
self.assertEqual(next(it), "abc")
670+
self.assertEqual(next(it), "a")
671+
self.assertEqual(next(it), "b")
672+
self.assertEqual(next(it), "c")
673+
with self.assertRaises(StopIteration):
674+
next(it)
675+
676+
def test_match_index(self):
677+
m = re.match(r"(a)(b)(c)", "abc")
653678
self.assertEqual(m.index("abc"), 0)
654679
self.assertEqual(m.index("a"), 1)
655680
self.assertEqual(m.index("b"), 2)
656681
self.assertEqual(m.index("c"), 3)
657682
self.assertRaises(ValueError, m.index, "123")
658683

684+
# With start index.
685+
self.assertRaises(ValueError, m.index, "abc", 1)
686+
self.assertEqual(m.index("a", 1), 1)
687+
self.assertEqual(m.index("b", 1), 2)
688+
self.assertEqual(m.index("c", 1), 3)
689+
self.assertRaises(ValueError, m.index, "123", 1)
690+
691+
self.assertRaises(ValueError, m.index, "abc", 2)
692+
self.assertRaises(ValueError, m.index, "a", 2)
693+
self.assertEqual(m.index("b", 2), 2)
694+
self.assertEqual(m.index("c", 2), 3)
695+
self.assertRaises(ValueError, m.index, "123", 2)
696+
697+
self.assertRaises(ValueError, m.index, "abc", 3)
698+
self.assertRaises(ValueError, m.index, "a", 3)
699+
self.assertRaises(ValueError, m.index, "b", 3)
700+
self.assertEqual(m.index("c", 3), 3)
701+
self.assertRaises(ValueError, m.index, "123", 3)
702+
703+
self.assertRaises(ValueError, m.index, "abc", 4)
704+
self.assertRaises(ValueError, m.index, "a", 4)
705+
self.assertRaises(ValueError, m.index, "b", 4)
706+
self.assertRaises(ValueError, m.index, "c", 4)
707+
self.assertRaises(ValueError, m.index, "123", 4)
708+
709+
# With start index and stop index.
710+
self.assertRaises(ValueError, m.index, "b", 0, 2)
711+
self.assertEqual(m.index("b", 1, 3), 2)
712+
self.assertEqual(m.index("b", 2, 4), 2)
713+
self.assertRaises(ValueError, m.index, "b", 3, 4)
714+
self.assertRaises(ValueError, m.index, "b", -1, 0)
715+
716+
# Non-string objects.
717+
self.assertRaises(ValueError, m.index, 123)
718+
self.assertRaises(ValueError, m.index, [1, 2, 3])
719+
self.assertRaises(ValueError, m.index, object())
720+
721+
def test_match_count(self):
722+
m = re.match(r"(a)(b)(c)", "abc")
659723
self.assertEqual(m.count("abc"), 1)
660724
self.assertEqual(m.count("a"), 1)
661725
self.assertEqual(m.count("b"), 1)
662726
self.assertEqual(m.count("c"), 1)
663727
self.assertEqual(m.count("123"), 0)
664728

729+
# Non-string objects.
730+
self.assertEqual(m.count(123), 0)
731+
self.assertEqual(m.count([1, 2, 3]), 0)
732+
self.assertEqual(m.count(object()), 0)
733+
734+
def test_match_match_case(self):
735+
m = re.match(r"(a)(b)(c)", "abc")
736+
665737
match m:
666-
case [_, "a", "b", "c"]:
667-
pass
738+
case [abc, "a", "b", "c"]:
739+
self.assertEqual(abc, "abc")
668740
case _:
669741
self.fail()
670742

671743
match re.match(r"(\d+)-(\d+)-(\d+)", "2025-05-07"):
672-
case [_, year, month, day]:
744+
case [date, year, month, day]:
745+
self.assertEqual(date, "2025-05-07")
673746
self.assertEqual(year, "2025")
674747
self.assertEqual(month, "05")
675748
self.assertEqual(day, "07")
676749
case _:
677750
self.fail()
678751

679-
for s, k, v in re.finditer(r"(\w+):(\w+)", "abc:123"):
680-
self.assertEqual(s, "abc:123")
681-
self.assertEqual(k, "abc")
682-
self.assertEqual(v, "123")
683752

684753
def test_re_fullmatch(self):
685754
# Issue 16203: Proposal: add re.fullmatch() method.

Modules/_sre/sre.c

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2453,7 +2453,7 @@ match_item(PyObject *op, Py_ssize_t index)
24532453
}
24542454

24552455
static PyObject*
2456-
match_subscript(PyObject *op, PyObject* item)
2456+
match_getitem(PyObject *op, PyObject* item)
24572457
{
24582458
MatchObject *self = _MatchObject_CAST(op);
24592459

@@ -2491,7 +2491,10 @@ match_subscript(PyObject *op, PyObject* item)
24912491
if (self->pattern->groupindex) {
24922492
PyObject* index = PyDict_GetItemWithError(self->pattern->groupindex, item);
24932493
if (index && PyLong_Check(index)) {
2494-
return match_item(op, PyLong_AsSsize_t(index));
2494+
Py_ssize_t i = PyLong_AsSsize_t(index);
2495+
if (i != -1) {
2496+
return match_item(op, i);
2497+
}
24952498
}
24962499
}
24972500
if (!PyErr_Occurred()) {
@@ -2695,33 +2698,21 @@ _sre_SRE_Match_index_impl(MatchObject *self, PyObject *value,
26952698
Py_ssize_t start, Py_ssize_t stop)
26962699
/*[clinic end generated code: output=846597f6f96f829c input=7f41b5a99e0ad88e]*/
26972700
{
2698-
Py_ssize_t i;
2701+
PySlice_AdjustIndices(self->groups, &start, &stop, 1);
26992702

2700-
if (start < 0) {
2701-
start += self->groups;
2702-
if (start < 0) {
2703-
start = 0;
2704-
}
2705-
}
2706-
if (stop < 0) {
2707-
stop += self->groups;
2708-
}
2709-
else if (stop > self->groups) {
2710-
stop = self->groups;
2711-
}
2712-
for (i = start; i < stop; i++) {
2703+
for (Py_ssize_t i = start; i < stop; i++) {
27132704
PyObject* group = match_getslice_by_index(self, i, Py_None);
27142705
if (group == NULL) {
27152706
return NULL;
27162707
}
27172708
int cmp = PyObject_RichCompareBool(group, value, Py_EQ);
27182709
Py_DECREF(group);
2710+
if (cmp < 0) {
2711+
return NULL;
2712+
}
27192713
if (cmp > 0) {
27202714
return PyLong_FromSsize_t(i);
27212715
}
2722-
else if (cmp < 0) {
2723-
return NULL;
2724-
}
27252716
}
27262717
PyErr_SetString(PyExc_ValueError, "match.index(x): x not in match");
27272718
return NULL;
@@ -2750,12 +2741,12 @@ _sre_SRE_Match_count_impl(MatchObject *self, PyObject *value)
27502741
}
27512742
int cmp = PyObject_RichCompareBool(group, value, Py_EQ);
27522743
Py_DECREF(group);
2744+
if (cmp < 0) {
2745+
return NULL;
2746+
}
27532747
if (cmp > 0) {
27542748
count++;
27552749
}
2756-
else if (cmp < 0) {
2757-
return NULL;
2758-
}
27592750
}
27602751
return PyLong_FromSsize_t(count);
27612752
}
@@ -3421,7 +3412,7 @@ static PyType_Slot match_slots[] = {
34213412
{Py_sq_item, match_item},
34223413

34233414
// Support group names provided as subscripts
3424-
{Py_mp_subscript, match_subscript},
3415+
{Py_mp_subscript, match_getitem},
34253416

34263417
{0, NULL},
34273418
};

0 commit comments

Comments
 (0)