Issue #6012: Add cleanup support to O& argument parsing.

This commit is contained in:
Martin v. Löwis 2009-05-29 14:47:46 +00:00
parent 2703fd9134
commit c15bdef819
9 changed files with 120 additions and 11 deletions

View File

@ -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

View File

@ -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 ----------------------------------------------------

View File

@ -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)

View File

@ -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__":

View File

@ -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.

View File

@ -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 */
}; };

View File

@ -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);

View File

@ -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;
} }

View File

@ -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 **);