8000 Feature/named arg support by s4v4g3 · Pull Request #953 · pythonnet/pythonnet · GitHub
[go: up one dir, main page]

Skip to content

Feature/named arg support #953

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 6 commits into from
Sep 12, 2019
Merged
Changes from 1 commit
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
Next Next commit
Added support for named arguments (#849)
  • Loading branch information
joesavage-silabs committed Sep 10, 2019
commit a7963e1eb400d9e4df581565c432c9237dbd9e07
98 changes: 78 additions & 20 deletions src/runtime/methodbinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
using System.Collections;
using System.Reflection;
using System.Text;
using System.Collections.Generic;
using System.Linq;

namespace Python.Runtime
{
Expand Down Expand Up @@ -280,6 +282,22 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, Meth
{
// loop to find match, return invoker w/ or /wo error
MethodBase[] _methods = null;

var kwargDict = new Dictionary<string, IntPtr>();
if (kw != IntPtr.Zero)
{
var pynkwargs = (int)Runtime.PyDict_Size(kw);
IntPtr keylist = Runtime.PyDict_Keys(kw);
IntPtr valueList = Runtime.PyDict_Values(kw);
for (int i = 0; i < pynkwargs; ++i)
{
var keyStr = Runtime.GetManagedString(Runtime.PyList_GetItem(keylist, i));
kwargDict[keyStr] = Runtime.PyList_GetItem(valueList, i);
}
Runtime.XDecref(keylist);
Runtime.XDecref(valueList);
}

var pynargs = (int)Runtime.PyTuple_Size(args);
var isGeneric = false;
if (info != null)
Expand All @@ -303,11 +321,12 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, Meth
ArrayList defaultArgList;
bool paramsArray;

if (!MatchesArgumentCount(pynargs, pi, out paramsArray, out defaultArgList)) {
if (!MatchesArgumentCount(pynargs, pi, kwargDict, out paramsArray, out defaultArgList))
{
continue;
}
var outs = 0;
var margs = TryConvertArguments(pi, paramsArray, args, pynargs, defaultArgList,
var margs = TryConvertArguments(pi, paramsArray, args, pynargs, kwargDict, defaultArgList,
needsResolution: _methods.Length > 1,
outs: out outs);

Expand Down Expand Up @@ -351,19 +370,21 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, Meth
}

/// <summary>
/// Attempts to convert Python argument tuple into an array of managed objects,
/// that can be passed to a method.
/// Attempts to convert Python positional argument tuple and keyword argument table
/// into an array of managed objects, that can be passed to a method.
/// </summary>
/// <param name="pi">Information about expected parameters</param>
/// <param name="paramsArray"><c>true</c>, if the last parameter is a params array.</param>
/// <param name="args">A pointer to the Python argument tuple</param>
/// <param name="pyArgCount">Number of arguments, passed by Python</param>
/// <param name="kwargDict">Dictionary of keyword argument name to python object pointer</param>
/// <param name="defaultArgList">A list of default values for omitted parameters</param>
/// <param name="needsResolution"><c>true</c>, if overloading resolution is required</param>
/// <param name="outs">Returns number of output parameters</param>
/// <returns>An array of .NET arguments, that can be passed to a method.</returns>
static object[] TryConvertArguments(ParameterInfo[] pi, bool paramsArray,
IntPtr args, int pyArgCount,
Dictionary<string, IntPtr> kwargDict,
ArrayList defaultArgList,
bool needsResolution,
out int outs)
Expand All @@ -374,7 +395,10 @@ static object[] TryConvertArguments(ParameterInfo[] pi, bool paramsArray,

for (int paramIndex = 0; paramIndex < pi.Length; paramIndex++)
{
if (paramIndex >= pyArgCount)
var parameter = pi[paramIndex];
bool hasNamedParam = kwargDict.ContainsKey(parameter.Name);

if (paramIndex >= pyArgCount && !hasNamedParam)
{
if (defaultArgList != null)
{
Expand All @@ -384,12 +408,19 @@ static object[] TryConvertArguments(ParameterInfo[] pi, bool paramsArray,
continue;
}

var parameter = pi[paramIndex];
IntPtr 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);
IntPtr op;
if (hasNamedParam)
{
op = kwargDict[parameter.Name];
}
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);
}

bool isOut;
if (!TryConvertArgument(op, parameter.ParameterType, needsResolution, out margs[paramIndex], out isOut))
Expand Down Expand Up @@ -505,29 +536,56 @@ static Type TryComputeClrArgumentType(Type parameterType, IntPtr argument, bool
return clrtype;
}

static bool MatchesArgumentCount(int argumentCount, ParameterInfo[] parameters,
static bool MatchesArgumentCount(int positionalArgumentCount, ParameterInfo[] parameters,
Dictionary<string, IntPtr> kwargDict,
out bool paramsArray,
out ArrayList defaultArgList)
{
defaultArgList = null;
var match = false;
paramsArray = false;

if (argumentCount == parameters.Length)
// check to make sure that all kwarg names match real parameter names
var paramNames = parameters.Select(p => p.Name).ToArray();
if (kwargDict.Keys.Any(key => !paramNames.Contains(key)))
{
return false;
}

if (positionalArgumentCount == parameters.Length)
{
match = true;
} else if (argumentCount < parameters.Length)
}
else if (positionalArgumentCount < parameters.Length)
{
// every parameter past 'positionalArgumentCount' must have either
// a corresponding keyword argument or a default parameter
match = true;
defaultArgList = new ArrayList();
for (var v = argumentCount; v < parameters.Length; v++) {
if (parameters[v].DefaultValue == DBNull.Value) {
match = false;
} else {
defaultArgList.Add(parameters[v].DefaultValue);
for (var v = positionalArgumentCount; v < parameters.Length; v++)
{
if (kwargDict.ContainsKey(parameters[v].Name))
{
// we have a keyword argument for this parameter,
// no need to check for a default parameter, but put a null
// placeholder in defaultArgList
defaultArgList.Add(null);
}
else
{
// find default parameter
if (parameters[v].DefaultValue == DBNull.Value)
{
match = false;
}
else
{
defaultArgList.Add(parameters[v].DefaultValue);
}
}
}
} else if (argumentCount > parameters.Length && parameters.Length > 0 &&
}
else if (positionalArgumentCount > parameters.Length && parameters.Length > 0 &&
Attribute.IsDefined(parameters[parameters.Length - 1], typeof(ParamArrayAttribute)))
{
// This is a `foo(params object[] bar)` style method
Expand Down
0