Skip to content

Commit 0a7642e

Browse files
generatedunixname1734921407115435meta-codesync[bot]
authored andcommitted
Sync pre-release CPython 3.15 branch from GitHub (2026-06-22)
Summary: Imported python/cpython `3.15.0b2+dev` from upstream rev [`a940f93`](https://www.github.com/python/cpython/commit/a940f933af981a90efbd81912091def59c8c2018) (committed 2026-06-22 00:29:07+00:00). # Commit Info - Base: (`3.15.0b2+dev`) - [`b83a217`](https://www.github.com/python/cpython/commit/b83a217558ee30c48a3aa08f0cb7c869f9c75e96) (commit date: 2026-06-19 23:31:12+00:00) - Imported: (`3.15.0b2+dev`) - [`a940f93`](https://www.github.com/python/cpython/commit/a940f933af981a90efbd81912091def59c8c2018) (commit date: 2026-06-22 00:29:07+00:00) # Noteworthy file changes - Test files (3 added) - Low-signal files (2 added) (NEWS.d, docs, .github) Complete list of added/removed files: https://www.internalfb.com/intern/everpaste/?color=0&handle=GHebASlRhais1WkFADnU5_JYN-ANbr0LAAAz Differential Revision: D109269832 fbshipit-source-id: bd221efe7947d2bdbce657312ac545499672959a
1 parent 94a8a01 commit 0a7642e

20 files changed

Lines changed: 722 additions & 39 deletions

.readthedocs.yml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,21 @@ build:
1111
os: ubuntu-24.04
1212
tools:
1313
python: "3"
14+
apt_packages:
15+
- jq
1416

1517
jobs:
16-
post_checkout:
18+
post_system_dependencies:
1719
# https://docs.readthedocs.com/platform/stable/guides/build/skip-build.html#skip-builds-based-on-conditions
1820
#
19-
# Cancel building pull requests when there aren't changes in the Doc
21+
# Cancel building pull requests when there are no changes in the Doc
2022
# directory or RTD configuration, or if we can't cleanly merge the base
2123
# branch.
2224
- |
2325
set -eEux;
2426
if [ "$READTHEDOCS_VERSION_TYPE" = "external" ];
2527
then
26-
base_branch=main;
28+
base_branch=$(wget -qO- "https://api.github.com/repos/python/cpython/pulls/$READTHEDOCS_VERSION" | jq -er ".base.ref");
2729
git fetch --depth=50 origin $base_branch:origin-$base_branch;
2830
for attempt in $(seq 10);
2931
do

Doc/glossary.rst

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -96,21 +96,24 @@ Glossary
9696
:meth:`~object.__aexit__` methods. Introduced by :pep:`492`.
9797

9898
asynchronous generator
99-
A function which returns an :term:`asynchronous generator iterator`. It
100-
looks like a coroutine function defined with :keyword:`async def` except
101-
that it contains :keyword:`yield` expressions for producing a series of
102-
values usable in an :keyword:`async for` loop.
103-
104-
Usually refers to an asynchronous generator function, but may refer to an
105-
*asynchronous generator iterator* in some contexts. In cases where the
106-
intended meaning isn't clear, using the full terms avoids ambiguity.
99+
Informally used to mean either an :term:`asynchronous generator
100+
function` or an :term:`asynchronous generator iterator`, depending on
101+
context. The formal terms :term:`asynchronous generator function` and
102+
:term:`asynchronous generator iterator` are uncommon in practice;
103+
"asynchronous generator" alone is almost always sufficient.
104+
105+
asynchronous generator function
106+
A function which returns an :term:`asynchronous generator iterator`.
107+
It looks like a coroutine function defined with :keyword:`async def`
108+
except that it contains :keyword:`yield` expressions for producing a
109+
series of values usable in an :keyword:`async for` loop. See :pep:`525`.
107110

108111
An asynchronous generator function may contain :keyword:`await`
109112
expressions as well as :keyword:`async for`, and :keyword:`async with`
110113
statements.
111114

112115
asynchronous generator iterator
113-
An object created by an :term:`asynchronous generator` function.
116+
An object created by an :term:`asynchronous generator function`.
114117

115118
This is an :term:`asynchronous iterator` which when called using the
116119
:meth:`~object.__anext__` method returns an awaitable object which will execute
@@ -641,23 +644,33 @@ Glossary
641644
.. index:: single: generator
642645

643646
generator
644-
A function which returns a :term:`generator iterator`. It looks like a
645-
normal function except that it contains :keyword:`yield` expressions
646-
for producing a series of values usable in a for-loop or that can be
647-
retrieved one at a time with the :func:`next` function.
647+
Informally used to mean either a :term:`generator function` or a
648+
:term:`generator iterator`, depending on context. The formal terms
649+
:term:`generator function` and :term:`generator iterator` are uncommon
650+
in practice; "generator" alone is almost always sufficient.
648651

649-
Usually refers to a generator function, but may refer to a
650-
*generator iterator* in some contexts. In cases where the intended
651-
meaning isn't clear, using the full terms avoids ambiguity.
652+
.. index:: single: generator function
653+
654+
generator function
655+
A function which returns a :term:`generator` object. It looks like a
656+
normal function except that it contains :keyword:`yield` expressions
657+
for producing a series of values usable in a :keyword:`for`\-loop or
658+
that can be retrieved one at a time with the :func:`next` function.
659+
See :ref:`yieldexpr`.
652660

653661
generator iterator
654-
An object created by a :term:`generator` function.
662+
An object created by a :term:`generator function` or a
663+
:term:`generator expression`.
655664

656665
Each :keyword:`yield` temporarily suspends processing, remembering the
657-
execution state (including local variables and pending
658-
try-statements). When the *generator iterator* resumes, it picks up where
659-
it left off (in contrast to functions which start fresh on every
660-
invocation).
666+
execution state (including local variables and pending try-statements).
667+
When the *generator iterator* resumes, it picks up where it left off
668+
(in contrast to functions which start fresh on every invocation).
669+
670+
Generator iterators also implement the :meth:`~generator.send` method
671+
to send a value into the suspended generator, and the
672+
:meth:`~generator.throw` method to raise an exception at the point
673+
where the generator was paused. See :ref:`generator-methods`.
661674

662675
.. index:: single: generator expression
663676

Doc/library/dialog.rst

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,31 @@ The :mod:`!tkinter.simpledialog` module contains convenience classes and
1515
functions for creating simple modal dialogs to get a value from the user.
1616

1717

18-
.. function:: askfloat(title, prompt, **kw)
19-
askinteger(title, prompt, **kw)
20-
askstring(title, prompt, **kw)
18+
.. function:: askfloat(title, prompt, *, initialvalue=None, minvalue=None, maxvalue=None, parent=None)
19+
askinteger(title, prompt, *, initialvalue=None, minvalue=None, maxvalue=None, parent=None)
20+
askstring(title, prompt, *, initialvalue=None, show=None, parent=None)
2121
22-
The above three functions provide dialogs that prompt the user to enter a value
23-
of the desired type.
22+
Prompt the user to enter a value of the desired type and return it, or
23+
``None`` if the dialog is cancelled.
24+
25+
*title* is the dialog title and *prompt* the message shown above the entry.
26+
*initialvalue* is the value initially placed in the entry.
27+
*parent* is the window over which the dialog is shown.
28+
:func:`askinteger` and :func:`askfloat` also accept *minvalue* and
29+
*maxvalue*, which bound the accepted value.
30+
:func:`askstring` also accepts *show*, a character used to mask the entered
31+
text, for example ``'*'`` to hide a password.
2432

2533
.. class:: Dialog(parent, title=None)
2634

2735
The base class for custom dialogs.
36+
Instantiating it shows the dialog modally and returns once the user closes
37+
it; the entered value is then available in the :attr:`!result` attribute.
38+
39+
.. attribute:: result
40+
41+
The value produced by :meth:`apply`, or ``None`` if the dialog was
42+
cancelled.
2843

2944
.. method:: body(master)
3045

@@ -46,7 +61,8 @@ functions for creating simple modal dialogs to get a value from the user.
4661

4762
.. method:: apply()
4863

49-
Process the data entered by the user.
64+
Process the data entered by the user, for example by storing it in the
65+
:attr:`!result` attribute.
5066
Called after :meth:`validate` succeeds and just before the dialog is
5167
destroyed.
5268
The default implementation does nothing; override it to act on or store

Lib/_pydatetime.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ def _days_before_year(year):
5555

5656
def _days_in_month(year, month):
5757
"year, month -> number of days in that month in that year."
58-
assert 1 <= month <= 12, month
58+
assert 1 <= month <= 12, f"month must be in 1..12, not {month}"
5959
if month == 2 and _is_leap(year):
6060
return 29
6161
return _DAYS_IN_MONTH[month]
@@ -1987,7 +1987,7 @@ def fromisoformat(cls, date_string):
19871987
if became_next_day:
19881988
year, month, day = date_components
19891989
# Only wrap day/month when it was previously valid
1990-
if month <= 12 and day <= (days_in_month := _days_in_month(year, month)):
1990+
if 1 <= month <= 12 and day <= (days_in_month := _days_in_month(year, month)):
19911991
# Calculate midnight of the next day
19921992
day += 1
19931993
if day > days_in_month:

Lib/inspect.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2720,6 +2720,10 @@ def __init__(self, name, kind, *, default=_empty, annotation=_empty):
27202720
raise ValueError(msg)
27212721
self._kind = _POSITIONAL_ONLY
27222722
name = 'implicit{}'.format(name[1:])
2723+
elif name == '.format':
2724+
# gh-151665: Hidden parameter of compiler-generated annotation and type
2725+
# alias/typevar evaluators. Show it as "format".
2726+
name = 'format'
27232727

27242728
# It's possible for C functions to have a positional-only parameter
27252729
# where the name is a keyword, so for compatibility we'll allow it.

Lib/test/datetimetester.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3773,6 +3773,7 @@ def test_fromisoformat_fails_datetime_valueerror(self):
37733773
"2009-04-01T12:30:90", # Second out of range
37743774
"2009-04-01T12:90:45", # Minute out of range
37753775
"2009-04-01T25:30:45", # Hour out of range
3776+
"2009-00-01T24:00:00", # Month below range
37763777
"2009-13-01T24:00:00", # Month out of range
37773778
"9999-12-31T24:00:00", # Year out of range
37783779
]

Lib/test/test_tkinter/test_dnd.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import unittest
2+
import tkinter
3+
from tkinter import dnd
4+
from test.support import requires
5+
from test.test_tkinter.support import setUpModule # noqa: F401
6+
from test.test_tkinter.support import AbstractTkTest
7+
8+
requires('gui')
9+
10+
11+
class Target:
12+
def __init__(self, widget, log):
13+
self.widget = widget
14+
self.log = log
15+
widget.dnd_accept = self.dnd_accept
16+
17+
def dnd_accept(self, source, event):
18+
self.log.append('accept')
19+
return self
20+
21+
def dnd_enter(self, source, event):
22+
self.log.append('enter')
23+
24+
def dnd_motion(self, source, event):
25+
self.log.append('motion')
26+
27+
def dnd_leave(self, source, event):
28+
self.log.append('leave')
29+
30+
def dnd_commit(self, source, event):
31+
self.log.append('commit')
32+
33+
34+
class Source:
35+
def __init__(self, log):
36+
self.log = log
37+
38+
def dnd_end(self, target, event):
39+
self.log.append('end')
40+
41+
42+
class FakeEvent:
43+
def __init__(self, widget, num=1):
44+
self.num = num
45+
self.widget = widget
46+
self.x = self.y = self.x_root = self.y_root = 0
47+
48+
49+
class DndTest(AbstractTkTest, unittest.TestCase):
50+
51+
def setUp(self):
52+
super().setUp()
53+
self.canvas = tkinter.Canvas(self.root)
54+
self.canvas.pack()
55+
# on_motion() locates the target with winfo_containing(). Bypass that
56+
# real screen lookup, which depends on the window being visible and
57+
# unobscured, so the test does not hinge on the window manager.
58+
self.canvas.winfo_containing = lambda x, y: self.canvas
59+
self.log = []
60+
self.source = Source(self.log)
61+
self.target = Target(self.canvas, self.log)
62+
63+
def test_drag_and_drop(self):
64+
handler = dnd.dnd_start(self.source, FakeEvent(self.canvas))
65+
self.assertIsNotNone(handler)
66+
handler.on_motion(FakeEvent(self.canvas)) # Enter the target.
67+
handler.on_motion(FakeEvent(self.canvas)) # Move within the target.
68+
handler.on_release(FakeEvent(self.canvas)) # Drop on the target.
69+
self.assertEqual(self.log,
70+
['accept', 'enter', 'accept', 'motion', 'commit', 'end'])
71+
72+
def test_cancel(self):
73+
handler = dnd.dnd_start(self.source, FakeEvent(self.canvas))
74+
handler.on_motion(FakeEvent(self.canvas)) # Enter the target.
75+
handler.cancel() # Leaves the target without committing.
76+
self.assertEqual(self.log, ['accept', 'enter', 'leave', 'end'])
77+
78+
def test_no_target(self):
79+
# Nothing under the pointer accepts the drag.
80+
self.canvas.winfo_containing = lambda x, y: None
81+
handler = dnd.dnd_start(self.source, FakeEvent(self.canvas))
82+
handler.on_motion(FakeEvent(self.canvas))
83+
handler.on_release(FakeEvent(self.canvas))
84+
self.assertEqual(self.log, ['end'])
85+
86+
def test_no_recursive_start(self):
87+
handler = dnd.dnd_start(self.source, FakeEvent(self.canvas))
88+
self.assertIsNotNone(handler)
89+
# A drag is already in progress, so a second start is ignored.
90+
self.assertIsNone(dnd.dnd_start(self.source, FakeEvent(self.canvas)))
91+
handler.cancel()
92+
93+
def test_high_button_number_ignored(self):
94+
self.assertIsNone(dnd.dnd_start(self.source, FakeEvent(self.canvas, num=6)))
95+
96+
97+
if __name__ == "__main__":
98+
unittest.main()
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import os
2+
import unittest
3+
from tkinter import filedialog
4+
from tkinter.commondialog import Dialog
5+
from test.support import requires, swap_attr
6+
from test.test_tkinter.support import setUpModule # noqa: F401
7+
from test.test_tkinter.support import AbstractTkTest
8+
9+
requires('gui')
10+
11+
12+
class NativeDialogTest(AbstractTkTest, unittest.TestCase):
13+
# Open, SaveAs and Directory wrap modal native dialogs. The _test_callback
14+
# seam is called by show() just before the dialog would open; replacing it
15+
# with a function that raises exercises show() without blocking on the
16+
# dialog.
17+
18+
def check(self, dialog_class, command):
19+
self.assertEqual(dialog_class.command, command)
20+
master = None
21+
def test_callback(dialog, parent):
22+
nonlocal master
23+
master = parent
24+
raise ZeroDivisionError
25+
with swap_attr(Dialog, '_test_callback', test_callback):
26+
d = dialog_class(self.root, title='Test')
27+
self.assertRaises(ZeroDivisionError, d.show)
28+
self.assertIs(master, self.root)
29+
30+
def test_open(self):
31+
self.check(filedialog.Open, 'tk_getOpenFile')
32+
33+
def test_saveas(self):
34+
self.check(filedialog.SaveAs, 'tk_getSaveFile')
35+
36+
def test_directory(self):
37+
self.check(filedialog.Directory, 'tk_chooseDirectory')
38+
39+
40+
class FileDialogTest(AbstractTkTest, unittest.TestCase):
41+
# The pure-Python FileDialog runs its own modal loop in go(); its logic is
42+
# exercised here without entering the loop.
43+
44+
def test_selection(self):
45+
d = filedialog.FileDialog(self.root)
46+
d.directory = os.path.abspath(os.sep)
47+
d.set_selection('spam.txt')
48+
self.assertEqual(os.path.basename(d.get_selection()), 'spam.txt')
49+
50+
def test_filter(self):
51+
d = filedialog.FileDialog(self.root)
52+
d.set_filter(os.getcwd(), '*.py')
53+
self.assertEqual(d.get_filter(), (os.getcwd(), '*.py'))
54+
55+
def test_ok_cancel(self):
56+
d = filedialog.FileDialog(self.root)
57+
d.directory = os.getcwd()
58+
d.set_selection('spam.txt')
59+
d.ok_command() # Accepts the current selection.
60+
self.assertEqual(os.path.basename(d.how), 'spam.txt')
61+
d.cancel_command() # Returns no selection.
62+
self.assertIsNone(d.how)
63+
64+
def test_subclasses(self):
65+
for cls in filedialog.LoadFileDialog, filedialog.SaveFileDialog:
66+
with self.subTest(cls=cls.__name__):
67+
d = cls(self.root)
68+
self.assertIsInstance(d, filedialog.FileDialog)
69+
self.assertEqual(d.top.title(), cls.title)
70+
71+
72+
if __name__ == "__main__":
73+
unittest.main()

0 commit comments

Comments
 (0)