Skip to content

Commit 805daa2

Browse files
[3.14] Revert "gh-112068: C API: Add support of nullable arguments in PyArg_Parse (GH-121303)" (GH-136991) (#137006)
1 parent c328d14 commit 805daa2

File tree

12 files changed

+139
-317
lines changed

12 files changed

+139
-317
lines changed

Doc/c-api/arg.rst

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -113,18 +113,14 @@ There are three ways strings and buffers can be converted to C:
113113
``z`` (:class:`str` or ``None``) [const char \*]
114114
Like ``s``, but the Python object may also be ``None``, in which case the C
115115
pointer is set to ``NULL``.
116-
It is the same as ``s?`` with the C pointer was initialized to ``NULL``.
117116

118117
``z*`` (:class:`str`, :term:`bytes-like object` or ``None``) [Py_buffer]
119118
Like ``s*``, but the Python object may also be ``None``, in which case the
120119
``buf`` member of the :c:type:`Py_buffer` structure is set to ``NULL``.
121-
It is the same as ``s*?`` with the ``buf`` member of the :c:type:`Py_buffer`
122-
structure was initialized to ``NULL``.
123120

124121
``z#`` (:class:`str`, read-only :term:`bytes-like object` or ``None``) [const char \*, :c:type:`Py_ssize_t`]
125122
Like ``s#``, but the Python object may also be ``None``, in which case the C
126123
pointer is set to ``NULL``.
127-
It is the same as ``s#?`` with the C pointer was initialized to ``NULL``.
128124

129125
``y`` (read-only :term:`bytes-like object`) [const char \*]
130126
This format converts a bytes-like object to a C pointer to a
@@ -387,17 +383,6 @@ Other objects
387383
Non-tuple sequences are deprecated if *items* contains format units
388384
which store a borrowed buffer or a borrowed reference.
389385

390-
``unit?`` (anything or ``None``) [*matching-variable(s)*]
391-
``?`` modifies the behavior of the preceding format unit.
392-
The C variable(s) corresponding to that parameter should be initialized
393-
to their default value --- when the argument is ``None``,
394-
:c:func:`PyArg_ParseTuple` does not touch the contents of the corresponding
395-
C variable(s).
396-
If the argument is not ``None``, it is parsed according to the specified
397-
format unit.
398-
399-
.. versionadded:: 3.14
400-
401386
A few other characters have a meaning in a format string. These may not occur
402387
inside nested parentheses. They are:
403388

Doc/whatsnew/3.14.rst

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2940,11 +2940,6 @@ New features
29402940
file.
29412941
(Contributed by Victor Stinner in :gh:`127350`.)
29422942

2943-
* Add support of nullable arguments in :c:func:`PyArg_ParseTuple` and
2944-
similar functions.
2945-
Adding ``?`` after any format unit makes ``None`` be accepted as a value.
2946-
(Contributed by Serhiy Storchaka in :gh:`112068`.)
2947-
29482943
* The ``k`` and ``K`` formats in :c:func:`PyArg_ParseTuple` and
29492944
similar functions now use :meth:`~object.__index__` if available,
29502945
like all other integer formats.

Lib/test/test_capi/test_getargs.py

Lines changed: 0 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -1389,123 +1389,6 @@ def test_nested_sequence(self):
13891389
"argument 1 must be sequence of length 1, not 0"):
13901390
parse(([],), {}, '(' + f + ')', ['a'])
13911391

1392-
def test_specific_type_errors(self):
1393-
parse = _testcapi.parse_tuple_and_keywords
1394-
1395-
def check(format, arg, expected, got='list'):
1396-
errmsg = f'must be {expected}, not {got}'
1397-
with self.assertRaisesRegex(TypeError, errmsg):
1398-
parse((arg,), {}, format, ['a'])
1399-
1400-
check('k', [], 'int')
1401-
check('k?', [], 'int or None')
1402-
check('K', [], 'int')
1403-
check('K?', [], 'int or None')
1404-
check('c', [], 'a byte string of length 1')
1405-
check('c?', [], 'a byte string of length 1 or None')
1406-
check('c', b'abc', 'a byte string of length 1',
1407-
'a bytes object of length 3')
1408-
check('c?', b'abc', 'a byte string of length 1 or None',
1409-
'a bytes object of length 3')
1410-
check('c', bytearray(b'abc'), 'a byte string of length 1',
1411-
'a bytearray object of length 3')
1412-
check('c?', bytearray(b'abc'), 'a byte string of length 1 or None',
1413-
'a bytearray object of length 3')
1414-
check('C', [], 'a unicode character')
1415-
check('C?', [], 'a unicode character or None')
1416-
check('C', 'abc', 'a unicode character',
1417-
'a string of length 3')
1418-
check('C?', 'abc', 'a unicode character or None',
1419-
'a string of length 3')
1420-
check('s', [], 'str')
1421-
check('s?', [], 'str or None')
1422-
check('z', [], 'str or None')
1423-
check('z?', [], 'str or None')
1424-
check('es', [], 'str')
1425-
check('es?', [], 'str or None')
1426-
check('es#', [], 'str')
1427-
check('es#?', [], 'str or None')
1428-
check('et', [], 'str, bytes or bytearray')
1429-
check('et?', [], 'str, bytes, bytearray or None')
1430-
check('et#', [], 'str, bytes or bytearray')
1431-
check('et#?', [], 'str, bytes, bytearray or None')
1432-
check('w*', [], 'read-write bytes-like object')
1433-
check('w*?', [], 'read-write bytes-like object or None')
1434-
check('S', [], 'bytes')
1435-
check('S?', [], 'bytes or None')
1436-
check('U', [], 'str')
1437-
check('U?', [], 'str or None')
1438-
check('Y', [], 'bytearray')
1439-
check('Y?', [], 'bytearray or None')
1440-
check('(OO)', 42, '2-item tuple', 'int')
1441-
check('(OO)?', 42, '2-item tuple or None', 'int')
1442-
check('(OO)', (1, 2, 3), 'tuple of length 2', '3')
1443-
1444-
def test_nullable(self):
1445-
parse = _testcapi.parse_tuple_and_keywords
1446-
1447-
def check(format, arg, allows_none=False):
1448-
# Because some format units (such as y*) require cleanup,
1449-
# we force the parsing code to perform the cleanup by adding
1450-
# an argument that always fails.
1451-
# By checking for an exception, we ensure that the parsing
1452-
# of the first argument was successful.
1453-
self.assertRaises(OverflowError, parse,
1454-
(arg, 256), {}, format + '?b', ['a', 'b'])
1455-
self.assertRaises(OverflowError, parse,
1456-
(None, 256), {}, format + '?b', ['a', 'b'])
1457-
self.assertRaises(OverflowError, parse,
1458-
(arg, 256), {}, format + 'b', ['a', 'b'])
1459-
self.assertRaises(OverflowError if allows_none else TypeError, parse,
1460-
(None, 256), {}, format + 'b', ['a', 'b'])
1461-
1462-
check('b', 42)
1463-
check('B', 42)
1464-
check('h', 42)
1465-
check('H', 42)
1466-
check('i', 42)
1467-
check('I', 42)
1468-
check('n', 42)
1469-
check('l', 42)
1470-
check('k', 42)
1471-
check('L', 42)
1472-
check('K', 42)
1473-
check('f', 2.5)
1474-
check('d', 2.5)
1475-
check('D', 2.5j)
1476-
check('c', b'a')
1477-
check('C', 'a')
1478-
check('p', True, allows_none=True)
1479-
check('y', b'buffer')
1480-
check('y*', b'buffer')
1481-
check('y#', b'buffer')
1482-
check('s', 'string')
1483-
check('s*', 'string')
1484-
check('s#', 'string')
1485-
check('z', 'string', allows_none=True)
1486-
check('z*', 'string', allows_none=True)
1487-
check('z#', 'string', allows_none=True)
1488-
check('w*', bytearray(b'buffer'))
1489-
check('U', 'string')
1490-
check('S', b'bytes')
1491-
check('Y', bytearray(b'bytearray'))
1492-
check('O', object, allows_none=True)
1493-
1494-
check('(OO)', (1, 2))
1495-
self.assertEqual(parse((((1, 2), 3),), {}, '((OO)?O)', ['a']), (1, 2, 3))
1496-
self.assertEqual(parse(((None, 3),), {}, '((OO)?O)', ['a']), (NULL, NULL, 3))
1497-
self.assertEqual(parse((((1, 2), 3),), {}, '((OO)O)', ['a']), (1, 2, 3))
1498-
self.assertRaises(TypeError, parse, ((None, 3),), {}, '((OO)O)', ['a'])
1499-
1500-
parse((None,), {}, 'es?', ['a'])
1501-
parse((None,), {}, 'es#?', ['a'])
1502-
parse((None,), {}, 'et?', ['a'])
1503-
parse((None,), {}, 'et#?', ['a'])
1504-
parse((None,), {}, 'O!?', ['a'])
1505-
parse((None,), {}, 'O&?', ['a'])
1506-
1507-
# TODO: More tests for es?, es#?, et?, et#?, O!, O&
1508-
15091392
@unittest.skipIf(_testinternalcapi is None, 'needs _testinternalcapi')
15101393
def test_gh_119213(self):
15111394
rc, out, err = script_helper.assert_python_ok("-c", """if True:

Lib/test/test_mmap.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -732,7 +732,7 @@ def test_tagname(self):
732732
m2.close()
733733
m1.close()
734734

735-
with self.assertRaisesRegex(TypeError, 'must be str or None'):
735+
with self.assertRaisesRegex(TypeError, 'tagname'):
736736
mmap.mmap(-1, 8, tagname=1)
737737

738738
@cpython_only

Misc/NEWS.d/3.14.0b1.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2012,7 +2012,7 @@ interpreter.
20122012
.. nonce: ofI5Fl
20132013
.. section: C API
20142014
2015-
Add support of nullable arguments in :c:func:`PyArg_Parse` and similar
2015+
[Reverted in :gh:`136991`] Add support of nullable arguments in :c:func:`PyArg_Parse` and similar
20162016
functions. Adding ``?`` after any format unit makes ``None`` be accepted as
20172017
a value.
20182018

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Revert support of nullable arguments in :c:func:`PyArg_Parse`.

Modules/_ctypes/_ctypes.c

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3918,7 +3918,9 @@ _validate_paramflags(ctypes_state *st, PyTypeObject *type, PyObject *paramflags)
39183918
PyObject *name = Py_None;
39193919
PyObject *defval;
39203920
PyObject *typ;
3921-
if (!PyArg_ParseTuple(item, "i|U?O", &flag, &name, &defval)) {
3921+
if (!PyArg_ParseTuple(item, "i|OO", &flag, &name, &defval) ||
3922+
!(name == Py_None || PyUnicode_Check(name)))
3923+
{
39223924
PyErr_SetString(PyExc_TypeError,
39233925
"paramflags must be a sequence of (int [,string [,value]]) tuples");
39243926
return 0;
@@ -3983,8 +3985,10 @@ PyCFuncPtr_FromDll(PyTypeObject *type, PyObject *args, PyObject *kwds)
39833985
void *handle;
39843986
PyObject *paramflags = NULL;
39853987

3986-
if (!PyArg_ParseTuple(args, "O|O?", &ftuple, &paramflags))
3988+
if (!PyArg_ParseTuple(args, "O|O", &ftuple, &paramflags))
39873989
return NULL;
3990+
if (paramflags == Py_None)
3991+
paramflags = NULL;
39883992

39893993
ftuple = PySequence_Tuple(ftuple);
39903994
if (!ftuple)
@@ -4116,8 +4120,10 @@ PyCFuncPtr_FromVtblIndex(PyTypeObject *type, PyObject *args, PyObject *kwds)
41164120
GUID *iid = NULL;
41174121
Py_ssize_t iid_len = 0;
41184122

4119-
if (!PyArg_ParseTuple(args, "is|O?z#", &index, &name, &paramflags, &iid, &iid_len))
4123+
if (!PyArg_ParseTuple(args, "is|Oz#", &index, &name, &paramflags, &iid, &iid_len))
41204124
return NULL;
4125+
if (paramflags == Py_None)
4126+
paramflags = NULL;
41214127

41224128
ctypes_state *st = get_module_state_by_def(Py_TYPE(type));
41234129
if (!_validate_paramflags(st, type, paramflags)) {

Modules/_interpretersmodule.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1415,11 +1415,14 @@ interp_get_config(PyObject *self, PyObject *args, PyObject *kwds)
14151415
PyObject *idobj = NULL;
14161416
int restricted = 0;
14171417
if (!PyArg_ParseTupleAndKeywords(args, kwds,
1418-
"O?|$p:get_config", kwlist,
1418+
"O|$p:get_config", kwlist,
14191419
&idobj, &restricted))
14201420
{
14211421
return NULL;
14221422
}
1423+
if (idobj == Py_None) {
1424+
idobj = NULL;
1425+
}
14231426

14241427
int reqready = 0;
14251428
PyInterpreterState *interp = \
@@ -1536,14 +1539,14 @@ capture_exception(PyObject *self, PyObject *args, PyObject *kwds)
15361539
static char *kwlist[] = {"exc", NULL};
15371540
PyObject *exc_arg = NULL;
15381541
if (!PyArg_ParseTupleAndKeywords(args, kwds,
1539-
"|O?:capture_exception", kwlist,
1542+
"|O:capture_exception", kwlist,
15401543
&exc_arg))
15411544
{
15421545
return NULL;
15431546
}
15441547

15451548
PyObject *exc = exc_arg;
1546-
if (exc == NULL) {
1549+
if (exc == NULL || exc == Py_None) {
15471550
exc = PyErr_GetRaisedException();
15481551
if (exc == NULL) {
15491552
Py_RETURN_NONE;

Modules/_json.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1228,16 +1228,23 @@ encoder_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
12281228
static char *kwlist[] = {"markers", "default", "encoder", "indent", "key_separator", "item_separator", "sort_keys", "skipkeys", "allow_nan", NULL};
12291229

12301230
PyEncoderObject *s;
1231-
PyObject *markers = Py_None, *defaultfn, *encoder, *indent, *key_separator;
1231+
PyObject *markers, *defaultfn, *encoder, *indent, *key_separator;
12321232
PyObject *item_separator;
12331233
int sort_keys, skipkeys, allow_nan;
12341234

1235-
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!?OOOUUppp:make_encoder", kwlist,
1236-
&PyDict_Type, &markers, &defaultfn, &encoder, &indent,
1235+
if (!PyArg_ParseTupleAndKeywords(args, kwds, "OOOOUUppp:make_encoder", kwlist,
1236+
&markers, &defaultfn, &encoder, &indent,
12371237
&key_separator, &item_separator,
12381238
&sort_keys, &skipkeys, &allow_nan))
12391239
return NULL;
12401240

1241+
if (markers != Py_None && !PyDict_Check(markers)) {
1242+
PyErr_Format(PyExc_TypeError,
1243+
"make_encoder() argument 1 must be dict or None, "
1244+
"not %.200s", Py_TYPE(markers)->tp_name);
1245+
return NULL;
1246+
}
1247+
12411248
s = (PyEncoderObject *)type->tp_alloc(type, 0);
12421249
if (s == NULL)
12431250
return NULL;

Modules/_threadmodule.c

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -667,12 +667,12 @@ PyThreadHandleObject_join(PyObject *op, PyObject *args)
667667
PyThreadHandleObject *self = PyThreadHandleObject_CAST(op);
668668

669669
PyObject *timeout_obj = NULL;
670-
if (!PyArg_ParseTuple(args, "|O?:join", &timeout_obj)) {
670+
if (!PyArg_ParseTuple(args, "|O:join", &timeout_obj)) {
671671
return NULL;
672672
}
673673

674674
PyTime_t timeout_ns = -1;
675-
if (timeout_obj != NULL) {
675+
if (timeout_obj != NULL && timeout_obj != Py_None) {
676676
if (_PyTime_FromSecondsObject(&timeout_ns, timeout_obj,
677677
_PyTime_ROUND_TIMEOUT) < 0) {
678678
return NULL;
@@ -1945,10 +1945,10 @@ thread_PyThread_start_joinable_thread(PyObject *module, PyObject *fargs,
19451945
PyObject *func = NULL;
19461946
int daemon = 1;
19471947
thread_module_state *state = get_thread_state(module);
1948-
PyObject *hobj = Py_None;
1948+
PyObject *hobj = NULL;
19491949
if (!PyArg_ParseTupleAndKeywords(fargs, fkwargs,
1950-
"O|O!?p:start_joinable_thread", keywords,
1951-
&func, state->thread_handle_type, &hobj, &daemon)) {
1950+
"O|Op:start_joinable_thread", keywords,
1951+
&func, &hobj, &daemon)) {
19521952
return NULL;
19531953
}
19541954

@@ -1958,6 +1958,14 @@ thread_PyThread_start_joinable_thread(PyObject *module, PyObject *fargs,
19581958
return NULL;
19591959
}
19601960

1961+
if (hobj == NULL) {
1962+
hobj = Py_None;
1963+
}
1964+
else if (hobj != Py_None && !Py_IS_TYPE(hobj, state->thread_handle_type)) {
1965+
PyErr_SetString(PyExc_TypeError, "'handle' must be a _ThreadHandle");
1966+
return NULL;
1967+
}
1968+
19611969
if (PySys_Audit("_thread.start_joinable_thread", "OiO", func, daemon,
19621970
hobj) < 0) {
19631971
return NULL;

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy