| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -25,6 +25,7 @@ details about the cause of the failure | |||
| 25 | 25 | ||
| 26 | 26 | - Fix incorrect dereference of wrapper object in `tp_repr`, which may result in a program crash | |
| 27 | 27 | - Fix incorrect dereference in params array handling | |
| 28 | + - Fixes issue with function resolution when calling overloaded function with keyword arguments from python ([#1097][i1097]) | ||
| 28 | 29 | - Fix `object[]` parameters taking precedence when should not in overload resolution | |
| 29 | 30 | - Fixed a bug where all .NET class instances were considered Iterable | |
| 30 | 31 | - Fix incorrect choice of method to invoke when using keyword arguments. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -279,6 +279,23 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info) | |||
| 279 | 279 | return Bind(inst, args, kw, info, null); | |
| 280 | 280 | } | |
| 281 | 281 | ||
| 282 | + 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 | + | ||
| 282 | 299 | internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, MethodInfo[] methodinfo) | |
| 283 | 300 | { | |
| 284 | 301 | // 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 | |||
| 311 | 328 | _methods = GetMethods(); | |
| 312 | 329 | } | |
| 313 | 330 | ||
| 331 | + var argMatchedMethods = new List<MatchedMethod>(_methods.Length); | ||
| 332 | + | ||
| 314 | 333 | // TODO: Clean up | |
| 315 | 334 | foreach (MethodBase mi in _methods) | |
| 316 | 335 | { | |
@@ -321,8 +340,10 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, Meth | |||
| 321 | 340 | ParameterInfo[] pi = mi.GetParameters(); | |
| 322 | 341 | ArrayList defaultArgList; | |
| 323 | 342 | bool paramsArray; | |
| 343 | + int kwargsMatched; | ||
| 344 | + int defaultsNeeded; | ||
| 324 | 345 | ||
| 325 | - if (!MatchesArgumentCount(pynargs, pi, kwargDict, out paramsArray, out defaultArgList)) | ||
| 346 | + if (!MatchesArgumentCount(pynargs, pi, kwargDict, out paramsArray, out defaultArgList, out kwargsMatched, out defaultsNeeded)) | ||
| 326 | 347 | { | |
| 327 | 348 | continue; | |
| 328 | 349 | } | |
@@ -336,6 +357,47 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, Meth | |||
| 336 | 357 | continue; | |
| 337 | 358 | } | |
| 338 | 359 | ||
| 360 | + 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 | + | ||
| 339 | 401 | object target = null; | |
| 340 | 402 | if (!mi.IsStatic && inst != IntPtr.Zero) | |
| 341 | 403 | { | |
@@ -575,11 +637,16 @@ static Type TryComputeClrArgumentType(Type parameterType, IntPtr argument, bool | |||
| 575 | 637 | static bool MatchesArgumentCount(int positionalArgumentCount, ParameterInfo[] parameters, | |
| 576 | 638 | Dictionary<string, IntPtr> kwargDict, | |
| 577 | 639 | out bool paramsArray, | |
| 578 | - out ArrayList defaultArgList) | ||
| 640 | + out ArrayList defaultArgList, | ||
| 641 | + out int kwargsMatched, | ||
| 642 | + out int defaultsNeeded) | ||
| 579 | 643 | { | |
| 580 | 644 | defaultArgList = null; | |
| 581 | 645 | var match = false; | |
| 582 | 646 | paramsArray = parameters.Length > 0 ? Attribute.IsDefined(parameters[parameters.Length - 1], typeof(ParamArrayAttribute)) : false; | |
| 647 | + var kwargCount = kwargDict.Count; | ||
| 648 | + kwargsMatched = 0; | ||
| 649 | + defaultsNeeded = 0; | ||
| 583 | 650 | ||
| 584 | 651 | if (positionalArgumentCount == parameters.Length && kwargDict.Count == 0) | |
| 585 | 652 | { | |
@@ -599,6 +666,7 @@ static bool MatchesArgumentCount(int positionalArgumentCount, ParameterInfo[] pa | |||
| 599 | 666 | // no need to check for a default parameter, but put a null | |
| 600 | 667 | // placeholder in defaultArgList | |
| 601 | 668 | defaultArgList.Add(null); | |
| 669 | + kwargsMatched++; | ||
| 602 | 670 | } | |
| 603 | 671 | else if (parameters[v].IsOptional) | |
| 604 | 672 | { | |
@@ -607,6 +675,7 @@ static bool MatchesArgumentCount(int positionalArgumentCount, ParameterInfo[] pa | |||
| 607 | 675 | // The GetDefaultValue() extension method will return the value | |
| 608 | 676 | // to be passed in as the parameter value | |
| 609 | 677 | defaultArgList.Add(parameters[v].GetDefaultValue()); | |
| 678 | + defaultsNeeded++; | ||
| 610 | 679 | } | |
| 611 | 680 | else if(!paramsArray) | |
| 612 | 681 | { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -684,7 +684,25 @@ public static string OptionalAndDefaultParams2([Optional]int a, [Optional]int b, | |||
| 684 | 684 | return string.Format("{0}{1}{2}{3}", a, b, c, d); | |
| 685 | 685 | } | |
| 686 | 686 | ||
| 687 | - | ||
| 687 | + public static string DefaultParamsWithOverloading(int a = 2, int b = 1) | ||
| 688 | + { | ||
| 689 | + return $"{a}{b}"; | ||
| 690 | + } | ||
| 691 | + | ||
| 692 | + public static string DefaultParamsWithOverloading(string a = "a", string b = "b") | ||
| 693 | + { | ||
| 694 | + return $"{a}{b}X"; | ||
| 695 | + } | ||
| 696 | + | ||
| 697 | + public static string DefaultParamsWithOverloading(int a = 0, int b = 1, int c = 2) | ||
| 698 | + { | ||
| 699 | + return $"{a}{b}{c}XX"; | ||
| 700 | + } | ||
| 701 | + | ||
| 702 | + public static string DefaultParamsWithOverloading(int a = 5, int b = 6, int c = 7, int d = 8) | ||
| 703 | + { | ||
| 704 | + return $"{a}{b}{c}{d}XXX"; | ||
| 705 | + } | ||
| 688 | 706 | } | |
| 689 | 707 | ||
| 690 | 708 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1153,6 +1153,35 @@ def test_optional_and_default_params(): | |||
| 1153 | 1153 | res = MethodTest.OptionalAndDefaultParams2(b=2, c=3) | |
| 1154 | 1154 | assert res == "0232" | |
| 1155 | 1155 | ||
| 1156 | + def test_default_params_overloads(): | ||
| 1157 | + res = MethodTest.DefaultParamsWithOverloading(1, 2) | ||
| 1158 | + assert res == "12" | ||
| 1159 | + | ||
| 1160 | + res = MethodTest.DefaultParamsWithOverloading(b=5) | ||
| 1161 | + assert res == "25" | ||
| 1162 | + | ||
| 1163 | + res = MethodTest.DefaultParamsWithOverloading("d") | ||
| 1164 | + assert res == "dbX" | ||
| 1165 | + | ||
| 1166 | + res = MethodTest.DefaultParamsWithOverloading(b="c") | ||
| 1167 | + assert res == "acX" | ||
| 1168 | + | ||
| 1169 | + res = MethodTest.DefaultParamsWithOverloading(c=3) | ||
| 1170 | + assert res == "013XX" | ||
| 1171 | + | ||
| 1172 | + res = MethodTest.DefaultParamsWithOverloading(5, c=2) | ||
| 1173 | + assert res == "512XX" | ||
| 1174 | + | ||
| 1175 | + res = MethodTest.DefaultParamsWithOverloading(c=0, d=1) | ||
| 1176 | + assert res == "5601XXX" | ||
| 1177 | + | ||
| 1178 | + res = MethodTest.DefaultParamsWithOverloading(1, d=1) | ||
| 1179 | + assert res == "1671XXX" | ||
| 1180 | + | ||
| 1181 | + def test_default_params_overloads_ambiguous_call(): | ||
| 1182 | + with pytest.raises(TypeError): | ||
| 1183 | + MethodTest.DefaultParamsWithOverloading() | ||
| 1184 | + | ||
| 1156 | 1185 | def test_keyword_arg_method_resolution(): | |
| 1157 | 1186 | from Python.Test import MethodArityTest | |
| 1158 | 1187 | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments