diff options
author | William S Fulton <wsf@fultondesigns.co.uk> | 2015-01-08 23:33:47 +0000 |
---|---|---|
committer | William S Fulton <wsf@fultondesigns.co.uk> | 2015-01-09 00:34:17 +0000 |
commit | 38ba81811e4fd7743570cf3ca6b9a57a720444a1 (patch) | |
tree | 19e5a04b7b9515e01279edc93dfecef0724c54dc | |
parent | 34787ab98e86b1c82a4d41cecf58ff01d816a4ae (diff) | |
download | swig-38ba81811e4fd7743570cf3ca6b9a57a720444a1.tar.gz |
Fix Python default argument handing broken since swig-3.0.3
Default values are no longer generated as Python code by default.
They must be explicitly turned on using the "python:defaultargs" feature.
Closes #294
Closes #296
The problems in these two issues when "python:defaultargs" is turned
on still need to be fixed and should be addressed in separate patches.
The important thing is the default code generation is now fixed.
-rw-r--r-- | CHANGES.current | 10 | ||||
-rw-r--r-- | Examples/test-suite/default_args.i | 4 | ||||
-rw-r--r-- | Examples/test-suite/python/Makefile.in | 1 | ||||
-rw-r--r-- | Examples/test-suite/python/default_args_runme.py | 156 | ||||
-rw-r--r-- | Examples/test-suite/python/python_default_args_runme.py | 3 | ||||
-rw-r--r-- | Examples/test-suite/python_default_args.i | 10 | ||||
-rw-r--r-- | Lib/python/pyuserdir.swg | 10 | ||||
-rw-r--r-- | Source/Modules/python.cxx | 42 |
8 files changed, 153 insertions, 83 deletions
diff --git a/CHANGES.current b/CHANGES.current index 50de69c90..7c9712f0e 100644 --- a/CHANGES.current +++ b/CHANGES.current @@ -5,6 +5,16 @@ See the RELEASENOTES file for a summary of changes in each release. Version 3.0.4 (in progress) =========================== +2015-01-08: wsfulton + [Python] Fix #294 #296 - Regression introduced in SWIG-3.0.3 when + wrapping functions with default arguments. Now any method with default + arguments obtains the default arguments from C++ instead of generating + Python code with the default arguments. + + The "python:defaultargs" feature has been introduced for users to + optionally generate Python methods with the default arguments instead + the default *args. + 2015-01-08: olly Allow C++11 "explicit constexpr". Fixes github issue#284 reported by Paweł Tomulik. Also handle "constexpr explicit" and "constexpr diff --git a/Examples/test-suite/default_args.i b/Examples/test-suite/default_args.i index 50667d9b9..cd66676ac 100644 --- a/Examples/test-suite/default_args.i +++ b/Examples/test-suite/default_args.i @@ -14,7 +14,9 @@ #include <string> // All kinds of numbers: hex, octal (which pose special problems to Python), negative... - void lots_of_args(int pos = -1, unsigned rgb = 0xabcdef, int mode = 0644) { } + void trickyvalue1(int first, int pos = -1) {} + void trickyvalue2(int first, unsigned rgb = 0xabcdef) {} + void trickyvalue3(int first, int mode = 0644) {} // Long long arguments are not handled at Python level currently but still work. void seek(long long offset = 0LL) {} diff --git a/Examples/test-suite/python/Makefile.in b/Examples/test-suite/python/Makefile.in index 82a0e9db1..3c87577a2 100644 --- a/Examples/test-suite/python/Makefile.in +++ b/Examples/test-suite/python/Makefile.in @@ -58,6 +58,7 @@ CPP_TEST_CASES += \ primitive_types \ python_abstractbase \ python_append \ + python_default_args \ python_director \ python_nondynamic \ python_overload_simple_cast \ diff --git a/Examples/test-suite/python/default_args_runme.py b/Examples/test-suite/python/default_args_runme.py index ad5e03d11..45465bac9 100644 --- a/Examples/test-suite/python/default_args_runme.py +++ b/Examples/test-suite/python/default_args_runme.py @@ -1,67 +1,91 @@ -import default_args - -ec = default_args.EnumClass() -if not ec.blah(): - raise RuntimeError,"EnumClass::blah() default arguments don't work" - -if default_args.Statics_staticMethod() != 60: - raise RuntimeError - -if default_args.cfunc1(1) != 2: - raise RuntimeError - -if default_args.cfunc2(1) != 3: - raise RuntimeError - -if default_args.cfunc3(1) != 4: - raise RuntimeError - - -f = default_args.Foo() - -f.newname() -f.newname(1) - - -try: - f = default_args.Foo(1) - error = 1 -except: - error = 0 -if error: raise RuntimeError,"Foo::Foo ignore is not working" - -try: - f = default_args.Foo(1,2) - error = 1 -except: - error = 0 -if error: raise RuntimeError,"Foo::Foo ignore is not working" - -try: - f = default_args.Foo(1,2,3) - error = 1 -except: - error = 0 -if error: raise RuntimeError,"Foo::Foo ignore is not working" - -try: - m = f.meth(1) - error = 1 -except: - error = 0 -if error: raise RuntimeError,"Foo::meth ignore is not working" - -try: - m = f.meth(1,2) - error = 1 -except: - error = 0 -if error: raise RuntimeError,"Foo::meth ignore is not working" - -try: - m = f.meth(1,2,3) - error = 1 -except: - error = 0 -if error: raise RuntimeError,"Foo::meth ignore is not working" +# Note that this test is also used by python_default_args_runme.py hence the use of __main__ and the run function + +def run(module_name): + default_args = __import__(module_name) + print "running...." + ec = default_args.EnumClass() + if not ec.blah(): + raise RuntimeError,"EnumClass::blah() default arguments don't work" + + de = default_args.DerivedEnumClass() + de.accelerate() + de.accelerate(default_args.EnumClass.SLOW) + + if default_args.Statics_staticMethod() != 60: + raise RuntimeError + + if default_args.cfunc1(1) != 2: + raise RuntimeError + + if default_args.cfunc2(1) != 3: + raise RuntimeError + + if default_args.cfunc3(1) != 4: + raise RuntimeError + + + f = default_args.Foo() + + f.newname() + f.newname(1) + + + try: + f = default_args.Foo(1) + error = 1 + except: + error = 0 + if error: raise RuntimeError,"Foo::Foo ignore is not working" + + try: + f = default_args.Foo(1,2) + error = 1 + except: + error = 0 + if error: raise RuntimeError,"Foo::Foo ignore is not working" + + try: + f = default_args.Foo(1,2,3) + error = 1 + except: + error = 0 + if error: raise RuntimeError,"Foo::Foo ignore is not working" + + try: + m = f.meth(1) + error = 1 + except: + error = 0 + if error: raise RuntimeError,"Foo::meth ignore is not working" + + try: + m = f.meth(1,2) + error = 1 + except: + error = 0 + if error: raise RuntimeError,"Foo::meth ignore is not working" + + try: + m = f.meth(1,2,3) + error = 1 + except: + error = 0 + if error: raise RuntimeError,"Foo::meth ignore is not working" + + if default_args.Klass.inc(100, default_args.Klass(22)).val != 122: + raise RuntimeError, "Klass::inc failed" + + if default_args.Klass.inc(100).val != 99: + raise RuntimeError, "Klass::inc failed" + + if default_args.Klass.inc().val != 0: + raise RuntimeError, "Klass::inc failed" + + default_args.trickyvalue1(10); default_args.trickyvalue1(10, 10) + default_args.trickyvalue2(10); default_args.trickyvalue2(10, 10) + default_args.trickyvalue3(10); default_args.trickyvalue3(10, 10) + default_args.seek(); default_args.seek(10) + +if __name__=="__main__": + run('default_args') diff --git a/Examples/test-suite/python/python_default_args_runme.py b/Examples/test-suite/python/python_default_args_runme.py new file mode 100644 index 000000000..7f35fbed6 --- /dev/null +++ b/Examples/test-suite/python/python_default_args_runme.py @@ -0,0 +1,3 @@ +# Test %feature("python:defaultargs") using the test code in default_args_runme.py (which does not use the feature) +import default_args_runme +default_args_runme.run('python_default_args') diff --git a/Examples/test-suite/python_default_args.i b/Examples/test-suite/python_default_args.i new file mode 100644 index 000000000..66e66a671 --- /dev/null +++ b/Examples/test-suite/python_default_args.i @@ -0,0 +1,10 @@ +%module python_default_args + +%pythondefaultargs; + +// Turn off the feature for the tricky cases that can't be handled +%nopythondefaultargs seek; +%nopythondefaultargs Space::Klass::inc; +%nopythondefaultargs DerivedEnumClass::accelerate; + +%include "default_args.i" diff --git a/Lib/python/pyuserdir.swg b/Lib/python/pyuserdir.swg index d3c3eb188..2a1d80790 100644 --- a/Lib/python/pyuserdir.swg +++ b/Lib/python/pyuserdir.swg @@ -185,6 +185,16 @@ These methods "may be called" if needed. #define %clearpythonappend %feature("pythonappend","") +/* ------------------------------------------------------------------------- */ +/* + Python default argument handling (for non-builtin) +*/ + +#define %pythondefaultargs %feature("python:defaultargs") +#define %nopythondefaultargs %feature("python:defaultargs", "0") +#define %clearpythondefaultargs %feature("python:defaultargs", "") + + /* ------------------------------------------------------------------------- */ /* diff --git a/Source/Modules/python.cxx b/Source/Modules/python.cxx index 6a0b286ca..217d7ce7d 100644 --- a/Source/Modules/python.cxx +++ b/Source/Modules/python.cxx @@ -1856,27 +1856,37 @@ public: * at C++ code level where they can always be handled. * ------------------------------------------------------------ */ bool is_representable_as_pyargs(Node *n) { - ParmList *plist = CopyParmList(Getattr(n, "parms")); - Parm *p; - Parm *pnext; + bool is_representable = true; - for (p = plist; p; p = pnext) { - String *tm = Getattr(p, "tmap:in"); - if (tm) { - pnext = Getattr(p, "tmap:in:next"); - if (checkAttribute(p, "tmap:in:numinputs", "0")) { - continue; + if (Getattr(n, "sym:overloaded")) { + if (GetFlag(n, "feature:python:defaultargs")) { + ParmList *plist = CopyParmList(Getattr(n, "parms")); + Parm *p; + Parm *pnext; + + for (p = plist; p; p = pnext) { + String *tm = Getattr(p, "tmap:in"); + if (tm) { + pnext = Getattr(p, "tmap:in:next"); + if (checkAttribute(p, "tmap:in:numinputs", "0")) { + continue; + } + } else { + pnext = nextSibling(p); + } + if (String *value = Getattr(p, "value")) { + String *type = Getattr(p, "type"); + if (!convertValue(value, type)) { + is_representable = false; + break; + } + } } } else { - pnext = nextSibling(p); - } - if (String *value = Getattr(p, "value")) { - String *type = Getattr(p, "type"); - if (!convertValue(value, type)) - return false; + is_representable = false; } } - return true; + return is_representable; } |