Skip to content

Commit 8f28847

Browse files
committed
Merge remote-tracking branch 'upstream/main' into pythongh-112536-tsan-tests
2 parents b571b9d + 3325699 commit 8f28847

File tree

105 files changed

+1171
-652
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

105 files changed

+1171
-652
lines changed

.github/workflows/build.yml

+10
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,14 @@ jobs:
301301
- name: SSL tests
302302
run: ./python Lib/test/ssltests.py
303303

304+
build_wasi:
305+
name: 'WASI'
306+
needs: check_source
307+
if: needs.check_source.outputs.run_tests == 'true'
308+
uses: ./.github/workflows/reusable-wasi.yml
309+
with:
310+
config_hash: ${{ needs.check_source.outputs.config_hash }}
311+
304312
test_hypothesis:
305313
name: "Hypothesis tests on Ubuntu"
306314
runs-on: ubuntu-20.04
@@ -525,6 +533,7 @@ jobs:
525533
- build_ubuntu
526534
- build_ubuntu_free_threading
527535
- build_ubuntu_ssltests
536+
- build_wasi
528537
- build_windows
529538
- build_windows_free_threading
530539
- test_hypothesis
@@ -558,6 +567,7 @@ jobs:
558567
build_ubuntu,
559568
build_ubuntu_free_threading,
560569
build_ubuntu_ssltests,
570+
build_wasi,
561571
build_windows,
562572
build_windows_free_threading,
563573
build_asan,

.github/workflows/reusable-wasi.yml

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
on:
2+
workflow_call:
3+
inputs:
4+
config_hash:
5+
required: true
6+
type: string
7+
8+
jobs:
9+
build_wasi_reusable:
10+
name: 'build and test'
11+
timeout-minutes: 60
12+
runs-on: ubuntu-20.04
13+
env:
14+
WASMTIME_VERSION: 18.0.2
15+
WASI_SDK_VERSION: 20
16+
WASI_SDK_PATH: /opt/wasi-sdk
17+
CROSS_BUILD_PYTHON: cross-build/build
18+
CROSS_BUILD_WASI: cross-build/wasm32-wasi
19+
steps:
20+
- uses: actions/checkout@v4
21+
# No problem resolver registered as one doesn't currently exist for Clang.
22+
- name: "Install wasmtime"
23+
uses: jcbhmr/setup-wasmtime@v2
24+
with:
25+
wasmtime-version: ${{ env.WASMTIME_VERSION }}
26+
- name: "Restore WASI SDK"
27+
id: cache-wasi-sdk
28+
uses: actions/cache@v4
29+
with:
30+
path: ${{ env.WASI_SDK_PATH }}
31+
key: ${{ runner.os }}-wasi-sdk-${{ env.WASI_SDK_VERSION }}
32+
- name: "Install WASI SDK"
33+
if: steps.cache-wasi-sdk.outputs.cache-hit != 'true'
34+
run: |
35+
mkdir ${{ env.WASI_SDK_PATH }} && \
36+
curl -s -S --location https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${{ env.WASI_SDK_VERSION }}/wasi-sdk-${{ env.WASI_SDK_VERSION }}.0-linux.tar.gz | \
37+
tar --strip-components 1 --directory ${{ env.WASI_SDK_PATH }} --extract --gunzip
38+
- name: "Configure ccache action"
39+
uses: hendrikmuhs/[email protected]
40+
with:
41+
save: ${{ github.event_name == 'push' }}
42+
max-size: "200M"
43+
- name: "Add ccache to PATH"
44+
run: echo "PATH=/usr/lib/ccache:$PATH" >> $GITHUB_ENV
45+
- name: "Install Python"
46+
uses: actions/setup-python@v5
47+
with:
48+
python-version: '3.x'
49+
- name: "Restore Python build config.cache"
50+
uses: actions/cache@v4
51+
with:
52+
path: ${{ env.CROSS_BUILD_PYTHON }}/config.cache
53+
key: ${{ github.job }}-${{ runner.os }}-${{ env.IMAGE_VERSION }}-${{ inputs.config_hash }}
54+
- name: "Configure build Python"
55+
run: python3 Tools/wasm/wasi.py configure-build-python -- --config-cache --with-pydebug
56+
- name: "Make build Python"
57+
run: python3 Tools/wasm/wasi.py make-build-python
58+
- name: "Restore host config.cache"
59+
uses: actions/cache@v4
60+
with:
61+
path: ${{ env.CROSS_BUILD_WASI }}/config.cache
62+
key: ${{ github.job }}-${{ runner.os }}-${{ env.IMAGE_VERSION }}-wasi-sdk-${{ env.WASI_SDK_VERSION }}-${{ inputs.config_hash }}
63+
- name: "Configure host"
64+
# `--with-pydebug` inferred from configure-build-python
65+
run: python3 Tools/wasm/wasi.py configure-host -- --config-cache
66+
- name: "Make host"
67+
run: python3 Tools/wasm/wasi.py make-host
68+
- name: "Display build info"
69+
run: make --directory ${{ env.CROSS_BUILD_WASI }} pythoninfo
70+
- name: "Test"
71+
run: make --directory ${{ env.CROSS_BUILD_WASI }} test

.github/workflows/stale.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: Mark stale pull requests
22

33
on:
44
schedule:
5-
- cron: "0 0 * * *"
5+
- cron: "0 */12 * * *"
66

77
permissions:
88
pull-requests: write

Doc/library/itertools.rst

+103-9
Original file line numberDiff line numberDiff line change
@@ -998,7 +998,7 @@ The following recipes have a more mathematical flavor:
998998

999999
def sum_of_squares(it):
10001000
"Add up the squares of the input values."
1001-
# sum_of_squares([10, 20, 30]) -> 1400
1001+
# sum_of_squares([10, 20, 30]) --> 1400
10021002
return math.sumprod(*tee(it))
10031003
10041004
def reshape(matrix, cols):
@@ -1019,17 +1019,16 @@ The following recipes have a more mathematical flavor:
10191019

10201020
def convolve(signal, kernel):
10211021
"""Discrete linear convolution of two iterables.
1022+
Equivalent to polynomial multiplication.
10221023

1023-
The kernel is fully consumed before the calculations begin.
1024-
The signal is consumed lazily and can be infinite.
1025-
1026-
Convolutions are mathematically commutative.
1027-
If the signal and kernel are swapped,
1028-
the output will be the same.
1024+
Convolutions are mathematically commutative; however, the inputs are
1025+
evaluated differently. The signal is consumed lazily and can be
1026+
infinite. The kernel is fully consumed before the calculations begin.
10291027

10301028
Article: https://betterexplained.com/articles/intuitive-convolution/
10311029
Video: https://www.youtube.com/watch?v=KuXjwB4LzSA
10321030
"""
1031+
# convolve([1, -1, -20], [1, -3]) --> 1 -4 -17 60
10331032
# convolve(data, [0.25, 0.25, 0.25, 0.25]) --> Moving average (blur)
10341033
# convolve(data, [1/2, 0, -1/2]) --> 1st derivative estimate
10351034
# convolve(data, [1, -2, 1]) --> 2nd derivative estimate
@@ -1067,7 +1066,7 @@ The following recipes have a more mathematical flavor:
10671066
f(x) = x³ -4x² -17x + 60
10681067
f'(x) = 3x² -8x -17
10691068
"""
1070-
# polynomial_derivative([1, -4, -17, 60]) -> [3, -8, -17]
1069+
# polynomial_derivative([1, -4, -17, 60]) --> [3, -8, -17]
10711070
n = len(coefficients)
10721071
powers = reversed(range(1, n))
10731072
return list(map(operator.mul, coefficients, powers))
@@ -1169,6 +1168,12 @@ The following recipes have a more mathematical flavor:
11691168

11701169
>>> take(10, count())
11711170
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
1171+
>>> # Verify that the input is consumed lazily
1172+
>>> it = iter('abcdef')
1173+
>>> take(3, it)
1174+
['a', 'b', 'c']
1175+
>>> list(it)
1176+
['d', 'e', 'f']
11721177

11731178
>>> list(prepend(1, [2, 3, 4]))
11741179
[1, 2, 3, 4]
@@ -1181,25 +1186,45 @@ The following recipes have a more mathematical flavor:
11811186

11821187
>>> list(tail(3, 'ABCDEFG'))
11831188
['E', 'F', 'G']
1189+
>>> # Verify the input is consumed greedily
1190+
>>> input_iterator = iter('ABCDEFG')
1191+
>>> output_iterator = tail(3, input_iterator)
1192+
>>> list(input_iterator)
1193+
[]
11841194

11851195
>>> it = iter(range(10))
11861196
>>> consume(it, 3)
1197+
>>> # Verify the input is consumed lazily
11871198
>>> next(it)
11881199
3
1200+
>>> # Verify the input is consumed completely
11891201
>>> consume(it)
11901202
>>> next(it, 'Done')
11911203
'Done'
11921204

11931205
>>> nth('abcde', 3)
11941206
'd'
1195-
11961207
>>> nth('abcde', 9) is None
11971208
True
1209+
>>> # Verify that the input is consumed lazily
1210+
>>> it = iter('abcde')
1211+
>>> nth(it, 2)
1212+
'c'
1213+
>>> list(it)
1214+
['d', 'e']
11981215

11991216
>>> [all_equal(s) for s in ('', 'A', 'AAAA', 'AAAB', 'AAABA')]
12001217
[True, True, True, False, False]
12011218
>>> [all_equal(s, key=str.casefold) for s in ('', 'A', 'AaAa', 'AAAB', 'AAABA')]
12021219
[True, True, True, False, False]
1220+
>>> # Verify that the input is consumed lazily and that only
1221+
>>> # one element of a second equivalence class is used to disprove
1222+
>>> # the assertion that all elements are equal.
1223+
>>> it = iter('aaabbbccc')
1224+
>>> all_equal(it)
1225+
False
1226+
>>> ''.join(it)
1227+
'bbccc'
12031228

12041229
>>> quantify(range(99), lambda x: x%2==0)
12051230
50
@@ -1222,6 +1247,11 @@ The following recipes have a more mathematical flavor:
12221247

12231248
>>> list(ncycles('abc', 3))
12241249
['a', 'b', 'c', 'a', 'b', 'c', 'a', 'b', 'c']
1250+
>>> # Verify greedy consumption of input iterator
1251+
>>> input_iterator = iter('abc')
1252+
>>> output_iterator = ncycles(input_iterator, 3)
1253+
>>> list(input_iterator)
1254+
[]
12251255

12261256
>>> sum_of_squares([10, 20, 30])
12271257
1400
@@ -1248,19 +1278,41 @@ The following recipes have a more mathematical flavor:
12481278

12491279
>>> list(transpose([(1, 2, 3), (11, 22, 33)]))
12501280
[(1, 11), (2, 22), (3, 33)]
1281+
>>> # Verify that the inputs are consumed lazily
1282+
>>> input1 = iter([1, 2, 3])
1283+
>>> input2 = iter([11, 22, 33])
1284+
>>> output_iterator = transpose([input1, input2])
1285+
>>> next(output_iterator)
1286+
(1, 11)
1287+
>>> list(zip(input1, input2))
1288+
[(2, 22), (3, 33)]
12511289

12521290
>>> list(matmul([(7, 5), (3, 5)], [[2, 5], [7, 9]]))
12531291
[(49, 80), (41, 60)]
12541292
>>> list(matmul([[2, 5], [7, 9], [3, 4]], [[7, 11, 5, 4, 9], [3, 5, 2, 6, 3]]))
12551293
[(29, 47, 20, 38, 33), (76, 122, 53, 82, 90), (33, 53, 23, 36, 39)]
12561294

1295+
>>> list(convolve([1, -1, -20], [1, -3])) == [1, -4, -17, 60]
1296+
True
12571297
>>> data = [20, 40, 24, 32, 20, 28, 16]
12581298
>>> list(convolve(data, [0.25, 0.25, 0.25, 0.25]))
12591299
[5.0, 15.0, 21.0, 29.0, 29.0, 26.0, 24.0, 16.0, 11.0, 4.0]
12601300
>>> list(convolve(data, [1, -1]))
12611301
[20, 20, -16, 8, -12, 8, -12, -16]
12621302
>>> list(convolve(data, [1, -2, 1]))
12631303
[20, 0, -36, 24, -20, 20, -20, -4, 16]
1304+
>>> # Verify signal is consumed lazily and the kernel greedily
1305+
>>> signal_iterator = iter([10, 20, 30, 40, 50])
1306+
>>> kernel_iterator = iter([1, 2, 3])
1307+
>>> output_iterator = convolve(signal_iterator, kernel_iterator)
1308+
>>> list(kernel_iterator)
1309+
[]
1310+
>>> next(output_iterator)
1311+
10
1312+
>>> next(output_iterator)
1313+
40
1314+
>>> list(signal_iterator)
1315+
[30, 40, 50]
12641316

12651317
>>> from fractions import Fraction
12661318
>>> from decimal import Decimal
@@ -1348,6 +1400,17 @@ The following recipes have a more mathematical flavor:
13481400
>>> # Test list input. Lists do not support None for the stop argument
13491401
>>> list(iter_index(list('AABCADEAF'), 'A'))
13501402
[0, 1, 4, 7]
1403+
>>> # Verify that input is consumed lazily
1404+
>>> input_iterator = iter('AABCADEAF')
1405+
>>> output_iterator = iter_index(input_iterator, 'A')
1406+
>>> next(output_iterator)
1407+
0
1408+
>>> next(output_iterator)
1409+
1
1410+
>>> next(output_iterator)
1411+
4
1412+
>>> ''.join(input_iterator)
1413+
'DEAF'
13511414

13521415
>>> list(sieve(30))
13531416
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
@@ -1499,6 +1562,17 @@ The following recipes have a more mathematical flavor:
14991562
[0, 2, 4, 6, 8]
15001563
>>> list(odds)
15011564
[1, 3, 5, 7, 9]
1565+
>>> # Verify that the input is consumed lazily
1566+
>>> input_iterator = iter(range(10))
1567+
>>> evens, odds = partition(is_odd, input_iterator)
1568+
>>> next(odds)
1569+
1
1570+
>>> next(odds)
1571+
3
1572+
>>> next(evens)
1573+
0
1574+
>>> list(input_iterator)
1575+
[4, 5, 6, 7, 8, 9]
15021576

15031577
>>> list(subslices('ABCD'))
15041578
['A', 'AB', 'ABC', 'ABCD', 'B', 'BC', 'BCD', 'C', 'CD', 'D']
@@ -1518,13 +1592,27 @@ The following recipes have a more mathematical flavor:
15181592
['A', 'B', 'C', 'D']
15191593
>>> list(unique_everseen('ABBcCAD', str.casefold))
15201594
['A', 'B', 'c', 'D']
1595+
>>> # Verify that the input is consumed lazily
1596+
>>> input_iterator = iter('AAAABBBCCDAABBB')
1597+
>>> output_iterator = unique_everseen(input_iterator)
1598+
>>> next(output_iterator)
1599+
'A'
1600+
>>> ''.join(input_iterator)
1601+
'AAABBBCCDAABBB'
15211602

15221603
>>> list(unique_justseen('AAAABBBCCDAABBB'))
15231604
['A', 'B', 'C', 'D', 'A', 'B']
15241605
>>> list(unique_justseen('ABBCcAD', str.casefold))
15251606
['A', 'B', 'C', 'A', 'D']
15261607
>>> list(unique_justseen('ABBcCAD', str.casefold))
15271608
['A', 'B', 'c', 'A', 'D']
1609+
>>> # Verify that the input is consumed lazily
1610+
>>> input_iterator = iter('AAAABBBCCDAABBB')
1611+
>>> output_iterator = unique_justseen(input_iterator)
1612+
>>> next(output_iterator)
1613+
'A'
1614+
>>> ''.join(input_iterator)
1615+
'AAABBBCCDAABBB'
15281616

15291617
>>> d = dict(a=1, b=2, c=3)
15301618
>>> it = iter_except(d.popitem, KeyError)
@@ -1545,6 +1633,12 @@ The following recipes have a more mathematical flavor:
15451633

15461634
>>> first_true('ABC0DEF1', '9', str.isdigit)
15471635
'0'
1636+
>>> # Verify that inputs are consumed lazily
1637+
>>> it = iter('ABC0DEF1')
1638+
>>> first_true(it, predicate=str.isdigit)
1639+
'0'
1640+
>>> ''.join(it)
1641+
'DEF1'
15481642

15491643

15501644
.. testcode::

0 commit comments

Comments
 (0)