Issue #6012: Add cleanup support to O& argument parsing.
This commit is contained in:
parent
2703fd9134
commit
c15bdef819
|
@ -250,6 +250,14 @@ variable(s) whose address should be passed.
|
||||||
the conversion has failed. When the conversion fails, the *converter* function
|
the conversion has failed. When the conversion fails, the *converter* function
|
||||||
should raise an exception and leave the content of *address* unmodified.
|
should raise an exception and leave the content of *address* unmodified.
|
||||||
|
|
||||||
|
If the *converter* returns Py_CLEANUP_SUPPORTED, it may get called a second time
|
||||||
|
if the argument parsing eventually fails, giving the converter a chance to release
|
||||||
|
any memory that it had already allocated. In this second call, the *object* parameter
|
||||||
|
will be NULL; *address* will have the same value as in the original call.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.1
|
||||||
|
Py_CLEANUP_SUPPORTED was added.
|
||||||
|
|
||||||
``S`` (string) [PyStringObject \*]
|
``S`` (string) [PyStringObject \*]
|
||||||
Like ``O`` but requires that the Python object is a string object. Raises
|
Like ``O`` but requires that the Python object is a string object. Raises
|
||||||
:exc:`TypeError` if the object is not a string object. The C variable may also
|
:exc:`TypeError` if the object is not a string object. The C variable may also
|
||||||
|
|
|
@ -379,11 +379,13 @@ Many of the following APIs take two arguments encoding and errors. These
|
||||||
parameters encoding and errors have the same semantics as the ones of the
|
parameters encoding and errors have the same semantics as the ones of the
|
||||||
builtin unicode() Unicode object constructor.
|
builtin unicode() Unicode object constructor.
|
||||||
|
|
||||||
Setting encoding to *NULL* causes the default encoding to be used which is
|
Setting encoding to *NULL* causes the default encoding to be used
|
||||||
ASCII. The file system calls should use :cdata:`Py_FileSystemDefaultEncoding`
|
which is ASCII. The file system calls should use
|
||||||
as the encoding for file names. This variable should be treated as read-only: On
|
:cfunc:`PyUnicode_FSConverter` for encoding file names. This uses the
|
||||||
some systems, it will be a pointer to a static string, on others, it will change
|
variable :cdata:`Py_FileSystemDefaultEncoding` internally. This
|
||||||
at run-time (such as when the application invokes setlocale).
|
variable should be treated as read-only: On some systems, it will be a
|
||||||
|
pointer to a static string, on others, it will change at run-time
|
||||||
|
(such as when the application invokes setlocale).
|
||||||
|
|
||||||
Error handling is set by errors which may also be set to *NULL* meaning to use
|
Error handling is set by errors which may also be set to *NULL* meaning to use
|
||||||
the default handling defined for the codec. Default error handling for all
|
the default handling defined for the codec. Default error handling for all
|
||||||
|
@ -782,6 +784,19 @@ the user settings on the machine running the codec.
|
||||||
object. Error handling is "strict". Return *NULL* if an exception was
|
object. Error handling is "strict". Return *NULL* if an exception was
|
||||||
raised by the codec.
|
raised by the codec.
|
||||||
|
|
||||||
|
For decoding file names and other environment strings, :cdata:`Py_FileSystemEncoding`
|
||||||
|
should be used as the encoding, and ``"surrogateescape"`` should be used as the error
|
||||||
|
handler. For encoding file names during argument parsing, the ``O&`` converter should
|
||||||
|
be used, passsing PyUnicode_FSConverter as the conversion function:
|
||||||
|
|
||||||
|
.. cfunction:: int PyUnicode_FSConverter(PyObject* obj, void* result)
|
||||||
|
|
||||||
|
Convert *obj* into *result*, using the file system encoding, and the ``surrogateescape``
|
||||||
|
error handler. *result* must be a ``PyObject*``, yielding a bytes or bytearray object
|
||||||
|
which must be released if it is no longer used.
|
||||||
|
|
||||||
|
.. versionadded:: 3.1
|
||||||
|
|
||||||
.. % --- Methods & Slots ----------------------------------------------------
|
.. % --- Methods & Slots ----------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,8 @@ PyAPI_FUNC(int) PyModule_AddStringConstant(PyObject *, const char *, const char
|
||||||
#define PyModule_AddIntMacro(m, c) PyModule_AddIntConstant(m, #c, c)
|
#define PyModule_AddIntMacro(m, c) PyModule_AddIntConstant(m, #c, c)
|
||||||
#define PyModule_AddStringMacro(m, c) PyModule_AddStringConstant(m, #c, c)
|
#define PyModule_AddStringMacro(m, c) PyModule_AddStringConstant(m, #c, c)
|
||||||
|
|
||||||
|
#define Py_CLEANUP_SUPPORTED 0x20000
|
||||||
|
|
||||||
#define PYTHON_API_VERSION 1013
|
#define PYTHON_API_VERSION 1013
|
||||||
#define PYTHON_API_STRING "1013"
|
#define PYTHON_API_STRING "1013"
|
||||||
/* The API version is maintained (independently from the Python version)
|
/* The API version is maintained (independently from the Python version)
|
||||||
|
|
|
@ -115,6 +115,10 @@ class TestPendingCalls(unittest.TestCase):
|
||||||
self.pendingcalls_submit(l, n)
|
self.pendingcalls_submit(l, n)
|
||||||
self.pendingcalls_wait(l, n)
|
self.pendingcalls_wait(l, n)
|
||||||
|
|
||||||
|
# Bug #6012
|
||||||
|
class Test6012(unittest.TestCase):
|
||||||
|
def test(self):
|
||||||
|
self.assertEqual(_testcapi.argparsing("Hello", "World"), 1)
|
||||||
|
|
||||||
def test_main():
|
def test_main():
|
||||||
support.run_unittest(CAPITest)
|
support.run_unittest(CAPITest)
|
||||||
|
@ -159,7 +163,7 @@ def test_main():
|
||||||
t.start()
|
t.start()
|
||||||
t.join()
|
t.join()
|
||||||
|
|
||||||
support.run_unittest(TestPendingCalls)
|
support.run_unittest(TestPendingCalls, Test6012)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -12,6 +12,8 @@ What's New in Python 3.1 release candidate 1?
|
||||||
Core and Builtins
|
Core and Builtins
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
- Issue #6012: Add cleanup support to O& argument parsing.
|
||||||
|
|
||||||
- Issue #6089: Fixed str.format with certain invalid field specifiers
|
- Issue #6089: Fixed str.format with certain invalid field specifiers
|
||||||
that would raise SystemError.
|
that would raise SystemError.
|
||||||
|
|
||||||
|
|
|
@ -1415,6 +1415,36 @@ raise_memoryerror(PyObject *self)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Issue 6012 */
|
||||||
|
static PyObject *str1, *str2;
|
||||||
|
static int
|
||||||
|
failing_converter(PyObject *obj, void *arg)
|
||||||
|
{
|
||||||
|
/* Clone str1, then let the conversion fail. */
|
||||||
|
assert(str1);
|
||||||
|
str2 = str1;
|
||||||
|
Py_INCREF(str2);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
static PyObject*
|
||||||
|
argparsing(PyObject *o, PyObject *args)
|
||||||
|
{
|
||||||
|
PyObject *res;
|
||||||
|
str1 = str2 = NULL;
|
||||||
|
if (!PyArg_ParseTuple(args, "O&O&",
|
||||||
|
PyUnicode_FSConverter, &str1,
|
||||||
|
failing_converter, &str2)) {
|
||||||
|
if (!str2)
|
||||||
|
/* argument converter not called? */
|
||||||
|
return NULL;
|
||||||
|
/* Should be 1 */
|
||||||
|
res = PyLong_FromLong(Py_REFCNT(str2));
|
||||||
|
Py_DECREF(str2);
|
||||||
|
PyErr_Clear();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
static PyMethodDef TestMethods[] = {
|
static PyMethodDef TestMethods[] = {
|
||||||
{"raise_exception", raise_exception, METH_VARARGS},
|
{"raise_exception", raise_exception, METH_VARARGS},
|
||||||
|
@ -1433,7 +1463,6 @@ static PyMethodDef TestMethods[] = {
|
||||||
PyDoc_STR("This is a pretty normal docstring.")},
|
PyDoc_STR("This is a pretty normal docstring.")},
|
||||||
{"test_string_to_double", (PyCFunction)test_string_to_double, METH_NOARGS},
|
{"test_string_to_double", (PyCFunction)test_string_to_double, METH_NOARGS},
|
||||||
{"test_capsule", (PyCFunction)test_capsule, METH_NOARGS},
|
{"test_capsule", (PyCFunction)test_capsule, METH_NOARGS},
|
||||||
|
|
||||||
{"getargs_tuple", getargs_tuple, METH_VARARGS},
|
{"getargs_tuple", getargs_tuple, METH_VARARGS},
|
||||||
{"getargs_keywords", (PyCFunction)getargs_keywords,
|
{"getargs_keywords", (PyCFunction)getargs_keywords,
|
||||||
METH_VARARGS|METH_KEYWORDS},
|
METH_VARARGS|METH_KEYWORDS},
|
||||||
|
@ -1468,6 +1497,7 @@ static PyMethodDef TestMethods[] = {
|
||||||
#endif
|
#endif
|
||||||
{"traceback_print", traceback_print, METH_VARARGS},
|
{"traceback_print", traceback_print, METH_VARARGS},
|
||||||
{"exception_print", exception_print, METH_VARARGS},
|
{"exception_print", exception_print, METH_VARARGS},
|
||||||
|
{"argparsing", argparsing, METH_VARARGS},
|
||||||
{NULL, NULL} /* sentinel */
|
{NULL, NULL} /* sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -804,8 +804,6 @@ posix_2str(PyObject *args,
|
||||||
if (!PyArg_ParseTuple(args, format,
|
if (!PyArg_ParseTuple(args, format,
|
||||||
PyUnicode_FSConverter, &opath1,
|
PyUnicode_FSConverter, &opath1,
|
||||||
PyUnicode_FSConverter, &opath2)) {
|
PyUnicode_FSConverter, &opath2)) {
|
||||||
Py_XDECREF(opath1);
|
|
||||||
Py_XDECREF(opath2);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
path1 = bytes2str(opath1, 1);
|
path1 = bytes2str(opath1, 1);
|
||||||
|
|
|
@ -1539,6 +1539,10 @@ PyUnicode_FSConverter(PyObject* arg, void* addr)
|
||||||
PyObject *output = NULL;
|
PyObject *output = NULL;
|
||||||
Py_ssize_t size;
|
Py_ssize_t size;
|
||||||
void *data;
|
void *data;
|
||||||
|
if (arg == NULL) {
|
||||||
|
Py_DECREF(*(PyObject**)addr);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
if (PyBytes_Check(arg) || PyByteArray_Check(arg)) {
|
if (PyBytes_Check(arg) || PyByteArray_Check(arg)) {
|
||||||
output = arg;
|
output = arg;
|
||||||
Py_INCREF(output);
|
Py_INCREF(output);
|
||||||
|
@ -1573,7 +1577,7 @@ PyUnicode_FSConverter(PyObject* arg, void* addr)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
*(PyObject**)addr = output;
|
*(PyObject**)addr = output;
|
||||||
return 1;
|
return Py_CLEANUP_SUPPORTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -141,6 +141,7 @@ _PyArg_VaParse_SizeT(PyObject *args, char *format, va_list va)
|
||||||
|
|
||||||
#define GETARGS_CAPSULE_NAME_CLEANUP_PTR "getargs.cleanup_ptr"
|
#define GETARGS_CAPSULE_NAME_CLEANUP_PTR "getargs.cleanup_ptr"
|
||||||
#define GETARGS_CAPSULE_NAME_CLEANUP_BUFFER "getargs.cleanup_buffer"
|
#define GETARGS_CAPSULE_NAME_CLEANUP_BUFFER "getargs.cleanup_buffer"
|
||||||
|
#define GETARGS_CAPSULE_NAME_CLEANUP_CONVERT "getargs.cleanup_convert"
|
||||||
|
|
||||||
static void
|
static void
|
||||||
cleanup_ptr(PyObject *self)
|
cleanup_ptr(PyObject *self)
|
||||||
|
@ -194,6 +195,46 @@ addcleanup(void *ptr, PyObject **freelist, PyCapsule_Destructor destr)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
cleanup_convert(PyObject *self)
|
||||||
|
{
|
||||||
|
typedef int (*destr_t)(PyObject *, void *);
|
||||||
|
destr_t destr = (destr_t)PyCapsule_GetContext(self);
|
||||||
|
void *ptr = PyCapsule_GetPointer(self,
|
||||||
|
GETARGS_CAPSULE_NAME_CLEANUP_CONVERT);
|
||||||
|
if (ptr && destr)
|
||||||
|
destr(NULL, ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
addcleanup_convert(void *ptr, PyObject **freelist, int (*destr)(PyObject*,void*))
|
||||||
|
{
|
||||||
|
PyObject *cobj;
|
||||||
|
if (!*freelist) {
|
||||||
|
*freelist = PyList_New(0);
|
||||||
|
if (!*freelist) {
|
||||||
|
destr(NULL, ptr);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cobj = PyCapsule_New(ptr, GETARGS_CAPSULE_NAME_CLEANUP_CONVERT,
|
||||||
|
cleanup_convert);
|
||||||
|
if (!cobj) {
|
||||||
|
destr(NULL, ptr);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (PyCapsule_SetContext(cobj, destr) == -1) {
|
||||||
|
/* This really should not happen. */
|
||||||
|
Py_FatalError("capsule refused setting of context.");
|
||||||
|
}
|
||||||
|
if (PyList_Append(*freelist, cobj)) {
|
||||||
|
Py_DECREF(cobj); /* This will also call destr. */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
Py_DECREF(cobj);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
cleanreturn(int retval, PyObject *freelist)
|
cleanreturn(int retval, PyObject *freelist)
|
||||||
{
|
{
|
||||||
|
@ -1253,10 +1294,15 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
|
||||||
typedef int (*converter)(PyObject *, void *);
|
typedef int (*converter)(PyObject *, void *);
|
||||||
converter convert = va_arg(*p_va, converter);
|
converter convert = va_arg(*p_va, converter);
|
||||||
void *addr = va_arg(*p_va, void *);
|
void *addr = va_arg(*p_va, void *);
|
||||||
|
int res;
|
||||||
format++;
|
format++;
|
||||||
if (! (*convert)(arg, addr))
|
if (! (res = (*convert)(arg, addr)))
|
||||||
return converterr("(unspecified)",
|
return converterr("(unspecified)",
|
||||||
arg, msgbuf, bufsize);
|
arg, msgbuf, bufsize);
|
||||||
|
if (res == Py_CLEANUP_SUPPORTED &&
|
||||||
|
addcleanup_convert(addr, freelist, convert) == -1)
|
||||||
|
return converterr("(cleanup problem)",
|
||||||
|
arg, msgbuf, bufsize);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
p = va_arg(*p_va, PyObject **);
|
p = va_arg(*p_va, PyObject **);
|
||||||
|
|
Loading…
Reference in New Issue