Index: Python/getargs.c =================================================================== --- Python/getargs.c (revision 57622) +++ Python/getargs.c (working copy) @@ -921,6 +921,34 @@ break; } + case 'Z': {/* unicode, may be NULL (None) */ + if (*format == '#') { /* any buffer-like object */ + Py_UNICODE **p = va_arg(*p_va, Py_UNICODE **); + FETCH_SIZE; + + if (arg == Py_None) { + *p = 0; + STORE_SIZE(0); + } + else if (PyUnicode_Check(arg)) { + *p = PyUnicode_AS_UNICODE(arg); + STORE_SIZE(PyUnicode_GET_SIZE(arg)); + } + format++; + } else { + Py_UNICODE **p = va_arg(*p_va, Py_UNICODE **); + + if (arg == Py_None) + *p = 0; + else if (PyUnicode_Check(arg)) + *p = PyUnicode_AS_UNICODE(arg); + else + return converterr("string or None", + arg, msgbuf, bufsize); + } + break; + } + case 'e': {/* encoded string */ char **buffer; const char *encoding; Index: Doc/c-api/utilities.rst =================================================================== --- Doc/c-api/utilities.rst (revision 57622) +++ Doc/c-api/utilities.rst (working copy) @@ -484,6 +484,13 @@ by interpreting their read-buffer pointer as pointer to a :ctype:`Py_UNICODE` array. +``Z`` (Unicode or ``None``) [Py_UNICODE \*] + Like ``s``, but the Python object may also be ``None``, in which case the C + pointer is set to *NULL*. + +``Z#`` (Unicode or ``None``) [Py_UNICODE \*, int] + This is to ``u#`` as ``Z`` is to ``u``. + ``es`` (string, Unicode object or character buffer compatible object) [const char \*encoding, char \*\*buffer] This variant on ``s`` is used for encoding Unicode and objects convertible to Unicode into a character buffer. It only works for encoded data without embedded Index: Modules/_fileio.c =================================================================== --- Modules/_fileio.c (revision 57622) +++ Modules/_fileio.c (working copy) @@ -45,7 +45,7 @@ internal_close(PyFileIOObject *self) { int save_errno = 0; - if (self->fd >= 0) { + if (self->fd >= 3) { int fd = self->fd; self->fd = -1; Py_BEGIN_ALLOW_THREADS Index: Modules/_testcapimodule.c =================================================================== --- Modules/_testcapimodule.c (revision 57622) +++ Modules/_testcapimodule.c (working copy) @@ -497,7 +497,60 @@ return Py_None; } +/* Test Z and Z# codes for PyArg_ParseTuple */ static PyObject * +test_Z_code(PyObject *self) +{ + PyObject *tuple, *obj; + Py_UNICODE *value1, *value2; + int len1, len2; + + tuple = PyTuple_New(2); + if (tuple == NULL) + return NULL; + + obj = PyUnicode_FromString("test"); + PyTuple_SET_ITEM(tuple, 0, obj); + Py_INCREF(Py_None); + PyTuple_SET_ITEM(tuple, 1, Py_None); + + /* swap values on purpose */ + value1 = NULL; + value2 = PyUnicode_AS_UNICODE(obj); + + /* Test Z for both values */ + if (PyArg_ParseTuple(tuple, "ZZ:test_Z_code", &value1, &value2) < 0) + return NULL; + if (value1 != PyUnicode_AS_UNICODE(obj)) + return raiseTestError("test_Z_code", + "Z code returned wrong value for 'test'"); + if (value2 != NULL) + return raiseTestError("test_Z_code", + "Z code returned wrong value for None"); + + value1 = NULL; + value2 = PyUnicode_AS_UNICODE(obj); + len1 = -1; + len2 = -1; + + /* Test Z# for both values */ + if (PyArg_ParseTuple(tuple, "Z#Z#:test_Z_code", &value1, &len1, + &value2, &len2) < 0) + return NULL; + if (value1 != PyUnicode_AS_UNICODE(obj) || + len1 != PyUnicode_GET_SIZE(obj)) + return raiseTestError("test_Z_code", + "Z# code returned wrong values for 'test'"); + if (value2 != NULL || + len2 != 0) + return raiseTestError("test_Z_code", + "Z# code returned wrong values for None'"); + + Py_DECREF(tuple); + Py_RETURN_NONE; +} + +static PyObject * codec_incrementalencoder(PyObject *self, PyObject *args) { const char *encoding, *errors = NULL; @@ -862,6 +915,7 @@ (PyCFunction)codec_incrementaldecoder, METH_VARARGS}, #endif {"test_u_code", (PyCFunction)test_u_code, METH_NOARGS}, + {"test_Z_code", (PyCFunction)test_Z_code, METH_NOARGS}, #ifdef WITH_THREAD {"_test_thread_state", test_thread_state, METH_VARARGS}, #endif