Skip to content

Commit 1f85570

Browse files
committed
gh-155297: Fix re.IGNORECASE for Unicode property categories
1 parent 5afbb60 commit 1f85570

3 files changed

Lines changed: 29 additions & 10 deletions

File tree

Lib/test/test_re.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -977,6 +977,20 @@ def test_property_escapes(self):
977977
self.assertTrue(re.fullmatch(r'[\p{Lu}\p{Nd}]+', 'AB12'))
978978
self.assertIsNone(re.fullmatch(r'[\p{Lu}\p{Nd}]+', 'ab'))
979979

980+
# IGNORECASE applies to cased literals in a character class, but
981+
# Unicode property category predicates are evaluated against the
982+
# original input character.
983+
self.assertEqual(re.findall(r'[\p{Lu}a]', 'AaBb1', re.I),
984+
list('AaB'))
985+
self.assertEqual(re.findall(r'[\P{Lu}a]', 'AaBb1', re.I),
986+
list('Aab1'))
987+
self.assertTrue(re.fullmatch(r'[a||\p{Lu}]', 'B', re.I))
988+
self.assertEqual(re.findall(r'[a--\p{Lu}]', 'AaB', re.I),
989+
list('a'))
990+
self.assertTrue(re.fullmatch(r'(?:\p{Lu}|a)', 'B', re.I))
991+
self.assertTrue(re.fullmatch(r'(?i:[\p{Lu}a])', 'B'))
992+
self.assertIsNone(re.fullmatch(r'(?-i:[\p{Lu}a])', 'b', re.I))
993+
980994
# XID_Start and XID_Continue.
981995
self.assertTrue(re.fullmatch(r'\p{XID_Start}+', 'fo\xf6Д'))
982996
self.assertIsNone(re.fullmatch(r'\p{XID_Start}', '1'))
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix incorrect ``\p{...}`` matches in character classes with
2+
:const:`~re.IGNORECASE`.

Modules/_sre/sre_lib.h

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,8 @@ SRE(at)(SRE_STATE* state, const SRE_CHAR* ptr, SRE_CODE at)
8989
}
9090

9191
LOCAL(int)
92-
SRE(charset)(SRE_STATE* state, const SRE_CODE* set, SRE_CODE ch)
92+
SRE(charset)(SRE_STATE* state, const SRE_CODE* set, SRE_CODE ch,
93+
SRE_CODE original_ch)
9394
{
9495
/* check if character is a member of the given set */
9596

@@ -110,7 +111,9 @@ SRE(charset)(SRE_STATE* state, const SRE_CODE* set, SRE_CODE ch)
110111

111112
case SRE_OP_CATEGORY:
112113
/* <CATEGORY> <code> */
113-
if (sre_category(set[0], (int) ch))
114+
/* Unicode property categories are not case-folded. */
115+
if (sre_category(set[0], (int) (set[0] >= SRE_CATEGORY_ALPHA ?
116+
original_ch : ch)))
114117
return ok;
115118
set++;
116119
break;
@@ -180,11 +183,11 @@ SRE(charset_loc_ignore)(SRE_STATE* state, const SRE_CODE* set, SRE_CODE ch)
180183
{
181184
SRE_CODE lo, up;
182185
lo = sre_lower_locale(ch);
183-
if (SRE(charset)(state, set, lo))
186+
if (SRE(charset)(state, set, lo, ch))
184187
return 1;
185188

186189
up = sre_upper_locale(ch);
187-
return up != lo && SRE(charset)(state, set, up);
190+
return up != lo && SRE(charset)(state, set, up, ch);
188191
}
189192

190193
LOCAL(Py_ssize_t) SRE(match)(SRE_STATE* state, const SRE_CODE* pattern, int toplevel);
@@ -209,7 +212,7 @@ SRE(count)(SRE_STATE* state, const SRE_CODE* pattern, Py_ssize_t maxcount)
209212
case SRE_OP_IN:
210213
/* repeated set */
211214
TRACE(("|%p|%p|COUNT IN\n", pattern, ptr));
212-
while (ptr < end && SRE(charset)(state, pattern + 2, *ptr))
215+
while (ptr < end && SRE(charset)(state, pattern + 2, *ptr, *ptr))
213216
ptr++;
214217
break;
215218

@@ -754,7 +757,7 @@ SRE(match)(SRE_STATE* state, const SRE_CODE* pattern, int toplevel)
754757
/* <IN> <skip> <set> */
755758
TRACE(("|%p|%p|IN\n", pattern, ptr));
756759
if (ptr >= end ||
757-
!SRE(charset)(state, pattern + 1, *ptr))
760+
!SRE(charset)(state, pattern + 1, *ptr, *ptr))
758761
RETURN_FAILURE;
759762
pattern += pattern[0];
760763
ptr++;
@@ -824,7 +827,7 @@ SRE(match)(SRE_STATE* state, const SRE_CODE* pattern, int toplevel)
824827
TRACE(("|%p|%p|IN_IGNORE\n", pattern, ptr));
825828
if (ptr >= end
826829
|| !SRE(charset)(state, pattern+1,
827-
(SRE_CODE)sre_lower_ascii(*ptr)))
830+
(SRE_CODE)sre_lower_ascii(*ptr), *ptr))
828831
RETURN_FAILURE;
829832
pattern += pattern[0];
830833
ptr++;
@@ -834,7 +837,7 @@ SRE(match)(SRE_STATE* state, const SRE_CODE* pattern, int toplevel)
834837
TRACE(("|%p|%p|IN_UNI_IGNORE\n", pattern, ptr));
835838
if (ptr >= end
836839
|| !SRE(charset)(state, pattern+1,
837-
(SRE_CODE)sre_lower_unicode(*ptr)))
840+
(SRE_CODE)sre_lower_unicode(*ptr), *ptr))
838841
RETURN_FAILURE;
839842
pattern += pattern[0];
840843
ptr++;
@@ -873,7 +876,7 @@ SRE(match)(SRE_STATE* state, const SRE_CODE* pattern, int toplevel)
873876
if (pattern[1] == SRE_OP_IN &&
874877
(ptr >= end ||
875878
!SRE(charset)(state, pattern + 3,
876-
(SRE_CODE) *ptr)))
879+
(SRE_CODE) *ptr, (SRE_CODE) *ptr)))
877880
continue;
878881
state->ptr = ptr;
879882
DO_JUMP(JUMP_BRANCH, jump_branch, pattern+1);
@@ -1833,7 +1836,7 @@ SRE(search)(SRE_STATE* state, SRE_CODE* pattern)
18331836
end = (SRE_CHAR *)state->end;
18341837
state->must_advance = 0;
18351838
for (;;) {
1836-
while (ptr < end && !SRE(charset)(state, charset, *ptr))
1839+
while (ptr < end && !SRE(charset)(state, charset, *ptr, *ptr))
18371840
ptr++;
18381841
if (ptr >= end)
18391842
return 0;

0 commit comments

Comments
 (0)