diff -r 284b3de802b7 Lib/test/test_capi.py --- a/Lib/test/test_capi.py Sat Jan 30 04:45:02 2016 +0000 +++ b/Lib/test/test_capi.py Sat Jan 30 13:22:01 2016 +0200 @@ -236,6 +236,9 @@ class CAPITest(unittest.TestCase): 'return_result_with_error.* ' 'returned a result with an error set') + def test_buildvalue_N(self): + _testcapi.test_buildvalue_N() + @unittest.skipUnless(threading, 'Threading required for this test.') class TestPendingCalls(unittest.TestCase): diff -r 284b3de802b7 Modules/_testcapimodule.c --- a/Modules/_testcapimodule.c Sat Jan 30 04:45:02 2016 +0000 +++ b/Modules/_testcapimodule.c Sat Jan 30 13:22:01 2016 +0200 @@ -872,6 +872,171 @@ test_L_code(PyObject *self) #endif /* ifdef HAVE_LONG_LONG */ +static int +test_buildvalue_N_error(const char *fmt) +{ + PyObject *arg, *res; + + arg = PyList_New(0); + if (arg == NULL) { + return -1; + } + + Py_INCREF(arg); + res = Py_BuildValue(fmt, "a", arg); + if (res == NULL) { + return -1; + } + Py_DECREF(res); + if (Py_REFCNT(arg) != 1) { + PyErr_Format(TestError, "test_buildvalue_N: " + "arg was not decrefed in successful " + "Py_BuildValue(\"%s\")", fmt); + return -1; + } + + Py_INCREF(arg); + res = Py_BuildValue(fmt, "\x80", arg); + if (res != NULL || !PyErr_Occurred()) { + PyErr_Format(TestError, "test_buildvalue_N: " + "Py_BuildValue(\"%s\") didn't complain", fmt); + return -1; + } + PyErr_Clear(); + if (Py_REFCNT(arg) != 1) { + PyErr_Format(TestError, "test_buildvalue_N: " + "arg was not decrefed in failed " + "Py_BuildValue(\"%s\")", fmt); + return -1; + } + Py_DECREF(arg); + return 0; +} + +static int +test_buildvalue_N_error2(const char *fmt) +{ + PyObject *arg, *res; + + arg = PyObject_CallFunction((PyObject *)&PyByteArray_Type, "i", 1000); + if (arg == NULL) { + return -1; + } + memset(PyByteArray_AS_STRING(arg), 'x', PyByteArray_GET_SIZE(arg)); + + Py_INCREF(arg); + res = Py_BuildValue(fmt, arg, "a", PyByteArray_AS_STRING(arg)); + if (res == NULL) { + return -1; + } + Py_DECREF(res); + if (Py_REFCNT(arg) != 1) { + PyErr_Format(TestError, "test_buildvalue_N: " + "arg was not decrefed in successful " + "Py_BuildValue(\"%s\")", fmt); + return -1; + } + + Py_INCREF(arg); + res = Py_BuildValue(fmt, arg, "\x80", PyByteArray_AS_STRING(arg)); + if (res != NULL || !PyErr_Occurred()) { + PyErr_Format(TestError, "test_buildvalue_N: " + "Py_BuildValue(\"%s\") didn't complain", fmt); + return -1; + } + PyErr_Clear(); + if (Py_REFCNT(arg) != 1) { + PyErr_Format(TestError, "test_buildvalue_N: " + "arg was not decrefed in failed " + "Py_BuildValue(\"%s\")", fmt); + return -1; + } + Py_DECREF(arg); + return 0; +} + +static PyObject * +test_buildvalue_N(PyObject *self, PyObject *args) +{ + PyObject *arg, *res; + + arg = PyList_New(0); + Py_INCREF(arg); + res = Py_BuildValue("N", arg); + if (res == NULL) { + return NULL; + } + if (res != arg) { + return raiseTestError("test_buildvalue_N", + "Py_BuildValue(\"N\") returned wrong result"); + } + if (Py_REFCNT(arg) != 2) { + return raiseTestError("test_buildvalue_N", + "arg was not decrefed in Py_BuildValue(\"N\")"); + } + Py_DECREF(res); + Py_DECREF(arg); + + if (test_buildvalue_N_error("sN") < 0) + return NULL; + if (test_buildvalue_N_error("(sN)") < 0) + return NULL; + if (test_buildvalue_N_error("[sN]") < 0) + return NULL; + if (test_buildvalue_N_error("{sN}") < 0) + return NULL; + if (test_buildvalue_N_error("{()s(())N}") < 0) + return NULL; + + if (test_buildvalue_N_error2("(Ns)s") < 0) + return NULL; + if (test_buildvalue_N_error2("[Ns]s") < 0) + return NULL; + if (test_buildvalue_N_error2("{()Ns()}s") < 0) + return NULL; + if (test_buildvalue_N_error2("{()N(())s}s") < 0) + return NULL; + + if (test_buildvalue_N_error2("((Ns)s)") < 0) + return NULL; + if (test_buildvalue_N_error2("([Ns]s)") < 0) + return NULL; + if (test_buildvalue_N_error2("({()Ns()}s)") < 0) + return NULL; + if (test_buildvalue_N_error2("({()N(())s}s)") < 0) + return NULL; + + if (test_buildvalue_N_error2("[(Ns)s]") < 0) + return NULL; + if (test_buildvalue_N_error2("[[Ns]s]") < 0) + return NULL; + if (test_buildvalue_N_error2("[{()Ns()}s]") < 0) + return NULL; + if (test_buildvalue_N_error2("[{()N(())s}s]") < 0) + return NULL; + + if (test_buildvalue_N_error2("{()(Ns)s()}") < 0) + return NULL; + if (test_buildvalue_N_error2("{()[Ns]s()}") < 0) + return NULL; + if (test_buildvalue_N_error2("{(){()Ns()}s()}") < 0) + return NULL; + if (test_buildvalue_N_error2("{(){()N(())s}s()}") < 0) + return NULL; + + if (test_buildvalue_N_error2("{()(Ns)(())s}") < 0) + return NULL; + if (test_buildvalue_N_error2("{()[Ns](())s}") < 0) + return NULL; + if (test_buildvalue_N_error2("{(){()Ns()}(())s}") < 0) + return NULL; + if (test_buildvalue_N_error2("{(){()N(())s}(())s}") < 0) + return NULL; + + Py_RETURN_NONE; +} + + /* Test tuple argument processing */ static PyObject * getargs_tuple(PyObject *self, PyObject *args) @@ -3653,6 +3818,7 @@ static PyMethodDef TestMethods[] = { {"test_pep3118_obsolete_write_locks", (PyCFunction)test_pep3118_obsolete_write_locks, METH_NOARGS}, #endif {"getbuffer_with_null_view", getbuffer_with_null_view, METH_O}, + {"test_buildvalue_N", test_buildvalue_N, METH_VARARGS}, {"getargs_tuple", getargs_tuple, METH_VARARGS}, {"getargs_keywords", (PyCFunction)getargs_keywords, METH_VARARGS|METH_KEYWORDS}, diff -r 284b3de802b7 Python/modsupport.c --- a/Python/modsupport.c Sat Jan 30 04:45:02 2016 +0000 +++ b/Python/modsupport.c Sat Jan 30 13:22:01 2016 +0200 @@ -53,6 +53,116 @@ countformat(const char *format, int endc return count; } +/* Decref 'N' arguments, ignore all other arguments. */ + +static int +do_ignore(const char **p_format, va_list *p_va, int flags) +{ + for (;;) { + switch (*(*p_format)++) { + case '(': + case '[': + case '{': + case ')': + case ']': + case '}': + case ',': + case ':': + case ' ': + case '\t': + break; + + case 'b': + case 'B': + case 'h': + case 'i': + case 'c': + case 'C': + va_arg(*p_va, int); + break; + + case 'H': + case 'I': + va_arg(*p_va, unsigned int); + break; + + case 'n': + va_arg(*p_va, Py_ssize_t); + break; + + case 'l': + va_arg(*p_va, long); + break; + + case 'k': + va_arg(*p_va, unsigned long); + break; + +#ifdef HAVE_LONG_LONG + case 'L': + va_arg(*p_va, PY_LONG_LONG); + break; + + case 'K': + va_arg(*p_va, unsigned PY_LONG_LONG); + break; +#endif + case 'f': + case 'd': + va_arg(*p_va, va_double); + break; + + case 'D': + va_arg(*p_va, Py_complex *); + break; + + case 'u': + case 's': + case 'z': + case 'U': /* XXX deprecated alias */ + case 'y': + if (*(*p_format - 1) == 'u') { + va_arg(*p_va, Py_UNICODE *); + } + else { + va_arg(*p_va, const char *); + } + if (**p_format == '#') { + ++*p_format; + if (flags & FLAG_SIZE_T) { + va_arg(*p_va, Py_ssize_t); + } + else { + va_arg(*p_va, int); + } + } + break; + + case 'N': + case 'S': + case 'O': + if (**p_format == '&') { + typedef PyObject *(*converter)(void *); + va_arg(*p_va, converter); + va_arg(*p_va, void *); + ++*p_format; + } + else { + PyObject *v = va_arg(*p_va, PyObject *); + if (*(*p_format - 1) == 'N') { + Py_XDECREF(v); + } + } + break; + + case '\0': + return 0; + + default: + return -1; + } + } +} /* Generic function to create a value -- the inverse of getargs() */ /* After an original idea and first implementation by Steven Miale */ @@ -68,43 +178,39 @@ do_mkdict(const char **p_format, va_list { PyObject *d; int i; - int itemfailed = 0; if (n < 0) return NULL; if ((d = PyDict_New()) == NULL) return NULL; - /* Note that we can't bail immediately on error as this will leak - refcounts on any 'N' arguments. */ for (i = 0; i < n; i+= 2) { PyObject *k, *v; - int err; k = do_mkvalue(p_format, p_va, flags); if (k == NULL) { - itemfailed = 1; - Py_INCREF(Py_None); - k = Py_None; + Py_DECREF(d); + return NULL; } v = do_mkvalue(p_format, p_va, flags); if (v == NULL) { - itemfailed = 1; - Py_INCREF(Py_None); - v = Py_None; - } - err = PyDict_SetItem(d, k, v); - Py_DECREF(k); - Py_DECREF(v); - if (err < 0 || itemfailed) { + Py_DECREF(k); Py_DECREF(d); return NULL; } + if (PyDict_SetItem(d, k, v) < 0) { + Py_DECREF(k); + Py_DECREF(v); + Py_DECREF(d); + return NULL; + } + Py_DECREF(k); + Py_DECREF(v); } - if (d != NULL && **p_format != endchar) { + if (**p_format != endchar) { Py_DECREF(d); - d = NULL; PyErr_SetString(PyExc_SystemError, "Unmatched paren in format"); + return NULL; } - else if (endchar) + if (endchar) ++*p_format; return d; } @@ -114,29 +220,22 @@ do_mklist(const char **p_format, va_list { PyObject *v; int i; - int itemfailed = 0; if (n < 0) return NULL; v = PyList_New(n); if (v == NULL) return NULL; - /* Note that we can't bail immediately on error as this will leak - refcounts on any 'N' arguments. */ for (i = 0; i < n; i++) { - PyObject *w = do_mkvalue(p_format, p_va, flags); + PyObject *w; + + w = do_mkvalue(p_format, p_va, flags); if (w == NULL) { - itemfailed = 1; - Py_INCREF(Py_None); - w = Py_None; + Py_DECREF(v); + return NULL; } PyList_SET_ITEM(v, i, w); } - if (itemfailed) { - /* do_mkvalue() should have already set an error */ - Py_DECREF(v); - return NULL; - } if (**p_format != endchar) { Py_DECREF(v); PyErr_SetString(PyExc_SystemError, @@ -153,37 +252,20 @@ do_mktuple(const char **p_format, va_lis { PyObject *v; int i; - int itemfailed = 0; if (n < 0) return NULL; if ((v = PyTuple_New(n)) == NULL) return NULL; - /* Note that we can't bail immediately on error as this will leak - refcounts on any 'N' arguments. */ for (i = 0; i < n; i++) { PyObject *w; - if (itemfailed) { - PyObject *exception, *value, *tb; - PyErr_Fetch(&exception, &value, &tb); - w = do_mkvalue(p_format, p_va, flags); - PyErr_Restore(exception, value, tb); - } - else { - w = do_mkvalue(p_format, p_va, flags); - } + w = do_mkvalue(p_format, p_va, flags); if (w == NULL) { - itemfailed = 1; - Py_INCREF(Py_None); - w = Py_None; + Py_DECREF(v); + return NULL; } PyTuple_SET_ITEM(v, i, w); } - if (itemfailed) { - /* do_mkvalue() should have already set an error */ - Py_DECREF(v); - return NULL; - } if (**p_format != endchar) { Py_DECREF(v); PyErr_SetString(PyExc_SystemError, @@ -450,9 +532,10 @@ va_build_value(const char *format, va_li { const char *f = format; int n = countformat(f, '\0'); + PyObject *res; va_list lva; - Py_VA_COPY(lva, va); + Py_VA_COPY(lva, va); if (n < 0) return NULL; @@ -461,8 +544,13 @@ va_build_value(const char *format, va_li return Py_None; } if (n == 1) - return do_mkvalue(&f, &lva, flags); - return do_mktuple(&f, &lva, '\0', n, flags); + res = do_mkvalue(&f, &lva, flags); + else + res = do_mktuple(&f, &lva, '\0', n, flags); + if (res == NULL && !PyErr_ExceptionMatches(PyExc_SystemError)) { + do_ignore(&f, &lva, flags); + } + return res; }