Skip to content

Commit 8994ad0

Browse files
Merge branch 'python:main' into main
2 parents 16bfb44 + 2c0a21c commit 8994ad0

File tree

18 files changed

+622
-275
lines changed

18 files changed

+622
-275
lines changed

Doc/library/math.rst

Lines changed: 250 additions & 226 deletions
Large diffs are not rendered by default.

Lib/test/test_os.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3967,10 +3967,10 @@ def _check_xattrs_str(self, s, getxattr, setxattr, removexattr, listxattr, **kwa
39673967
xattr.remove("user.test")
39683968
self.assertEqual(set(listxattr(fn)), xattr)
39693969
self.assertEqual(getxattr(fn, s("user.test2"), **kwargs), b"foo")
3970-
setxattr(fn, s("user.test"), b"a"*1024, **kwargs)
3971-
self.assertEqual(getxattr(fn, s("user.test"), **kwargs), b"a"*1024)
3970+
setxattr(fn, s("user.test"), b"a"*256, **kwargs)
3971+
self.assertEqual(getxattr(fn, s("user.test"), **kwargs), b"a"*256)
39723972
removexattr(fn, s("user.test"), **kwargs)
3973-
many = sorted("user.test{}".format(i) for i in range(100))
3973+
many = sorted("user.test{}".format(i) for i in range(32))
39743974
for thing in many:
39753975
setxattr(fn, thing, b"x", **kwargs)
39763976
self.assertEqual(set(listxattr(fn)), set(init_xattr) | set(many))

Lib/test/test_re.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2640,6 +2640,12 @@ def test_bug_gh100061(self):
26402640
self.assertEqual(re.match("(?>(?:ab?c){1,3})", "aca").span(), (0, 2))
26412641
self.assertEqual(re.match("(?:ab?c){1,3}+", "aca").span(), (0, 2))
26422642

2643+
def test_bug_gh101955(self):
2644+
# Possessive quantifier with nested alternative with capture groups
2645+
self.assertEqual(re.match('((x)|y|z)*+', 'xyz').groups(), ('z', 'x'))
2646+
self.assertEqual(re.match('((x)|y|z){3}+', 'xyz').groups(), ('z', 'x'))
2647+
self.assertEqual(re.match('((x)|y|z){3,}+', 'xyz').groups(), ('z', 'x'))
2648+
26432649
@unittest.skipIf(multiprocessing is None, 'test requires multiprocessing')
26442650
def test_regression_gh94675(self):
26452651
pattern = re.compile(r'(?<=[({}])(((//[^\n]*)?[\n])([\000-\040])*)*'
@@ -2681,6 +2687,29 @@ def test_character_set_none(self):
26812687
self.assertIsNone(re.search(p, s))
26822688
self.assertIsNone(re.search('(?s:.)' + p, s))
26832689

2690+
def check_interrupt(self, pattern, string, maxcount):
2691+
class Interrupt(Exception):
2692+
pass
2693+
p = re.compile(pattern)
2694+
for n in range(maxcount):
2695+
try:
2696+
p._fail_after(n, Interrupt)
2697+
p.match(string)
2698+
return n
2699+
except Interrupt:
2700+
pass
2701+
finally:
2702+
p._fail_after(-1, None)
2703+
2704+
@unittest.skipUnless(hasattr(re.Pattern, '_fail_after'), 'requires debug build')
2705+
def test_memory_leaks(self):
2706+
self.check_interrupt(r'(.)*:', 'abc:', 100)
2707+
self.check_interrupt(r'([^:])*?:', 'abc:', 100)
2708+
self.check_interrupt(r'([^:])*+:', 'abc:', 100)
2709+
self.check_interrupt(r'(.){2,4}:', 'abc:', 100)
2710+
self.check_interrupt(r'([^:]){2,4}?:', 'abc:', 100)
2711+
self.check_interrupt(r'([^:]){2,4}+:', 'abc:', 100)
2712+
26842713

26852714
def get_debug_out(pat):
26862715
with captured_stdout() as out:

Lib/test/test_sysconfig.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -591,7 +591,7 @@ def test_osx_ext_suffix(self):
591591
suffix = sysconfig.get_config_var('EXT_SUFFIX')
592592
self.assertTrue(suffix.endswith('-darwin.so'), suffix)
593593

594-
@unittest.skipIf(sys.platform == 'wasi', 'venv is unsupported on WASI')
594+
@requires_subprocess()
595595
def test_config_vars_depend_on_site_initialization(self):
596596
script = textwrap.dedent("""
597597
import sysconfig
@@ -615,7 +615,7 @@ def test_config_vars_depend_on_site_initialization(self):
615615
self.assertEqual(no_site_config_vars['base'], site_config_vars['installed_base'])
616616
self.assertEqual(no_site_config_vars['platbase'], site_config_vars['installed_platbase'])
617617

618-
@unittest.skipIf(sys.platform == 'wasi', 'venv is unsupported on WASI')
618+
@requires_subprocess()
619619
def test_config_vars_recalculation_after_site_initialization(self):
620620
script = textwrap.dedent("""
621621
import sysconfig
@@ -639,7 +639,7 @@ def test_config_vars_recalculation_after_site_initialization(self):
639639
#self.assertEqual(config_vars['after']['prefix'], venv.prefix) # FIXME: prefix gets overwriten by _init_posix
640640
#self.assertEqual(config_vars['after']['exec_prefix'], venv.prefix) # FIXME: exec_prefix gets overwriten by _init_posix
641641

642-
@unittest.skipIf(sys.platform == 'wasi', 'venv is unsupported on WASI')
642+
@requires_subprocess()
643643
def test_paths_depend_on_site_initialization(self):
644644
script = textwrap.dedent("""
645645
import sysconfig
@@ -656,7 +656,7 @@ def test_paths_depend_on_site_initialization(self):
656656

657657
self.assertNotEqual(site_paths, no_site_paths)
658658

659-
@unittest.skipIf(sys.platform == 'wasi', 'venv is unsupported on WASI')
659+
@requires_subprocess()
660660
def test_makefile_overwrites_config_vars(self):
661661
script = textwrap.dedent("""
662662
import sys, sysconfig
@@ -689,6 +689,7 @@ def test_makefile_overwrites_config_vars(self):
689689
self.assertNotEqual(data['prefix'], data['base_prefix'])
690690
self.assertNotEqual(data['exec_prefix'], data['base_exec_prefix'])
691691

692+
692693
class MakefileTests(unittest.TestCase):
693694

694695
@unittest.skipIf(sys.platform.startswith('win'),
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix SystemError when match regular expression pattern containing some
2+
combination of possessive quantifier, alternative and capture group.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix memory leaks when :mod:`regular expression <re>` matching terminates
2+
abruptly, either because of a signal or because memory allocation fails.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix test_os extended attribute tests to work on filesystems with 1 KiB xattr size
2+
limit.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
The iOS testbed was modified so that it can be used by third-party projects
2+
for testing purposes.

Modules/_sre/clinic/sre.c.h

Lines changed: 43 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Modules/_sre/sre.c

Lines changed: 127 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,85 @@ data_stack_grow(SRE_STATE* state, Py_ssize_t size)
267267
return 0;
268268
}
269269

270+
/* memory pool functions for SRE_REPEAT, this can avoid memory
271+
leak when SRE(match) function terminates abruptly.
272+
state->repeat_pool_used is a doubly-linked list, so that we
273+
can remove a SRE_REPEAT node from it.
274+
state->repeat_pool_unused is a singly-linked list, we put/get
275+
node at the head. */
276+
static SRE_REPEAT *
277+
repeat_pool_malloc(SRE_STATE *state)
278+
{
279+
SRE_REPEAT *repeat;
280+
281+
if (state->repeat_pool_unused) {
282+
/* remove from unused pool (singly-linked list) */
283+
repeat = state->repeat_pool_unused;
284+
state->repeat_pool_unused = repeat->pool_next;
285+
}
286+
else {
287+
repeat = PyMem_Malloc(sizeof(SRE_REPEAT));
288+
if (!repeat) {
289+
return NULL;
290+
}
291+
}
292+
293+
/* add to used pool (doubly-linked list) */
294+
SRE_REPEAT *temp = state->repeat_pool_used;
295+
if (temp) {
296+
temp->pool_prev = repeat;
297+
}
298+
repeat->pool_prev = NULL;
299+
repeat->pool_next = temp;
300+
state->repeat_pool_used = repeat;
301+
302+
return repeat;
303+
}
304+
305+
static void
306+
repeat_pool_free(SRE_STATE *state, SRE_REPEAT *repeat)
307+
{
308+
SRE_REPEAT *prev = repeat->pool_prev;
309+
SRE_REPEAT *next = repeat->pool_next;
310+
311+
/* remove from used pool (doubly-linked list) */
312+
if (prev) {
313+
prev->pool_next = next;
314+
}
315+
else {
316+
state->repeat_pool_used = next;
317+
}
318+
if (next) {
319+
next->pool_prev = prev;
320+
}
321+
322+
/* add to unused pool (singly-linked list) */
323+
repeat->pool_next = state->repeat_pool_unused;
324+
state->repeat_pool_unused = repeat;
325+
}
326+
327+
static void
328+
repeat_pool_clear(SRE_STATE *state)
329+
{
330+
/* clear used pool */
331+
SRE_REPEAT *next = state->repeat_pool_used;
332+
state->repeat_pool_used = NULL;
333+
while (next) {
334+
SRE_REPEAT *temp = next;
335+
next = temp->pool_next;
336+
PyMem_Free(temp);
337+
}
338+
339+
/* clear unused pool */
340+
next = state->repeat_pool_unused;
341+
state->repeat_pool_unused = NULL;
342+
while (next) {
343+
SRE_REPEAT *temp = next;
344+
next = temp->pool_next;
345+
PyMem_Free(temp);
346+
}
347+
}
348+
270349
/* generate 8-bit version */
271350

272351
#define SRE_CHAR Py_UCS1
@@ -511,6 +590,11 @@ state_init(SRE_STATE* state, PatternObject* pattern, PyObject* string,
511590
state->pos = start;
512591
state->endpos = end;
513592

593+
#ifdef Py_DEBUG
594+
state->fail_after_count = pattern->fail_after_count;
595+
state->fail_after_exc = pattern->fail_after_exc; // borrowed ref
596+
#endif
597+
514598
return string;
515599
err:
516600
/* We add an explicit cast here because MSVC has a bug when
@@ -533,6 +617,8 @@ state_fini(SRE_STATE* state)
533617
/* See above PyMem_Free() for why we explicitly cast here. */
534618
PyMem_Free((void*) state->mark);
535619
state->mark = NULL;
620+
/* SRE_REPEAT pool */
621+
repeat_pool_clear(state);
536622
}
537623

538624
/* calculate offset from start of string */
@@ -619,6 +705,9 @@ pattern_traverse(PatternObject *self, visitproc visit, void *arg)
619705
Py_VISIT(self->groupindex);
620706
Py_VISIT(self->indexgroup);
621707
Py_VISIT(self->pattern);
708+
#ifdef Py_DEBUG
709+
Py_VISIT(self->fail_after_exc);
710+
#endif
622711
return 0;
623712
}
624713

@@ -628,6 +717,9 @@ pattern_clear(PatternObject *self)
628717
Py_CLEAR(self->groupindex);
629718
Py_CLEAR(self->indexgroup);
630719
Py_CLEAR(self->pattern);
720+
#ifdef Py_DEBUG
721+
Py_CLEAR(self->fail_after_exc);
722+
#endif
631723
return 0;
632724
}
633725

@@ -690,7 +782,7 @@ _sre_SRE_Pattern_match_impl(PatternObject *self, PyTypeObject *cls,
690782
Py_ssize_t status;
691783
PyObject *match;
692784

693-
if (!state_init(&state, (PatternObject *)self, string, pos, endpos))
785+
if (!state_init(&state, self, string, pos, endpos))
694786
return NULL;
695787

696788
INIT_TRACE(&state);
@@ -1381,6 +1473,29 @@ _sre_SRE_Pattern___deepcopy__(PatternObject *self, PyObject *memo)
13811473
return Py_NewRef(self);
13821474
}
13831475

1476+
#ifdef Py_DEBUG
1477+
/*[clinic input]
1478+
_sre.SRE_Pattern._fail_after
1479+
1480+
count: int
1481+
exception: object
1482+
/
1483+
1484+
For debugging.
1485+
[clinic start generated code]*/
1486+
1487+
static PyObject *
1488+
_sre_SRE_Pattern__fail_after_impl(PatternObject *self, int count,
1489+
PyObject *exception)
1490+
/*[clinic end generated code: output=9a6bf12135ac50c2 input=ef80a45c66c5499d]*/
1491+
{
1492+
self->fail_after_count = count;
1493+
Py_INCREF(exception);
1494+
Py_XSETREF(self->fail_after_exc, exception);
1495+
Py_RETURN_NONE;
1496+
}
1497+
#endif /* Py_DEBUG */
1498+
13841499
static PyObject *
13851500
pattern_repr(PatternObject *obj)
13861501
{
@@ -1506,6 +1621,10 @@ _sre_compile_impl(PyObject *module, PyObject *pattern, int flags,
15061621
self->pattern = NULL;
15071622
self->groupindex = NULL;
15081623
self->indexgroup = NULL;
1624+
#ifdef Py_DEBUG
1625+
self->fail_after_count = -1;
1626+
self->fail_after_exc = NULL;
1627+
#endif
15091628

15101629
self->codesize = n;
15111630

@@ -2604,7 +2723,8 @@ pattern_new_match(_sremodulestate* module_state,
26042723
if (!match)
26052724
return NULL;
26062725

2607-
match->pattern = (PatternObject*)Py_NewRef(pattern);
2726+
Py_INCREF(pattern);
2727+
match->pattern = pattern;
26082728

26092729
match->string = Py_NewRef(state->string);
26102730

@@ -2740,7 +2860,7 @@ _sre_SRE_Scanner_match_impl(ScannerObject *self, PyTypeObject *cls)
27402860
return NULL;
27412861
}
27422862

2743-
match = pattern_new_match(module_state, (PatternObject*) self->pattern,
2863+
match = pattern_new_match(module_state, self->pattern,
27442864
state, status);
27452865

27462866
if (status == 0)
@@ -2790,7 +2910,7 @@ _sre_SRE_Scanner_search_impl(ScannerObject *self, PyTypeObject *cls)
27902910
return NULL;
27912911
}
27922912

2793-
match = pattern_new_match(module_state, (PatternObject*) self->pattern,
2913+
match = pattern_new_match(module_state, self->pattern,
27942914
state, status);
27952915

27962916
if (status == 0)
@@ -2826,7 +2946,8 @@ pattern_scanner(_sremodulestate *module_state,
28262946
return NULL;
28272947
}
28282948

2829-
scanner->pattern = Py_NewRef(self);
2949+
Py_INCREF(self);
2950+
scanner->pattern = self;
28302951

28312952
PyObject_GC_Track(scanner);
28322953
return (PyObject*) scanner;
@@ -3020,6 +3141,7 @@ static PyMethodDef pattern_methods[] = {
30203141
_SRE_SRE_PATTERN_SCANNER_METHODDEF
30213142
_SRE_SRE_PATTERN___COPY___METHODDEF
30223143
_SRE_SRE_PATTERN___DEEPCOPY___METHODDEF
3144+
_SRE_SRE_PATTERN__FAIL_AFTER_METHODDEF
30233145
{"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS,
30243146
PyDoc_STR("See PEP 585")},
30253147
{NULL, NULL}

0 commit comments

Comments
 (0)