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.
This commit is contained in:
parent
34787ab98e
commit
38ba81811e
8 changed files with 139 additions and 69 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {}
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ CPP_TEST_CASES += \
|
|||
primitive_types \
|
||||
python_abstractbase \
|
||||
python_append \
|
||||
python_default_args \
|
||||
python_director \
|
||||
python_nondynamic \
|
||||
python_overload_simple_cast \
|
||||
|
|
|
|||
|
|
@ -1,67 +1,91 @@
|
|||
import default_args
|
||||
# Note that this test is also used by python_default_args_runme.py hence the use of __main__ and the run function
|
||||
|
||||
ec = default_args.EnumClass()
|
||||
if not ec.blah():
|
||||
raise RuntimeError,"EnumClass::blah() default arguments don't work"
|
||||
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"
|
||||
|
||||
if default_args.Statics_staticMethod() != 60:
|
||||
raise RuntimeError
|
||||
|
||||
if default_args.cfunc1(1) != 2:
|
||||
raise RuntimeError
|
||||
de = default_args.DerivedEnumClass()
|
||||
de.accelerate()
|
||||
de.accelerate(default_args.EnumClass.SLOW)
|
||||
|
||||
if default_args.cfunc2(1) != 3:
|
||||
raise RuntimeError
|
||||
if default_args.Statics_staticMethod() != 60:
|
||||
raise RuntimeError
|
||||
|
||||
if default_args.cfunc3(1) != 4:
|
||||
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 = default_args.Foo()
|
||||
|
||||
f.newname()
|
||||
f.newname(1)
|
||||
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)
|
||||
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)
|
||||
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:
|
||||
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)
|
||||
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)
|
||||
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"
|
||||
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')
|
||||
|
||||
|
|
|
|||
3
Examples/test-suite/python/python_default_args_runme.py
Normal file
3
Examples/test-suite/python/python_default_args_runme.py
Normal file
|
|
@ -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')
|
||||
10
Examples/test-suite/python_default_args.i
Normal file
10
Examples/test-suite/python_default_args.i
Normal file
|
|
@ -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"
|
||||
|
|
@ -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", "")
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue