From 4715a4e72c7ab6119a65f0e1af7c7400ebfaebe8 Mon Sep 17 00:00:00 2001 From: William S Fulton Date: Fri, 21 Sep 2018 08:40:55 +0100 Subject: [PATCH] Python -builtin __contains__ fix for map and set like containers. Fix when using -builtin and wrapping std::map, std::set, std::unordered_map or std::unordered_set to ensure __contains__ is called. This is a wrapper for the STL container's find method. Without it, Python will do its own slower sequence search. --- CHANGES.current | 5 +++ .../test-suite/python/std_containers_runme.py | 20 +++++++++++ Lib/python/builtin.swg | 36 +++++++++++++++++++ Lib/python/std_map.i | 2 +- Lib/python/std_set.i | 4 +++ Lib/python/std_unordered_map.i | 5 ++- Lib/python/std_unordered_set.i | 4 +++ Source/Modules/python.cxx | 2 ++ 8 files changed, 76 insertions(+), 2 deletions(-) diff --git a/CHANGES.current b/CHANGES.current index d73925dbb..76b49d924 100644 --- a/CHANGES.current +++ b/CHANGES.current @@ -7,6 +7,11 @@ the issue number to the end of the URL: https://github.com/swig/swig/issues/ Version 4.0.0 (in progress) =========================== +2018-09-21: wsfulton + [Python] Fix when using -builtin and wrapping std::map, std::set, std::unordered_map or + std::unordered_set to ensure __contains__ is called. This is a wrapper for the STL + container's find method. Without it, Python will do its own slower sequence search. + 2018-09-19: wsfulton [Python] Fix functors (wrapped as __call__) when using -builtin -modern -fastunpack. diff --git a/Examples/test-suite/python/std_containers_runme.py b/Examples/test-suite/python/std_containers_runme.py index d4625daa0..63ff74c2a 100644 --- a/Examples/test-suite/python/std_containers_runme.py +++ b/Examples/test-suite/python/std_containers_runme.py @@ -69,6 +69,15 @@ for k in map: if map[k] != imap[k]: raise RuntimeError, "bad map" +# Test __contains__ (required for 'x in y' to work) +if not imap.__contains__('hello'): + raise RuntimeError("hello imap.__contains__") +if 'hello' not in imap: + raise RuntimeError("hello not in imap") +if imap.__contains__('oops'): + raise RuntimeError("oops imap.__contains__") +if 'oops' in imap: + raise RuntimeError("oops in imap") mapc = {} c1 = std_containers.C() @@ -114,3 +123,14 @@ for i in s: if i != j: raise RuntimeError j = j + 1 + +# Test __contains__ (required for 'x in y' to work) +if not s.__contains__(3): + raise RuntimeError("3 s.__contains__") +if 3 not in s: + raise RuntimeError("3 not in s") +if s.__contains__(-1): + raise RuntimeError("-1 s.__contains__") +if -1 in s: + raise RuntimeError("-1 in s") + diff --git a/Lib/python/builtin.swg b/Lib/python/builtin.swg index 926f2d1c6..5b8050be2 100644 --- a/Lib/python/builtin.swg +++ b/Lib/python/builtin.swg @@ -635,6 +635,42 @@ SwigPyBuiltin_ssizeobjargproc_closure(SwigPyWrapperFunction wrapper, PyObject *a return result; } +#define SWIGPY_OBJOBJPROC_CLOSURE(wrapper) \ +SWIGINTERN int \ +wrapper##_objobjproc_closure(PyObject *a, PyObject *b) { \ + return SwigPyBuiltin_objobjproc_closure(wrapper, a, b); \ +} +SWIGINTERN int +SwigPyBuiltin_objobjproc_closure(SwigPyWrapperFunction wrapper, PyObject *a, PyObject *b) { + int result; + PyObject *pyresult; + PyObject *tuple; + tuple = PyTuple_New(1); + assert(tuple); + PyTuple_SET_ITEM(tuple, 0, b); + Py_XINCREF(b); + pyresult = wrapper(a, tuple); + result = pyresult ? (PyObject_IsTrue(pyresult) ? 1 : 0) : -1; + Py_XDECREF(pyresult); + Py_DECREF(tuple); + return result; +} + +#define SWIGPY_FUNPACK_OBJOBJPROC_CLOSURE(wrapper) \ +SWIGINTERN int \ +wrapper##_objobjproc_closure(PyObject *a, PyObject *b) { \ + return SwigPyBuiltin_funpack_objobjproc_closure(wrapper, a, b); \ +} +SWIGINTERN int +SwigPyBuiltin_funpack_objobjproc_closure(SwigPyWrapperFunction wrapper, PyObject *a, PyObject *b) { + int result; + PyObject *pyresult; + pyresult = wrapper(a, b); + result = pyresult ? (PyObject_IsTrue(pyresult) ? 1 : 0) : -1; + Py_XDECREF(pyresult); + return result; +} + #define SWIGPY_OBJOBJARGPROC_CLOSURE(wrapper) \ SWIGINTERN int \ wrapper##_objobjargproc_closure(PyObject *a, PyObject *b, PyObject *c) { \ diff --git a/Lib/python/std_map.i b/Lib/python/std_map.i index 781c4d498..8ae483cef 100644 --- a/Lib/python/std_map.i +++ b/Lib/python/std_map.i @@ -156,6 +156,7 @@ %feature("python:slot", "mp_length", functype="lenfunc") __len__; %feature("python:slot", "mp_subscript", functype="binaryfunc") __getitem__; %feature("python:slot", "tp_iter", functype="getiterfunc") key_iterator; + %feature("python:slot", "sq_contains", functype="objobjproc") __contains__; %extend { %newobject iterkeys(PyObject **PYTHON_SELF); @@ -263,7 +264,6 @@ return itemList; } - // Python 2.2 methods bool __contains__(const key_type& key) { return self->find(key) != self->end(); } diff --git a/Lib/python/std_set.i b/Lib/python/std_set.i index 53f97e475..e80daee7e 100644 --- a/Lib/python/std_set.i +++ b/Lib/python/std_set.i @@ -36,6 +36,10 @@ %swig_sequence_iterator(set); %swig_container_methods(set); +#if defined(SWIGPYTHON_BUILTIN) + %feature("python:slot", "sq_contains", functype="objobjproc") __contains__; +#endif + %extend { void append(value_type x) { self->insert(x); diff --git a/Lib/python/std_unordered_map.i b/Lib/python/std_unordered_map.i index 959f303a3..0a900c65c 100644 --- a/Lib/python/std_unordered_map.i +++ b/Lib/python/std_unordered_map.i @@ -131,6 +131,10 @@ %swig_sequence_forward_iterator(Map); %swig_container_methods(Map) +#if defined(SWIGPYTHON_BUILTIN) + %feature("python:slot", "sq_contains", functype="objobjproc") __contains__; +#endif + %extend { mapped_type __getitem__(const key_type& key) const throw (std::out_of_range) { Map::const_iterator i = self->find(key); @@ -204,7 +208,6 @@ return itemList; } - // Python 2.2 methods bool __contains__(const key_type& key) { return self->find(key) != self->end(); } diff --git a/Lib/python/std_unordered_set.i b/Lib/python/std_unordered_set.i index 6c646509c..4a4d849a7 100644 --- a/Lib/python/std_unordered_set.i +++ b/Lib/python/std_unordered_set.i @@ -43,6 +43,10 @@ %swig_sequence_forward_iterator(unordered_set); %swig_container_methods(unordered_set); +#if defined(SWIGPYTHON_BUILTIN) + %feature("python:slot", "sq_contains", functype="objobjproc") __contains__; +#endif + %extend { void append(value_type x) { self->insert(x); diff --git a/Source/Modules/python.cxx b/Source/Modules/python.cxx index a916a1da9..62f6fd41c 100755 --- a/Source/Modules/python.cxx +++ b/Source/Modules/python.cxx @@ -210,6 +210,7 @@ static String *getClosure(String *functype, String *wrapper, int funpack = 0) { "ssizessizeargfunc", "SWIGPY_SSIZESSIZEARGFUNC_CLOSURE", "ssizeobjargproc", "SWIGPY_SSIZEOBJARGPROC_CLOSURE", "ssizessizeobjargproc", "SWIGPY_SSIZESSIZEOBJARGPROC_CLOSURE", + "objobjproc", "SWIGPY_OBJOBJPROC_CLOSURE", "objobjargproc", "SWIGPY_OBJOBJARGPROC_CLOSURE", "reprfunc", "SWIGPY_REPRFUNC_CLOSURE", "hashfunc", "SWIGPY_HASHFUNC_CLOSURE", @@ -229,6 +230,7 @@ static String *getClosure(String *functype, String *wrapper, int funpack = 0) { "ssizessizeargfunc", "SWIGPY_SSIZESSIZEARGFUNC_CLOSURE", "ssizeobjargproc", "SWIGPY_SSIZEOBJARGPROC_CLOSURE", "ssizessizeobjargproc", "SWIGPY_SSIZESSIZEOBJARGPROC_CLOSURE", + "objobjproc", "SWIGPY_FUNPACK_OBJOBJPROC_CLOSURE", "objobjargproc", "SWIGPY_OBJOBJARGPROC_CLOSURE", "reprfunc", "SWIGPY_REPRFUNC_CLOSURE", "hashfunc", "SWIGPY_HASHFUNC_CLOSURE",