Index: src/org/python/core/PyReflectedConstructor.java =================================================================== --- src/org/python/core/PyReflectedConstructor.java (revision 6942) +++ src/org/python/core/PyReflectedConstructor.java (working copy) @@ -115,32 +115,77 @@ } ReflectedCallData callData = new ReflectedCallData(); Object method = null; - // Remove keyword args + + // If we have keyword args, there are two ways this can be handled; + // a) we find a constructor that takes keyword args, and use it. + // b) we don't, in which case we strip the keyword args, and pass the + // non-keyword args, and then use the keyword args to set bean properties + // If we don't have keyword args; just look for a constructor that + // takes the right number of args. int nkeywords = keywords.length; + ReflectedArgs rargs = null; PyObject[] allArgs = args; + boolean usingKeywordArgsCtor = false; if (nkeywords > 0) { - args = new PyObject[allArgs.length - nkeywords]; - System.arraycopy(allArgs, 0, args, 0, args.length); - } - // Check for a matching constructor to call - int n = nargs; - for (int i = 0; i < n; i++) { - ReflectedArgs rargs = argslist[i]; - if (rargs.matches(null, args, Py.NoKeywords, callData)) { - method = rargs.data; - break; + // We have keyword args. + + // Look for a constructor; the ReflectedArgs#matches() method exits early in the case + // where keyword args are used + int n = nargs; + for (int i = 0; i < n; i++) { + rargs = argslist[i]; + if (rargs.matches(null, args, keywords, callData)) { + method = rargs.data; + break; + } } - } + + if (method != null) { + // Constructor found that will accept the keyword args + usingKeywordArgsCtor = true; + } else { + // No constructor found that will take keyword args + + // Remove the keyword args + args = new PyObject[allArgs.length - nkeywords]; + System.arraycopy(allArgs, 0, args, 0, args.length); + + // Look for a constructor with no keyword args + for (int i = 0; i < n; i++) { + rargs = argslist[i]; + if (rargs.matches(null, args, Py.NoKeywords, callData)) { + method = rargs.data; + break; + } + } + } + } else { + // Just look for a constructor with no keyword args + int n = nargs; + for (int i = 0; i < n; i++) { + rargs = argslist[i]; + if (rargs.matches(null, args, Py.NoKeywords, callData)) { + method = rargs.data; + break; + } + } + } + // Throw an error if no valid set of arguments if (method == null) { throwError(callData.errArg, args.length, false, false); } // Do the actual constructor call constructProxy(self, (Constructor)method, callData.getArgsArray(), javaClass); - // Do setattr's for keyword args - int offset = args.length; - for (int i = 0; i < nkeywords; i++) { - self.__setattr__(keywords[i], allArgs[i + offset]); + // Do setattr's for keyword args. This convenience allows Java bean properties to be set in + // by a Python constructor call. + // However, this is not done if the Java constructor accepts (PyObject[], String[]) as its arguments, + // in which case the intention is that the Java constructor will handle the keyword arguments itself. + if (!usingKeywordArgsCtor) { + int offset = args.length; + for (int i = 0; i < nkeywords; i++) { + self.__setattr__(keywords[i], allArgs[i + offset]); + } } return Py.None; } Index: tests/java/org/python/tests/constructor_kwargs/ConstructorKWArgsTest.java =================================================================== --- tests/java/org/python/tests/constructor_kwargs/ConstructorKWArgsTest.java (revision 0) +++ tests/java/org/python/tests/constructor_kwargs/ConstructorKWArgsTest.java (revision 0) @@ -0,0 +1,23 @@ +package org.python.tests.constructor_kwargs; + +import junit.framework.TestCase; + +import org.python.core.PyString; +import org.python.core.PyStringMap; +import org.python.core.PySystemState; +import org.python.util.PythonInterpreter; + +public class ConstructorKWArgsTest extends TestCase { + + private PythonInterpreter interp; + + @Override + protected void setUp() throws Exception { + PySystemState sys = new PySystemState(); + interp = new PythonInterpreter(new PyStringMap(), sys); + } + + public void testConstructorKWArgs() { + interp.execfile("tests/python/constructorkwargs_test.py"); + } +} Index: tests/java/org/python/tests/constructor_kwargs/KWArgsObject.java =================================================================== --- tests/java/org/python/tests/constructor_kwargs/KWArgsObject.java (revision 0) +++ tests/java/org/python/tests/constructor_kwargs/KWArgsObject.java (revision 0) @@ -0,0 +1,21 @@ +package org.python.tests.constructor_kwargs; + +import java.util.HashMap; +import java.util.Arrays; + +import org.python.core.PyObject; + +public class KWArgsObject { + private HashMap data = new HashMap(); + + public KWArgsObject(PyObject[] values, String[] names) { + int offset = values.length-names.length; + for (int i = 0; i