From c8e5645f15054a87945d5f62dc23c6e49a394db5 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Mon, 22 Oct 2001 00:43:43 +0000 Subject: [PATCH] Methods of built-in types now properly check for keyword arguments (formerly these were silently ignored). The only built-in methods that take keyword arguments are __call__, __init__ and __new__. --- Include/descrobject.h | 7 +++++++ Lib/test/test_descr.py | 9 +++++++++ Misc/NEWS | 4 ++++ Objects/descrobject.c | 11 +++++++++++ Objects/typeobject.c | 26 ++++++++++++++------------ 5 files changed, 45 insertions(+), 12 deletions(-) diff --git a/Include/descrobject.h b/Include/descrobject.h index 9a071101531..918dd5a0420 100644 --- a/Include/descrobject.h +++ b/Include/descrobject.h @@ -14,15 +14,22 @@ typedef struct PyGetSetDef { typedef PyObject *(*wrapperfunc)(PyObject *self, PyObject *args, void *wrapped); +typedef PyObject *(*wrapperfunc_kwds)(PyObject *self, PyObject *args, + void *wrapped, PyObject *kwds); + struct wrapperbase { char *name; int offset; void *function; wrapperfunc wrapper; char *doc; + int flags; PyObject *name_strobj; }; +/* Flags for above struct */ +#define PyWrapperFlag_KEYWORDS 1 /* wrapper function takes keyword args */ + /* Various kinds of descriptor objects */ #define PyDescr_COMMON \ diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 962c1cc7a04..d7a06447e5e 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -2340,6 +2340,14 @@ def str_of_str_subclass(): vereq(capture.getvalue(), '41\n41\n') capture.close() +def kwdargs(): + if verbose: print "Testing keyword arguments to __init__, __call__..." + def f(a): return a + vereq(f.__call__(a=42), 42) + a = [] + list.__init__(a, sequence=[0, 1, 2]) + vereq(a, [0, 1, 2]) + def test_main(): class_docstrings() lists() @@ -2389,6 +2397,7 @@ def test_main(): subclasspropagation() buffer_inherit() str_of_str_subclass() + kwdargs() if verbose: print "All OK" if __name__ == "__main__": diff --git a/Misc/NEWS b/Misc/NEWS index a039a7195cb..83c978c51e3 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -4,6 +4,10 @@ XXX Planned XXX Release date: 14-Nov-2001 Type/class unification and new-style classes +- Methods of built-in types now properly check for keyword arguments + (formerly these were silently ignored). The only built-in methods + that take keyword arguments are __call__, __init__ and __new__. + Core and builtins Extension modules diff --git a/Objects/descrobject.c b/Objects/descrobject.c index e4d9f33023f..c5e793d874e 100644 --- a/Objects/descrobject.c +++ b/Objects/descrobject.c @@ -805,6 +805,17 @@ wrapper_call(wrapperobject *wp, PyObject *args, PyObject *kwds) wrapperfunc wrapper = wp->descr->d_base->wrapper; PyObject *self = wp->self; + if (wp->descr->d_base->flags & PyWrapperFlag_KEYWORDS) { + wrapperfunc_kwds wk = (wrapperfunc_kwds)wrapper; + return (*wk)(self, args, wp->descr->d_wrapped, kwds); + } + + if (kwds != NULL && (!PyDict_Check(kwds) || PyDict_Size(kwds) != 0)) { + PyErr_Format(PyExc_TypeError, + "wrapper %s doesn't take keyword arguments", + wp->descr->d_base->name); + return NULL; + } return (*wrapper)(self, args, wp->descr->d_wrapped); } diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 4e8f9676d39..5952b4efcff 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -2251,12 +2251,11 @@ wrap_hashfunc(PyObject *self, PyObject *args, void *wrapped) } static PyObject * -wrap_call(PyObject *self, PyObject *args, void *wrapped) +wrap_call(PyObject *self, PyObject *args, void *wrapped, PyObject *kwds) { ternaryfunc func = (ternaryfunc)wrapped; - /* XXX What about keyword arguments? */ - return (*func)(self, args, NULL); + return (*func)(self, args, kwds); } static PyObject * @@ -2328,12 +2327,11 @@ wrap_descr_set(PyObject *self, PyObject *args, void *wrapped) } static PyObject * -wrap_init(PyObject *self, PyObject *args, void *wrapped) +wrap_init(PyObject *self, PyObject *args, void *wrapped, PyObject *kwds) { initproc func = (initproc)wrapped; - /* XXX What about keyword arguments? */ - if (func(self, args, NULL) < 0) + if (func(self, args, kwds) < 0) return NULL; Py_INCREF(Py_None); return Py_None; @@ -3177,6 +3175,7 @@ slot_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds) typedef struct wrapperbase slotdef; #undef TPSLOT +#undef FLSLOT #undef ETSLOT #undef SQSLOT #undef MPSLOT @@ -3188,6 +3187,9 @@ typedef struct wrapperbase slotdef; #define TPSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC) \ {NAME, offsetof(PyTypeObject, SLOT), (void *)(FUNCTION), WRAPPER, DOC} +#define FLSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC, FLAGS) \ + {NAME, offsetof(PyTypeObject, SLOT), (void *)(FUNCTION), WRAPPER, \ + DOC, FLAGS} #define ETSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC) \ {NAME, offsetof(etype, SLOT), (void *)(FUNCTION), WRAPPER, DOC} #define SQSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC) \ @@ -3346,8 +3348,8 @@ static slotdef slotdefs[] = { "x.__cmp__(y) <==> cmp(x,y)"), TPSLOT("__hash__", tp_hash, slot_tp_hash, wrap_hashfunc, "x.__hash__() <==> hash(x)"), - TPSLOT("__call__", tp_call, slot_tp_call, wrap_call, - "x.__call__(...) <==> x(...)"), + FLSLOT("__call__", tp_call, slot_tp_call, (wrapperfunc)wrap_call, + "x.__call__(...) <==> x(...)", PyWrapperFlag_KEYWORDS), TPSLOT("__getattribute__", tp_getattro, slot_tp_getattr_hook, wrap_binaryfunc, "x.__getattribute__('name') <==> x.name"), TPSLOT("__getattribute__", tp_getattr, NULL, NULL, ""), @@ -3379,11 +3381,11 @@ static slotdef slotdefs[] = { "descr.__get__(obj[, type]) -> value"), TPSLOT("__set__", tp_descr_set, slot_tp_descr_set, wrap_descr_set, "descr.__set__(obj, value)"), - TPSLOT("__init__", tp_init, slot_tp_init, wrap_init, + FLSLOT("__init__", tp_init, slot_tp_init, (wrapperfunc)wrap_init, "x.__init__(...) initializes x; " - "see x.__class__.__doc__ for signature"), - TPSLOT("__new__", tp_new, slot_tp_new, NULL, - ""), + "see x.__class__.__doc__ for signature", + PyWrapperFlag_KEYWORDS), + TPSLOT("__new__", tp_new, slot_tp_new, NULL, ""), {NULL} };