diff --git a/src/runtime/classobject.cs b/src/runtime/classobject.cs index 5e79e8253..03f784ed7 100644 --- a/src/runtime/classobject.cs +++ b/src/runtime/classobject.cs @@ -223,7 +223,6 @@ public static int mp_ass_subscript(IntPtr ob, IntPtr idx, IntPtr v) { // Arg may be a tuple in the case of an indexer with multiple // parameters. If so, use it directly, else make a new tuple // with the index arg (method binders expect arg tuples). - IntPtr args = idx; bool free = false; @@ -234,13 +233,29 @@ public static int mp_ass_subscript(IntPtr ob, IntPtr idx, IntPtr v) { free = true; } + // Get the args passed in. int i = Runtime.PyTuple_Size(args); - IntPtr real = Runtime.PyTuple_New(i + 1); + IntPtr defaultArgs = cls.indexer.GetDefaultArgs(args); + int numOfDefaultArgs = Runtime.PyTuple_Size(defaultArgs); + int temp = i + numOfDefaultArgs; + IntPtr real = Runtime.PyTuple_New(temp + 1); for (int n = 0; n < i; n++) { IntPtr item = Runtime.PyTuple_GetItem(args, n); Runtime.Incref(item); Runtime.PyTuple_SetItem(real, n, item); } + + // Add Default Args if needed + for (int n = 0; n < numOfDefaultArgs; n++) { + IntPtr item = Runtime.PyTuple_GetItem(defaultArgs, n); + Runtime.Incref(item); + Runtime.PyTuple_SetItem(real, n + i, item); + } + // no longer need defaultArgs + Runtime.Decref(defaultArgs); + i = temp; + + // Add value to argument list Runtime.Incref(v); Runtime.PyTuple_SetItem(real, i, v); diff --git a/src/runtime/indexer.cs b/src/runtime/indexer.cs index 8118dc339..0781a3a0a 100644 --- a/src/runtime/indexer.cs +++ b/src/runtime/indexer.cs @@ -62,7 +62,57 @@ internal void SetItem(IntPtr inst, IntPtr args) { SetterBinder.Invoke(inst, args, IntPtr.Zero); } - } + internal bool NeedsDefaultArgs(IntPtr args){ + int pynargs = Runtime.PyTuple_Size(args); + var methods = SetterBinder.GetMethods(); + if(methods.Length == 0) + return false; + + MethodBase mi = methods[0]; + ParameterInfo[] pi = mi.GetParameters(); + // need to subtract one for the value + int clrnargs = pi.Length - 1; + if (pynargs == clrnargs || pynargs > clrnargs) + return false; + + for (int v = pynargs; v < clrnargs; v++){ + if (pi[v].DefaultValue == DBNull.Value) + return false; + } + return true; + } + + /// + /// This will return default arguments a new instance of a tuple. The size + /// of the tuple will indicate the number of default arguments. + /// + /// This is pointing to the tuple args passed in + /// a new instance of the tuple containing the default args + internal IntPtr GetDefaultArgs(IntPtr args){ + + // if we don't need default args return empty tuple + if(!NeedsDefaultArgs(args)) + return Runtime.PyTuple_New(0); + int pynargs = Runtime.PyTuple_Size(args); + + // Get the default arg tuple + var methods = SetterBinder.GetMethods(); + MethodBase mi = methods[0]; + ParameterInfo[] pi = mi.GetParameters(); + int clrnargs = pi.Length - 1; + IntPtr defaultArgs = Runtime.PyTuple_New(clrnargs - pynargs); + for (int i = 0; i < (clrnargs - pynargs); i++) { + if (pi[i + pynargs].DefaultValue == DBNull.Value) + continue; + else{ + IntPtr arg = Converter.ToPython(pi[i + pynargs].DefaultValue, pi[i + pynargs].ParameterType); + Runtime.PyTuple_SetItem(defaultArgs, i, arg); + } + } + return defaultArgs; + } + } + } diff --git a/src/runtime/methodbinder.cs b/src/runtime/methodbinder.cs index 80d3968fd..3b83ca379 100644 --- a/src/runtime/methodbinder.cs +++ b/src/runtime/methodbinder.cs @@ -226,7 +226,7 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, int pynargs = Runtime.PyTuple_Size(args); object arg; bool isGeneric = false; - + ArrayList defaultArgList = null; if (info != null) { _methods = (MethodBase[])Array.CreateInstance( typeof(MethodBase), 1 @@ -247,7 +247,17 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, int outs = 0; if (pynargs == clrnargs) { - match = true; + match = true; + } else if(pynargs < clrnargs){ + match = true; + defaultArgList = new ArrayList(); + for (int v = pynargs; v < clrnargs; v++) + { + if (pi[v].DefaultValue == DBNull.Value) + match = false; + else + defaultArgList.Add((object)pi[v].DefaultValue); + } } else if ((pynargs > clrnargs) && (clrnargs > 0) && (pi[clrnargs-1].ParameterType.IsArray)) { // The last argument of the mananged functions seems to @@ -262,30 +272,43 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, for (int n = 0; n < clrnargs; n++) { IntPtr op; - if (arrayStart == n) { - // map remaining Python arguments to a tuple since - // the managed function accepts it - hopefully :] - op = Runtime.PyTuple_GetSlice(args, arrayStart, pynargs); - } - else { - op = Runtime.PyTuple_GetItem(args, n); - } - Type type = pi[n].ParameterType; - if (pi[n].IsOut || type.IsByRef) { - outs++; - } - - if (!Converter.ToManaged(op, type, out arg, false)) { - Exceptions.Clear(); - margs = null; - break; + if (n < pynargs) + { + if (arrayStart == n) + { + // map remaining Python arguments to a tuple since + // the managed function accepts it - hopefully :] + op = Runtime.PyTuple_GetSlice(args, arrayStart, pynargs); + } + else + { + op = Runtime.PyTuple_GetItem(args, n); + } + Type type = pi[n].ParameterType; + if (pi[n].IsOut || type.IsByRef) + { + outs++; + } + + if (!Converter.ToManaged(op, type, out arg, false)) + { + Exceptions.Clear(); + margs = null; + break; + } + if (arrayStart == n) + { + // GetSlice() creates a new reference but GetItem() + // returns only a borrow reference. + Runtime.Decref(op); + } + margs[n] = arg; } - if (arrayStart == n) { - // GetSlice() creates a new reference but GetItem() - // returns only a borrow reference. - Runtime.Decref(op); + else + { + if (defaultArgList != null) + margs[n] = defaultArgList[n - pynargs]; } - margs[n] = arg; } if (margs == null) { diff --git a/src/testing/indexertest.cs b/src/testing/indexertest.cs index fa4a8c8e2..7caba4e64 100644 --- a/src/testing/indexertest.cs +++ b/src/testing/indexertest.cs @@ -347,6 +347,23 @@ public MultiTypeIndexerTest() : base() {} } + public class MultiDefaultKeyIndexerTest : IndexerBase { + + public MultiDefaultKeyIndexerTest() : base() { } + + public string this[int i1, int i2 = 2] { + get { + string key = i1.ToString() + i2.ToString(); + return (string)t[key]; + } + set { + string key = i1.ToString() + i2.ToString(); + t[key] = value; + } + } + + } + diff --git a/src/testing/methodtest.cs b/src/testing/methodtest.cs index 086aa58d5..8bda5215f 100644 --- a/src/testing/methodtest.cs +++ b/src/testing/methodtest.cs @@ -155,6 +155,16 @@ public static void TestVoidSingleRefParam (ref int i) { i = 42; } + public static int TestSingleDefaultParam(int i = 5) { + return i; + } + public static int TestTwoDefaultParam(int i = 5, int j = 6) { + return i + j; + } + public static int TestOneArgAndTwoDefaultParam(int z, int i = 5, int j = 6) { + return i + j + z; + } + // overload selection test support diff --git a/src/tests/test_indexer.py b/src/tests/test_indexer.py index cb572e3a8..691ebb871 100644 --- a/src/tests/test_indexer.py +++ b/src/tests/test_indexer.py @@ -8,6 +8,8 @@ # =========================================================================== import sys, os, string, unittest, types +import clr +clr.AddReference("Python.Test") import Python.Test as Test import six @@ -630,6 +632,17 @@ def test(): object[0, 1, spam] = "wrong" self.assertRaises(TypeError, test) + + + def testMultiDefaultKeyIndexer(self): + """Test indexers that take multiple indices with a default key arguments.""" + #default argument is 2 in the MultiDefaultKeyIndexerTest object + object = Test.MultiDefaultKeyIndexerTest() + object[0, 2] = "zero one spam" + self.assertTrue(object[0] == "zero one spam") + + object[1] = "one nine spam" + self.assertTrue(object[1, 2] == "one nine spam") def testIndexerWrongKeyType(self): diff --git a/src/tests/test_method.py b/src/tests/test_method.py index cdfc1e33b..3f9c1fdff 100644 --- a/src/tests/test_method.py +++ b/src/tests/test_method.py @@ -447,6 +447,27 @@ def test(): # None cannot be converted to a value type self.assertRaises(TypeError, test) + + def testSingleDefaultParam(self): + """Test void method with single ref-parameter.""" + result = MethodTest.TestSingleDefaultParam() + self.assertTrue(result == 5) + + def testOneArgAndTwoDefaultParam(self): + """Test void method with single ref-parameter.""" + result = MethodTest.TestOneArgAndTwoDefaultParam(11) + self.assertTrue(result == 22) + + result = MethodTest.TestOneArgAndTwoDefaultParam(15) + self.assertTrue(result == 26) + + result = MethodTest.TestOneArgAndTwoDefaultParam(20) + self.assertTrue(result == 31) + + def testTwoDefaultParam(self): + """Test void method with single ref-parameter.""" + result = MethodTest.TestTwoDefaultParam() + self.assertTrue(result == 11) def testExplicitSelectionWithOutModifier(self):