Skip to content

Commit ff60191

Browse files
generatedunixname1734921407115435meta-codesync[bot]
authored andcommitted
Sync pre-release CPython main branch from GitHub (2026-09-27)
Summary: Imported python/cpython `3.16.0a0` from upstream rev [`421c411`](https://www.github.com/python/cpython/commit/421c411672710e29df4c027b6824df17586c9d51) (committed 2026-09-27 04:03:50+00:00). # Commit Info - Base: (`3.16.0a0`) - [`ee1bbf0`](https://www.github.com/python/cpython/commit/ee1bbf037ff74ff3c5e7b95623d78aa34744cd49) (commit date: 2026-09-26 02:05:24+00:00) - Imported: (`3.16.0a0`) - [`421c411`](https://www.github.com/python/cpython/commit/421c411672710e29df4c027b6824df17586c9d51) (commit date: 2026-09-27 04:03:50+00:00) # Noteworthy file changes - Test files (2 added) - Low-signal files (8 added) (NEWS.d, docs, .github) Complete list of added/removed files: https://www.internalfb.com/intern/everpaste/?color=0&handle=GKfbqTB6GlsETeIDAEbVqqEtqdA9br0LAAAz Differential Revision: D122023959 fbshipit-source-id: 4c123df45309c968f045f86e92d638ec298da8a5
1 parent 57f7a9d commit ff60191

36 files changed

Lines changed: 674 additions & 298 deletions

‎Doc/conf.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,10 @@
469469
# https://github.com/sphinx-doc/sphinx/issues/12359
470470
epub_use_index = False
471471

472+
# The default depth of 3 lists over a thousand entries, which makes the
473+
# table of contents hard to navigate in e-book readers
474+
epub_tocdepth = 2
475+
472476
# translation tag
473477
# ---------------
474478

‎Doc/library/dis.rst‎

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1899,32 +1899,38 @@ iterations of the loop.
18991899

19001900
The operand determines which intrinsic function is called:
19011901

1902-
+------------------------------------------+-----------------------------------+
1903-
| Operand | Description |
1904-
+==========================================+===================================+
1905-
| ``INTRINSIC_2_INVALID`` | Not valid |
1906-
+------------------------------------------+-----------------------------------+
1907-
| ``INTRINSIC_PREP_RERAISE_STAR`` | Calculates the |
1908-
| | :exc:`ExceptionGroup` to raise |
1909-
| | from a ``try-except*``. |
1910-
+------------------------------------------+-----------------------------------+
1911-
| ``INTRINSIC_TYPEVAR_WITH_BOUND`` | Creates a :class:`typing.TypeVar` |
1912-
| | with a bound. |
1913-
+------------------------------------------+-----------------------------------+
1914-
| ``INTRINSIC_TYPEVAR_WITH_CONSTRAINTS`` | Creates a |
1915-
| | :class:`typing.TypeVar` with |
1916-
| | constraints. |
1917-
+------------------------------------------+-----------------------------------+
1918-
| ``INTRINSIC_SET_FUNCTION_TYPE_PARAMS`` | Sets the ``__type_params__`` |
1919-
| | attribute of a function. |
1920-
+------------------------------------------+-----------------------------------+
1921-
| ``INTRINSIC_ADD_CONDITIONAL_ANNOTATION`` | Adds an annotation index to the |
1922-
| | ``__conditional_annotations__`` |
1923-
| | set. |
1924-
+------------------------------------------+-----------------------------------+
1902+
+------------------------------------------+-----------------------------------------+
1903+
| Operand | Description |
1904+
+==========================================+=========================================+
1905+
| ``INTRINSIC_2_INVALID`` | Not valid |
1906+
+------------------------------------------+-----------------------------------------+
1907+
| ``INTRINSIC_PREP_RERAISE_STAR`` | Calculates the |
1908+
| | :exc:`ExceptionGroup` to raise |
1909+
| | from a ``try-except*``. |
1910+
+------------------------------------------+-----------------------------------------+
1911+
| ``INTRINSIC_TYPEVAR_WITH_BOUND`` | Creates a :class:`typing.TypeVar` |
1912+
| | with a bound. |
1913+
+------------------------------------------+-----------------------------------------+
1914+
| ``INTRINSIC_TYPEVAR_WITH_CONSTRAINTS`` | Creates a |
1915+
| | :class:`typing.TypeVar` with |
1916+
| | constraints. |
1917+
+------------------------------------------+-----------------------------------------+
1918+
| ``INTRINSIC_SET_FUNCTION_TYPE_PARAMS`` | Sets the ``__type_params__`` |
1919+
| | attribute of a function. |
1920+
+------------------------------------------+-----------------------------------------+
1921+
| ``INTRINSIC_ADD_CONDITIONAL_ANNOTATION`` | Adds an annotation index to the |
1922+
| | ``__conditional_annotations__`` |
1923+
| | set. |
1924+
+------------------------------------------+-----------------------------------------+
1925+
| ``INTRINSIC_MATCH_CLASS_ISINSTANCE`` | Do :func:`isinstance` checks for |
1926+
| | :ref:`Class patterns <class-patterns>`. |
1927+
+------------------------------------------+-----------------------------------------+
19251928

19261929
.. versionadded:: 3.12
19271930

1931+
.. versionchanged:: 3.16
1932+
Added ``INTRINSIC_MATCH_CLASS_ISINSTANCE``.
1933+
19281934

19291935
.. opcode:: LOAD_SPECIAL
19301936

‎Doc/library/optparse.rst‎

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,12 @@ Choosing an argument parsing library
1616
The standard library includes three argument parsing libraries:
1717

1818
* :mod:`getopt`: a module that closely mirrors the procedural C ``getopt`` API.
19-
Included in the standard library since before the initial Python 1.0 release.
2019
* :mod:`!optparse`: a declarative replacement for ``getopt`` that
2120
provides equivalent functionality without requiring each application
22-
to implement its own procedural option parsing logic. Included
23-
in the standard library since the Python 2.3 release.
21+
to implement its own procedural option parsing logic.
2422
* :mod:`argparse`: a more opinionated alternative to ``optparse`` that
2523
provides more functionality by default, at the expense of reduced application
26-
flexibility in controlling exactly how arguments are processed. Included in
27-
the standard library since the Python 2.7 and Python 3.2 releases.
24+
flexibility in controlling exactly how arguments are processed.
2825

2926
In the absence of more specific argument parsing design constraints, :mod:`argparse`
3027
is the recommended choice for implementing command line applications, as it offers

‎Include/internal/pycore_intrinsics.h‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,9 @@
3131
#define INTRINSIC_SET_FUNCTION_TYPE_PARAMS 4
3232
#define INTRINSIC_SET_TYPEPARAM_DEFAULT 5
3333
#define INTRINSIC_ADD_CONDITIONAL_ANNOTATION 6
34+
#define INTRINSIC_MATCH_CLASS_ISINSTANCE 7
3435

35-
#define MAX_INTRINSIC_2 6
36+
#define MAX_INTRINSIC_2 7
3637

3738
typedef PyObject *(*intrinsic_func1)(PyThreadState* tstate, PyObject *value);
3839
typedef PyObject *(*intrinsic_func2)(PyThreadState* tstate, PyObject *value1, PyObject *value2);

‎Include/internal/pycore_magic_number.h‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,7 @@ Known values:
303303
Python 3.16a1 3703 (Replace DELETE_GLOBAL with PUSH_NULL; STORE_GLOBAL)
304304
Python 3.16a1 3704 (Replace DELETE_ATTR with PUSH_NULL; STORE_ATTR)
305305
Python 3.16a1 3705 (Add INTRINSIC_ADD_CONDITIONAL_ANNOTATION)
306+
Python 3.16a1 3706 (Add INTRINSIC_MATCH_CLASS_ISINSTANCE)
306307
307308
Python 3.17 will start with 3750
308309
@@ -312,7 +313,7 @@ Known values:
312313
313314
*/
314315

315-
#define PYC_MAGIC_NUMBER 3705
316+
#define PYC_MAGIC_NUMBER 3706
316317
/* This is equivalent to converting PYC_MAGIC_NUMBER to 2 bytes
317318
(little-endian) and then appending b'\r\n'. */
318319
#define PYC_MAGIC_NUMBER_TOKEN \

‎Lib/_ast_unparse.py‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -925,7 +925,11 @@ def visit_Attribute(self, node):
925925
# Special case: 3.__abs__() is a syntax error, so if node.value
926926
# is an integer literal then we need to either parenthesize
927927
# it or add an extra space to get 3 .__abs__().
928-
if isinstance(node.value, Constant) and isinstance(node.value.value, int):
928+
# bool is a subclass of int, but True.real and False.real are
929+
# valid without a space.
930+
if (isinstance(node.value, Constant)
931+
and isinstance(node.value.value, int)
932+
and not isinstance(node.value.value, bool)):
929933
self.write(" ")
930934
self.write(".")
931935
self.write(node.attr)

‎Lib/idlelib/editor.py‎

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1120,19 +1120,50 @@ def _close(self):
11201120

11211121
def last_mtime(self):
11221122
file = self.io.filename
1123-
return os.path.getmtime(file) if file else 0
1123+
if not file:
1124+
return None
1125+
try:
1126+
return os.path.getmtime(file)
1127+
except OSError:
1128+
# File is gone or cannot be stat'ed.
1129+
return None
11241130

11251131
def focus_in_event(self, event):
11261132
mtime = self.last_mtime()
1127-
if self.mtime != mtime:
1133+
if mtime == self.mtime:
1134+
return
1135+
if self.mtime is not None and mtime is None:
1136+
# The file was there and is now gone; reloading cannot work.
1137+
self.deleted_file_event(event)
1138+
else:
11281139
self.mtime = mtime
1129-
if self. askyesno(
1140+
if self.askyesno(
11301141
'Reload', '"%s"\n\nThis script has been modified by another program.'
11311142
'\nDo you want to reload it?' % self.io.filename, parent=self.text):
11321143
self.io.loadfile(self.io.filename)
11331144
else:
11341145
self.set_saved(False)
11351146

1147+
def deleted_file_event(self, event):
1148+
# The file was deleted or renamed while open; ask what to do with the
1149+
# buffer instead of offering a reload that could only fail. Forget the
1150+
# old mtime before showing the dialog so a FocusIn delivered while this
1151+
# dialog (or a Close/Save As sub-dialog) is open does not reopen it; a
1152+
# successful Save As restores it via set_saved(True).
1153+
self.mtime = None
1154+
dialog = simpledialog.SimpleDialog(
1155+
self.text,
1156+
title='File Deleted',
1157+
text='"%s"\n\nThis file no longer exists.' % self.io.filename,
1158+
buttons=('Close', 'Save As', 'Ignore'),
1159+
default=1,
1160+
cancel=2)
1161+
choice = dialog.go()
1162+
if choice == 0:
1163+
self.close()
1164+
elif choice == 1:
1165+
self.io.save_as(event)
1166+
11361167
def load_extensions(self):
11371168
self.extensions = {}
11381169
self.load_standard_extensions()

‎Lib/idlelib/idle_test/test_editor.py‎

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
"Test editor, coverage 53%."
22

33
from idlelib import editor
4+
import os
5+
import tempfile
6+
import types
47
import unittest
8+
from pathlib import Path
59
from collections import namedtuple
610
from unittest import mock
711
from test.support import requires
@@ -321,5 +325,81 @@ def test_rmenu_check_copy(self):
321325
eq(self.window.rmenu_check_cut(), 'normal')
322326

323327

328+
class LastMtimeTest(unittest.TestCase):
329+
# Exercise last_mtime as an unbound method on a stub; no GUI needed.
330+
331+
def test_existing_file_returns_mtime(self):
332+
with tempfile.TemporaryDirectory() as d:
333+
p = os.path.join(d, 'f.py')
334+
Path(p).touch()
335+
stub = types.SimpleNamespace(io=types.SimpleNamespace(filename=p))
336+
self.assertEqual(Editor.last_mtime(stub), os.path.getmtime(p))
337+
338+
def test_deleted_file_returns_none(self):
339+
with tempfile.TemporaryDirectory() as d:
340+
p = os.path.join(d, 'gone.py')
341+
Path(p).touch()
342+
os.remove(p)
343+
stub = types.SimpleNamespace(io=types.SimpleNamespace(filename=p))
344+
self.assertIsNone(Editor.last_mtime(stub))
345+
346+
def test_not_yet_created_filename(self):
347+
# __init__ calls last_mtime() before self.mtime is set, so last_mtime()
348+
# must not read self.mtime (the stub has no mtime attribute).
349+
stub = types.SimpleNamespace(
350+
io=types.SimpleNamespace(filename='/no/such/file.py'))
351+
self.assertIsNone(Editor.last_mtime(stub))
352+
353+
def test_no_filename_returns_none(self):
354+
stub = types.SimpleNamespace(io=types.SimpleNamespace(filename=None))
355+
self.assertIsNone(Editor.last_mtime(stub))
356+
357+
358+
class DeletedFileEventTest(unittest.TestCase):
359+
# Exercise the deleted-file handling as unbound methods; dialog is mocked.
360+
361+
def make_stub(self):
362+
return types.SimpleNamespace(
363+
mtime=1.0,
364+
text=None,
365+
io=types.SimpleNamespace(filename='/gone.py', save_as=mock.Mock()),
366+
close=mock.Mock(),
367+
set_saved=mock.Mock(),
368+
deleted_file_event=mock.Mock(),
369+
askyesno=mock.Mock(),
370+
last_mtime=lambda: None)
371+
372+
def test_focus_in_routes_deleted_to_dialog(self):
373+
stub = self.make_stub()
374+
Editor.focus_in_event(stub, 'event')
375+
stub.deleted_file_event.assert_called_once_with('event')
376+
stub.askyesno.assert_not_called()
377+
378+
def _run_choice(self, choice):
379+
stub = self.make_stub()
380+
with mock.patch.object(editor.simpledialog, 'SimpleDialog') as SD:
381+
SD.return_value.go.return_value = choice
382+
Editor.deleted_file_event(stub, 'event')
383+
return stub
384+
385+
def test_close_choice_closes_window(self):
386+
stub = self._run_choice(0)
387+
self.assertTrue(stub.close.called)
388+
# mtime is cleared before Close so the queued FocusIn does not reprompt.
389+
self.assertIsNone(stub.mtime)
390+
391+
def test_save_as_choice_clears_mtime_and_saves(self):
392+
stub = self._run_choice(1)
393+
stub.io.save_as.assert_called_once_with('event')
394+
# A cancelled Save As leaves mtime None so it does not reprompt.
395+
self.assertIsNone(stub.mtime)
396+
397+
def test_ignore_choice_clears_mtime(self):
398+
stub = self._run_choice(2)
399+
self.assertIsNone(stub.mtime)
400+
stub.io.save_as.assert_not_called()
401+
stub.set_saved.assert_not_called()
402+
403+
324404
if __name__ == '__main__':
325405
unittest.main(verbosity=2)

‎Lib/idlelib/idle_test/test_run.py‎

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,15 @@ def __eq__(self, other):
4949
('int.reel', AttributeError,
5050
"type object 'int' has no attribute 'reel'. "
5151
"Did you mean '.real' instead of '.reel'?\n"), # More in 3.15.
52+
(r'raise NameError("123\n456")', NameError, "123\n456\n"),
5253
)
5354

5455
@force_not_colorized
5556
def test_get_message(self):
5657
for code, exc, msg in self.data:
5758
with self.subTest(code=code):
5859
try:
59-
eval(compile(code, '', 'eval'))
60+
exec(compile(code, '', 'exec'))
6061
except exc:
6162
typ, val, tb = sys.exc_info()
6263
actual = run.get_message_lines(typ, val, tb)[0]
@@ -68,15 +69,20 @@ def test_get_message(self):
6869
new_callable=lambda: (lambda t, e: None))
6970
def test_get_multiple_message(self, mock):
7071
d = self.data
71-
data2 = ((d[0], d[1]), (d[1], d[2]), (d[2], d[0]))
72+
data2 = ((d[0], d[1]),
73+
(d[1], d[2]),
74+
(d[2], d[3]),
75+
(d[3], d[0]),
76+
(d[1], d[3]),
77+
(d[0], d[2]))
7278
subtests = 0
7379
for (code1, exc1, msg1), (code2, exc2, msg2) in data2:
7480
with self.subTest(codes=(code1,code2)):
7581
try:
76-
eval(compile(code1, '', 'eval'))
82+
exec(compile(code1, '', 'exec'))
7783
except exc1:
7884
try:
79-
eval(compile(code2, '', 'eval'))
85+
exec(compile(code2, '', 'exec'))
8086
except exc2:
8187
with captured_stderr() as output:
8288
run.print_exception()

‎Lib/idlelib/run.py‎

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
f'''{sys.executable} -c "__import__('idlelib.run').run.main()"'''
55
'.run' is needed because __import__ returns idlelib, not idlelib.run.
66
"""
7-
import contextlib
87
import functools
98
import io
109
import linecache
@@ -244,15 +243,8 @@ def show_socket_error(err, address):
244243

245244

246245
def get_message_lines(typ, exc, tb):
247-
"Return line composing the exception message."
248-
if typ in (AttributeError, NameError):
249-
# 3.10+ hints are not directly accessible from python (#44026).
250-
err = io.StringIO()
251-
with contextlib.redirect_stderr(err):
252-
sys.__excepthook__(typ, exc, tb)
253-
return [err.getvalue().split("\n")[-2] + "\n"]
254-
else:
255-
return traceback.format_exception_only(typ, exc)
246+
"Return lines of the exception message, with any suggestion."
247+
return list(traceback.TracebackException(typ, exc, tb).format_exception_only())
256248

257249

258250
def print_exception():

0 commit comments

Comments
 (0)