Skip to content

Commit a58fcc4

Browse files
generatedunixname1734921407115435meta-codesync[bot]
authored andcommitted
Import CPython 3.14.6+ stable branch (2026-07-17)
Summary: Imported python/cpython `3.14.6+` from upstream rev [`9bd5e97`](https://www.github.com/python/cpython/commit/9bd5e97b7521aef5f568c70d6b4c9436e29a3e97) (committed 2026-07-17 22:36:17+00:00). # Commit Info - Base: (`3.14.6+`) - [`890bd0e`](https://www.github.com/python/cpython/commit/890bd0ed9130c8baa1e5a29fcfac266b1504889e) (commit date: 2026-07-17 02:50:00+00:00) - Imported: (`3.14.6+`) - [`9bd5e97`](https://www.github.com/python/cpython/commit/9bd5e97b7521aef5f568c70d6b4c9436e29a3e97) (commit date: 2026-07-17 22:36:17+00:00) # Noteworthy file changes - Test files (2 added) - Low-signal files (13 added) (NEWS.d, docs, .github) Complete list of added/removed files: https://www.internalfb.com/intern/everpaste/?color=0&handle=GGimOh5tpQAEA7IDABAi1epCWHtlbr0LAAAz Reviewed By: itamaro, KasraGhodsi Differential Revision: D112680178 fbshipit-source-id: 1911ca84159915ae6cb55136ebff06c75d99b31e
1 parent 0d25f98 commit a58fcc4

32 files changed

Lines changed: 496 additions & 79 deletions

Doc/library/calendar.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,7 @@ interpreted as prescribed by the ISO 8601 standard. Year 0 is 1 BC, year -1 is
362362

363363
For simple text calendars this module provides the following functions.
364364

365-
.. function:: setfirstweekday(weekday)
365+
.. function:: setfirstweekday(firstweekday)
366366

367367
Sets the weekday (``0`` is Monday, ``6`` is Sunday) to start each week. The
368368
values :const:`MONDAY`, :const:`TUESDAY`, :const:`WEDNESDAY`, :const:`THURSDAY`,

Lib/test/test_concurrent_futures/test_interpreter_pool.py

Lines changed: 33 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -129,12 +129,12 @@ def test_init_script(self):
129129
"""
130130
os.write(w, b'\0')
131131

132-
executor = self.executor_type(initializer=initscript)
133-
before_init = os.read(r, 100)
134-
fut = executor.submit(script)
135-
after_init = read_msg(r)
136-
fut.result()
137-
after_run = read_msg(r)
132+
with self.executor_type(initializer=initscript) as executor:
133+
before_init = os.read(r, 100)
134+
fut = executor.submit(script)
135+
after_init = read_msg(r)
136+
fut.result()
137+
after_run = read_msg(r)
138138

139139
self.assertEqual(before_init, b'\0')
140140
self.assertEqual(after_init, msg1)
@@ -150,11 +150,11 @@ def test_init_func(self):
150150
r, w = self.pipe()
151151
os.write(w, b'\0')
152152

153-
executor = self.executor_type(
154-
initializer=write_msg, initargs=(w, msg))
155-
before = os.read(r, 100)
156-
executor.submit(mul, 10, 10)
157-
after = read_msg(r)
153+
with self.executor_type(
154+
initializer=write_msg, initargs=(w, msg)) as executor:
155+
before = os.read(r, 100)
156+
executor.submit(mul, 10, 10)
157+
after = read_msg(r)
158158

159159
self.assertEqual(before, b'\0')
160160
self.assertEqual(after, msg)
@@ -260,11 +260,10 @@ def test_submit_script(self):
260260
import os
261261
os.write({w}, __name__.encode('utf-8') + b'\\0')
262262
"""
263-
executor = self.executor_type()
264-
265-
fut = executor.submit(script)
266-
res = fut.result()
267-
after = read_msg(r)
263+
with self.executor_type() as executor:
264+
fut = executor.submit(script)
265+
res = fut.result()
266+
after = read_msg(r)
268267

269268
self.assertEqual(after, b'__main__')
270269
self.assertIs(res, None)
@@ -278,41 +277,40 @@ def task2():
278277
spam += 1
279278
return spam
280279

281-
executor = self.executor_type()
282-
283-
fut = executor.submit(task1)
284-
with self.assertRaises(_interpreters.NotShareableError):
285-
fut.result()
280+
with self.executor_type() as executor:
281+
fut = executor.submit(task1)
282+
with self.assertRaises(_interpreters.NotShareableError):
283+
fut.result()
286284

287-
fut = executor.submit(task2)
288-
with self.assertRaises(_interpreters.NotShareableError):
289-
fut.result()
285+
fut = executor.submit(task2)
286+
with self.assertRaises(_interpreters.NotShareableError):
287+
fut.result()
290288

291289
def test_submit_local_instance(self):
292290
class Spam:
293291
def __init__(self):
294292
self.value = True
295293

296-
executor = self.executor_type()
297-
fut = executor.submit(Spam)
298-
with self.assertRaises(_interpreters.NotShareableError):
299-
fut.result()
294+
with self.executor_type() as executor:
295+
fut = executor.submit(Spam)
296+
with self.assertRaises(_interpreters.NotShareableError):
297+
fut.result()
300298

301299
def test_submit_instance_method(self):
302300
class Spam:
303301
def run(self):
304302
return True
305303
spam = Spam()
306304

307-
executor = self.executor_type()
308-
fut = executor.submit(spam.run)
309-
with self.assertRaises(_interpreters.NotShareableError):
310-
fut.result()
305+
with self.executor_type() as executor:
306+
fut = executor.submit(spam.run)
307+
with self.assertRaises(_interpreters.NotShareableError):
308+
fut.result()
311309

312310
def test_submit_func_globals(self):
313-
executor = self.executor_type()
314-
fut = executor.submit(get_current_name)
315-
name = fut.result()
311+
with self.executor_type() as executor:
312+
fut = executor.submit(get_current_name)
313+
name = fut.result()
316314

317315
self.assertEqual(name, __name__)
318316
self.assertNotEqual(name, '__main__')
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import unittest
2+
import copy
3+
4+
from test.support import threading_helper
5+
6+
threading_helper.requires_working_threading(module=True)
7+
class ExceptionTests(unittest.TestCase):
8+
def test_setstate_data_race(self):
9+
E = Exception()
10+
11+
def func():
12+
for i in range(100):
13+
setattr(E, 'x', i)
14+
copy.copy(E)
15+
16+
threading_helper.run_concurrently(func, nthreads=4)

Lib/test/test_free_threading/test_gc.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,35 @@ def setter():
124124
finally:
125125
gc.set_threshold(*current_threshold)
126126

127+
def test_get_count(self):
128+
class CyclicReference:
129+
def __init__(self):
130+
self.ref = self
131+
132+
NUM_ALLOCATORS = 7
133+
NUM_READERS = 1
134+
NUM_THREADS = NUM_ALLOCATORS + NUM_READERS
135+
NUM_ITERS = 1000
136+
137+
barrier = threading.Barrier(NUM_THREADS)
138+
139+
def allocator():
140+
barrier.wait()
141+
for _ in range(NUM_ITERS):
142+
CyclicReference()
143+
144+
145+
def reader():
146+
barrier.wait()
147+
for _ in range(NUM_ITERS):
148+
gc.get_count()
149+
150+
threads = [Thread(target=allocator) for _ in range(NUM_ALLOCATORS)]
151+
threads.extend(Thread(target=reader) for _ in range(NUM_READERS))
152+
153+
with threading_helper.start_threads(threads):
154+
pass
155+
127156

128157
if __name__ == "__main__":
129158
unittest.main()

Lib/test/test_free_threading/test_itertools.py

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,112 @@
11
import unittest
2+
from threading import Thread, Barrier
23
from itertools import (
4+
accumulate,
5+
chain,
6+
combinations_with_replacement,
7+
cycle,
8+
permutations,
39
tee,
10+
zip_longest,
411
)
512
from test.support import threading_helper
613

714

815
threading_helper.requires_working_threading(module=True)
916

1017

18+
def work_iterator(it):
19+
while True:
20+
try:
21+
next(it)
22+
except StopIteration:
23+
break
24+
25+
26+
class ItertoolsThreading(unittest.TestCase):
27+
28+
@threading_helper.reap_threads
29+
def test_cycle(self):
30+
number_of_threads = 6
31+
number_of_iterations = 10
32+
number_of_cycles = 400
33+
34+
barrier = Barrier(number_of_threads)
35+
def work(it):
36+
barrier.wait()
37+
for _ in range(number_of_cycles):
38+
_ = next(it)
39+
40+
data = (1, 2, 3, 4)
41+
for it in range(number_of_iterations):
42+
cycle_iterator = cycle(data)
43+
worker_threads = []
44+
for ii in range(number_of_threads):
45+
worker_threads.append(
46+
Thread(target=work, args=[cycle_iterator]))
47+
48+
with threading_helper.start_threads(worker_threads):
49+
pass
50+
51+
barrier.reset()
52+
53+
@threading_helper.reap_threads
54+
def test_chain(self):
55+
number_of_threads = 6
56+
number_of_iterations = 20
57+
58+
barrier = Barrier(number_of_threads)
59+
def work(it):
60+
barrier.wait()
61+
while True:
62+
try:
63+
next(it)
64+
except StopIteration:
65+
break
66+
67+
data = [(1, )] * 200
68+
for it in range(number_of_iterations):
69+
chain_iterator = chain(*data)
70+
worker_threads = []
71+
for ii in range(number_of_threads):
72+
worker_threads.append(
73+
Thread(target=work, args=[chain_iterator]))
74+
75+
with threading_helper.start_threads(worker_threads):
76+
pass
77+
78+
barrier.reset()
79+
80+
@threading_helper.reap_threads
81+
def test_combinations_with_replacement(self):
82+
number_of_iterations = 6
83+
for _ in range(number_of_iterations):
84+
it = combinations_with_replacement(tuple(range(2)), 2)
85+
threading_helper.run_concurrently(work_iterator, nthreads=6, args=[it])
86+
87+
@threading_helper.reap_threads
88+
def test_permutations(self):
89+
number_of_iterations = 6
90+
for _ in range(number_of_iterations):
91+
it = permutations(tuple(range(2)), 2)
92+
threading_helper.run_concurrently(work_iterator, nthreads=6, args=[it])
93+
94+
@threading_helper.reap_threads
95+
def test_accumulate(self):
96+
number_of_iterations = 10
97+
for _ in range(number_of_iterations):
98+
it = accumulate(tuple(range(40)))
99+
threading_helper.run_concurrently(work_iterator, nthreads=10, args=[it])
100+
101+
@threading_helper.reap_threads
102+
def test_zip_longest(self):
103+
number_of_iterations = 10
104+
for _ in range(number_of_iterations):
105+
it = zip_longest(list(range(4)), list(range(8)), fillvalue=0)
106+
threading_helper.run_concurrently(work_iterator, nthreads=10, args=[it])
107+
108+
109+
11110
class TestTeeConcurrent(unittest.TestCase):
12111
# itertools.tee branches share a linked list of internal data cells.
13112
# Concurrent iteration must not corrupt that shared state or crash the
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import unittest
2+
from threading import Thread, Barrier
3+
from itertools import combinations, product
4+
from test.support import threading_helper
5+
6+
7+
threading_helper.requires_working_threading(module=True)
8+
9+
def test_concurrent_iteration(iterator, number_of_threads):
10+
barrier = Barrier(number_of_threads)
11+
def iterator_worker(it):
12+
barrier.wait()
13+
while True:
14+
try:
15+
_ = next(it)
16+
except StopIteration:
17+
return
18+
19+
worker_threads = []
20+
for ii in range(number_of_threads):
21+
worker_threads.append(
22+
Thread(target=iterator_worker, args=[iterator]))
23+
24+
with threading_helper.start_threads(worker_threads):
25+
pass
26+
27+
barrier.reset()
28+
29+
class ItertoolsThreading(unittest.TestCase):
30+
31+
@threading_helper.reap_threads
32+
def test_combinations(self):
33+
number_of_threads = 10
34+
number_of_iterations = 24
35+
36+
for it in range(number_of_iterations):
37+
iterator = combinations((1, 2, 3, 4, 5), 2)
38+
test_concurrent_iteration(iterator, number_of_threads)
39+
40+
@threading_helper.reap_threads
41+
def test_product(self):
42+
number_of_threads = 10
43+
number_of_iterations = 24
44+
45+
for it in range(number_of_iterations):
46+
iterator = product((1, 2, 3, 4, 5), (10, 20, 30))
47+
test_concurrent_iteration(iterator, number_of_threads)
48+
49+
50+
if __name__ == "__main__":
51+
unittest.main()

Lib/test/test_free_threading/test_pickle.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,39 @@ def mutator():
4040
with threading_helper.start_threads(threads):
4141
pass
4242

43+
def test_pickle_dumps_with_concurrent_list_mutations(self):
44+
# gh-149816: Pickling a list while another thread mutates it
45+
# used to be a UAF in free-threaded mode. batch_list_exact()
46+
# used PyList_GET_ITEM (borrowed) followed by Py_INCREF, and a
47+
# concurrent replace/pop could free the item between those two
48+
# operations.
49+
shared = [list(range(20)) for _ in range(50)]
50+
51+
def dumper():
52+
for _ in range(1000):
53+
try:
54+
pickle.dumps(shared)
55+
except (RuntimeError, IndexError):
56+
pass
57+
58+
def mutator():
59+
for i in range(1000):
60+
idx = i % 50
61+
shared[idx] = list(range(i % 20))
62+
if i % 10 == 0:
63+
try:
64+
shared.pop()
65+
except IndexError:
66+
pass
67+
shared.append([i])
68+
69+
threads = []
70+
for _ in range(10):
71+
threads.append(threading.Thread(target=dumper))
72+
threads.append(threading.Thread(target=mutator))
73+
74+
with threading_helper.start_threads(threads):
75+
pass
76+
4377
if __name__ == "__main__":
4478
unittest.main()

0 commit comments

Comments
 (0)