Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
3 changes: 2 additions & 1 deletion 3 CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
together with Nuitka
- Fixes bug where delegates get casts (dotnetcore)
- Determine size of interpreter longs at runtime
- Handling exceptions ocurred in ModuleObject's getattribute
- Handling exceptions ocurred in ModuleObject's getattribute
- Fill `__classcell__` correctly for Python subclasses of .NET types
- Fixed issue with params methods that are not passed an array.

## [2.4.0][]

Expand Down
57 changes: 48 additions & 9 deletions 57 src/runtime/methodbinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,41 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, Meth
return null;
}

static IntPtr HandleParamsArray(IntPtr args, int arrayStart, int pyArgCount, out bool isNewReference)
{
isNewReference = false;
IntPtr op;
// for a params method, we may have a sequence or single/multiple items
// here we look to see if the item at the paramIndex is there or not
// and then if it is a sequence itself.
if ((pyArgCount - arrayStart) == 1)
{
// we only have one argument left, so we need to check it
// to see if it is a sequence or a single item
IntPtr item = Runtime.PyTuple_GetItem(args, arrayStart);
if (!Runtime.PyString_Check(item) && Runtime.PySequence_Check(item))
{
// it's a sequence (and not a string), so we use it as the op
op = item;
}
else
{
isNewReference = true;
op = Runtime.PyTuple_GetSlice(args, arrayStart, pyArgCount);
if (item != IntPtr.Zero)
{
Runtime.XDecref(item);
}
}
}
else
{
isNewReference = true;
op = Runtime.PyTuple_GetSlice(args, arrayStart, pyArgCount);
}
return op;
}

/// <summary>
/// Attempts to convert Python positional argument tuple and keyword argument table
/// into an array of managed objects, that can be passed to a method.
Expand Down Expand Up @@ -397,8 +432,9 @@ static object[] TryConvertArguments(ParameterInfo[] pi, bool paramsArray,
{
var parameter = pi[paramIndex];
bool hasNamedParam = kwargDict.ContainsKey(parameter.Name);
bool isNewReference = false;

if (paramIndex >= pyArgCount && !hasNamedParam)
if (paramIndex >= pyArgCount && !(hasNamedParam || (paramsArray && paramIndex == arrayStart)))
{
if (defaultArgList != null)
{
Expand All @@ -415,11 +451,14 @@ static object[] TryConvertArguments(ParameterInfo[] pi, bool paramsArray,
}
else
{
op = (arrayStart == paramIndex)
// map remaining Python arguments to a tuple since
// the managed function accepts it - hopefully :]
? Runtime.PyTuple_GetSlice(args, arrayStart, pyArgCount)
: Runtime.PyTuple_GetItem(args, paramIndex);
if(arrayStart == paramIndex)
{
op = HandleParamsArray(args, arrayStart, pyArgCount, out isNewReference);
}
else
{
op = Runtime.PyTuple_GetItem(args, paramIndex);
}
}

bool isOut;
Expand All @@ -428,7 +467,7 @@ static object[] TryConvertArguments(ParameterInfo[] pi, bool paramsArray,
return null;
}

if (arrayStart == paramIndex)
if (isNewReference)
{
// TODO: is this a bug? Should this happen even if the conversion fails?
// GetSlice() creates a new reference but GetItem()
Expand Down Expand Up @@ -543,7 +582,7 @@ static bool MatchesArgumentCount(int positionalArgumentCount, ParameterInfo[] pa
{
defaultArgList = null;
var match = false;
paramsArray = false;
paramsArray = parameters.Length > 0 ? Attribute.IsDefined(parameters[parameters.Length - 1], typeof(ParamArrayAttribute)) : false;

if (positionalArgumentCount == parameters.Length)
{
Expand Down Expand Up @@ -572,7 +611,7 @@ static bool MatchesArgumentCount(int positionalArgumentCount, ParameterInfo[] pa
// to be passed in as the parameter value
defaultArgList.Add(parameters[v].GetDefaultValue());
}
else
else if(!paramsArray)
{
match = false;
}
Expand Down
31 changes: 31 additions & 0 deletions 31 src/tests/test_method.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,37 @@ def test_non_params_array_in_last_place():
result = MethodTest.TestNonParamsArrayInLastPlace(1, 2, 3)
assert result

def test_params_methods_with_no_params():
"""Tests that passing no arguments to a params method
passes an empty array"""
result = MethodTest.TestValueParamsArg()
assert len(result) == 0

result = MethodTest.TestOneArgWithParams('Some String')
assert len(result) == 0

result = MethodTest.TestTwoArgWithParams('Some String', 'Some Other String')
assert len(result) == 0

def test_params_methods_with_non_lists():
"""Tests that passing single parameters to a params
method will convert into an array on the .NET side"""
result = MethodTest.TestOneArgWithParams('Some String', [1, 2, 3, 4])
assert len(result) == 4

result = MethodTest.TestOneArgWithParams('Some String', 1, 2, 3, 4)
assert len(result) == 4

result = MethodTest.TestOneArgWithParams('Some String', [5])
assert len(result) == 1

result = MethodTest.TestOneArgWithParams('Some String', 5)
assert len(result) == 1

def test_params_method_with_lists():
"""Tests passing multiple lists to a params object[] method"""
result = MethodTest.TestObjectParamsArg([],[])
assert len(result) == 2

def test_string_out_params():
"""Test use of string out-parameters."""
Expand Down
Morty Proxy This is a proxified and sanitized view of the page, visit original site.