diff --git a/Doc/c-api/structures.rst b/Doc/c-api/structures.rst --- a/Doc/c-api/structures.rst +++ b/Doc/c-api/structures.rst @@ -164,19 +164,22 @@ convention flags can be combined with a object representing all arguments. This parameter is typically processed using :c:func:`PyArg_ParseTuple` or :c:func:`PyArg_UnpackTuple`. .. data:: METH_KEYWORDS Methods with these flags must be of type :c:type:`PyCFunctionWithKeywords`. The function expects three parameters: *self*, *args*, and a dictionary of - all the keyword arguments. The flag is typically combined with - :const:`METH_VARARGS`, and the parameters are typically processed using - :c:func:`PyArg_ParseTupleAndKeywords`. + all the keyword arguments. The flag is an alias to + ``METH_VARARGS | METH_KEYWORDS``, and the parameters are typically processed + using :c:func:`PyArg_ParseTupleAndKeywords`. + + .. versionchanged:: 3.6 + It is an alias to ``METH_VARARGS | METH_KEYWORDS``. .. data:: METH_NOARGS Methods without parameters don't need to check whether arguments are given if they are listed with the :const:`METH_NOARGS` flag. They need to be of type :c:type:`PyCFunction`. The first parameter is typically named *self* and will hold a reference to the module or object instance. In all cases the second diff --git a/Doc/whatsnew/3.6.rst b/Doc/whatsnew/3.6.rst --- a/Doc/whatsnew/3.6.rst +++ b/Doc/whatsnew/3.6.rst @@ -666,8 +666,11 @@ Changes in the C API * :c:func:`PyMem_Malloc` allocator family now uses the :ref:`pymalloc allocator ` rather than system :c:func:`malloc`. Applications calling :c:func:`PyMem_Malloc` without holding the GIL can now crash. Set the :envvar:`PYTHONMALLOC` environment variable to ``debug`` to validate the usage of memory allocators in your application. See :issue:`26249`. * :c:func:`Py_Exit` (and the main interpreter) now override the exit status with 120 if flushing buffered data failed. See :issue:`5319`. + +* :data:`METH_KEYWORDS` is now an alias to ``METH_VARARGS | METH_KEYWORDS``. + (Contributed by Berker Peksag in :issue:`15657`.) diff --git a/Include/methodobject.h b/Include/methodobject.h --- a/Include/methodobject.h +++ b/Include/methodobject.h @@ -48,17 +48,18 @@ typedef struct PyMethodDef PyMethodDef; #define PyCFunction_New(ML, SELF) PyCFunction_NewEx((ML), (SELF), NULL) PyAPI_FUNC(PyObject *) PyCFunction_NewEx(PyMethodDef *, PyObject *, PyObject *); /* Flag passed to newmethodobject */ /* #define METH_OLDARGS 0x0000 -- unsupported now */ #define METH_VARARGS 0x0001 -#define METH_KEYWORDS 0x0002 +/* METH_KEYWORDS is an alias to METH_VARARGS | METH_KEYWORDS */ +#define METH_KEYWORDS 0x0003 /* METH_NOARGS and METH_O must not be combined with the flags above. */ #define METH_NOARGS 0x0004 #define METH_O 0x0008 /* METH_CLASS and METH_STATIC are a little different; these control the construction of methods for a class. These cannot be used for functions in modules. */ #define METH_CLASS 0x0010 diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py @@ -236,16 +236,19 @@ class CAPITest(unittest.TestCase): _testcapi.return_result_with_error() self.assertRegex(str(cm.exception), 'return_result_with_error.* ' 'returned a result with an error set') def test_buildvalue_N(self): _testcapi.test_buildvalue_N() + def test_meth_keywords(self): + self.assertEqual(_testcapi.test_meth_keywords(name='guido'), 'OK') + @unittest.skipUnless(threading, 'Threading required for this test.') class TestPendingCalls(unittest.TestCase): def pendingcalls_submit(self, l, n): def callback(): #this function can be interrupted by thread switching so let's #use an atomic operation diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3928,16 +3928,29 @@ tracemalloc_get_traceback(PyObject *self return NULL; ptr = PyLong_AsVoidPtr(ptr_obj); if (PyErr_Occurred()) return NULL; return _PyTraceMalloc_GetTraceback(domain, (Py_uintptr_t)ptr); } +static PyObject * +test_meth_keywords(PyObject *self, PyObject *args, PyObject *kwargs) +{ + const char *name = NULL; + + static char *kwlist[] = {"name", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, + "|s:test_meth_keywords", kwlist, &name)) + return NULL; + + return Py_BuildValue("s", "OK"); +} static PyMethodDef TestMethods[] = { {"raise_exception", raise_exception, METH_VARARGS}, {"raise_memoryerror", (PyCFunction)raise_memoryerror, METH_NOARGS}, {"set_errno", set_errno, METH_VARARGS}, {"test_config", (PyCFunction)test_config, METH_NOARGS}, {"test_sizeof_c_types", (PyCFunction)test_sizeof_c_types, METH_NOARGS}, {"test_datetime_capi", test_datetime_capi, METH_NOARGS}, @@ -4129,16 +4142,17 @@ static PyMethodDef TestMethods[] = { {"get_recursion_depth", get_recursion_depth, METH_NOARGS}, {"pymem_buffer_overflow", pymem_buffer_overflow, METH_NOARGS}, {"pymem_api_misuse", pymem_api_misuse, METH_NOARGS}, {"pymem_malloc_without_gil", pymem_malloc_without_gil, METH_NOARGS}, {"pyobject_malloc_without_gil", pyobject_malloc_without_gil, METH_NOARGS}, {"tracemalloc_track", tracemalloc_track, METH_VARARGS}, {"tracemalloc_untrack", tracemalloc_untrack, METH_VARARGS}, {"tracemalloc_get_traceback", tracemalloc_get_traceback, METH_VARARGS}, + {"test_meth_keywords", (PyCFunction)test_meth_keywords, METH_KEYWORDS}, {NULL, NULL} /* sentinel */ }; #define AddSym(d, n, f, v) {PyObject *o = f(v); PyDict_SetItemString(d, n, o); Py_DECREF(o);} typedef struct { char bool_member; char byte_member; diff --git a/Objects/methodobject.c b/Objects/methodobject.c --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -89,17 +89,17 @@ PyCFunction_Call(PyObject *func, PyObjec /* PyCFunction_Call() must not be called with an exception set, because it may clear it (directly or indirectly) and so the caller loses its exception */ assert(!PyErr_Occurred()); flags = PyCFunction_GET_FLAGS(func) & ~(METH_CLASS | METH_STATIC | METH_COEXIST); - if (flags == (METH_VARARGS | METH_KEYWORDS)) { + if (flags == METH_KEYWORDS) { res = (*(PyCFunctionWithKeywords)meth)(self, args, kwds); } else { if (kwds != NULL && PyDict_Size(kwds) != 0) { PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments", f->m_ml->ml_name); return NULL; }