Skip to content

Commit 8311e18

Browse files
committed
Merge branch 'main' into gh-110481-inter-thread-queue
2 parents c631856 + 31633f4 commit 8311e18

33 files changed

+946
-239
lines changed

Doc/c-api/code.rst

+3-2
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,13 @@ bound into a function.
2222
.. c:var:: PyTypeObject PyCode_Type
2323
2424
This is an instance of :c:type:`PyTypeObject` representing the Python
25-
:class:`code` type.
25+
:ref:`code object <code-objects>`.
2626

2727

2828
.. c:function:: int PyCode_Check(PyObject *co)
2929
30-
Return true if *co* is a :class:`code` object. This function always succeeds.
30+
Return true if *co* is a :ref:`code object <code-objects>`.
31+
This function always succeeds.
3132
3233
.. c:function:: int PyCode_GetNumFree(PyCodeObject *co)
3334

Doc/library/enum.rst

+15-2
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,19 @@ Data Types
286286
appropriate value will be chosen for you. See :class:`auto` for the
287287
details.
288288

289+
.. attribute:: Enum._name_
290+
291+
Name of the member.
292+
293+
.. attribute:: Enum._value_
294+
295+
Value of the member, can be set in :meth:`~object.__new__`.
296+
297+
.. attribute:: Enum._order_
298+
299+
No longer used, kept for backward compatibility.
300+
(class attribute, removed during class creation).
301+
289302
.. attribute:: Enum._ignore_
290303

291304
``_ignore_`` is only used during creation and is removed from the
@@ -823,8 +836,8 @@ Supported ``_sunder_`` names
823836
- :attr:`~Enum._ignore_` -- a list of names, either as a :class:`list` or a
824837
:class:`str`, that will not be transformed into members, and will be removed
825838
from the final class
826-
- :attr:`~Enum._order_` -- used in Python 2/3 code to ensure member order is
827-
consistent (class attribute, removed during class creation)
839+
- :attr:`~Enum._order_` -- no longer used, kept for backward
840+
compatibility (class attribute, removed during class creation)
828841
- :meth:`~Enum._generate_next_value_` -- used to get an appropriate value for
829842
an enum member; may be overridden
830843

Doc/tools/.nitignore

-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ Doc/library/email.compat32-message.rst
3131
Doc/library/email.errors.rst
3232
Doc/library/email.parser.rst
3333
Doc/library/email.policy.rst
34-
Doc/library/enum.rst
3534
Doc/library/exceptions.rst
3635
Doc/library/faulthandler.rst
3736
Doc/library/fcntl.rst

Doc/whatsnew/3.13.rst

+17
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,17 @@ Improved Error Messages
101101
variables. See also :ref:`using-on-controlling-color`.
102102
(Contributed by Pablo Galindo Salgado in :gh:`112730`.)
103103

104+
* When an incorrect keyword argument is passed to a function, the error message
105+
now potentially suggests the correct keyword argument.
106+
(Contributed by Pablo Galindo Salgado and Shantanu Jain in :gh:`107944`.)
107+
108+
>>> "better error messages!".split(max_split=1)
109+
Traceback (most recent call last):
110+
File "<stdin>", line 1, in <module>
111+
"better error messages!".split(max_split=1)
112+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^
113+
TypeError: split() got an unexpected keyword argument 'max_split'. Did you mean 'maxsplit'?
114+
104115
Other Language Changes
105116
======================
106117

@@ -1328,6 +1339,12 @@ Build Changes
13281339
:ref:`limited C API <limited-c-api>`.
13291340
(Contributed by Victor Stinner in :gh:`85283`.)
13301341

1342+
* ``wasm32-wasi`` is now a tier 2 platform.
1343+
(Contributed by Brett Cannon in :gh:`115192`.)
1344+
1345+
* ``wasm32-emscripten`` is no longer a supported platform.
1346+
(Contributed by Brett Cannon in :gh:`115192`.)
1347+
13311348

13321349
C API Changes
13331350
=============

Include/internal/pycore_symtable.h

+12-12
Original file line numberDiff line numberDiff line change
@@ -109,18 +109,18 @@ extern PyObject* _Py_Mangle(PyObject *p, PyObject *name);
109109

110110
/* Flags for def-use information */
111111

112-
#define DEF_GLOBAL 1 /* global stmt */
113-
#define DEF_LOCAL 2 /* assignment in code block */
114-
#define DEF_PARAM 2<<1 /* formal parameter */
115-
#define DEF_NONLOCAL 2<<2 /* nonlocal stmt */
116-
#define USE 2<<3 /* name is used */
117-
#define DEF_FREE 2<<4 /* name used but not defined in nested block */
118-
#define DEF_FREE_CLASS 2<<5 /* free variable from class's method */
119-
#define DEF_IMPORT 2<<6 /* assignment occurred via import */
120-
#define DEF_ANNOT 2<<7 /* this name is annotated */
121-
#define DEF_COMP_ITER 2<<8 /* this name is a comprehension iteration variable */
122-
#define DEF_TYPE_PARAM 2<<9 /* this name is a type parameter */
123-
#define DEF_COMP_CELL 2<<10 /* this name is a cell in an inlined comprehension */
112+
#define DEF_GLOBAL 1 /* global stmt */
113+
#define DEF_LOCAL 2 /* assignment in code block */
114+
#define DEF_PARAM (2<<1) /* formal parameter */
115+
#define DEF_NONLOCAL (2<<2) /* nonlocal stmt */
116+
#define USE (2<<3) /* name is used */
117+
#define DEF_FREE (2<<4) /* name used but not defined in nested block */
118+
#define DEF_FREE_CLASS (2<<5) /* free variable from class's method */
119+
#define DEF_IMPORT (2<<6) /* assignment occurred via import */
120+
#define DEF_ANNOT (2<<7) /* this name is annotated */
121+
#define DEF_COMP_ITER (2<<8) /* this name is a comprehension iteration variable */
122+
#define DEF_TYPE_PARAM (2<<9) /* this name is a type parameter */
123+
#define DEF_COMP_CELL (2<<10) /* this name is a cell in an inlined comprehension */
124124

125125
#define DEF_BOUND (DEF_LOCAL | DEF_PARAM | DEF_IMPORT)
126126

Lib/pickletools.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1253,7 +1253,7 @@ def __init__(self, name, code, arg,
12531253
stack_before=[],
12541254
stack_after=[pyint],
12551255
proto=2,
1256-
doc="""Long integer using found-byte length.
1256+
doc="""Long integer using four-byte length.
12571257
12581258
A more efficient encoding of a Python long; the long4 encoding
12591259
says it all."""),

Lib/test/test_call.py

+29-3
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ def test_varargs16_kw(self):
155155
min, 0, default=1, key=2, foo=3)
156156

157157
def test_varargs17_kw(self):
158-
msg = r"'foo' is an invalid keyword argument for print\(\)$"
158+
msg = r"print\(\) got an unexpected keyword argument 'foo'$"
159159
self.assertRaisesRegex(TypeError, msg,
160160
print, 0, sep=1, end=2, file=3, flush=4, foo=5)
161161

@@ -928,7 +928,7 @@ def check_suggestion_includes(self, message):
928928
self.assertIn(f"Did you mean '{message}'?", str(cm.exception))
929929

930930
@contextlib.contextmanager
931-
def check_suggestion_not_pressent(self):
931+
def check_suggestion_not_present(self):
932932
with self.assertRaises(TypeError) as cm:
933933
yield
934934
self.assertNotIn("Did you mean", str(cm.exception))
@@ -946,7 +946,7 @@ def foo(blech=None, /, aaa=None, *args, late1=None):
946946

947947
for keyword, suggestion in cases:
948948
with self.subTest(keyword):
949-
ctx = self.check_suggestion_includes(suggestion) if suggestion else self.check_suggestion_not_pressent()
949+
ctx = self.check_suggestion_includes(suggestion) if suggestion else self.check_suggestion_not_present()
950950
with ctx:
951951
foo(**{keyword:None})
952952

@@ -987,6 +987,32 @@ def case_change_over_substitution(BLuch=None, Luch = None, fluch = None):
987987
with self.check_suggestion_includes(suggestion):
988988
func(bluch=None)
989989

990+
def test_unexpected_keyword_suggestion_via_getargs(self):
991+
with self.check_suggestion_includes("maxsplit"):
992+
"foo".split(maxsplt=1)
993+
994+
self.assertRaisesRegex(
995+
TypeError, r"split\(\) got an unexpected keyword argument 'blech'$",
996+
"foo".split, blech=1
997+
)
998+
with self.check_suggestion_not_present():
999+
"foo".split(blech=1)
1000+
with self.check_suggestion_not_present():
1001+
"foo".split(more_noise=1, maxsplt=1)
1002+
1003+
# Also test the vgetargskeywords path
1004+
with self.check_suggestion_includes("name"):
1005+
ImportError(namez="oops")
1006+
1007+
self.assertRaisesRegex(
1008+
TypeError, r"ImportError\(\) got an unexpected keyword argument 'blech'$",
1009+
ImportError, blech=1
1010+
)
1011+
with self.check_suggestion_not_present():
1012+
ImportError(blech=1)
1013+
with self.check_suggestion_not_present():
1014+
ImportError(blech=1, namez="oops")
1015+
9901016
@cpython_only
9911017
class TestRecursion(unittest.TestCase):
9921018

Lib/test/test_capi/test_getargs.py

+13-13
Original file line numberDiff line numberDiff line change
@@ -667,15 +667,15 @@ def test_invalid_keyword(self):
667667
try:
668668
getargs_keywords((1,2),3,arg5=10,arg666=666)
669669
except TypeError as err:
670-
self.assertEqual(str(err), "'arg666' is an invalid keyword argument for this function")
670+
self.assertEqual(str(err), "this function got an unexpected keyword argument 'arg666'")
671671
else:
672672
self.fail('TypeError should have been raised')
673673

674674
def test_surrogate_keyword(self):
675675
try:
676676
getargs_keywords((1,2), 3, (4,(5,6)), (7,8,9), **{'\uDC80': 10})
677677
except TypeError as err:
678-
self.assertEqual(str(err), "'\udc80' is an invalid keyword argument for this function")
678+
self.assertEqual(str(err), "this function got an unexpected keyword argument '\udc80'")
679679
else:
680680
self.fail('TypeError should have been raised')
681681

@@ -742,12 +742,12 @@ def test_too_many_args(self):
742742
def test_invalid_keyword(self):
743743
# extraneous keyword arg
744744
with self.assertRaisesRegex(TypeError,
745-
"'monster' is an invalid keyword argument for this function"):
745+
"this function got an unexpected keyword argument 'monster'"):
746746
getargs_keyword_only(1, 2, monster=666)
747747

748748
def test_surrogate_keyword(self):
749749
with self.assertRaisesRegex(TypeError,
750-
"'\udc80' is an invalid keyword argument for this function"):
750+
"this function got an unexpected keyword argument '\udc80'"):
751751
getargs_keyword_only(1, 2, **{'\uDC80': 10})
752752

753753
def test_weird_str_subclass(self):
@@ -761,7 +761,7 @@ def __hash__(self):
761761
"invalid keyword argument for this function"):
762762
getargs_keyword_only(1, 2, **{BadStr("keyword_only"): 3})
763763
with self.assertRaisesRegex(TypeError,
764-
"invalid keyword argument for this function"):
764+
"this function got an unexpected keyword argument"):
765765
getargs_keyword_only(1, 2, **{BadStr("monster"): 666})
766766

767767
def test_weird_str_subclass2(self):
@@ -774,7 +774,7 @@ def __hash__(self):
774774
"invalid keyword argument for this function"):
775775
getargs_keyword_only(1, 2, **{BadStr("keyword_only"): 3})
776776
with self.assertRaisesRegex(TypeError,
777-
"invalid keyword argument for this function"):
777+
"this function got an unexpected keyword argument"):
778778
getargs_keyword_only(1, 2, **{BadStr("monster"): 666})
779779

780780

@@ -807,7 +807,7 @@ def test_required_args(self):
807807

808808
def test_empty_keyword(self):
809809
with self.assertRaisesRegex(TypeError,
810-
"'' is an invalid keyword argument for this function"):
810+
"this function got an unexpected keyword argument ''"):
811811
self.getargs(1, 2, **{'': 666})
812812

813813

@@ -1204,7 +1204,7 @@ def test_basic(self):
12041204
"function missing required argument 'a'"):
12051205
parse((), {}, 'O', ['a'])
12061206
with self.assertRaisesRegex(TypeError,
1207-
"'b' is an invalid keyword argument"):
1207+
"this function got an unexpected keyword argument 'b'"):
12081208
parse((), {'b': 1}, '|O', ['a'])
12091209
with self.assertRaisesRegex(TypeError,
12101210
fr"argument for function given by name \('a'\) "
@@ -1278,10 +1278,10 @@ def test_nonascii_keywords(self):
12781278
fr"and position \(1\)"):
12791279
parse((1,), {name: 2}, 'O|O', [name, 'b'])
12801280
with self.assertRaisesRegex(TypeError,
1281-
f"'{name}' is an invalid keyword argument"):
1281+
f"this function got an unexpected keyword argument '{name}'"):
12821282
parse((), {name: 1}, '|O', ['b'])
12831283
with self.assertRaisesRegex(TypeError,
1284-
"'b' is an invalid keyword argument"):
1284+
"this function got an unexpected keyword argument 'b'"):
12851285
parse((), {'b': 1}, '|O', [name])
12861286

12871287
invalid = name.encode() + (name.encode()[:-1] or b'\x80')
@@ -1301,17 +1301,17 @@ def test_nonascii_keywords(self):
13011301
for name2 in ('b', 'ë', 'ĉ', 'Ɐ', '𐀁'):
13021302
with self.subTest(name2=name2):
13031303
with self.assertRaisesRegex(TypeError,
1304-
f"'{name2}' is an invalid keyword argument"):
1304+
f"this function got an unexpected keyword argument '{name2}'"):
13051305
parse((), {name2: 1}, '|O', [name])
13061306

13071307
name2 = name.encode().decode('latin1')
13081308
if name2 != name:
13091309
with self.assertRaisesRegex(TypeError,
1310-
f"'{name2}' is an invalid keyword argument"):
1310+
f"this function got an unexpected keyword argument '{name2}'"):
13111311
parse((), {name2: 1}, '|O', [name])
13121312
name3 = name + '3'
13131313
with self.assertRaisesRegex(TypeError,
1314-
f"'{name2}' is an invalid keyword argument"):
1314+
f"this function got an unexpected keyword argument '{name2}'"):
13151315
parse((), {name2: 1, name3: 2}, '|OO', [name, name3])
13161316

13171317
def test_nested_tuple(self):

Lib/test/test_exceptions.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1917,7 +1917,7 @@ def test_attributes(self):
19171917
self.assertEqual(exc.name, 'somename')
19181918
self.assertEqual(exc.path, 'somepath')
19191919

1920-
msg = "'invalid' is an invalid keyword argument for ImportError"
1920+
msg = r"ImportError\(\) got an unexpected keyword argument 'invalid'"
19211921
with self.assertRaisesRegex(TypeError, msg):
19221922
ImportError('test', invalid='keyword')
19231923

Lib/test/test_io.py

+52
Original file line numberDiff line numberDiff line change
@@ -2497,6 +2497,28 @@ def test_interleaved_read_write(self):
24972497
f.flush()
24982498
self.assertEqual(raw.getvalue(), b'a2c')
24992499

2500+
def test_read1_after_write(self):
2501+
with self.BytesIO(b'abcdef') as raw:
2502+
with self.tp(raw, 3) as f:
2503+
f.write(b"1")
2504+
self.assertEqual(f.read1(1), b'b')
2505+
f.flush()
2506+
self.assertEqual(raw.getvalue(), b'1bcdef')
2507+
with self.BytesIO(b'abcdef') as raw:
2508+
with self.tp(raw, 3) as f:
2509+
f.write(b"1")
2510+
self.assertEqual(f.read1(), b'bcd')
2511+
f.flush()
2512+
self.assertEqual(raw.getvalue(), b'1bcdef')
2513+
with self.BytesIO(b'abcdef') as raw:
2514+
with self.tp(raw, 3) as f:
2515+
f.write(b"1")
2516+
# XXX: read(100) returns different numbers of bytes
2517+
# in Python and C implementations.
2518+
self.assertEqual(f.read1(100)[:3], b'bcd')
2519+
f.flush()
2520+
self.assertEqual(raw.getvalue(), b'1bcdef')
2521+
25002522
def test_interleaved_readline_write(self):
25012523
with self.BytesIO(b'ab\ncdef\ng\n') as raw:
25022524
with self.tp(raw) as f:
@@ -2509,6 +2531,36 @@ def test_interleaved_readline_write(self):
25092531
f.flush()
25102532
self.assertEqual(raw.getvalue(), b'1b\n2def\n3\n')
25112533

2534+
def test_xxx(self):
2535+
with self.BytesIO(b'abcdefgh') as raw:
2536+
with self.tp(raw) as f:
2537+
f.write(b'123')
2538+
self.assertEqual(f.read(), b'defgh')
2539+
f.write(b'456')
2540+
f.flush()
2541+
self.assertEqual(raw.getvalue(), b'123defgh456')
2542+
with self.BytesIO(b'abcdefgh') as raw:
2543+
with self.tp(raw) as f:
2544+
f.write(b'123')
2545+
self.assertEqual(f.read(3), b'def')
2546+
f.write(b'456')
2547+
f.flush()
2548+
self.assertEqual(raw.getvalue(), b'123def456')
2549+
with self.BytesIO(b'abcdefgh') as raw:
2550+
with self.tp(raw) as f:
2551+
f.write(b'123')
2552+
self.assertEqual(f.read1(), b'defgh')
2553+
f.write(b'456')
2554+
f.flush()
2555+
self.assertEqual(raw.getvalue(), b'123defgh456')
2556+
with self.BytesIO(b'abcdefgh') as raw:
2557+
with self.tp(raw) as f:
2558+
f.write(b'123')
2559+
self.assertEqual(f.read1(3), b'def')
2560+
f.write(b'456')
2561+
f.flush()
2562+
self.assertEqual(raw.getvalue(), b'123def456')
2563+
25122564
# You can't construct a BufferedRandom over a non-seekable stream.
25132565
test_unseekable = None
25142566

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Promote WASI to a tier 2 platform and drop Emscripten from tier 3 in
2+
configure.ac.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Avoid vendoring ``vcruntime140_threads.dll`` when building with Visual Studio 2022 version 17.8.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Improve error message for function calls with bad keyword arguments via getargs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Adapt :class:`set` and :class:`frozenset` methods to Argument Clinic.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
:meth:`io.BufferedRandom.read1` now flushes the underlying write buffer.

Modules/_io/bufferedio.c

+10
Original file line numberDiff line numberDiff line change
@@ -1050,6 +1050,16 @@ _io__Buffered_read1_impl(buffered *self, Py_ssize_t n)
10501050
Py_DECREF(res);
10511051
return NULL;
10521052
}
1053+
/* Flush the write buffer if necessary */
1054+
if (self->writable) {
1055+
PyObject *r = buffered_flush_and_rewind_unlocked(self);
1056+
if (r == NULL) {
1057+
LEAVE_BUFFERED(self)
1058+
Py_DECREF(res);
1059+
return NULL;
1060+
}
1061+
Py_DECREF(r);
1062+
}
10531063
_bufferedreader_reset_buf(self);
10541064
r = _bufferedreader_raw_read(self, PyBytes_AS_STRING(res), n);
10551065
LEAVE_BUFFERED(self)

0 commit comments

Comments
 (0)