Fix kwarg func resolution (#1136) · pythonnet/pythonnet@c81c3c3

@@ -279,6 +279,23 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info)

279279

return Bind(inst, args, kw, info, null);

280280

}

281281282+

private readonly struct MatchedMethod {

283+

public MatchedMethod(int kwargsMatched, int defaultsNeeded, object[] margs, int outs, MethodBase mb)

284+

{

285+

KwargsMatched = kwargsMatched;

286+

DefaultsNeeded = defaultsNeeded;

287+

ManagedArgs = margs;

288+

Outs = outs;

289+

Method = mb;

290+

}

291+292+

public int KwargsMatched { get; }

293+

public int DefaultsNeeded { get; }

294+

public object[] ManagedArgs { get; }

295+

public int Outs { get; }

296+

public MethodBase Method { get; }

297+

}

298+282299

internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, MethodInfo[] methodinfo)

283300

{

284301

// loop to find match, return invoker w/ or /wo error

@@ -311,6 +328,8 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, Meth

311328

_methods = GetMethods();

312329

}

313330331+

var argMatchedMethods = new List<MatchedMethod>(_methods.Length);

332+314333

// TODO: Clean up

315334

foreach (MethodBase mi in _methods)

316335

{

@@ -321,8 +340,10 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, Meth

321340

ParameterInfo[] pi = mi.GetParameters();

322341

ArrayList defaultArgList;

323342

bool paramsArray;

343+

int kwargsMatched;

344+

int defaultsNeeded;

324345325-

if (!MatchesArgumentCount(pynargs, pi, kwargDict, out paramsArray, out defaultArgList))

346+

if (!MatchesArgumentCount(pynargs, pi, kwargDict, out paramsArray, out defaultArgList, out kwargsMatched, out defaultsNeeded))

326347

{

327348

continue;

328349

}

@@ -336,6 +357,47 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, Meth

336357

continue;

337358

}

338359360+

var matchedMethod = new MatchedMethod(kwargsMatched, defaultsNeeded, margs, outs, mi);

361+

argMatchedMethods.Add(matchedMethod);

362+

}

363+

if (argMatchedMethods.Count > 0)

364+

{

365+

var bestKwargMatchCount = argMatchedMethods.Max(x => x.KwargsMatched);

366+

var fewestDefaultsRequired = argMatchedMethods.Where(x => x.KwargsMatched == bestKwargMatchCount).Min(x => x.DefaultsNeeded);

367+368+

int bestCount = 0;

369+

int bestMatchIndex = -1;

370+371+

for (int index = 0; index < argMatchedMethods.Count; index++)

372+

{

373+

var testMatch = argMatchedMethods[index];

374+

if (testMatch.DefaultsNeeded == fewestDefaultsRequired && testMatch.KwargsMatched == bestKwargMatchCount)

375+

{

376+

bestCount++;

377+

if (bestMatchIndex == -1)

378+

bestMatchIndex = index;

379+

}

380+

}

381+382+

if (bestCount > 1 && fewestDefaultsRequired > 0)

383+

{

384+

// Best effort for determining method to match on gives multiple possible

385+

// matches and we need at least one default argument - bail from this point

386+

return null;

387+

}

388+389+

// If we're here either:

390+

// (a) There is only one best match

391+

// (b) There are multiple best matches but none of them require

392+

// default arguments

393+

// in the case of (a) we're done by default. For (b) regardless of which

394+

// method we choose, all arguments are specified _and_ can be converted

395+

// from python to C# so picking any will suffice

396+

MatchedMethod bestMatch = argMatchedMethods[bestMatchIndex];

397+

var margs = bestMatch.ManagedArgs;

398+

var outs = bestMatch.Outs;

399+

var mi = bestMatch.Method;

400+339401

object target = null;

340402

if (!mi.IsStatic && inst != IntPtr.Zero)

341403

{

@@ -575,11 +637,16 @@ static Type TryComputeClrArgumentType(Type parameterType, IntPtr argument, bool

575637

static bool MatchesArgumentCount(int positionalArgumentCount, ParameterInfo[] parameters,

576638

Dictionary<string, IntPtr> kwargDict,

577639

out bool paramsArray,

578-

out ArrayList defaultArgList)

640+

out ArrayList defaultArgList,

641+

out int kwargsMatched,

642+

out int defaultsNeeded)

579643

{

580644

defaultArgList = null;

581645

var match = false;

582646

paramsArray = parameters.Length > 0 ? Attribute.IsDefined(parameters[parameters.Length - 1], typeof(ParamArrayAttribute)) : false;

647+

var kwargCount = kwargDict.Count;

648+

kwargsMatched = 0;

649+

defaultsNeeded = 0;

583650584651

if (positionalArgumentCount == parameters.Length && kwargDict.Count == 0)

585652

{

@@ -599,6 +666,7 @@ static bool MatchesArgumentCount(int positionalArgumentCount, ParameterInfo[] pa

599666

// no need to check for a default parameter, but put a null

600667

// placeholder in defaultArgList

601668

defaultArgList.Add(null);

669+

kwargsMatched++;

602670

}

603671

else if (parameters[v].IsOptional)

604672

{

@@ -607,6 +675,7 @@ static bool MatchesArgumentCount(int positionalArgumentCount, ParameterInfo[] pa

607675

// The GetDefaultValue() extension method will return the value

608676

// to be passed in as the parameter value

609677

defaultArgList.Add(parameters[v].GetDefaultValue());

678+

defaultsNeeded++;

610679

}

611680

else if(!paramsArray)

612681

{