From 3367cecc6b6d1641fad85c1684b15094445c228f Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Wed, 30 Dec 2015 18:48:20 +0100 Subject: [PATCH] detect unreferenced keyword arguments in function calls --- include/pybind11/pybind11.h | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index c159b11a4..4f7d58945 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -24,6 +24,7 @@ #endif #include "cast.h" +#include NAMESPACE_BEGIN(pybind11) @@ -97,16 +98,17 @@ private: (void) unused; } - template static void process_extras(const std::tuple &args, + template static int process_extras(const std::tuple &args, PyObject *pyArgs, PyObject *kwargs, bool is_method) { - process_extras(args, pyArgs, kwargs, is_method, typename detail::make_index_sequence::type()); + return process_extras(args, pyArgs, kwargs, is_method, typename detail::make_index_sequence::type()); } - template static void process_extras(const std::tuple &args, + template static int process_extras(const std::tuple &args, PyObject *pyArgs, PyObject *kwargs, bool is_method, detail::index_sequence) { - int index = is_method ? 1 : 0; - int unused[] = { 0, (process_extra(std::get(args), index, pyArgs, kwargs), 0)... }; + int index = is_method ? 1 : 0, kwarg_refs = 0; + int unused[] = { 0, (process_extra(std::get(args), index, kwarg_refs, pyArgs, kwargs), 0)... }; (void) unused; (void) index; + return kwarg_refs; } static void process_extra(const char *doc, function_entry *entry, const char **, const char **) { entry->doc = doc; } @@ -133,8 +135,8 @@ private: static void process_extra(const pybind11::return_value_policy p, function_entry *entry, const char **, const char **) { entry->policy = p; } static void process_extra(pybind11::sibling s, function_entry *entry, const char **, const char **) { entry->sibling = s.value; } - template static void process_extra(T, int &, PyObject *, PyObject *) { } - static void process_extra(const pybind11::arg &a, int &index, PyObject *args, PyObject *kwargs) { + template static void process_extra(T, int &, int&, PyObject *, PyObject *) { } + static void process_extra(const pybind11::arg &a, int &index, int &kwarg_refs, PyObject *args, PyObject *kwargs) { if (kwargs) { if (PyTuple_GET_ITEM(args, index) != nullptr) { index++; @@ -144,12 +146,13 @@ private: if (value) { Py_INCREF(value); PyTuple_SetItem(args, index, value); + kwarg_refs++; } } index++; } template - static void process_extra(const pybind11::arg_t &a, int &index, PyObject *args, PyObject *kwargs) { + static void process_extra(const pybind11::arg_t &a, int &index, int &kwarg_refs, PyObject *args, PyObject *kwargs) { if (PyTuple_GET_ITEM(args, index) != nullptr) { index++; return; @@ -158,6 +161,7 @@ private: if (kwargs) value = PyDict_GetItemString(kwargs, a.name); if (value) { + kwarg_refs++; Py_INCREF(value); } else { value = detail::type_caster::type>::cast( @@ -185,9 +189,9 @@ public: m_entry->impl = [](function_entry *entry, PyObject *pyArgs, PyObject *kwargs, PyObject *parent) -> PyObject * { capture *data = (capture *) entry->data; - process_extras(data->extras, pyArgs, kwargs, entry->is_method); + int kwarg_refs = process_extras(data->extras, pyArgs, kwargs, entry->is_method); cast_in args; - if (!args.load(pyArgs, true)) + if (kwarg_refs != (kwargs ? PyDict_Size(kwargs) : 0) || !args.load(pyArgs, true)) return (PyObject *) 1; /* Special return code: try next overload */ return cast_out::cast(args.template call(data->f), entry->policy, parent); }; @@ -248,9 +252,9 @@ private: m_entry->impl = [](function_entry *entry, PyObject *pyArgs, PyObject *kwargs, PyObject *parent) -> PyObject *{ capture *data = (capture *) entry->data; - process_extras(data->extras, pyArgs, kwargs, entry->is_method); + int kwarg_refs = process_extras(data->extras, pyArgs, kwargs, entry->is_method); cast_in args; - if (!args.load(pyArgs, true)) + if (kwarg_refs != (kwargs ? PyDict_Size(kwargs) : 0) || !args.load(pyArgs, true)) return (PyObject *) 1; /* Special return code: try next overload */ return cast_out::cast(args.template call(data->f), entry->policy, parent); };