Skip to content

Commit c952b12

Browse files
committed
Fix inconsistent set ordering in annotations
1 parent 2d82ab7 commit c952b12

File tree

21 files changed

+786
-251
lines changed

21 files changed

+786
-251
lines changed

.github/workflows/mypy.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ on:
1313
- "Lib/test/libregrtest/**"
1414
- "Lib/tomllib/**"
1515
- "Misc/mypy/**"
16+
- "Tools/build/compute-changes.py"
1617
- "Tools/build/generate_sbom.py"
18+
- "Tools/build/verify_ensurepip_wheels.py"
19+
- "Tools/build/update_file.py"
1720
- "Tools/cases_generator/**"
1821
- "Tools/clinic/**"
1922
- "Tools/jit/**"

Doc/deprecations/pending-removal-in-3.14.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ Pending removal in Python 3.14
7878
:meth:`~pathlib.PurePath.relative_to`: passing additional arguments is
7979
deprecated.
8080

81-
* :mod:`pkgutil`: :func:`!pkgutil.find_loader` and :func:!pkgutil.get_loader`
81+
* :mod:`pkgutil`: :func:`!pkgutil.find_loader` and :func:`!pkgutil.get_loader`
8282
now raise :exc:`DeprecationWarning`;
8383
use :func:`importlib.util.find_spec` instead.
8484
(Contributed by Nikita Sobolev in :gh:`97850`.)

Doc/using/windows.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ as ``python3.14.exe``) to be available. The directory will be
9595
administrator. Click Start and search for "Edit environment variables for your
9696
account" for the system settings page to add the path.
9797

98+
Each Python runtime you install will have its own directory for scripts. These
99+
also need to be added to :envvar:`PATH` if you want to use them.
100+
98101
The Python install manager will be automatically updated to new releases. This
99102
does not affect any installs of Python runtimes. Uninstalling the Python install
100103
manager does not uninstall any Python runtimes.
@@ -713,6 +716,16 @@ default).
713716
your ``pythonw.exe`` and ``pyw.exe`` aliases are consistent with your
714717
others.
715718
"
719+
"``pip`` gives me a ""command not found"" error when I type it in my
720+
terminal.","Have you activated a virtual environment? Run the
721+
``.venv\Scripts\activate`` script in your terminal to activate.
722+
"
723+
"","The package may be available but missing the generated executable.
724+
We recommend using the ``python -m pip`` command instead, or alternatively
725+
the ``python -m pip install --force pip`` command will recreate the
726+
executables and show you the path to add to :envvar:`PATH`. These scripts are
727+
separated for each runtime, and so you may need to add multiple paths.
728+
"
716729

717730

718731
.. _windows-embeddable:

Lib/test/test_dict.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -338,17 +338,34 @@ def __setitem__(self, key, value):
338338
self.assertRaises(Exc, baddict2.fromkeys, [1])
339339

340340
# test fast path for dictionary inputs
341+
res = dict(zip(range(6), [0]*6))
341342
d = dict(zip(range(6), range(6)))
342-
self.assertEqual(dict.fromkeys(d, 0), dict(zip(range(6), [0]*6)))
343-
343+
self.assertEqual(dict.fromkeys(d, 0), res)
344+
# test fast path for set inputs
345+
d = set(range(6))
346+
self.assertEqual(dict.fromkeys(d, 0), res)
347+
# test slow path for other iterable inputs
348+
d = list(range(6))
349+
self.assertEqual(dict.fromkeys(d, 0), res)
350+
351+
# test fast path when object's constructor returns large non-empty dict
344352
class baddict3(dict):
345353
def __new__(cls):
346354
return d
347-
d = {i : i for i in range(10)}
355+
d = {i : i for i in range(1000)}
348356
res = d.copy()
349357
res.update(a=None, b=None, c=None)
350358
self.assertEqual(baddict3.fromkeys({"a", "b", "c"}), res)
351359

360+
# test slow path when object is a proper subclass of dict
361+
class baddict4(dict):
362+
def __init__(self):
363+
dict.__init__(self, d)
364+
d = {i : i for i in range(1000)}
365+
res = d.copy()
366+
res.update(a=None, b=None, c=None)
367+
self.assertEqual(baddict4.fromkeys({"a", "b", "c"}), res)
368+
352369
def test_copy(self):
353370
d = {1: 1, 2: 2, 3: 3}
354371
self.assertIsNot(d.copy(), d)
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import threading
2+
from unittest import TestCase
3+
from test.support import threading_helper
4+
from random import randint
5+
from io import BytesIO
6+
from sys import getsizeof
7+
8+
9+
class TestBytesIO(TestCase):
10+
# Test pretty much everything that can break under free-threading.
11+
# Non-deterministic, but at least one of these things will fail if
12+
# BytesIO object is not free-thread safe.
13+
14+
def check(self, funcs, *args):
15+
barrier = threading.Barrier(len(funcs))
16+
threads = []
17+
18+
for func in funcs:
19+
thread = threading.Thread(target=func, args=(barrier, *args))
20+
21+
threads.append(thread)
22+
23+
with threading_helper.start_threads(threads):
24+
pass
25+
26+
@threading_helper.requires_working_threading()
27+
@threading_helper.reap_threads
28+
def test_free_threading(self):
29+
"""Test for segfaults and aborts."""
30+
31+
def write(barrier, b, *ignore):
32+
barrier.wait()
33+
try: b.write(b'0' * randint(100, 1000))
34+
except ValueError: pass # ignore write fail to closed file
35+
36+
def writelines(barrier, b, *ignore):
37+
barrier.wait()
38+
b.write(b'0\n' * randint(100, 1000))
39+
40+
def truncate(barrier, b, *ignore):
41+
barrier.wait()
42+
try: b.truncate(0)
43+
except: BufferError # ignore exported buffer
44+
45+
def read(barrier, b, *ignore):
46+
barrier.wait()
47+
b.read()
48+
49+
def read1(barrier, b, *ignore):
50+
barrier.wait()
51+
b.read1()
52+
53+
def readline(barrier, b, *ignore):
54+
barrier.wait()
55+
b.readline()
56+
57+
def readlines(barrier, b, *ignore):
58+
barrier.wait()
59+
b.readlines()
60+
61+
def readinto(barrier, b, into, *ignore):
62+
barrier.wait()
63+
b.readinto(into)
64+
65+
def close(barrier, b, *ignore):
66+
barrier.wait()
67+
b.close()
68+
69+
def getvalue(barrier, b, *ignore):
70+
barrier.wait()
71+
b.getvalue()
72+
73+
def getbuffer(barrier, b, *ignore):
74+
barrier.wait()
75+
b.getbuffer()
76+
77+
def iter(barrier, b, *ignore):
78+
barrier.wait()
79+
list(b)
80+
81+
def getstate(barrier, b, *ignore):
82+
barrier.wait()
83+
b.__getstate__()
84+
85+
def setstate(barrier, b, st, *ignore):
86+
barrier.wait()
87+
b.__setstate__(st)
88+
89+
def sizeof(barrier, b, *ignore):
90+
barrier.wait()
91+
getsizeof(b)
92+
93+
self.check([write] * 10, BytesIO())
94+
self.check([writelines] * 10, BytesIO())
95+
self.check([write] * 10 + [truncate] * 10, BytesIO())
96+
self.check([truncate] + [read] * 10, BytesIO(b'0\n'*204800))
97+
self.check([truncate] + [read1] * 10, BytesIO(b'0\n'*204800))
98+
self.check([truncate] + [readline] * 10, BytesIO(b'0\n'*20480))
99+
self.check([truncate] + [readlines] * 10, BytesIO(b'0\n'*20480))
100+
self.check([truncate] + [readinto] * 10, BytesIO(b'0\n'*204800), bytearray(b'0\n'*204800))
101+
self.check([close] + [write] * 10, BytesIO())
102+
self.check([truncate] + [getvalue] * 10, BytesIO(b'0\n'*204800))
103+
self.check([truncate] + [getbuffer] * 10, BytesIO(b'0\n'*204800))
104+
self.check([truncate] + [iter] * 10, BytesIO(b'0\n'*20480))
105+
self.check([truncate] + [getstate] * 10, BytesIO(b'0\n'*204800))
106+
self.check([truncate] + [setstate] * 10, BytesIO(b'0\n'*204800), (b'123', 0, None))
107+
self.check([truncate] + [sizeof] * 10, BytesIO(b'0\n'*204800))
108+
109+
# no tests for seek or tell because they don't break anything

Lib/test/test_generated_cases.py

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2069,6 +2069,189 @@ def test_missing_override_failure(self):
20692069
with self.assertRaisesRegex(AssertionError, "All abstract uops"):
20702070
self.run_cases_test(input, input2, output)
20712071

2072+
def test_validate_uop_input_length_mismatch(self):
2073+
input = """
2074+
op(OP, (arg1 -- out)) {
2075+
SPAM();
2076+
}
2077+
"""
2078+
input2 = """
2079+
op(OP, (arg1, arg2 -- out)) {
2080+
}
2081+
"""
2082+
output = """
2083+
"""
2084+
with self.assertRaisesRegex(SyntaxError,
2085+
"Must have the same number of inputs"):
2086+
self.run_cases_test(input, input2, output)
2087+
2088+
def test_validate_uop_output_length_mismatch(self):
2089+
input = """
2090+
op(OP, (arg1 -- out)) {
2091+
SPAM();
2092+
}
2093+
"""
2094+
input2 = """
2095+
op(OP, (arg1 -- out1, out2)) {
2096+
}
2097+
"""
2098+
output = """
2099+
"""
2100+
with self.assertRaisesRegex(SyntaxError,
2101+
"Must have the same number of outputs"):
2102+
self.run_cases_test(input, input2, output)
2103+
2104+
def test_validate_uop_input_name_mismatch(self):
2105+
input = """
2106+
op(OP, (foo -- out)) {
2107+
SPAM();
2108+
}
2109+
"""
2110+
input2 = """
2111+
op(OP, (bar -- out)) {
2112+
}
2113+
"""
2114+
output = """
2115+
"""
2116+
with self.assertRaisesRegex(SyntaxError,
2117+
"Inputs must have equal names"):
2118+
self.run_cases_test(input, input2, output)
2119+
2120+
def test_validate_uop_output_name_mismatch(self):
2121+
input = """
2122+
op(OP, (arg1 -- foo)) {
2123+
SPAM();
2124+
}
2125+
"""
2126+
input2 = """
2127+
op(OP, (arg1 -- bar)) {
2128+
}
2129+
"""
2130+
output = """
2131+
"""
2132+
with self.assertRaisesRegex(SyntaxError,
2133+
"Outputs must have equal names"):
2134+
self.run_cases_test(input, input2, output)
2135+
2136+
def test_validate_uop_unused_input(self):
2137+
input = """
2138+
op(OP, (unused -- )) {
2139+
}
2140+
"""
2141+
input2 = """
2142+
op(OP, (foo -- )) {
2143+
}
2144+
"""
2145+
output = """
2146+
case OP: {
2147+
stack_pointer += -1;
2148+
assert(WITHIN_STACK_BOUNDS());
2149+
break;
2150+
}
2151+
"""
2152+
self.run_cases_test(input, input2, output)
2153+
2154+
input = """
2155+
op(OP, (foo -- )) {
2156+
}
2157+
"""
2158+
input2 = """
2159+
op(OP, (unused -- )) {
2160+
}
2161+
"""
2162+
output = """
2163+
case OP: {
2164+
stack_pointer += -1;
2165+
assert(WITHIN_STACK_BOUNDS());
2166+
break;
2167+
}
2168+
"""
2169+
self.run_cases_test(input, input2, output)
2170+
2171+
def test_validate_uop_unused_output(self):
2172+
input = """
2173+
op(OP, ( -- unused)) {
2174+
}
2175+
"""
2176+
input2 = """
2177+
op(OP, ( -- foo)) {
2178+
foo = NULL;
2179+
}
2180+
"""
2181+
output = """
2182+
case OP: {
2183+
JitOptSymbol *foo;
2184+
foo = NULL;
2185+
stack_pointer[0] = foo;
2186+
stack_pointer += 1;
2187+
assert(WITHIN_STACK_BOUNDS());
2188+
break;
2189+
}
2190+
"""
2191+
self.run_cases_test(input, input2, output)
2192+
2193+
input = """
2194+
op(OP, ( -- foo)) {
2195+
foo = NULL;
2196+
}
2197+
"""
2198+
input2 = """
2199+
op(OP, ( -- unused)) {
2200+
}
2201+
"""
2202+
output = """
2203+
case OP: {
2204+
stack_pointer += 1;
2205+
assert(WITHIN_STACK_BOUNDS());
2206+
break;
2207+
}
2208+
"""
2209+
self.run_cases_test(input, input2, output)
2210+
2211+
def test_validate_uop_input_size_mismatch(self):
2212+
input = """
2213+
op(OP, (arg1[2] -- )) {
2214+
}
2215+
"""
2216+
input2 = """
2217+
op(OP, (arg1[4] -- )) {
2218+
}
2219+
"""
2220+
output = """
2221+
"""
2222+
with self.assertRaisesRegex(SyntaxError,
2223+
"Inputs must have equal sizes"):
2224+
self.run_cases_test(input, input2, output)
2225+
2226+
def test_validate_uop_output_size_mismatch(self):
2227+
input = """
2228+
op(OP, ( -- out[2])) {
2229+
}
2230+
"""
2231+
input2 = """
2232+
op(OP, ( -- out[4])) {
2233+
}
2234+
"""
2235+
output = """
2236+
"""
2237+
with self.assertRaisesRegex(SyntaxError,
2238+
"Outputs must have equal sizes"):
2239+
self.run_cases_test(input, input2, output)
2240+
2241+
def test_validate_uop_unused_size_mismatch(self):
2242+
input = """
2243+
op(OP, (foo[2] -- )) {
2244+
}
2245+
"""
2246+
input2 = """
2247+
op(OP, (unused[4] -- )) {
2248+
}
2249+
"""
2250+
output = """
2251+
"""
2252+
with self.assertRaisesRegex(SyntaxError,
2253+
"Inputs must have equal sizes"):
2254+
self.run_cases_test(input, input2, output)
20722255

20732256
if __name__ == "__main__":
20742257
unittest.main()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
:meth:`~dict.fromkeys` no longer loops forever when adding a small set of keys to a large base dict. Patch by Angela Liss.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Make :class:`io.BytesIO` safe in :term:`free-threaded <free threading>` build.

0 commit comments

Comments
 (0)