Skip to content

Commit bfbf864

Browse files
Merge branch 'main' into doc-fix-140281
2 parents b9ca059 + d78d7a5 commit bfbf864

File tree

10 files changed

+95
-56
lines changed

10 files changed

+95
-56
lines changed

Doc/library/xmlrpc.client.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -524,7 +524,7 @@ Convenience Functions
524524

525525
.. function:: dumps(params, methodname=None, methodresponse=None, encoding=None, allow_none=False)
526526

527-
Convert *params* into an XML-RPC request. or into a response if *methodresponse*
527+
Convert *params* into an XML-RPC request, or into a response if *methodresponse*
528528
is true. *params* can be either a tuple of arguments or an instance of the
529529
:exc:`Fault` exception class. If *methodresponse* is true, only a single value
530530
can be returned, meaning that *params* must be of length 1. *encoding*, if

Lib/test/libregrtest/save_env.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@
99

1010
from .utils import print_warning
1111

12+
# Import termios to save and restore terminal echo. This is only available on
13+
# Unix, and it's fine if the module can't be found.
14+
try:
15+
import termios # noqa: F401
16+
except ModuleNotFoundError:
17+
pass
18+
1219

1320
class SkipTestEnvironment(Exception):
1421
pass
@@ -65,6 +72,7 @@ def __init__(self, test_name, verbose, quiet, *, pgo):
6572
'shutil_archive_formats', 'shutil_unpack_formats',
6673
'asyncio.events._event_loop_policy',
6774
'urllib.requests._url_tempfiles', 'urllib.requests._opener',
75+
'stty_echo',
6876
)
6977

7078
def get_module(self, name):
@@ -292,6 +300,24 @@ def restore_warnings_showwarning(self, fxn):
292300
warnings = self.get_module('warnings')
293301
warnings.showwarning = fxn
294302

303+
def get_stty_echo(self):
304+
termios = self.try_get_module('termios')
305+
if not os.isatty(fd := sys.__stdin__.fileno()):
306+
return None
307+
attrs = termios.tcgetattr(fd)
308+
lflags = attrs[3]
309+
return bool(lflags & termios.ECHO)
310+
def restore_stty_echo(self, echo):
311+
termios = self.get_module('termios')
312+
attrs = termios.tcgetattr(fd := sys.__stdin__.fileno())
313+
if echo:
314+
# Turn echo on.
315+
attrs[3] |= termios.ECHO
316+
else:
317+
# Turn echo off.
318+
attrs[3] &= ~termios.ECHO
319+
termios.tcsetattr(fd, termios.TCSADRAIN, attrs)
320+
295321
def resource_info(self):
296322
for name in self.resources:
297323
method_suffix = name.replace('.', '_')

Lib/test/pickletester.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1882,7 +1882,7 @@ def test_bad_newobj_ex_args(self):
18821882
with self.assertRaises(TypeError) as cm:
18831883
self.dumps(obj, proto)
18841884
self.assertEqual(str(cm.exception),
1885-
'functools.partial() argument after ** must be a mapping, not list')
1885+
'Value after ** must be a mapping, not list')
18861886
self.assertEqual(cm.exception.__notes__, [
18871887
'when serializing test.pickletester.REX object'])
18881888
else:

Lib/test/test_builtin.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1385,6 +1385,22 @@ def test_map_strict(self):
13851385
self.assertRaises(ValueError, tuple,
13861386
map(pack, (1, 2), (1, 2), 'abc', strict=True))
13871387

1388+
# gh-140517: Testing refleaks with mortal objects.
1389+
t1 = (None, object())
1390+
t2 = (object(), object())
1391+
t3 = (object(),)
1392+
1393+
self.assertRaises(ValueError, tuple,
1394+
map(pack, t1, 'a', strict=True))
1395+
self.assertRaises(ValueError, tuple,
1396+
map(pack, t1, t2, 'a', strict=True))
1397+
self.assertRaises(ValueError, tuple,
1398+
map(pack, t1, t2, t3, strict=True))
1399+
self.assertRaises(ValueError, tuple,
1400+
map(pack, 'a', t1, strict=True))
1401+
self.assertRaises(ValueError, tuple,
1402+
map(pack, 'a', t2, t3, strict=True))
1403+
13881404
def test_map_strict_iterators(self):
13891405
x = iter(range(5))
13901406
y = [0]

Lib/test/test_extcall.py

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@
137137
>>> g(*Nothing())
138138
Traceback (most recent call last):
139139
...
140-
TypeError: test.test_extcall.g() argument after * must be an iterable, not Nothing
140+
TypeError: Value after * must be an iterable, not Nothing
141141
142142
>>> class Nothing:
143143
... def __len__(self): return 5
@@ -146,7 +146,7 @@
146146
>>> g(*Nothing())
147147
Traceback (most recent call last):
148148
...
149-
TypeError: test.test_extcall.g() argument after * must be an iterable, not Nothing
149+
TypeError: Value after * must be an iterable, not Nothing
150150
151151
>>> class Nothing():
152152
... def __len__(self): return 5
@@ -266,7 +266,7 @@
266266
>>> h(*h)
267267
Traceback (most recent call last):
268268
...
269-
TypeError: test.test_extcall.h() argument after * must be an iterable, not function
269+
TypeError: Value after * must be an iterable, not function
270270
271271
>>> h(1, *h)
272272
Traceback (most recent call last):
@@ -281,55 +281,53 @@
281281
>>> dir(*h)
282282
Traceback (most recent call last):
283283
...
284-
TypeError: dir() argument after * must be an iterable, not function
284+
TypeError: Value after * must be an iterable, not function
285285
286286
>>> nothing = None
287287
>>> nothing(*h)
288288
Traceback (most recent call last):
289289
...
290-
TypeError: None argument after * must be an iterable, \
291-
not function
290+
TypeError: Value after * must be an iterable, not function
292291
293292
>>> h(**h)
294293
Traceback (most recent call last):
295294
...
296-
TypeError: test.test_extcall.h() argument after ** must be a mapping, not function
295+
TypeError: Value after ** must be a mapping, not function
297296
298297
>>> h(**[])
299298
Traceback (most recent call last):
300299
...
301-
TypeError: test.test_extcall.h() argument after ** must be a mapping, not list
300+
TypeError: Value after ** must be a mapping, not list
302301
303302
>>> h(a=1, **h)
304303
Traceback (most recent call last):
305304
...
306-
TypeError: test.test_extcall.h() argument after ** must be a mapping, not function
305+
TypeError: Value after ** must be a mapping, not function
307306
308307
>>> h(a=1, **[])
309308
Traceback (most recent call last):
310309
...
311-
TypeError: test.test_extcall.h() argument after ** must be a mapping, not list
310+
TypeError: Value after ** must be a mapping, not list
312311
313312
>>> h(**{'a': 1}, **h)
314313
Traceback (most recent call last):
315314
...
316-
TypeError: test.test_extcall.h() argument after ** must be a mapping, not function
315+
TypeError: Value after ** must be a mapping, not function
317316
318317
>>> h(**{'a': 1}, **[])
319318
Traceback (most recent call last):
320319
...
321-
TypeError: test.test_extcall.h() argument after ** must be a mapping, not list
320+
TypeError: Value after ** must be a mapping, not list
322321
323322
>>> dir(**h)
324323
Traceback (most recent call last):
325324
...
326-
TypeError: dir() argument after ** must be a mapping, not function
325+
TypeError: Value after ** must be a mapping, not function
327326
328327
>>> nothing(**h)
329328
Traceback (most recent call last):
330329
...
331-
TypeError: None argument after ** must be a mapping, \
332-
not function
330+
TypeError: Value after ** must be a mapping, not function
333331
334332
>>> dir(b=1, **{'b': 1})
335333
Traceback (most recent call last):
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Errors when calling functions with invalid values after ``*`` and ``**`` now do not
2+
include the function name. Patch by Ilia Solin.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fixed a reference leak when iterating over the result of :func:`map`
2+
with ``strict=True`` when the input iterables have different lengths.
3+
Patch by Mikhail Efimov.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Preserve and restore the state of ``stty echo`` as part of the test environment.

Python/bltinmodule.c

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1501,66 +1501,72 @@ map_next(PyObject *self)
15011501
}
15021502

15031503
Py_ssize_t nargs = 0;
1504-
for (i=0; i < niters; i++) {
1504+
for (i = 0; i < niters; i++) {
15051505
PyObject *it = PyTuple_GET_ITEM(lz->iters, i);
15061506
PyObject *val = Py_TYPE(it)->tp_iternext(it);
15071507
if (val == NULL) {
15081508
if (lz->strict) {
15091509
goto check;
15101510
}
1511-
goto exit;
1511+
goto exit_no_result;
15121512
}
15131513
stack[i] = val;
15141514
nargs++;
15151515
}
15161516

15171517
result = _PyObject_VectorcallTstate(tstate, lz->func, stack, nargs, NULL);
1518+
goto exit;
15181519

1519-
exit:
1520-
for (i=0; i < nargs; i++) {
1521-
Py_DECREF(stack[i]);
1522-
}
1523-
if (stack != small_stack) {
1524-
PyMem_Free(stack);
1525-
}
1526-
return result;
15271520
check:
15281521
if (PyErr_Occurred()) {
15291522
if (!PyErr_ExceptionMatches(PyExc_StopIteration)) {
15301523
// next() on argument i raised an exception (not StopIteration)
1531-
return NULL;
1524+
goto exit_no_result;
15321525
}
15331526
PyErr_Clear();
15341527
}
15351528
if (i) {
15361529
// ValueError: map() argument 2 is shorter than argument 1
15371530
// ValueError: map() argument 3 is shorter than arguments 1-2
15381531
const char* plural = i == 1 ? " " : "s 1-";
1539-
return PyErr_Format(PyExc_ValueError,
1540-
"map() argument %d is shorter than argument%s%d",
1541-
i + 1, plural, i);
1532+
PyErr_Format(PyExc_ValueError,
1533+
"map() argument %d is shorter than argument%s%d",
1534+
i + 1, plural, i);
1535+
goto exit_no_result;
15421536
}
15431537
for (i = 1; i < niters; i++) {
15441538
PyObject *it = PyTuple_GET_ITEM(lz->iters, i);
15451539
PyObject *val = (*Py_TYPE(it)->tp_iternext)(it);
15461540
if (val) {
15471541
Py_DECREF(val);
15481542
const char* plural = i == 1 ? " " : "s 1-";
1549-
return PyErr_Format(PyExc_ValueError,
1550-
"map() argument %d is longer than argument%s%d",
1551-
i + 1, plural, i);
1543+
PyErr_Format(PyExc_ValueError,
1544+
"map() argument %d is longer than argument%s%d",
1545+
i + 1, plural, i);
1546+
goto exit_no_result;
15521547
}
15531548
if (PyErr_Occurred()) {
15541549
if (!PyErr_ExceptionMatches(PyExc_StopIteration)) {
15551550
// next() on argument i raised an exception (not StopIteration)
1556-
return NULL;
1551+
goto exit_no_result;
15571552
}
15581553
PyErr_Clear();
15591554
}
15601555
// Argument i is exhausted. So far so good...
15611556
}
15621557
// All arguments are exhausted. Success!
1563-
goto exit;
1558+
1559+
exit_no_result:
1560+
assert(result == NULL);
1561+
1562+
exit:
1563+
for (i = 0; i < nargs; i++) {
1564+
Py_DECREF(stack[i]);
1565+
}
1566+
if (stack != small_stack) {
1567+
PyMem_Free(stack);
1568+
}
1569+
return result;
15641570
}
15651571

15661572
static PyObject *

Python/ceval.c

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3272,17 +3272,9 @@ int
32723272
_Py_Check_ArgsIterable(PyThreadState *tstate, PyObject *func, PyObject *args)
32733273
{
32743274
if (Py_TYPE(args)->tp_iter == NULL && !PySequence_Check(args)) {
3275-
/* _Py_Check_ArgsIterable() may be called with a live exception:
3276-
* clear it to prevent calling _PyObject_FunctionStr() with an
3277-
* exception set. */
3278-
_PyErr_Clear(tstate);
3279-
PyObject *funcstr = _PyObject_FunctionStr(func);
3280-
if (funcstr != NULL) {
3281-
_PyErr_Format(tstate, PyExc_TypeError,
3282-
"%U argument after * must be an iterable, not %.200s",
3283-
funcstr, Py_TYPE(args)->tp_name);
3284-
Py_DECREF(funcstr);
3285-
}
3275+
_PyErr_Format(tstate, PyExc_TypeError,
3276+
"Value after * must be an iterable, not %.200s",
3277+
Py_TYPE(args)->tp_name);
32863278
return -1;
32873279
}
32883280
return 0;
@@ -3298,15 +3290,10 @@ _PyEval_FormatKwargsError(PyThreadState *tstate, PyObject *func, PyObject *kwarg
32983290
* is not a mapping.
32993291
*/
33003292
if (_PyErr_ExceptionMatches(tstate, PyExc_AttributeError)) {
3301-
_PyErr_Clear(tstate);
3302-
PyObject *funcstr = _PyObject_FunctionStr(func);
3303-
if (funcstr != NULL) {
3304-
_PyErr_Format(
3305-
tstate, PyExc_TypeError,
3306-
"%U argument after ** must be a mapping, not %.200s",
3307-
funcstr, Py_TYPE(kwargs)->tp_name);
3308-
Py_DECREF(funcstr);
3309-
}
3293+
_PyErr_Format(
3294+
tstate, PyExc_TypeError,
3295+
"Value after ** must be a mapping, not %.200s",
3296+
Py_TYPE(kwargs)->tp_name);
33103297
}
33113298
else if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) {
33123299
PyObject *exc = _PyErr_GetRaisedException(tstate);

0 commit comments

Comments
 (0)