Skip to content

Commit d7f227e

Browse files
committed
Merge pull request #87 from fdanny/develop
Method Default Arguments
2 parents 201f16f + 20d81f1 commit d7f227e

File tree

7 files changed

+176
-27
lines changed

7 files changed

+176
-27
lines changed

src/runtime/classobject.cs

+17-2
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,6 @@ public static int mp_ass_subscript(IntPtr ob, IntPtr idx, IntPtr v) {
223223
// Arg may be a tuple in the case of an indexer with multiple
224224
// parameters. If so, use it directly, else make a new tuple
225225
// with the index arg (method binders expect arg tuples).
226-
227226
IntPtr args = idx;
228227
bool free = false;
229228

@@ -234,13 +233,29 @@ public static int mp_ass_subscript(IntPtr ob, IntPtr idx, IntPtr v) {
234233
free = true;
235234
}
236235

236+
// Get the args passed in.
237237
int i = Runtime.PyTuple_Size(args);
238-
IntPtr real = Runtime.PyTuple_New(i + 1);
238+
IntPtr defaultArgs = cls.indexer.GetDefaultArgs(args);
239+
int numOfDefaultArgs = Runtime.PyTuple_Size(defaultArgs);
240+
int temp = i + numOfDefaultArgs;
241+
IntPtr real = Runtime.PyTuple_New(temp + 1);
239242
for (int n = 0; n < i; n++) {
240243
IntPtr item = Runtime.PyTuple_GetItem(args, n);
241244
Runtime.Incref(item);
242245
Runtime.PyTuple_SetItem(real, n, item);
243246
}
247+
248+
// Add Default Args if needed
249+
for (int n = 0; n < numOfDefaultArgs; n++) {
250+
IntPtr item = Runtime.PyTuple_GetItem(defaultArgs, n);
251+
Runtime.Incref(item);
252+
Runtime.PyTuple_SetItem(real, n + i, item);
253+
}
254+
// no longer need defaultArgs
255+
Runtime.Decref(defaultArgs);
256+
i = temp;
257+
258+
// Add value to argument list
244259
Runtime.Incref(v);
245260
Runtime.PyTuple_SetItem(real, i, v);
246261

src/runtime/indexer.cs

+51-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,57 @@ internal void SetItem(IntPtr inst, IntPtr args) {
6262
SetterBinder.Invoke(inst, args, IntPtr.Zero);
6363
}
6464

65-
}
65+
internal bool NeedsDefaultArgs(IntPtr args){
66+
int pynargs = Runtime.PyTuple_Size(args);
67+
var methods = SetterBinder.GetMethods();
68+
if(methods.Length == 0)
69+
return false;
70+
71+
MethodBase mi = methods[0];
72+
ParameterInfo[] pi = mi.GetParameters();
73+
// need to subtract one for the value
74+
int clrnargs = pi.Length - 1;
75+
if (pynargs == clrnargs || pynargs > clrnargs)
76+
return false;
77+
78+
for (int v = pynargs; v < clrnargs; v++){
79+
if (pi[v].DefaultValue == DBNull.Value)
80+
return false;
81+
}
82+
return true;
83+
}
84+
85+
/// <summary>
86+
/// This will return default arguments a new instance of a tuple. The size
87+
/// of the tuple will indicate the number of default arguments.
88+
/// </summary>
89+
/// <param name="args">This is pointing to the tuple args passed in</param>
90+
/// <returns>a new instance of the tuple containing the default args</returns>
91+
internal IntPtr GetDefaultArgs(IntPtr args){
92+
93+
// if we don't need default args return empty tuple
94+
if(!NeedsDefaultArgs(args))
95+
return Runtime.PyTuple_New(0);
96+
int pynargs = Runtime.PyTuple_Size(args);
97+
98+
// Get the default arg tuple
99+
var methods = SetterBinder.GetMethods();
100+
MethodBase mi = methods[0];
101+
ParameterInfo[] pi = mi.GetParameters();
102+
int clrnargs = pi.Length - 1;
103+
IntPtr defaultArgs = Runtime.PyTuple_New(clrnargs - pynargs);
104+
for (int i = 0; i < (clrnargs - pynargs); i++) {
105+
if (pi[i + pynargs].DefaultValue == DBNull.Value)
106+
continue;
107+
else{
108+
IntPtr arg = Converter.ToPython(pi[i + pynargs].DefaultValue, pi[i + pynargs].ParameterType);
109+
Runtime.PyTuple_SetItem(defaultArgs, i, arg);
110+
}
111+
}
112+
return defaultArgs;
113+
}
66114

67115

116+
}
117+
68118
}

src/runtime/methodbinder.cs

+47-24
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw,
226226
int pynargs = Runtime.PyTuple_Size(args);
227227
object arg;
228228
bool isGeneric = false;
229-
229+
ArrayList defaultArgList = null;
230230
if (info != null) {
231231
_methods = (MethodBase[])Array.CreateInstance(
232232
typeof(MethodBase), 1
@@ -247,7 +247,17 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw,
247247
int outs = 0;
248248

249249
if (pynargs == clrnargs) {
250-
match = true;
250+
match = true;
251+
} else if(pynargs < clrnargs){
252+
match = true;
253+
defaultArgList = new ArrayList();
254+
for (int v = pynargs; v < clrnargs; v++)
255+
{
256+
if (pi[v].DefaultValue == DBNull.Value)
257+
match = false;
258+
else
259+
defaultArgList.Add((object)pi[v].DefaultValue);
260+
}
251261
} else if ((pynargs > clrnargs) && (clrnargs > 0) &&
252262
(pi[clrnargs-1].ParameterType.IsArray)) {
253263
// The last argument of the mananged functions seems to
@@ -262,30 +272,43 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw,
262272

263273
for (int n = 0; n < clrnargs; n++) {
264274
IntPtr op;
265-
if (arrayStart == n) {
266-
// map remaining Python arguments to a tuple since
267-
// the managed function accepts it - hopefully :]
268-
op = Runtime.PyTuple_GetSlice(args, arrayStart, pynargs);
269-
}
270-
else {
271-
op = Runtime.PyTuple_GetItem(args, n);
272-
}
273-
Type type = pi[n].ParameterType;
274-
if (pi[n].IsOut || type.IsByRef) {
275-
outs++;
276-
}
277-
278-
if (!Converter.ToManaged(op, type, out arg, false)) {
279-
Exceptions.Clear();
280-
margs = null;
281-
break;
275+
if (n < pynargs)
276+
{
277+
if (arrayStart == n)
278+
{
279+
// map remaining Python arguments to a tuple since
280+
// the managed function accepts it - hopefully :]
281+
op = Runtime.PyTuple_GetSlice(args, arrayStart, pynargs);
282+
}
283+
else
284+
{
285+
op = Runtime.PyTuple_GetItem(args, n);
286+
}
287+
Type type = pi[n].ParameterType;
288+
if (pi[n].IsOut || type.IsByRef)
289+
{
290+
outs++;
291+
}
292+
293+
if (!Converter.ToManaged(op, type, out arg, false))
294+
{
295+
Exceptions.Clear();
296+
margs = null;
297+
break;
298+
}
299+
if (arrayStart == n)
300+
{
301+
// GetSlice() creates a new reference but GetItem()
302+
// returns only a borrow reference.
303+
Runtime.Decref(op);
304+
}
305+
margs[n] = arg;
282306
}
283-
if (arrayStart == n) {
284-
// GetSlice() creates a new reference but GetItem()
285-
// returns only a borrow reference.
286-
Runtime.Decref(op);
307+
else
308+
{
309+
if (defaultArgList != null)
310+
margs[n] = defaultArgList[n - pynargs];
287311
}
288-
margs[n] = arg;
289312
}
290313

291314
if (margs == null) {

src/testing/indexertest.cs

+17
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,23 @@ public MultiTypeIndexerTest() : base() {}
347347

348348
}
349349

350+
public class MultiDefaultKeyIndexerTest : IndexerBase {
351+
352+
public MultiDefaultKeyIndexerTest() : base() { }
353+
354+
public string this[int i1, int i2 = 2] {
355+
get {
356+
string key = i1.ToString() + i2.ToString();
357+
return (string)t[key];
358+
}
359+
set {
360+
string key = i1.ToString() + i2.ToString();
361+
t[key] = value;
362+
}
363+
}
364+
365+
}
366+
350367

351368

352369

src/testing/methodtest.cs

+10
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,16 @@ public static void TestVoidSingleRefParam (ref int i) {
155155
i = 42;
156156
}
157157

158+
public static int TestSingleDefaultParam(int i = 5) {
159+
return i;
160+
}
161+
public static int TestTwoDefaultParam(int i = 5, int j = 6) {
162+
return i + j;
163+
}
164+
public static int TestOneArgAndTwoDefaultParam(int z, int i = 5, int j = 6) {
165+
return i + j + z;
166+
}
167+
158168

159169

160170
// overload selection test support

src/tests/test_indexer.py

+13
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
# ===========================================================================
99

1010
import sys, os, string, unittest, types
11+
import clr
12+
clr.AddReference("Python.Test")
1113
import Python.Test as Test
1214
import six
1315

@@ -630,6 +632,17 @@ def test():
630632
object[0, 1, spam] = "wrong"
631633

632634
self.assertRaises(TypeError, test)
635+
636+
637+
def testMultiDefaultKeyIndexer(self):
638+
"""Test indexers that take multiple indices with a default key arguments."""
639+
#default argument is 2 in the MultiDefaultKeyIndexerTest object
640+
object = Test.MultiDefaultKeyIndexerTest()
641+
object[0, 2] = "zero one spam"
642+
self.assertTrue(object[0] == "zero one spam")
643+
644+
object[1] = "one nine spam"
645+
self.assertTrue(object[1, 2] == "one nine spam")
633646

634647

635648
def testIndexerWrongKeyType(self):

src/tests/test_method.py

+21
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,27 @@ def test():
447447

448448
# None cannot be converted to a value type
449449
self.assertRaises(TypeError, test)
450+
451+
def testSingleDefaultParam(self):
452+
"""Test void method with single ref-parameter."""
453+
result = MethodTest.TestSingleDefaultParam()
454+
self.assertTrue(result == 5)
455+
456+
def testOneArgAndTwoDefaultParam(self):
457+
"""Test void method with single ref-parameter."""
458+
result = MethodTest.TestOneArgAndTwoDefaultParam(11)
459+
self.assertTrue(result == 22)
460+
461+
result = MethodTest.TestOneArgAndTwoDefaultParam(15)
462+
self.assertTrue(result == 26)
463+
464+
result = MethodTest.TestOneArgAndTwoDefaultParam(20)
465+
self.assertTrue(result == 31)
466+
467+
def testTwoDefaultParam(self):
468+
"""Test void method with single ref-parameter."""
469+
result = MethodTest.TestTwoDefaultParam()
470+
self.assertTrue(result == 11)
450471

451472

452473
def testExplicitSelectionWithOutModifier(self):

0 commit comments

Comments
 (0)