diff --git a/Doc/howto/mro.rst b/Doc/howto/mro.rst
index 0872bedcd3a2d34..72cc311e30d669c 100644
--- a/Doc/howto/mro.rst
+++ b/Doc/howto/mro.rst
@@ -10,7 +10,7 @@ The Python 2.3 Method Resolution Order
The Method Resolution Order discussed here was *introduced* in Python 2.3,
but it is still used in later versions -- including Python 3.
-By `Michele Simionato `__.
+By `Michele Simionato `__.
:Abstract:
diff --git a/Doc/library/hashlib.rst b/Doc/library/hashlib.rst
index ed0b0b2735b5c3d..5b666c5adac6456 100644
--- a/Doc/library/hashlib.rst
+++ b/Doc/library/hashlib.rst
@@ -50,12 +50,15 @@ hash supplied more than 2047 bytes of data at once in its constructor or
.. index:: single: OpenSSL; (use in module hashlib)
Constructors for hash algorithms that are always present in this module are
-:func:`sha1`, :func:`sha224`, :func:`sha256`, :func:`sha384`, :func:`sha512`,
-:func:`sha3_224`, :func:`sha3_256`, :func:`sha3_384`, :func:`sha3_512`,
-:func:`shake_128`, :func:`shake_256`, :func:`blake2b`, and :func:`blake2s`.
-:func:`md5` is normally available as well, though it may be missing or blocked
-if you are using a rare "FIPS compliant" build of Python.
-These correspond to :data:`algorithms_guaranteed`.
+:func:`md5`, :func:`sha1`, :func:`sha224`, :func:`sha256`, :func:`sha384`,
+:func:`sha512`, :func:`sha3_224`, :func:`sha3_256`, :func:`sha3_384`,
+:func:`sha3_512`, :func:`shake_128`, :func:`shake_256`, :func:`blake2b`, and
+:func:`blake2s`. These correspond to :data:`algorithms_guaranteed`.
+
+Any of these may nonetheless be missing or blocked in unusual environments,
+such as a rare "FIPS compliant" build of Python or when OpenSSL's "FIPS mode"
+is configured to exclude some algorithms from its default provider. Calling
+the constructor of an algorithm that is unavailable raises :exc:`ValueError`.
Additional algorithms may also be available if your Python distribution's
:mod:`!hashlib` was linked against a build of OpenSSL that provides others.
diff --git a/Doc/library/numbers.rst b/Doc/library/numbers.rst
index 57b35017072c97e..0ed24b4e6c5225d 100644
--- a/Doc/library/numbers.rst
+++ b/Doc/library/numbers.rst
@@ -90,20 +90,7 @@ Notes for type implementers
Implementers should be careful to make equal numbers equal and hash
them to the same values. This may be subtle if there are two different
-extensions of the real numbers. For example, :class:`fractions.Fraction`
-implements :func:`hash` as follows::
-
- def __hash__(self):
- if self.denominator == 1:
- # Get integers right.
- return hash(self.numerator)
- # Expensive check, but definitely correct.
- if self == float(self):
- return hash(float(self))
- else:
- # Use tuple's hash to avoid a high collision rate on
- # simple fractions.
- return hash((self.numerator, self.denominator))
+extensions of the real numbers. See also :ref:`numeric-hash`.
Adding More Numeric ABCs
diff --git a/Doc/library/tkinter.rst b/Doc/library/tkinter.rst
index c368141126403ac..cf342d74bc433e5 100644
--- a/Doc/library/tkinter.rst
+++ b/Doc/library/tkinter.rst
@@ -2699,7 +2699,8 @@ Base and mixin classes
Make *widget* a stand-alone top-level window, decorated by the window
manager with a title bar and so on.
Only :class:`Frame`, :class:`LabelFrame` and :class:`Toplevel` widgets
- may be used; passing any other widget type raises an error.
+ may be used (the :mod:`tkinter.ttk` versions are **not** accepted);
+ passing any other widget type raises an error.
:meth:`wm_manage` is an alias of :meth:`!manage`.
.. versionadded:: 3.3
@@ -3393,6 +3394,14 @@ Toplevel widgets
profile files is the :envvar:`HOME` environment variable or, if that
isn't defined, then :data:`os.curdir`.
+ .. note::
+
+ On Windows, creating a Tcl interpreter (by instantiating :class:`Tk` or
+ calling :func:`Tcl`) sets the :envvar:`HOME` environment variable for
+ the process, if it is not already set, to ``%HOMEDRIVE%%HOMEPATH%`` (or
+ :envvar:`USERPROFILE`, or ``c:\``). This is done by Tcl and can affect
+ other code that reads :envvar:`HOME`.
+
.. attribute:: tk
The Tk application object created by instantiating :class:`Tk`. This
diff --git a/Lib/hashlib.py b/Lib/hashlib.py
index 6c73eb9f31f8e47..c1e2d07c4357ff9 100644
--- a/Lib/hashlib.py
+++ b/Lib/hashlib.py
@@ -261,16 +261,15 @@ def file_digest(fileobj, digest, /, *, _bufsize=2**18):
return digestobj
-__logging = None
for __func_name in __always_supported:
# try them all, some may not work due to the OpenSSL
# version not supporting that algorithm.
try:
globals()[__func_name] = __get_hash(__func_name)
- except ValueError as __exc:
- import logging as __logging
- __logging.error('hash algorithm %s will not be supported at runtime '
- '[reason: %s]', __func_name, __exc)
+ except ValueError:
+ # Don't log here: logging at import time has global side effects and
+ # would tell the wrong audience; code that uses a missing algorithm
+ # gets a ValueError from the stub installed below.
# The following code can be simplified in Python 3.19
# once "string" is removed from the signature.
__code = f'''\
@@ -291,9 +290,8 @@ def {__func_name}(data=__UNSET, *, usedforsecurity=True, string=__UNSET):
'''
exec(__code, {"__UNSET": object()}, __locals := {})
globals()[__func_name] = __locals[__func_name]
- del __exc, __code, __locals
+ del __code, __locals
# Cleanup locals()
del __always_supported, __func_name, __get_hash
del __py_new, __hash_new, __get_openssl_constructor
-del __logging
diff --git a/Lib/idlelib/News3.txt b/Lib/idlelib/News3.txt
index 0f61da8368f2112..97becb858fea333 100644
--- a/Lib/idlelib/News3.txt
+++ b/Lib/idlelib/News3.txt
@@ -4,6 +4,11 @@ Released on 2026-10-01
=========================
+gh-85320: IDLE now reads and writes its configuration files and the
+breakpoints file using UTF-8 instead of the locale encoding.
+Files with non-ASCII characters and non-UTF-8 encoding may need
+to be opened in an editor and resaved with UTF-8 encoding.
+
gh-143774: Better explain the operation of Format / Format Paragraph.
Patch by Terry J. Reedy.
diff --git a/Lib/idlelib/config.py b/Lib/idlelib/config.py
index 1cabe4794500151..82afd6c49269d2d 100644
--- a/Lib/idlelib/config.py
+++ b/Lib/idlelib/config.py
@@ -73,8 +73,9 @@ def GetOptionList(self, section):
def Load(self):
"Load the configuration file from disk."
- if self.file:
- self.read(self.file)
+ if self.file and os.path.exists(self.file):
+ with open(self.file, encoding='utf-8', errors='replace') as f:
+ self.read_file(f)
class IdleUserConfParser(IdleConfParser):
"""
@@ -133,10 +134,10 @@ def Save(self):
if fname and fname[0] != '#':
if not self.IsEmpty():
try:
- cfgFile = open(fname, 'w')
+ cfgFile = open(fname, 'w', encoding='utf-8')
except OSError:
os.unlink(fname)
- cfgFile = open(fname, 'w')
+ cfgFile = open(fname, 'w', encoding='utf-8')
with cfgFile:
self.write(cfgFile)
elif os.path.exists(self.file):
diff --git a/Lib/idlelib/pyshell.py b/Lib/idlelib/pyshell.py
index b80c8e56c928105..b1662491935e4a0 100755
--- a/Lib/idlelib/pyshell.py
+++ b/Lib/idlelib/pyshell.py
@@ -242,12 +242,13 @@ def store_file_breaks(self):
breaks = self.breakpoints
filename = self.io.filename
try:
- with open(self.breakpointPath) as fp:
+ with open(self.breakpointPath,
+ encoding='utf-8', errors='replace') as fp:
lines = fp.readlines()
except OSError:
lines = []
try:
- with open(self.breakpointPath, "w") as new_file:
+ with open(self.breakpointPath, "w", encoding='utf-8') as new_file:
for line in lines:
if not line.startswith(filename + '='):
new_file.write(line)
@@ -272,7 +273,8 @@ def restore_file_breaks(self):
if filename is None:
return
if os.path.isfile(self.breakpointPath):
- with open(self.breakpointPath) as fp:
+ with open(self.breakpointPath,
+ encoding='utf-8', errors='replace') as fp:
lines = fp.readlines()
for line in lines:
if line.startswith(filename + '='):
diff --git a/Lib/ntpath.py b/Lib/ntpath.py
index 811e796f7766e94..b3c23f0abc2d889 100644
--- a/Lib/ntpath.py
+++ b/Lib/ntpath.py
@@ -345,7 +345,7 @@ def _isreservedname(name):
def expanduser(path):
"""Expand ~ and ~user constructs.
- If user or $HOME is unknown, do nothing."""
+ If user or home directory is unknown, do nothing."""
path = os.fspath(path)
if isinstance(path, bytes):
seps = b'\\/'
diff --git a/Lib/test/test_tkinter/test_tkinter_pipe.py b/Lib/test/test_tkinter/test_tkinter_pipe.py
new file mode 100644
index 000000000000000..775d0ff5e19b0ce
--- /dev/null
+++ b/Lib/test/test_tkinter/test_tkinter_pipe.py
@@ -0,0 +1,55 @@
+# test_tkinter_pipe.py
+import unittest
+import subprocess
+import sys
+from test import support
+
+
+@unittest.skipUnless(support.has_subprocess_support, "test requires subprocess")
+class TkinterPipeTest(unittest.TestCase):
+
+ def test_tkinter_pipe_buffered(self):
+ args = [sys.executable, "-i"]
+ proc = subprocess.Popen(args,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ proc.stdin.write(b"import tkinter\n")
+ proc.stdin.write(b"interpreter = tkinter.Tcl()\n")
+ proc.stdin.write(b"print('hello')\n")
+ proc.stdin.write(b"print('goodbye')\n")
+ proc.stdin.write(b"quit()\n")
+ stdout, stderr = proc.communicate(timeout=support.SHORT_TIMEOUT)
+ stdout = stdout.decode()
+ self.assertEqual(stdout.split(), ['hello', 'goodbye'])
+
+ def test_tkinter_pipe_unbuffered(self):
+ args = [sys.executable, "-i", "-u"]
+ proc = subprocess.Popen(args,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ proc.stdin.write(b"import tkinter\n")
+ proc.stdin.write(b"interpreter = tkinter.Tcl()\n")
+
+ proc.stdin.write(b"print('hello')\n")
+ proc.stdin.flush()
+ stdout = proc.stdout.readline()
+ stdout = stdout.decode()
+ self.assertEqual(stdout.strip(), 'hello')
+
+ proc.stdin.write(b"print('hello again')\n")
+ proc.stdin.flush()
+ stdout = proc.stdout.readline()
+ stdout = stdout.decode()
+ self.assertEqual(stdout.strip(), 'hello again')
+
+ proc.stdin.write(b"print('goodbye')\n")
+ proc.stdin.write(b"quit()\n")
+ stdout, stderr = proc.communicate(timeout=support.SHORT_TIMEOUT)
+ stdout = stdout.decode()
+ self.assertEqual(stdout.strip(), 'goodbye')
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/Misc/NEWS.d/next/Build/2026-06-26-16-30-00.gh-issue-152240.loongarch.rst b/Misc/NEWS.d/next/Build/2026-06-26-16-30-00.gh-issue-152240.loongarch.rst
new file mode 100644
index 000000000000000..ce9274a5c108377
--- /dev/null
+++ b/Misc/NEWS.d/next/Build/2026-06-26-16-30-00.gh-issue-152240.loongarch.rst
@@ -0,0 +1,2 @@
+Fix C stack unwinding tests on Linux LoongArch builds by teaching the manual
+frame pointer unwinder to recognize the LoongArch frame layout.
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-27-10-05-12.gh-issue-152375.L-ZBk6.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-27-10-05-12.gh-issue-152375.L-ZBk6.rst
new file mode 100644
index 000000000000000..db6ae3060d8328f
--- /dev/null
+++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-27-10-05-12.gh-issue-152375.L-ZBk6.rst
@@ -0,0 +1,2 @@
+Fix undefined behaviour when a :mod:`sys.monitoring` callback raised an
+exception while the program was following a branch or loop.
diff --git a/Misc/NEWS.d/next/IDLE/2026-06-28-06-46-46.gh-issue-85320.Hq2vKn.rst b/Misc/NEWS.d/next/IDLE/2026-06-28-06-46-46.gh-issue-85320.Hq2vKn.rst
new file mode 100644
index 000000000000000..ec34bf8ead9eb9f
--- /dev/null
+++ b/Misc/NEWS.d/next/IDLE/2026-06-28-06-46-46.gh-issue-85320.Hq2vKn.rst
@@ -0,0 +1,4 @@
+IDLE now reads and writes its configuration files and the breakpoints file
+using UTF-8 instead of the locale encoding. This keeps non-ASCII data (such
+as non-ASCII paths) from being corrupted and makes the files portable between
+environments.
diff --git a/Misc/NEWS.d/next/Library/2026-06-29-01-08-53.gh-issue-110357.QGWdZQ.rst b/Misc/NEWS.d/next/Library/2026-06-29-01-08-53.gh-issue-110357.QGWdZQ.rst
new file mode 100644
index 000000000000000..7c2384ea76fe531
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-06-29-01-08-53.gh-issue-110357.QGWdZQ.rst
@@ -0,0 +1,6 @@
+Importing :mod:`hashlib` no longer logs an error to stderr when a normally
+guaranteed hash algorithm is unavailable in the current runtime (for example
+under an OpenSSL FIPS configuration or a build using
+:option:`--without-builtin-hashlib-hashes <--with-builtin-hashlib-hashes>`).
+Code that actually uses the missing algorithm still gets a clear
+:exc:`ValueError`.
diff --git a/Misc/NEWS.d/next/Windows/2026-04-21-16-07-11.gh-issue-140146.TAcUHA.rst b/Misc/NEWS.d/next/Windows/2026-04-21-16-07-11.gh-issue-140146.TAcUHA.rst
new file mode 100644
index 000000000000000..2d3e42c86fd521f
--- /dev/null
+++ b/Misc/NEWS.d/next/Windows/2026-04-21-16-07-11.gh-issue-140146.TAcUHA.rst
@@ -0,0 +1,3 @@
+Prevent :mod:`tkinter` from hanging on Windows if stdin is redirected to a pipe in an
+interactive session. This is helpful for testing interactive usage of
+tkinter from a script, for example as part of the cpython test suite.
diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c
index e3de9006d5a427f..6506bd53b0377ad 100644
--- a/Modules/_testinternalcapi.c
+++ b/Modules/_testinternalcapi.c
@@ -98,6 +98,12 @@ static const uintptr_t min_frame_pointer_addr = 0x1000;
// https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi-1.9.html#STACK
# define FRAME_POINTER_NEXT_OFFSET 0
# define FRAME_POINTER_RETURN_OFFSET 2
+#elif defined(__loongarch__)
+// On LoongArch, the frame pointer is the caller's stack pointer.
+// The saved frame pointer is stored at fp[-2], and the return
+// address is stored at fp[-1].
+# define FRAME_POINTER_NEXT_OFFSET -2
+# define FRAME_POINTER_RETURN_OFFSET -1
#else
# define FRAME_POINTER_NEXT_OFFSET 0
# define FRAME_POINTER_RETURN_OFFSET 1
diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c
index 1f1fe4a91addf4c..62a79128d9726ca 100644
--- a/Modules/_tkinter.c
+++ b/Modules/_tkinter.c
@@ -3443,10 +3443,10 @@ static PyMethodDef moduleMethods[] =
};
#ifdef WAIT_FOR_STDIN
+#ifndef MS_WINDOWS
static int stdin_ready = 0;
-#ifndef MS_WINDOWS
static void
MyFileProc(void *clientData, int mask)
{
@@ -3465,22 +3465,40 @@ static PyThreadState *event_tstate = NULL;
static int
EventHook(void)
{
-#ifndef MS_WINDOWS
+#ifdef MS_WINDOWS
+ HANDLE hStdin;
+ DWORD type;
+#else
int tfile;
+ stdin_ready = 0;
#endif
PyEval_RestoreThread(event_tstate);
- stdin_ready = 0;
errorInCmd = 0;
-#ifndef MS_WINDOWS
+#ifdef MS_WINDOWS
+ hStdin = GetStdHandle(STD_INPUT_HANDLE);
+ type = GetFileType(hStdin);
+ while (1) {
+#else
tfile = fileno(stdin);
Tcl_CreateFileHandler(tfile, TCL_READABLE, MyFileProc,
(void *)(Py_intptr_t)tfile);
-#endif
while (!stdin_ready) {
+#endif
int result;
#ifdef MS_WINDOWS
- if (_kbhit()) {
- stdin_ready = 1;
+ if (type == FILE_TYPE_CHAR) {
+ if (_kbhit()) break;
+ }
+ else if (type == FILE_TYPE_PIPE) {
+ DWORD available;
+ if (PeekNamedPipe(hStdin, NULL, 0, NULL, &available, NULL)) {
+ if (available > 0) break;
+ }
+ else {
+ if (GetLastError() == ERROR_BROKEN_PIPE) break;
+ }
+ }
+ else if (type == FILE_TYPE_DISK) {
break;
}
#endif
diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h
index 0de4477426af358..b13884bf8214d41 100644
--- a/Python/ceval_macros.h
+++ b/Python/ceval_macros.h
@@ -389,14 +389,15 @@ static void dtrace_function_return(_PyInterpreterFrame *);
// for an exception handler, displaying the traceback, and so on
#define INSTRUMENTED_JUMP(src, dest, event) \
do { \
+ _Py_CODEUNIT *_dest = (dest); \
if (tstate->tracing) {\
- next_instr = dest; \
+ next_instr = _dest; \
} else { \
_PyFrame_SetStackPointer(frame, stack_pointer); \
- next_instr = _Py_call_instrumentation_jump(this_instr, tstate, event, frame, src, dest); \
+ next_instr = _Py_call_instrumentation_jump(this_instr, tstate, event, frame, src, _dest); \
stack_pointer = _PyFrame_GetStackPointer(frame); \
if (next_instr == NULL) { \
- next_instr = (dest)+1; \
+ next_instr = _dest + 1; \
JUMP_TO_LABEL(error); \
} \
} \