summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWilliam S Fulton <wsf@fultondesigns.co.uk>2015-01-08 23:33:47 +0000
committerWilliam S Fulton <wsf@fultondesigns.co.uk>2015-01-09 00:34:17 +0000
commit38ba81811e4fd7743570cf3ca6b9a57a720444a1 (patch)
tree19e5a04b7b9515e01279edc93dfecef0724c54dc
parent34787ab98e86b1c82a4d41cecf58ff01d816a4ae (diff)
downloadswig-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.current10
-rw-r--r--Examples/test-suite/default_args.i4
-rw-r--r--Examples/test-suite/python/Makefile.in1
-rw-r--r--Examples/test-suite/python/default_args_runme.py156
-rw-r--r--Examples/test-suite/python/python_default_args_runme.py3
-rw-r--r--Examples/test-suite/python_default_args.i10
-rw-r--r--Lib/python/pyuserdir.swg10
-rw-r--r--Source/Modules/python.cxx42
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;
}