gh-112068: C API: Add support of nullable arguments in PyArg_Parse (GH-121303)

This commit is contained in:
Serhiy Storchaka 2025-04-08 22:08:00 +03:00 committed by GitHub
parent 8421b648e9
commit f5f1ac84b3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 324 additions and 142 deletions

View file

@ -1,6 +1,8 @@
/* New getargs implementation */
#include <stdbool.h>
#define PY_CXX_CONST const
#include "Python.h"
#include "pycore_abstract.h" // _PyNumber_Index()
@ -466,9 +468,12 @@ converttuple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
const char *format = *p_format;
int i;
Py_ssize_t len;
bool nullable = false;
int istuple = PyTuple_Check(arg);
int mustbetuple = istuple;
assert(*format == '(');
format++;
for (;;) {
int c = *format++;
if (c == '(') {
@ -477,8 +482,12 @@ converttuple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
level++;
}
else if (c == ')') {
if (level == 0)
if (level == 0) {
if (*format == '?') {
nullable = true;
}
break;
}
level--;
}
else if (c == ':' || c == ';' || c == '\0')
@ -515,6 +524,13 @@ converttuple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
}
}
if (arg == Py_None && nullable) {
const char *msg = skipitem(p_format, p_va, flags);
if (msg != NULL) {
levels[0] = 0;
}
return msg;
}
if (istuple) {
/* fallthrough */
}
@ -523,9 +539,10 @@ converttuple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
{
levels[0] = 0;
PyOS_snprintf(msgbuf, bufsize,
"must be %d-item tuple, not %.50s",
n,
arg == Py_None ? "None" : Py_TYPE(arg)->tp_name);
"must be %d-item tuple%s, not %.50s",
n,
nullable ? " or None" : "",
arg == Py_None ? "None" : Py_TYPE(arg)->tp_name);
return msgbuf;
}
else {
@ -562,7 +579,7 @@ converttuple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
return msgbuf;
}
format = *p_format;
format = *p_format + 1;
for (i = 0; i < n; i++) {
const char *msg;
PyObject *item = PyTuple_GET_ITEM(arg, i);
@ -577,6 +594,10 @@ converttuple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
}
}
format++;
if (*format == '?') {
format++;
}
*p_format = format;
if (!istuple) {
Py_DECREF(arg);
@ -595,11 +616,8 @@ convertitem(PyObject *arg, const char **p_format, va_list *p_va, int flags,
const char *format = *p_format;
if (*format == '(' /* ')' */) {
format++;
msg = converttuple(arg, &format, p_va, flags, levels, msgbuf,
bufsize, freelist);
if (msg == NULL)
format++;
}
else {
msg = convertsimple(arg, &format, p_va, flags,
@ -629,7 +647,7 @@ _PyArg_BadArgument(const char *fname, const char *displayname,
}
static const char *
converterr(const char *expected, PyObject *arg, char *msgbuf, size_t bufsize)
converterr(bool nullable, const char *expected, PyObject *arg, char *msgbuf, size_t bufsize)
{
assert(expected != NULL);
assert(arg != NULL);
@ -639,20 +657,23 @@ converterr(const char *expected, PyObject *arg, char *msgbuf, size_t bufsize)
}
else {
PyOS_snprintf(msgbuf, bufsize,
"must be %.50s, not %.50s", expected,
"must be %.50s%s, not %.50s", expected,
nullable ? " or None" : "",
arg == Py_None ? "None" : Py_TYPE(arg)->tp_name);
}
return msgbuf;
}
static const char *
convertcharerr(const char *expected, const char *what, Py_ssize_t size,
convertcharerr(bool nullable, const char *expected, const char *what, Py_ssize_t size,
char *msgbuf, size_t bufsize)
{
assert(expected != NULL);
PyOS_snprintf(msgbuf, bufsize,
"must be %.50s, not %.50s of length %zd",
expected, what, size);
"must be %.50s%s, not %.50s of length %zd",
expected,
nullable ? " or None" : "",
what, size);
return msgbuf;
}
@ -672,15 +693,26 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
char *msgbuf, size_t bufsize, freelist_t *freelist)
{
#define RETURN_ERR_OCCURRED return msgbuf
#define HANDLE_NULLABLE \
if (*format == '?') { \
format++; \
if (arg == Py_None) { \
break; \
} \
nullable = true; \
}
const char *format = *p_format;
char c = *format++;
const char *sarg;
bool nullable = false;
switch (c) {
case 'b': { /* unsigned byte -- very short int */
unsigned char *p = va_arg(*p_va, unsigned char *);
HANDLE_NULLABLE;
long ival = PyLong_AsLong(arg);
if (ival == -1 && PyErr_Occurred())
RETURN_ERR_OCCURRED;
@ -694,7 +726,6 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
"unsigned byte integer is greater than maximum");
RETURN_ERR_OCCURRED;
}
else
*p = (unsigned char) ival;
break;
}
@ -702,6 +733,7 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
case 'B': {/* byte sized bitfield - both signed and unsigned
values allowed */
unsigned char *p = va_arg(*p_va, unsigned char *);
HANDLE_NULLABLE;
unsigned long ival = PyLong_AsUnsignedLongMask(arg);
if (ival == (unsigned long)-1 && PyErr_Occurred())
RETURN_ERR_OCCURRED;
@ -712,6 +744,7 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
case 'h': {/* signed short int */
short *p = va_arg(*p_va, short *);
HANDLE_NULLABLE;
long ival = PyLong_AsLong(arg);
if (ival == -1 && PyErr_Occurred())
RETURN_ERR_OCCURRED;
@ -733,6 +766,7 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
case 'H': { /* short int sized bitfield, both signed and
unsigned allowed */
unsigned short *p = va_arg(*p_va, unsigned short *);
HANDLE_NULLABLE;
unsigned long ival = PyLong_AsUnsignedLongMask(arg);
if (ival == (unsigned long)-1 && PyErr_Occurred())
RETURN_ERR_OCCURRED;
@ -743,6 +777,7 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
case 'i': {/* signed int */
int *p = va_arg(*p_va, int *);
HANDLE_NULLABLE;
long ival = PyLong_AsLong(arg);
if (ival == -1 && PyErr_Occurred())
RETURN_ERR_OCCURRED;
@ -764,6 +799,7 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
case 'I': { /* int sized bitfield, both signed and
unsigned allowed */
unsigned int *p = va_arg(*p_va, unsigned int *);
HANDLE_NULLABLE;
unsigned long ival = PyLong_AsUnsignedLongMask(arg);
if (ival == (unsigned long)-1 && PyErr_Occurred())
RETURN_ERR_OCCURRED;
@ -776,6 +812,7 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
{
PyObject *iobj;
Py_ssize_t *p = va_arg(*p_va, Py_ssize_t *);
HANDLE_NULLABLE;
Py_ssize_t ival = -1;
iobj = _PyNumber_Index(arg);
if (iobj != NULL) {
@ -789,6 +826,7 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
}
case 'l': {/* long int */
long *p = va_arg(*p_va, long *);
HANDLE_NULLABLE;
long ival = PyLong_AsLong(arg);
if (ival == -1 && PyErr_Occurred())
RETURN_ERR_OCCURRED;
@ -799,17 +837,19 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
case 'k': { /* long sized bitfield */
unsigned long *p = va_arg(*p_va, unsigned long *);
HANDLE_NULLABLE;
unsigned long ival;
if (PyLong_Check(arg))
ival = PyLong_AsUnsignedLongMask(arg);
else
return converterr("int", arg, msgbuf, bufsize);
return converterr(nullable, "int", arg, msgbuf, bufsize);
*p = ival;
break;
}
case 'L': {/* long long */
long long *p = va_arg( *p_va, long long * );
HANDLE_NULLABLE;
long long ival = PyLong_AsLongLong(arg);
if (ival == (long long)-1 && PyErr_Occurred())
RETURN_ERR_OCCURRED;
@ -820,17 +860,19 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
case 'K': { /* long long sized bitfield */
unsigned long long *p = va_arg(*p_va, unsigned long long *);
HANDLE_NULLABLE;
unsigned long long ival;
if (PyLong_Check(arg))
ival = PyLong_AsUnsignedLongLongMask(arg);
else
return converterr("int", arg, msgbuf, bufsize);
return converterr(nullable, "int", arg, msgbuf, bufsize);
*p = ival;
break;
}
case 'f': {/* float */
float *p = va_arg(*p_va, float *);
HANDLE_NULLABLE;
double dval = PyFloat_AsDouble(arg);
if (dval == -1.0 && PyErr_Occurred())
RETURN_ERR_OCCURRED;
@ -841,6 +883,7 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
case 'd': {/* double */
double *p = va_arg(*p_va, double *);
HANDLE_NULLABLE;
double dval = PyFloat_AsDouble(arg);
if (dval == -1.0 && PyErr_Occurred())
RETURN_ERR_OCCURRED;
@ -851,6 +894,7 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
case 'D': {/* complex double */
Py_complex *p = va_arg(*p_va, Py_complex *);
HANDLE_NULLABLE;
Py_complex cval;
cval = PyComplex_AsCComplex(arg);
if (PyErr_Occurred())
@ -862,9 +906,10 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
case 'c': {/* char */
char *p = va_arg(*p_va, char *);
HANDLE_NULLABLE;
if (PyBytes_Check(arg)) {
if (PyBytes_GET_SIZE(arg) != 1) {
return convertcharerr("a byte string of length 1",
return convertcharerr(nullable, "a byte string of length 1",
"a bytes object", PyBytes_GET_SIZE(arg),
msgbuf, bufsize);
}
@ -872,27 +917,28 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
}
else if (PyByteArray_Check(arg)) {
if (PyByteArray_GET_SIZE(arg) != 1) {
return convertcharerr("a byte string of length 1",
return convertcharerr(nullable, "a byte string of length 1",
"a bytearray object", PyByteArray_GET_SIZE(arg),
msgbuf, bufsize);
}
*p = PyByteArray_AS_STRING(arg)[0];
}
else
return converterr("a byte string of length 1", arg, msgbuf, bufsize);
return converterr(nullable, "a byte string of length 1", arg, msgbuf, bufsize);
break;
}
case 'C': {/* unicode char */
int *p = va_arg(*p_va, int *);
HANDLE_NULLABLE;
int kind;
const void *data;
if (!PyUnicode_Check(arg))
return converterr("a unicode character", arg, msgbuf, bufsize);
return converterr(nullable, "a unicode character", arg, msgbuf, bufsize);
if (PyUnicode_GET_LENGTH(arg) != 1) {
return convertcharerr("a unicode character",
return convertcharerr(nullable, "a unicode character",
"a string", PyUnicode_GET_LENGTH(arg),
msgbuf, bufsize);
}
@ -905,6 +951,7 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
case 'p': {/* boolean *p*redicate */
int *p = va_arg(*p_va, int *);
HANDLE_NULLABLE;
int val = PyObject_IsTrue(arg);
if (val > 0)
*p = 1;
@ -923,24 +970,31 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
const char *buf;
Py_ssize_t count;
if (*format == '*') {
if (getbuffer(arg, (Py_buffer*)p, &buf) < 0)
return converterr(buf, arg, msgbuf, bufsize);
format++;
HANDLE_NULLABLE;
if (getbuffer(arg, (Py_buffer*)p, &buf) < 0)
return converterr(nullable, buf, arg, msgbuf, bufsize);
if (addcleanup(p, freelist, cleanup_buffer)) {
return converterr(
"(cleanup problem)",
nullable, "(cleanup problem)",
arg, msgbuf, bufsize);
}
break;
}
count = convertbuffer(arg, (const void **)p, &buf);
if (count < 0)
return converterr(buf, arg, msgbuf, bufsize);
if (*format == '#') {
else if (*format == '#') {
Py_ssize_t *psize = va_arg(*p_va, Py_ssize_t*);
*psize = count;
format++;
} else {
HANDLE_NULLABLE;
count = convertbuffer(arg, (const void **)p, &buf);
if (count < 0)
return converterr(nullable, buf, arg, msgbuf, bufsize);
*psize = count;
}
else {
HANDLE_NULLABLE;
count = convertbuffer(arg, (const void **)p, &buf);
if (count < 0)
return converterr(nullable, buf, arg, msgbuf, bufsize);
if (strlen(*p) != (size_t)count) {
PyErr_SetString(PyExc_ValueError, "embedded null byte");
RETURN_ERR_OCCURRED;
@ -956,32 +1010,35 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
/* "s*" or "z*" */
Py_buffer *p = (Py_buffer *)va_arg(*p_va, Py_buffer *);
format++;
HANDLE_NULLABLE;
if (c == 'z' && arg == Py_None)
PyBuffer_FillInfo(p, NULL, NULL, 0, 1, 0);
else if (PyUnicode_Check(arg)) {
Py_ssize_t len;
sarg = PyUnicode_AsUTF8AndSize(arg, &len);
if (sarg == NULL)
return converterr(CONV_UNICODE,
return converterr(nullable, CONV_UNICODE,
arg, msgbuf, bufsize);
PyBuffer_FillInfo(p, arg, (void *)sarg, len, 1, 0);
}
else { /* any bytes-like object */
const char *buf;
if (getbuffer(arg, p, &buf) < 0)
return converterr(buf, arg, msgbuf, bufsize);
return converterr(nullable, buf, arg, msgbuf, bufsize);
}
if (addcleanup(p, freelist, cleanup_buffer)) {
return converterr(
"(cleanup problem)",
nullable, "(cleanup problem)",
arg, msgbuf, bufsize);
}
format++;
} else if (*format == '#') { /* a string or read-only bytes-like object */
/* "s#" or "z#" */
const void **p = (const void **)va_arg(*p_va, const char **);
Py_ssize_t *psize = va_arg(*p_va, Py_ssize_t*);
format++;
HANDLE_NULLABLE;
if (c == 'z' && arg == Py_None) {
*p = NULL;
*psize = 0;
@ -990,7 +1047,7 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
Py_ssize_t len;
sarg = PyUnicode_AsUTF8AndSize(arg, &len);
if (sarg == NULL)
return converterr(CONV_UNICODE,
return converterr(nullable, CONV_UNICODE,
arg, msgbuf, bufsize);
*p = sarg;
*psize = len;
@ -1000,22 +1057,22 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
const char *buf;
Py_ssize_t count = convertbuffer(arg, p, &buf);
if (count < 0)
return converterr(buf, arg, msgbuf, bufsize);
return converterr(nullable, buf, arg, msgbuf, bufsize);
*psize = count;
}
format++;
} else {
/* "s" or "z" */
const char **p = va_arg(*p_va, const char **);
Py_ssize_t len;
sarg = NULL;
HANDLE_NULLABLE;
if (c == 'z' && arg == Py_None)
*p = NULL;
else if (PyUnicode_Check(arg)) {
sarg = PyUnicode_AsUTF8AndSize(arg, &len);
if (sarg == NULL)
return converterr(CONV_UNICODE,
return converterr(nullable, CONV_UNICODE,
arg, msgbuf, bufsize);
if (strlen(sarg) != (size_t)len) {
PyErr_SetString(PyExc_ValueError, "embedded null character");
@ -1024,7 +1081,7 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
*p = sarg;
}
else
return converterr(c == 'z' ? "str or None" : "str",
return converterr(c == 'z' || nullable, "str",
arg, msgbuf, bufsize);
}
break;
@ -1053,48 +1110,14 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
recode_strings = 0;
else
return converterr(
"(unknown parser marker combination)",
nullable, "(unknown parser marker combination)",
arg, msgbuf, bufsize);
buffer = (char **)va_arg(*p_va, char **);
format++;
if (buffer == NULL)
return converterr("(buffer is NULL)",
return converterr(nullable, "(buffer is NULL)",
arg, msgbuf, bufsize);
/* Encode object */
if (!recode_strings &&
(PyBytes_Check(arg) || PyByteArray_Check(arg))) {
s = Py_NewRef(arg);
if (PyBytes_Check(arg)) {
size = PyBytes_GET_SIZE(s);
ptr = PyBytes_AS_STRING(s);
}
else {
size = PyByteArray_GET_SIZE(s);
ptr = PyByteArray_AS_STRING(s);
}
}
else if (PyUnicode_Check(arg)) {
/* Encode object; use default error handling */
s = PyUnicode_AsEncodedString(arg,
encoding,
NULL);
if (s == NULL)
return converterr("(encoding failed)",
arg, msgbuf, bufsize);
assert(PyBytes_Check(s));
size = PyBytes_GET_SIZE(s);
ptr = PyBytes_AS_STRING(s);
if (ptr == NULL)
ptr = "";
}
else {
return converterr(
recode_strings ? "str" : "str, bytes or bytearray",
arg, msgbuf, bufsize);
}
/* Write output; output is guaranteed to be 0-terminated */
Py_ssize_t *psize = NULL;
if (*format == '#') {
/* Using buffer length parameter '#':
@ -1117,15 +1140,55 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
trailing 0-byte
*/
Py_ssize_t *psize = va_arg(*p_va, Py_ssize_t*);
psize = va_arg(*p_va, Py_ssize_t*);
format++;
if (psize == NULL) {
Py_DECREF(s);
return converterr(
"(buffer_len is NULL)",
nullable, "(buffer_len is NULL)",
arg, msgbuf, bufsize);
}
}
HANDLE_NULLABLE;
/* Encode object */
if (!recode_strings &&
(PyBytes_Check(arg) || PyByteArray_Check(arg))) {
s = Py_NewRef(arg);
if (PyBytes_Check(arg)) {
size = PyBytes_GET_SIZE(s);
ptr = PyBytes_AS_STRING(s);
}
else {
size = PyByteArray_GET_SIZE(s);
ptr = PyByteArray_AS_STRING(s);
}
}
else if (PyUnicode_Check(arg)) {
/* Encode object; use default error handling */
s = PyUnicode_AsEncodedString(arg,
encoding,
NULL);
if (s == NULL)
return converterr(nullable, "(encoding failed)",
arg, msgbuf, bufsize);
assert(PyBytes_Check(s));
size = PyBytes_GET_SIZE(s);
ptr = PyBytes_AS_STRING(s);
if (ptr == NULL)
ptr = "";
}
else {
return converterr(
nullable,
recode_strings ? "str"
: nullable ? "str, bytes, bytearray"
: "str, bytes or bytearray",
arg, msgbuf, bufsize);
}
/* Write output; output is guaranteed to be 0-terminated */
if (psize != NULL) {
if (*buffer == NULL) {
*buffer = PyMem_NEW(char, size + 1);
if (*buffer == NULL) {
@ -1136,7 +1199,7 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
if (addcleanup(buffer, freelist, cleanup_ptr)) {
Py_DECREF(s);
return converterr(
"(cleanup problem)",
nullable, "(cleanup problem)",
arg, msgbuf, bufsize);
}
} else {
@ -1170,7 +1233,7 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
if ((Py_ssize_t)strlen(ptr) != size) {
Py_DECREF(s);
return converterr(
"encoded string without null bytes",
nullable, "encoded string without null bytes",
arg, msgbuf, bufsize);
}
*buffer = PyMem_NEW(char, size + 1);
@ -1181,7 +1244,7 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
}
if (addcleanup(buffer, freelist, cleanup_ptr)) {
Py_DECREF(s);
return converterr("(cleanup problem)",
return converterr(nullable, "(cleanup problem)",
arg, msgbuf, bufsize);
}
memcpy(*buffer, ptr, size+1);
@ -1192,29 +1255,32 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
case 'S': { /* PyBytes object */
PyObject **p = va_arg(*p_va, PyObject **);
HANDLE_NULLABLE;
if (PyBytes_Check(arg))
*p = arg;
else
return converterr("bytes", arg, msgbuf, bufsize);
return converterr(nullable, "bytes", arg, msgbuf, bufsize);
break;
}
case 'Y': { /* PyByteArray object */
PyObject **p = va_arg(*p_va, PyObject **);
HANDLE_NULLABLE;
if (PyByteArray_Check(arg))
*p = arg;
else
return converterr("bytearray", arg, msgbuf, bufsize);
return converterr(nullable, "bytearray", arg, msgbuf, bufsize);
break;
}
case 'U': { /* PyUnicode object */
PyObject **p = va_arg(*p_va, PyObject **);
HANDLE_NULLABLE;
if (PyUnicode_Check(arg)) {
*p = arg;
}
else
return converterr("str", arg, msgbuf, bufsize);
return converterr(nullable, "str", arg, msgbuf, bufsize);
break;
}
@ -1225,10 +1291,11 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
type = va_arg(*p_va, PyTypeObject*);
p = va_arg(*p_va, PyObject **);
format++;
HANDLE_NULLABLE;
if (PyType_IsSubtype(Py_TYPE(arg), type))
*p = arg;
else
return converterr(type->tp_name, arg, msgbuf, bufsize);
return converterr(nullable, type->tp_name, arg, msgbuf, bufsize);
}
else if (*format == '&') {
@ -1237,16 +1304,18 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
void *addr = va_arg(*p_va, void *);
int res;
format++;
HANDLE_NULLABLE;
if (! (res = (*convert)(arg, addr)))
return converterr("(unspecified)",
return converterr(nullable, "(unspecified)",
arg, msgbuf, bufsize);
if (res == Py_CLEANUP_SUPPORTED &&
addcleanup(addr, freelist, convert) == -1)
return converterr("(cleanup problem)",
return converterr(nullable, "(cleanup problem)",
arg, msgbuf, bufsize);
}
else {
p = va_arg(*p_va, PyObject **);
HANDLE_NULLABLE;
*p = arg;
}
break;
@ -1258,29 +1327,30 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
if (*format != '*')
return converterr(
"(invalid use of 'w' format character)",
nullable, "(invalid use of 'w' format character)",
arg, msgbuf, bufsize);
format++;
HANDLE_NULLABLE;
/* Caller is interested in Py_buffer, and the object supports it
directly. The request implicitly asks for PyBUF_SIMPLE, so the
result is C-contiguous with format 'B'. */
if (PyObject_GetBuffer(arg, (Py_buffer*)p, PyBUF_WRITABLE) < 0) {
PyErr_Clear();
return converterr("read-write bytes-like object",
return converterr(nullable, "read-write bytes-like object",
arg, msgbuf, bufsize);
}
assert(PyBuffer_IsContiguous((Py_buffer *)p, 'C'));
if (addcleanup(p, freelist, cleanup_buffer)) {
return converterr(
"(cleanup problem)",
nullable, "(cleanup problem)",
arg, msgbuf, bufsize);
}
break;
}
default:
return converterr("(impossible<bad format char>)", arg, msgbuf, bufsize);
return converterr(nullable, "(impossible<bad format char>)", arg, msgbuf, bufsize);
}
@ -2675,6 +2745,9 @@ err:
return "impossible<bad format char>";
}
if (*format == '?') {
format++;
}
*p_format = format;
return NULL;