Skip to content

Method Default Arguments #87

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Aug 4, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions src/runtime/classobject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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);

Expand Down
52 changes: 51 additions & 1 deletion src/runtime/indexer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

/// <summary>
/// This will return default arguments a new instance of a tuple. The size
/// of the tuple will indicate the number of default arguments.
/// </summary>
/// <param name="args">This is pointing to the tuple args passed in</param>
/// <returns>a new instance of the tuple containing the default args</returns>
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;
}


}

}
71 changes: 47 additions & 24 deletions src/runtime/methodbinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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) {
Expand Down
17 changes: 17 additions & 0 deletions src/testing/indexertest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}

}




Expand Down
10 changes: 10 additions & 0 deletions src/testing/methodtest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
13 changes: 13 additions & 0 deletions src/tests/test_indexer.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
# ===========================================================================

import sys, os, string, unittest, types
import clr
clr.AddReference("Python.Test")
import Python.Test as Test
import six

Expand Down Expand Up @@ -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):
Expand Down
21 changes: 21 additions & 0 deletions src/tests/test_method.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down