Skip to content

Commit 98d400f

Browse files
Revert "gh-112068: C API: Add support of nullable arguments in PyArg_Parse (GH-121303)"
This reverts commit f5f1ac8.
1 parent 4d02f31 commit 98d400f

File tree

11 files changed

+138
-317
lines changed

11 files changed

+138
-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
@@ -394,17 +390,6 @@ Other objects
394390
Non-tuple sequences are deprecated if *items* contains format units
395391
which store a borrowed buffer or a borrowed reference.
396392

397-
``unit?`` (anything or ``None``) [*matching-variable(s)*]
398-
``?`` modifies the behavior of the preceding format unit.
399-
The C variable(s) corresponding to that parameter should be initialized
400-
to their default value --- when the argument is ``None``,
401-
:c:func:`PyArg_ParseTuple` does not touch the contents of the corresponding
402-
C variable(s).
403-
If the argument is not ``None``, it is parsed according to the specified
404-
format unit.
405-
406-
.. versionadded:: 3.14
407-
408393
A few other characters have a meaning in a format string. These may not occur
409394
inside nested parentheses. They are:
410395

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
@@ -1429,123 +1429,6 @@ def test_nested_sequence(self):
14291429
"argument 1 must be sequence of length 1, not 0"):
14301430
parse(([],), {}, '(' + f + ')', ['a'])
14311431

1432-
def test_specific_type_errors(self):
1433-
parse = _testcapi.parse_tuple_and_keywords
1434-
1435-
def check(format, arg, expected, got='list'):
1436-
errmsg = f'must be {expected}, not {got}'
1437-
with self.assertRaisesRegex(TypeError, errmsg):
1438-
parse((arg,), {}, format, ['a'])
1439-
1440-
check('k', [], 'int')
1441-
check('k?', [], 'int or None')
1442-
check('K', [], 'int')
1443-
check('K?', [], 'int or None')
1444-
check('c', [], 'a byte string of length 1')
1445-
check('c?', [], 'a byte string of length 1 or None')
1446-
check('c', b'abc', 'a byte string of length 1',
1447-
'a bytes object of length 3')
1448-
check('c?', b'abc', 'a byte string of length 1 or None',
1449-
'a bytes object of length 3')
1450-
check('c', bytearray(b'abc'), 'a byte string of length 1',
1451-
'a bytearray object of length 3')
1452-
check('c?', bytearray(b'abc'), 'a byte string of length 1 or None',
1453-
'a bytearray object of length 3')
1454-
check('C', [], 'a unicode character')
1455-
check('C?', [], 'a unicode character or None')
1456-
check('C', 'abc', 'a unicode character',
1457-
'a string of length 3')
1458-
check('C?', 'abc', 'a unicode character or None',
1459-
'a string of length 3')
1460-
check('s', [], 'str')
1461-
check('s?', [], 'str or None')
1462-
check('z', [], 'str or None')
1463-
check('z?', [], 'str or None')
1464-
check('es', [], 'str')
1465-
check('es?', [], 'str or None')
1466-
check('es#', [], 'str')
1467-
check('es#?', [], 'str or None')
1468-
check('et', [], 'str, bytes or bytearray')
1469-
check('et?', [], 'str, bytes, bytearray or None')
1470-
check('et#', [], 'str, bytes or bytearray')
1471-
check('et#?', [], 'str, bytes, bytearray or None')
1472-
check('w*', [], 'read-write bytes-like object')
1473-
check('w*?', [], 'read-write bytes-like object or None')
1474-
check('S', [], 'bytes')
1475-
check('S?', [], 'bytes or None')
1476-
check('U', [], 'str')
1477-
check('U?', [], 'str or None')
1478-
check('Y', [], 'bytearray')
1479-
check('Y?', [], 'bytearray or None')
1480-
check('(OO)', 42, '2-item tuple', 'int')
1481-
check('(OO)?', 42, '2-item tuple or None', 'int')
1482-
check('(OO)', (1, 2, 3), 'tuple of length 2', '3')
1483-
1484-
def test_nullable(self):
1485-
parse = _testcapi.parse_tuple_and_keywords
1486-
1487-
def check(format, arg, allows_none=False):
1488-
# Because some format units (such as y*) require cleanup,
1489-
# we force the parsing code to perform the cleanup by adding
1490-
# an argument that always fails.
1491-
# By checking for an exception, we ensure that the parsing
1492-
# of the first argument was successful.
1493-
self.assertRaises(OverflowError, parse,
1494-
(arg, 256), {}, format + '?b', ['a', 'b'])
1495-
self.assertRaises(OverflowError, parse,
1496-
(None, 256), {}, format + '?b', ['a', 'b'])
1497-
self.assertRaises(OverflowError, parse,
1498-
(arg, 256), {}, format + 'b', ['a', 'b'])
1499-
self.assertRaises(OverflowError if allows_none else TypeError, parse,
1500-
(None, 256), {}, format + 'b', ['a', 'b'])
1501-
1502-
check('b', 42)
1503-
check('B', 42)
1504-
check('h', 42)
1505-
check('H', 42)
1506-
check('i', 42)
1507-
check('I', 42)
1508-
check('n', 42)
1509-
check('l', 42)
1510-
check('k', 42)
1511-
check('L', 42)
1512-
check('K', 42)
1513-
check('f', 2.5)
1514-
check('d', 2.5)
1515-
check('D', 2.5j)
1516-
check('c', b'a')
1517-
check('C', 'a')
1518-
check('p', True, allows_none=True)
1519-
check('y', b'buffer')
1520-
check('y*', b'buffer')
1521-
check('y#', b'buffer')
1522-
check('s', 'string')
1523-
check('s*', 'string')
1524-
check('s#', 'string')
1525-
check('z', 'string', allows_none=True)
1526-
check('z*', 'string', allows_none=True)
1527-
check('z#', 'string', allows_none=True)
1528-
check('w*', bytearray(b'buffer'))
1529-
check('U', 'string')
1530-
check('S', b'bytes')
1531-
check('Y', bytearray(b'bytearray'))
1532-
check('O', object, allows_none=True)
1533-
1534-
check('(OO)', (1, 2))
1535-
self.assertEqual(parse((((1, 2), 3),), {}, '((OO)?O)', ['a']), (1, 2, 3))
1536-
self.assertEqual(parse(((None, 3),), {}, '((OO)?O)', ['a']), (NULL, NULL, 3))
1537-
self.assertEqual(parse((((1, 2), 3),), {}, '((OO)O)', ['a']), (1, 2, 3))
1538-
self.assertRaises(TypeError, parse, ((None, 3),), {}, '((OO)O)', ['a'])
1539-
1540-
parse((None,), {}, 'es?', ['a'])
1541-
parse((None,), {}, 'es#?', ['a'])
1542-
parse((None,), {}, 'et?', ['a'])
1543-
parse((None,), {}, 'et#?', ['a'])
1544-
parse((None,), {}, 'O!?', ['a'])
1545-
parse((None,), {}, 'O&?', ['a'])
1546-
1547-
# TODO: More tests for es?, es#?, et?, et#?, O!, O&
1548-
15491432
@unittest.skipIf(_testinternalcapi is None, 'needs _testinternalcapi')
15501433
def test_gh_119213(self):
15511434
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

Modules/_ctypes/_ctypes.c

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3937,7 +3937,9 @@ _validate_paramflags(ctypes_state *st, PyTypeObject *type, PyObject *paramflags)
39373937
PyObject *name = Py_None;
39383938
PyObject *defval;
39393939
PyObject *typ;
3940-
if (!PyArg_ParseTuple(item, "i|U?O", &flag, &name, &defval)) {
3940+
if (!PyArg_ParseTuple(item, "i|OO", &flag, &name, &defval) ||
3941+
!(name == Py_None || PyUnicode_Check(name)))
3942+
{
39413943
PyErr_SetString(PyExc_TypeError,
39423944
"paramflags must be a sequence of (int [,string [,value]]) tuples");
39433945
return 0;
@@ -4002,8 +4004,10 @@ PyCFuncPtr_FromDll(PyTypeObject *type, PyObject *args, PyObject *kwds)
40024004
void *handle;
40034005
PyObject *paramflags = NULL;
40044006

4005-
if (!PyArg_ParseTuple(args, "O|O?", &ftuple, &paramflags))
4007+
if (!PyArg_ParseTuple(args, "O|O", &ftuple, &paramflags))
40064008
return NULL;
4009+
if (paramflags == Py_None)
4010+
paramflags = NULL;
40074011

40084012
ftuple = PySequence_Tuple(ftuple);
40094013
if (!ftuple)
@@ -4135,8 +4139,10 @@ PyCFuncPtr_FromVtblIndex(PyTypeObject *type, PyObject *args, PyObject *kwds)
41354139
GUID *iid = NULL;
41364140
Py_ssize_t iid_len = 0;
41374141

4138-
if (!PyArg_ParseTuple(args, "is|O?z#", &index, &name, &paramflags, &iid, &iid_len))
4142+
if (!PyArg_ParseTuple(args, "is|Oz#", &index, &name, &paramflags, &iid, &iid_len))
41394143
return NULL;
4144+
if (paramflags == Py_None)
4145+
paramflags = NULL;
41404146

41414147
ctypes_state *st = get_module_state_by_def(Py_TYPE(type));
41424148
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
@@ -1222,16 +1222,23 @@ encoder_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
12221222
static char *kwlist[] = {"markers", "default", "encoder", "indent", "key_separator", "item_separator", "sort_keys", "skipkeys", "allow_nan", NULL};
12231223

12241224
PyEncoderObject *s;
1225-
PyObject *markers = Py_None, *defaultfn, *encoder, *indent, *key_separator;
1225+
PyObject *markers, *defaultfn, *encoder, *indent, *key_separator;
12261226
PyObject *item_separator;
12271227
int sort_keys, skipkeys, allow_nan;
12281228

1229-
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!?OOOUUppp:make_encoder", kwlist,
1230-
&PyDict_Type, &markers, &defaultfn, &encoder, &indent,
1229+
if (!PyArg_ParseTupleAndKeywords(args, kwds, "OOOOUUppp:make_encoder", kwlist,
1230+
&markers, &defaultfn, &encoder, &indent,
12311231
&key_separator, &item_separator,
12321232
&sort_keys, &skipkeys, &allow_nan))
12331233
return NULL;
12341234

1235+
if (markers != Py_None && !PyDict_Check(markers)) {
1236+
PyErr_Format(PyExc_TypeError,
1237+
"make_encoder() argument 1 must be dict or None, "
1238+
"not %.200s", Py_TYPE(markers)->tp_name);
1239+
return NULL;
1240+
}
1241+
12351242
s = (PyEncoderObject *)type->tp_alloc(type, 0);
12361243
if (s == NULL)
12371244
return NULL;

Modules/_threadmodule.c

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

683683
PyObject *timeout_obj = NULL;
684-
if (!PyArg_ParseTuple(args, "|O?:join", &timeout_obj)) {
684+
if (!PyArg_ParseTuple(args, "|O:join", &timeout_obj)) {
685685
return NULL;
686686
}
687687

688688
PyTime_t timeout_ns = -1;
689-
if (timeout_obj != NULL) {
689+
if (timeout_obj != NULL && timeout_obj != Py_None) {
690690
if (_PyTime_FromSecondsObject(&timeout_ns, timeout_obj,
691691
_PyTime_ROUND_TIMEOUT) < 0) {
692692
return NULL;
@@ -1957,10 +1957,10 @@ thread_PyThread_start_joinable_thread(PyObject *module, PyObject *fargs,
19571957
PyObject *func = NULL;
19581958
int daemon = 1;
19591959
thread_module_state *state = get_thread_state(module);
1960-
PyObject *hobj = Py_None;
1960+
PyObject *hobj = NULL;
19611961
if (!PyArg_ParseTupleAndKeywords(fargs, fkwargs,
1962-
"O|O!?p:start_joinable_thread", keywords,
1963-
&func, state->thread_handle_type, &hobj, &daemon)) {
1962+
"O|Op:start_joinable_thread", keywords,
1963+
&func, &hobj, &daemon)) {
19641964
return NULL;
19651965
}
19661966

@@ -1970,6 +1970,14 @@ thread_PyThread_start_joinable_thread(PyObject *module, PyObject *fargs,
19701970
return NULL;
19711971
}
19721972

1973+
if (hobj == NULL) {
1974+
hobj = Py_None;
1975+
}
1976+
else if (hobj != Py_None && !Py_IS_TYPE(hobj, state->thread_handle_type)) {
1977+
PyErr_SetString(PyExc_TypeError, "'handle' must be a _ThreadHandle");
1978+
return NULL;
1979+
}
1980+
19731981
if (PySys_Audit("_thread.start_joinable_thread", "OiO", func, daemon,
19741982
hobj) < 0) {
19751983
return NULL;

Modules/mmapmodule.c

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#endif
2424

2525
#include <Python.h>
26+
#include "pycore_abstract.h" // _Py_convert_optional_to_ssize_t()
2627
#include "pycore_bytesobject.h" // _PyBytes_Find()
2728
#include "pycore_fileutils.h" // _Py_stat_struct
2829
#include "pycore_weakref.h" // FT_CLEAR_WEAKREFS()
@@ -529,7 +530,7 @@ mmap_read_method(PyObject *op, PyObject *args)
529530
mmap_object *self = mmap_object_CAST(op);
530531

531532
CHECK_VALID(NULL);
532-
if (!PyArg_ParseTuple(args, "|n?:read", &num_bytes))
533+
if (!PyArg_ParseTuple(args, "|O&:read", _Py_convert_optional_to_ssize_t, &num_bytes))
533534
return NULL;
534535
CHECK_VALID(NULL);
535536

@@ -1723,7 +1724,7 @@ new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
17231724
DWORD off_lo; /* lower 32 bits of offset */
17241725
DWORD size_hi; /* upper 32 bits of size */
17251726
DWORD size_lo; /* lower 32 bits of size */
1726-
PyObject *tagname = NULL;
1727+
PyObject *tagname = Py_None;
17271728
DWORD dwErr = 0;
17281729
int fileno;
17291730
HANDLE fh = 0;
@@ -1733,7 +1734,7 @@ new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
17331734
"tagname",
17341735
"access", "offset", NULL };
17351736

1736-
if (!PyArg_ParseTupleAndKeywords(args, kwdict, "in|U?iL", keywords,
1737+
if (!PyArg_ParseTupleAndKeywords(args, kwdict, "in|OiL", keywords,
17371738
&fileno, &map_size,
17381739
&tagname, &access, &offset)) {
17391740
return NULL;
@@ -1866,7 +1867,13 @@ new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
18661867
m_obj->weakreflist = NULL;
18671868
m_obj->exports = 0;
18681869
/* set the tag name */
1869-
if (tagname != NULL) {
1870+
if (!Py_IsNone(tagname)) {
1871+
if (!PyUnicode_Check(tagname)) {
1872+
Py_DECREF(m_obj);
1873+
return PyErr_Format(PyExc_TypeError, "expected str or None for "
1874+
"'tagname', not %.200s",
1875+
Py_TYPE(tagname)->tp_name);
1876+
}
18701877
m_obj->tagname = PyUnicode_AsWideCharString(tagname, NULL);
18711878
if (m_obj->tagname == NULL) {
18721879
Py_DECREF(m_obj);

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