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 @@ -148,16 +148,19 @@ specific C type of the *self* object. The :attr:`ml_flags` field is a bitfield which can include the following flags. The individual flags indicate either a calling convention or a binding convention. Of the calling convention flags, only :const:`METH_VARARGS` and :const:`METH_KEYWORDS` can be combined (but note that :const:`METH_KEYWORDS` alone is equivalent to ``METH_VARARGS | METH_KEYWORDS``). Any of the calling convention flags can be combined with a binding flag. +.. versionchanged:: 3.5 + ``METH_KEYWORDS`` is equivalent to ``METH_VARARGS | METH_KEYWORDS``. + .. data:: METH_VARARGS This is the typical calling convention, where the methods have the type :c:type:`PyCFunction`. The function expects two :c:type:`PyObject\*` values. The first one is the *self* object for methods; for module functions, it is the module object. The second parameter (often called *args*) is a tuple object representing all arguments. This parameter is typically processed @@ -167,16 +170,18 @@ convention flags can be combined with a .. 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`. + .. versionchanged:: 3.5 + ``METH_KEYWORDS`` is equivalent 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 parameter will be *NULL*. diff --git a/Doc/whatsnew/3.5.rst b/Doc/whatsnew/3.5.rst --- a/Doc/whatsnew/3.5.rst +++ b/Doc/whatsnew/3.5.rst @@ -847,8 +847,11 @@ Changes in the C API * Removed non-documented macro :c:macro:`PyObject_REPR` which leaked references. Use format character ``%R`` in :c:func:`PyUnicode_FromFormat`-like functions to format the :func:`repr` of the object. * Because the lack of the :attr:`__module__` attribute breaks pickling and introspection, a deprecation warning now is raised for builtin type without the :attr:`__module__` attribute. Would be an AttributeError in future. (:issue:`20204`) + +* :const:`METH_KEYWORDS` is now equivalent to ``METH_VARARGS | METH_KEYWORDS``. + (Contributed by Berker Peksag in :issue:`15657`.) diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3501,16 +3501,30 @@ test_PyTime_AsMicroseconds(PyObject *sel return NULL; t = _PyTime_FromNanoseconds(ns); ms = _PyTime_AsMicroseconds(t, round); /* This conversion rely on the fact that _PyTime_t is a number of nanoseconds */ return _PyTime_AsNanosecondsObject(ms); } +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}, {"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}, {"test_list_api", (PyCFunction)test_list_api, METH_NOARGS}, @@ -3602,16 +3616,17 @@ static PyMethodDef TestMethods[] = { #endif {"traceback_print", traceback_print, METH_VARARGS}, {"exception_print", exception_print, METH_VARARGS}, {"set_exc_info", test_set_exc_info, METH_VARARGS}, {"argparsing", argparsing, METH_VARARGS}, {"code_newempty", code_newempty, METH_VARARGS}, {"make_exception_with_doc", (PyCFunction)make_exception_with_doc, METH_VARARGS | METH_KEYWORDS}, + {"test_meth_keywords", (PyCFunction)test_meth_keywords, METH_KEYWORDS}, {"make_memoryview_from_NULL_pointer", (PyCFunction)make_memoryview_from_NULL_pointer, METH_NOARGS}, {"crash_no_current_thread", (PyCFunction)crash_no_current_thread, METH_NOARGS}, {"run_in_subinterp", run_in_subinterp, METH_VARARGS}, {"pytime_object_to_time_t", test_pytime_object_to_time_t, METH_VARARGS}, {"pytime_object_to_timeval", test_pytime_object_to_timeval, METH_VARARGS}, {"pytime_object_to_timespec", test_pytime_object_to_timespec, METH_VARARGS}, {"with_tp_del", with_tp_del, METH_VARARGS}, 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 looses 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_VARARGS | METH_KEYWORDS) || 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; }