diff --git a/Lib/test/libregrtest/save_env.py b/Lib/test/libregrtest/save_env.py index 4cf1a075b300135..138465012a252c5 100644 --- a/Lib/test/libregrtest/save_env.py +++ b/Lib/test/libregrtest/save_env.py @@ -9,6 +9,13 @@ from .utils import print_warning +# Import termios to save and restore terminal echo. This is only available on +# Unix, and it's fine if the module can't be found. +try: + import termios # noqa: F401 +except ModuleNotFoundError: + pass + class SkipTestEnvironment(Exception): pass @@ -65,6 +72,7 @@ def __init__(self, test_name, verbose, quiet, *, pgo): 'shutil_archive_formats', 'shutil_unpack_formats', 'asyncio.events._event_loop_policy', 'urllib.requests._url_tempfiles', 'urllib.requests._opener', + 'stty_echo', ) def get_module(self, name): @@ -292,6 +300,24 @@ def restore_warnings_showwarning(self, fxn): warnings = self.get_module('warnings') warnings.showwarning = fxn + def get_stty_echo(self): + termios = self.try_get_module('termios') + if not os.isatty(fd := sys.__stdin__.fileno()): + return None + attrs = termios.tcgetattr(fd) + lflags = attrs[3] + return bool(lflags & termios.ECHO) + def restore_stty_echo(self, echo): + termios = self.get_module('termios') + attrs = termios.tcgetattr(fd := sys.__stdin__.fileno()) + if echo: + # Turn echo on. + attrs[3] |= termios.ECHO + else: + # Turn echo off. + attrs[3] &= ~termios.ECHO + termios.tcsetattr(fd, termios.TCSADRAIN, attrs) + def resource_info(self): for name in self.resources: method_suffix = name.replace('.', '_') diff --git a/Lib/test/pickletester.py b/Lib/test/pickletester.py index 1a7658b13fa5e34..e3663e44546dedc 100644 --- a/Lib/test/pickletester.py +++ b/Lib/test/pickletester.py @@ -1882,7 +1882,7 @@ def test_bad_newobj_ex_args(self): with self.assertRaises(TypeError) as cm: self.dumps(obj, proto) self.assertEqual(str(cm.exception), - 'functools.partial() argument after ** must be a mapping, not list') + 'Value after ** must be a mapping, not list') self.assertEqual(cm.exception.__notes__, [ 'when serializing test.pickletester.REX object']) else: diff --git a/Lib/test/test_extcall.py b/Lib/test/test_extcall.py index d9d85fe79af883f..f003a5837ae0eb2 100644 --- a/Lib/test/test_extcall.py +++ b/Lib/test/test_extcall.py @@ -137,7 +137,7 @@ >>> g(*Nothing()) Traceback (most recent call last): ... - TypeError: test.test_extcall.g() argument after * must be an iterable, not Nothing + TypeError: Value after * must be an iterable, not Nothing >>> class Nothing: ... def __len__(self): return 5 @@ -146,7 +146,7 @@ >>> g(*Nothing()) Traceback (most recent call last): ... - TypeError: test.test_extcall.g() argument after * must be an iterable, not Nothing + TypeError: Value after * must be an iterable, not Nothing >>> class Nothing(): ... def __len__(self): return 5 @@ -266,7 +266,7 @@ >>> h(*h) Traceback (most recent call last): ... - TypeError: test.test_extcall.h() argument after * must be an iterable, not function + TypeError: Value after * must be an iterable, not function >>> h(1, *h) Traceback (most recent call last): @@ -281,55 +281,53 @@ >>> dir(*h) Traceback (most recent call last): ... - TypeError: dir() argument after * must be an iterable, not function + TypeError: Value after * must be an iterable, not function >>> nothing = None >>> nothing(*h) Traceback (most recent call last): ... - TypeError: None argument after * must be an iterable, \ -not function + TypeError: Value after * must be an iterable, not function >>> h(**h) Traceback (most recent call last): ... - TypeError: test.test_extcall.h() argument after ** must be a mapping, not function + TypeError: Value after ** must be a mapping, not function >>> h(**[]) Traceback (most recent call last): ... - TypeError: test.test_extcall.h() argument after ** must be a mapping, not list + TypeError: Value after ** must be a mapping, not list >>> h(a=1, **h) Traceback (most recent call last): ... - TypeError: test.test_extcall.h() argument after ** must be a mapping, not function + TypeError: Value after ** must be a mapping, not function >>> h(a=1, **[]) Traceback (most recent call last): ... - TypeError: test.test_extcall.h() argument after ** must be a mapping, not list + TypeError: Value after ** must be a mapping, not list >>> h(**{'a': 1}, **h) Traceback (most recent call last): ... - TypeError: test.test_extcall.h() argument after ** must be a mapping, not function + TypeError: Value after ** must be a mapping, not function >>> h(**{'a': 1}, **[]) Traceback (most recent call last): ... - TypeError: test.test_extcall.h() argument after ** must be a mapping, not list + TypeError: Value after ** must be a mapping, not list >>> dir(**h) Traceback (most recent call last): ... - TypeError: dir() argument after ** must be a mapping, not function + TypeError: Value after ** must be a mapping, not function >>> nothing(**h) Traceback (most recent call last): ... - TypeError: None argument after ** must be a mapping, \ -not function + TypeError: Value after ** must be a mapping, not function >>> dir(b=1, **{'b': 1}) Traceback (most recent call last): diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-08-00-41-46.gh-issue-136327.7AiTb_.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-07-08-00-41-46.gh-issue-136327.7AiTb_.rst new file mode 100644 index 000000000000000..3798e956c95ab6b --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-07-08-00-41-46.gh-issue-136327.7AiTb_.rst @@ -0,0 +1,2 @@ +Errors when calling functions with invalid values after ``*`` and ``**`` now do not +include the function name. Patch by Ilia Solin. diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-24-20-16-42.gh-issue-140517.cqun-K.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-24-20-16-42.gh-issue-140517.cqun-K.rst new file mode 100644 index 000000000000000..15aaea8ab027e30 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-24-20-16-42.gh-issue-140517.cqun-K.rst @@ -0,0 +1,3 @@ +Fixed a reference leak when iterating over the result of :func:`map` +with ``strict=True`` when the input iterables have different lengths. +Patch by Mikhail Efimov. diff --git a/Misc/NEWS.d/next/Tests/2025-10-23-16-39-49.gh-issue-140482.ZMtyeD.rst b/Misc/NEWS.d/next/Tests/2025-10-23-16-39-49.gh-issue-140482.ZMtyeD.rst new file mode 100644 index 000000000000000..20747ad7f113ecc --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2025-10-23-16-39-49.gh-issue-140482.ZMtyeD.rst @@ -0,0 +1 @@ +Preserve and restore the state of ``stty echo`` as part of the test environment. diff --git a/Python/ceval.c b/Python/ceval.c index defd084db9a4bb4..7ec5abf5a76bd97 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3272,17 +3272,9 @@ int _Py_Check_ArgsIterable(PyThreadState *tstate, PyObject *func, PyObject *args) { if (Py_TYPE(args)->tp_iter == NULL && !PySequence_Check(args)) { - /* _Py_Check_ArgsIterable() may be called with a live exception: - * clear it to prevent calling _PyObject_FunctionStr() with an - * exception set. */ - _PyErr_Clear(tstate); - PyObject *funcstr = _PyObject_FunctionStr(func); - if (funcstr != NULL) { - _PyErr_Format(tstate, PyExc_TypeError, - "%U argument after * must be an iterable, not %.200s", - funcstr, Py_TYPE(args)->tp_name); - Py_DECREF(funcstr); - } + _PyErr_Format(tstate, PyExc_TypeError, + "Value after * must be an iterable, not %.200s", + Py_TYPE(args)->tp_name); return -1; } return 0; @@ -3298,15 +3290,10 @@ _PyEval_FormatKwargsError(PyThreadState *tstate, PyObject *func, PyObject *kwarg * is not a mapping. */ if (_PyErr_ExceptionMatches(tstate, PyExc_AttributeError)) { - _PyErr_Clear(tstate); - PyObject *funcstr = _PyObject_FunctionStr(func); - if (funcstr != NULL) { - _PyErr_Format( - tstate, PyExc_TypeError, - "%U argument after ** must be a mapping, not %.200s", - funcstr, Py_TYPE(kwargs)->tp_name); - Py_DECREF(funcstr); - } + _PyErr_Format( + tstate, PyExc_TypeError, + "Value after ** must be a mapping, not %.200s", + Py_TYPE(kwargs)->tp_name); } else if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) { PyObject *exc = _PyErr_GetRaisedException(tstate);