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); \ } \ } \