From 30472543c11689a278ce45fec512326e7372b09b Mon Sep 17 00:00:00 2001 From: AlexCatarino Date: Thu, 28 Sep 2017 23:01:37 +0100 Subject: [PATCH 01/12] Implements System.Decimal support Convertes System.Decimal to decimal.decimal and vice-versa --- src/runtime/converter.cs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/runtime/converter.cs b/src/runtime/converter.cs index 5179c849b..17beb5796 100644 --- a/src/runtime/converter.cs +++ b/src/runtime/converter.cs @@ -30,6 +30,7 @@ private Converter() private static Type flagsType; private static Type boolType; private static Type typeType; + private static IntPtr decimalCtor; static Converter() { @@ -45,6 +46,9 @@ static Converter() flagsType = typeof(FlagsAttribute); boolType = typeof(Boolean); typeType = typeof(Type); + + IntPtr decimalMod = Runtime.PyImport_ImportModule("decimal"); + if (decimalMod == null) throw new PythonException(); } @@ -100,6 +104,9 @@ internal static IntPtr GetPythonTypeByAlias(Type op) if (op == boolType) return Runtime.PyBoolType; + if (op == decimalType) + return Runtime.PyFloatType; + return IntPtr.Zero; } @@ -221,6 +228,14 @@ internal static IntPtr ToPython(object value, Type type) case TypeCode.UInt64: return Runtime.PyLong_FromUnsignedLongLong((ulong)value); + case TypeCode.Decimal: + string d2s = ((decimal)value).ToString(nfi); + IntPtr d2p = Runtime.PyString_FromString(d2s); + IntPtr decimalArgs = Runtime.PyTuple_New(1); + Runtime.PyTuple_SetItem(decimalArgs, 0, d2p); + + return Runtime.PyObject_CallObject(decimalCtor, decimalArgs); + default: if (value is IEnumerable) { @@ -793,6 +808,18 @@ private static bool ToPrimitive(IntPtr value, Type obType, out object result, bo Runtime.XDecref(op); result = d; return true; + + case TypeCode.Decimal: + op = Runtime.PyObject_Str(value); + decimal m; + string sm = Runtime.GetManagedString(op); + if (!Decimal.TryParse(sm, NumberStyles.Number, nfi, out m)) + { + goto type_error; + } + Runtime.XDecref(op); + result = m; + return true; } From c6fb04ba53331420c38eb384a419a78b1d168b6e Mon Sep 17 00:00:00 2001 From: AlexCatarino Date: Thu, 28 Sep 2017 23:17:41 +0100 Subject: [PATCH 02/12] Implements System.DateTime support Converts System.DateTime and System.TimeSpan to datetime.datetime and datetime.timedelta and vice-versa --- src/runtime/converter.cs | 102 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/src/runtime/converter.cs b/src/runtime/converter.cs index 17beb5796..27de302e3 100644 --- a/src/runtime/converter.cs +++ b/src/runtime/converter.cs @@ -31,6 +31,9 @@ private Converter() private static Type boolType; private static Type typeType; private static IntPtr decimalCtor; + private static IntPtr dateTimeCtor; + private static IntPtr timeSpanCtor; + private static IntPtr tzInfoCtor; static Converter() { @@ -49,6 +52,34 @@ static Converter() IntPtr decimalMod = Runtime.PyImport_ImportModule("decimal"); if (decimalMod == null) throw new PythonException(); + + IntPtr dateTimeMod = Runtime.PyImport_ImportModule("datetime"); + if (dateTimeMod == null) throw new PythonException(); + + decimalCtor = Runtime.PyObject_GetAttrString(decimalMod, "Decimal"); + if (decimalCtor == null) throw new PythonException(); + + dateTimeCtor = Runtime.PyObject_GetAttrString(dateTimeMod, "datetime"); + if (dateTimeCtor == null) throw new PythonException(); + + timeSpanCtor = Runtime.PyObject_GetAttrString(dateTimeMod, "timedelta"); + if (timeSpanCtor == null) throw new PythonException(); + + IntPtr tzInfoMod = PythonEngine.ModuleFromString("custom_tzinfo", + "from datetime import timedelta, tzinfo\n" + + "class GMT(tzinfo):\n" + + " def __init__(self, hours, minutes):\n" + + " self.hours = hours\n" + + " self.minutes = minutes\n" + + " def utcoffset(self, dt):\n" + + " return timedelta(hours=self.hours, minutes=self.minutes)\n" + + " def tzname(self, dt):\n" + + " return \"GMT {0:00}:{1:00}\".format(self.hours, self.minutes)\n" + + " def dst (self, dt):\n" + + " return timedelta(0)\n").Handle; + + tzInfoCtor = Runtime.PyObject_GetAttrString(tzInfoMod, "GMT"); + if (tzInfoCtor == null) throw new PythonException(); } @@ -176,6 +207,14 @@ internal static IntPtr ToPython(object value, Type type) switch (tc) { case TypeCode.Object: + if (value is TimeSpan) + { + var timespan = (TimeSpan)value; + + IntPtr timeSpanArgs = Runtime.PyTuple_New(1); + Runtime.PyTuple_SetItem(timeSpanArgs, 0, Runtime.PyFloat_FromDouble(timespan.TotalDays)); + return Runtime.PyObject_CallObject(timeSpanCtor, timeSpanArgs); + } return CLRObject.GetInstHandle(value, type); case TypeCode.String: @@ -236,6 +275,21 @@ internal static IntPtr ToPython(object value, Type type) return Runtime.PyObject_CallObject(decimalCtor, decimalArgs); + case TypeCode.DateTime: + var datetime = (DateTime)value; + + IntPtr dateTimeArgs = Runtime.PyTuple_New(8); + Runtime.PyTuple_SetItem(dateTimeArgs, 0, Runtime.PyInt_FromInt32(datetime.Year)); + Runtime.PyTuple_SetItem(dateTimeArgs, 1, Runtime.PyInt_FromInt32(datetime.Month)); + Runtime.PyTuple_SetItem(dateTimeArgs, 2, Runtime.PyInt_FromInt32(datetime.Day)); + Runtime.PyTuple_SetItem(dateTimeArgs, 3, Runtime.PyInt_FromInt32(datetime.Hour)); + Runtime.PyTuple_SetItem(dateTimeArgs, 4, Runtime.PyInt_FromInt32(datetime.Minute)); + Runtime.PyTuple_SetItem(dateTimeArgs, 5, Runtime.PyInt_FromInt32(datetime.Second)); + Runtime.PyTuple_SetItem(dateTimeArgs, 6, Runtime.PyInt_FromInt32(datetime.Millisecond)); + Runtime.PyTuple_SetItem(dateTimeArgs, 7, TzInfo(datetime.Kind)); + + return Runtime.PyObject_CallObject(dateTimeCtor, dateTimeArgs); + default: if (value is IEnumerable) { @@ -257,6 +311,16 @@ internal static IntPtr ToPython(object value, Type type) } } + private static IntPtr TzInfo(DateTimeKind kind) + { + if (kind == DateTimeKind.Unspecified) return Runtime.PyNone; + var offset = kind == DateTimeKind.Local ? DateTimeOffset.Now.Offset : TimeSpan.Zero; + IntPtr tzInfoArgs = Runtime.PyTuple_New(2); + Runtime.PyTuple_SetItem(tzInfoArgs, 0, Runtime.PyFloat_FromDouble(offset.Hours)); + Runtime.PyTuple_SetItem(tzInfoArgs, 1, Runtime.PyFloat_FromDouble(offset.Minutes)); + return Runtime.PyObject_CallObject(tzInfoCtor, tzInfoArgs); + } + /// /// In a few situations, we don't have any advisory type information @@ -459,6 +523,32 @@ private static bool ToPrimitive(IntPtr value, Type obType, out object result, bo switch (tc) { + case TypeCode.Object: + if (obType == typeof(TimeSpan)) + { + op = Runtime.PyObject_Str(value); + TimeSpan ts; + var arr = Runtime.GetManagedString(op).Split(','); + string sts = arr.Length == 1 ? arr[0] : arr[1]; + if (!TimeSpan.TryParse(sts, out ts)) + { + goto type_error; + } + Runtime.XDecref(op); + + int days = 0; + if (arr.Length > 1) + { + if (!int.TryParse(arr[0].Split(' ')[0].Trim(), out days)) + { + goto type_error; + } + } + result = ts.Add(TimeSpan.FromDays(days)); + return true; + } + break; + case TypeCode.String: string st = Runtime.GetManagedString(value); if (st == null) @@ -820,6 +910,18 @@ private static bool ToPrimitive(IntPtr value, Type obType, out object result, bo Runtime.XDecref(op); result = m; return true; + + case TypeCode.DateTime: + op = Runtime.PyObject_Str(value); + DateTime dt; + string sdt = Runtime.GetManagedString(op); + if (!DateTime.TryParse(sdt, out dt)) + { + goto type_error; + } + Runtime.XDecref(op); + result = dt; + return true; } From f170be8ca748e75f3a74ffaca11a208e95e70fcb Mon Sep 17 00:00:00 2001 From: AlexCatarino Date: Thu, 28 Sep 2017 23:24:38 +0100 Subject: [PATCH 03/12] Implements Nullable support --- src/runtime/converter.cs | 8 +++++++- src/runtime/methodbinder.cs | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/runtime/converter.cs b/src/runtime/converter.cs index 27de302e3..7039dc24b 100644 --- a/src/runtime/converter.cs +++ b/src/runtime/converter.cs @@ -507,6 +507,12 @@ internal static bool ToManagedValue(IntPtr value, Type obType, return false; } + var underlyingType = Nullable.GetUnderlyingType(obType); + if (underlyingType != null) + { + return ToManagedValue(value, underlyingType, out result, setError); + } + return ToPrimitive(value, obType, out result, setError); } @@ -966,7 +972,7 @@ private static bool ToArray(IntPtr value, Type obType, out object result, bool s int size = Runtime.PySequence_Size(value); result = null; - if (size < 0) + if (size < 0 || elementType.IsGenericType) { if (setError) { diff --git a/src/runtime/methodbinder.cs b/src/runtime/methodbinder.cs index f0c58f34f..73bc01968 100644 --- a/src/runtime/methodbinder.cs +++ b/src/runtime/methodbinder.cs @@ -394,8 +394,14 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, Meth } if (!typematch) { + // this takes care of nullables + var underlyingType = Nullable.GetUnderlyingType(pi[n].ParameterType); + if (underlyingType == null) + { + underlyingType = pi[n].ParameterType; + } // this takes care of enum values - TypeCode argtypecode = Type.GetTypeCode(pi[n].ParameterType); + TypeCode argtypecode = Type.GetTypeCode(underlyingType); TypeCode paramtypecode = Type.GetTypeCode(clrtype); if (argtypecode == paramtypecode) { From 436f2304115900b9a4320cb7245624a05f625f9f Mon Sep 17 00:00:00 2001 From: AlexCatarino Date: Thu, 28 Sep 2017 23:28:39 +0100 Subject: [PATCH 04/12] Implements Implicit Conversion --- src/runtime/converter.cs | 14 ++++++++++++++ src/runtime/methodbinder.cs | 7 +++++++ 2 files changed, 21 insertions(+) diff --git a/src/runtime/converter.cs b/src/runtime/converter.cs index 7039dc24b..18c9ce0dd 100644 --- a/src/runtime/converter.cs +++ b/src/runtime/converter.cs @@ -513,6 +513,20 @@ internal static bool ToManagedValue(IntPtr value, Type obType, return ToManagedValue(value, underlyingType, out result, setError); } + var opImplicit = obType.GetMethod("op_Implicit", new[] { obType }); + if (opImplicit != null) + { + if (ToManagedValue(value, opImplicit.ReturnType, out result, setError)) + { + opImplicit = obType.GetMethod("op_Implicit", new[] { result.GetType() }); + if (opImplicit != null) + { + result = opImplicit.Invoke(null, new[] { result }); + } + return opImplicit != null; + } + } + return ToPrimitive(value, obType, out result, setError); } diff --git a/src/runtime/methodbinder.cs b/src/runtime/methodbinder.cs index 73bc01968..b01480473 100644 --- a/src/runtime/methodbinder.cs +++ b/src/runtime/methodbinder.cs @@ -408,6 +408,13 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, Meth typematch = true; clrtype = pi[n].ParameterType; } + // this takes care of implicit conversions + var opImplicit = pi[n].ParameterType.GetMethod("op_Implicit", new[] { clrtype }); + if (opImplicit != null) + { + typematch = opImplicit.ReturnType == pi[n].ParameterType; + clrtype = pi[n].ParameterType; + } } Runtime.XDecref(pyoptype); if (!typematch) From 03bfaed9b64006abc2ea55129f1ea938b822dea1 Mon Sep 17 00:00:00 2001 From: AlexCatarino Date: Fri, 29 Sep 2017 13:57:59 +0100 Subject: [PATCH 05/12] Adds method name to "no method matches" error --- AUTHORS.md | 1 + CHANGELOG.md | 1 + src/runtime/methodbinder.cs | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/AUTHORS.md b/AUTHORS.md index 78bb25f9e..9b36aaccb 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -12,6 +12,7 @@ ## Contributors +- Alexandre Catarino([@AlexCatarino](https://github.com/AlexCatarino)) - Arvid JB ([@ArvidJB](https://github.com/ArvidJB)) - Bradley Friedman ([@leith-bartrich](https://github.com/leith-bartrich)) - Christian Heimes ([@tiran](https://github.com/tiran)) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d408a8d1..376e4c2a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][]. - Fixed conversion of 'float' and 'double' values (#486) - Fixed 'clrmethod' for python 2 (#492) - Fixed double calling of constructor when deriving from .NET class (#495) +- Fixed missing information on 'No method matches given arguments' by adding the method name ## [2.3.0][] - 2017-03-11 diff --git a/src/runtime/methodbinder.cs b/src/runtime/methodbinder.cs index b01480473..1f33447fd 100644 --- a/src/runtime/methodbinder.cs +++ b/src/runtime/methodbinder.cs @@ -520,7 +520,7 @@ internal virtual IntPtr Invoke(IntPtr inst, IntPtr args, IntPtr kw, MethodBase i if (binding == null) { - Exceptions.SetError(Exceptions.TypeError, "No method matches given arguments"); + Exceptions.SetError(Exceptions.TypeError, "No method matches given arguments for " + methodinfo[0].Name); return IntPtr.Zero; } From 5299261f3db228b27c29a1907504f40a8a94b475 Mon Sep 17 00:00:00 2001 From: AlexCatarino Date: Thu, 28 Sep 2017 23:01:37 +0100 Subject: [PATCH 06/12] Implements System.Decimal support Convertes System.Decimal to decimal.decimal and vice-versa --- src/runtime/converter.cs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/runtime/converter.cs b/src/runtime/converter.cs index 13498e3dc..ad419660a 100644 --- a/src/runtime/converter.cs +++ b/src/runtime/converter.cs @@ -30,6 +30,7 @@ private Converter() private static Type flagsType; private static Type boolType; private static Type typeType; + private static IntPtr decimalCtor; static Converter() { @@ -45,6 +46,9 @@ static Converter() flagsType = typeof(FlagsAttribute); boolType = typeof(Boolean); typeType = typeof(Type); + + IntPtr decimalMod = Runtime.PyImport_ImportModule("decimal"); + if (decimalMod == null) throw new PythonException(); } @@ -100,6 +104,9 @@ internal static IntPtr GetPythonTypeByAlias(Type op) if (op == boolType) return Runtime.PyBoolType; + if (op == decimalType) + return Runtime.PyFloatType; + return IntPtr.Zero; } @@ -229,6 +236,14 @@ internal static IntPtr ToPython(object value, Type type) case TypeCode.UInt64: return Runtime.PyLong_FromUnsignedLongLong((ulong)value); + case TypeCode.Decimal: + string d2s = ((decimal)value).ToString(nfi); + IntPtr d2p = Runtime.PyString_FromString(d2s); + IntPtr decimalArgs = Runtime.PyTuple_New(1); + Runtime.PyTuple_SetItem(decimalArgs, 0, d2p); + + return Runtime.PyObject_CallObject(decimalCtor, decimalArgs); + default: if (value is IEnumerable) { @@ -801,6 +816,18 @@ private static bool ToPrimitive(IntPtr value, Type obType, out object result, bo Runtime.XDecref(op); result = d; return true; + + case TypeCode.Decimal: + op = Runtime.PyObject_Str(value); + decimal m; + string sm = Runtime.GetManagedString(op); + if (!Decimal.TryParse(sm, NumberStyles.Number, nfi, out m)) + { + goto type_error; + } + Runtime.XDecref(op); + result = m; + return true; } From ef37a34fa6d8bf436e232a52e859d785883a4344 Mon Sep 17 00:00:00 2001 From: AlexCatarino Date: Thu, 28 Sep 2017 23:17:41 +0100 Subject: [PATCH 07/12] Implements System.DateTime support Converts System.DateTime and System.TimeSpan to datetime.datetime and datetime.timedelta and vice-versa --- src/runtime/converter.cs | 102 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/src/runtime/converter.cs b/src/runtime/converter.cs index ad419660a..688bb7320 100644 --- a/src/runtime/converter.cs +++ b/src/runtime/converter.cs @@ -31,6 +31,9 @@ private Converter() private static Type boolType; private static Type typeType; private static IntPtr decimalCtor; + private static IntPtr dateTimeCtor; + private static IntPtr timeSpanCtor; + private static IntPtr tzInfoCtor; static Converter() { @@ -49,6 +52,34 @@ static Converter() IntPtr decimalMod = Runtime.PyImport_ImportModule("decimal"); if (decimalMod == null) throw new PythonException(); + + IntPtr dateTimeMod = Runtime.PyImport_ImportModule("datetime"); + if (dateTimeMod == null) throw new PythonException(); + + decimalCtor = Runtime.PyObject_GetAttrString(decimalMod, "Decimal"); + if (decimalCtor == null) throw new PythonException(); + + dateTimeCtor = Runtime.PyObject_GetAttrString(dateTimeMod, "datetime"); + if (dateTimeCtor == null) throw new PythonException(); + + timeSpanCtor = Runtime.PyObject_GetAttrString(dateTimeMod, "timedelta"); + if (timeSpanCtor == null) throw new PythonException(); + + IntPtr tzInfoMod = PythonEngine.ModuleFromString("custom_tzinfo", + "from datetime import timedelta, tzinfo\n" + + "class GMT(tzinfo):\n" + + " def __init__(self, hours, minutes):\n" + + " self.hours = hours\n" + + " self.minutes = minutes\n" + + " def utcoffset(self, dt):\n" + + " return timedelta(hours=self.hours, minutes=self.minutes)\n" + + " def tzname(self, dt):\n" + + " return \"GMT {0:00}:{1:00}\".format(self.hours, self.minutes)\n" + + " def dst (self, dt):\n" + + " return timedelta(0)\n").Handle; + + tzInfoCtor = Runtime.PyObject_GetAttrString(tzInfoMod, "GMT"); + if (tzInfoCtor == null) throw new PythonException(); } @@ -184,6 +215,14 @@ internal static IntPtr ToPython(object value, Type type) switch (tc) { case TypeCode.Object: + if (value is TimeSpan) + { + var timespan = (TimeSpan)value; + + IntPtr timeSpanArgs = Runtime.PyTuple_New(1); + Runtime.PyTuple_SetItem(timeSpanArgs, 0, Runtime.PyFloat_FromDouble(timespan.TotalDays)); + return Runtime.PyObject_CallObject(timeSpanCtor, timeSpanArgs); + } return CLRObject.GetInstHandle(value, type); case TypeCode.String: @@ -244,6 +283,21 @@ internal static IntPtr ToPython(object value, Type type) return Runtime.PyObject_CallObject(decimalCtor, decimalArgs); + case TypeCode.DateTime: + var datetime = (DateTime)value; + + IntPtr dateTimeArgs = Runtime.PyTuple_New(8); + Runtime.PyTuple_SetItem(dateTimeArgs, 0, Runtime.PyInt_FromInt32(datetime.Year)); + Runtime.PyTuple_SetItem(dateTimeArgs, 1, Runtime.PyInt_FromInt32(datetime.Month)); + Runtime.PyTuple_SetItem(dateTimeArgs, 2, Runtime.PyInt_FromInt32(datetime.Day)); + Runtime.PyTuple_SetItem(dateTimeArgs, 3, Runtime.PyInt_FromInt32(datetime.Hour)); + Runtime.PyTuple_SetItem(dateTimeArgs, 4, Runtime.PyInt_FromInt32(datetime.Minute)); + Runtime.PyTuple_SetItem(dateTimeArgs, 5, Runtime.PyInt_FromInt32(datetime.Second)); + Runtime.PyTuple_SetItem(dateTimeArgs, 6, Runtime.PyInt_FromInt32(datetime.Millisecond)); + Runtime.PyTuple_SetItem(dateTimeArgs, 7, TzInfo(datetime.Kind)); + + return Runtime.PyObject_CallObject(dateTimeCtor, dateTimeArgs); + default: if (value is IEnumerable) { @@ -265,6 +319,16 @@ internal static IntPtr ToPython(object value, Type type) } } + private static IntPtr TzInfo(DateTimeKind kind) + { + if (kind == DateTimeKind.Unspecified) return Runtime.PyNone; + var offset = kind == DateTimeKind.Local ? DateTimeOffset.Now.Offset : TimeSpan.Zero; + IntPtr tzInfoArgs = Runtime.PyTuple_New(2); + Runtime.PyTuple_SetItem(tzInfoArgs, 0, Runtime.PyFloat_FromDouble(offset.Hours)); + Runtime.PyTuple_SetItem(tzInfoArgs, 1, Runtime.PyFloat_FromDouble(offset.Minutes)); + return Runtime.PyObject_CallObject(tzInfoCtor, tzInfoArgs); + } + /// /// In a few situations, we don't have any advisory type information @@ -467,6 +531,32 @@ private static bool ToPrimitive(IntPtr value, Type obType, out object result, bo switch (tc) { + case TypeCode.Object: + if (obType == typeof(TimeSpan)) + { + op = Runtime.PyObject_Str(value); + TimeSpan ts; + var arr = Runtime.GetManagedString(op).Split(','); + string sts = arr.Length == 1 ? arr[0] : arr[1]; + if (!TimeSpan.TryParse(sts, out ts)) + { + goto type_error; + } + Runtime.XDecref(op); + + int days = 0; + if (arr.Length > 1) + { + if (!int.TryParse(arr[0].Split(' ')[0].Trim(), out days)) + { + goto type_error; + } + } + result = ts.Add(TimeSpan.FromDays(days)); + return true; + } + break; + case TypeCode.String: string st = Runtime.GetManagedString(value); if (st == null) @@ -828,6 +918,18 @@ private static bool ToPrimitive(IntPtr value, Type obType, out object result, bo Runtime.XDecref(op); result = m; return true; + + case TypeCode.DateTime: + op = Runtime.PyObject_Str(value); + DateTime dt; + string sdt = Runtime.GetManagedString(op); + if (!DateTime.TryParse(sdt, out dt)) + { + goto type_error; + } + Runtime.XDecref(op); + result = dt; + return true; } From 391c0378e2e474724f7fc2dd979f323fd2b963ff Mon Sep 17 00:00:00 2001 From: AlexCatarino Date: Thu, 28 Sep 2017 23:24:38 +0100 Subject: [PATCH 08/12] Implements Nullable support --- src/runtime/converter.cs | 8 +++++++- src/runtime/methodbinder.cs | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/runtime/converter.cs b/src/runtime/converter.cs index 688bb7320..00fee7f98 100644 --- a/src/runtime/converter.cs +++ b/src/runtime/converter.cs @@ -515,6 +515,12 @@ internal static bool ToManagedValue(IntPtr value, Type obType, return false; } + var underlyingType = Nullable.GetUnderlyingType(obType); + if (underlyingType != null) + { + return ToManagedValue(value, underlyingType, out result, setError); + } + return ToPrimitive(value, obType, out result, setError); } @@ -974,7 +980,7 @@ private static bool ToArray(IntPtr value, Type obType, out object result, bool s int size = Runtime.PySequence_Size(value); result = null; - if (size < 0) + if (size < 0 || elementType.IsGenericType) { if (setError) { diff --git a/src/runtime/methodbinder.cs b/src/runtime/methodbinder.cs index f0c58f34f..73bc01968 100644 --- a/src/runtime/methodbinder.cs +++ b/src/runtime/methodbinder.cs @@ -394,8 +394,14 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, Meth } if (!typematch) { + // this takes care of nullables + var underlyingType = Nullable.GetUnderlyingType(pi[n].ParameterType); + if (underlyingType == null) + { + underlyingType = pi[n].ParameterType; + } // this takes care of enum values - TypeCode argtypecode = Type.GetTypeCode(pi[n].ParameterType); + TypeCode argtypecode = Type.GetTypeCode(underlyingType); TypeCode paramtypecode = Type.GetTypeCode(clrtype); if (argtypecode == paramtypecode) { From 2465ae56c6bf7efebfb8a7f11c10a82b7c1a5e6d Mon Sep 17 00:00:00 2001 From: AlexCatarino Date: Thu, 28 Sep 2017 23:28:39 +0100 Subject: [PATCH 09/12] Implements Implicit Conversion --- src/runtime/converter.cs | 14 ++++++++++++++ src/runtime/methodbinder.cs | 7 +++++++ 2 files changed, 21 insertions(+) diff --git a/src/runtime/converter.cs b/src/runtime/converter.cs index 00fee7f98..2762b77a5 100644 --- a/src/runtime/converter.cs +++ b/src/runtime/converter.cs @@ -521,6 +521,20 @@ internal static bool ToManagedValue(IntPtr value, Type obType, return ToManagedValue(value, underlyingType, out result, setError); } + var opImplicit = obType.GetMethod("op_Implicit", new[] { obType }); + if (opImplicit != null) + { + if (ToManagedValue(value, opImplicit.ReturnType, out result, setError)) + { + opImplicit = obType.GetMethod("op_Implicit", new[] { result.GetType() }); + if (opImplicit != null) + { + result = opImplicit.Invoke(null, new[] { result }); + } + return opImplicit != null; + } + } + return ToPrimitive(value, obType, out result, setError); } diff --git a/src/runtime/methodbinder.cs b/src/runtime/methodbinder.cs index 73bc01968..b01480473 100644 --- a/src/runtime/methodbinder.cs +++ b/src/runtime/methodbinder.cs @@ -408,6 +408,13 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, Meth typematch = true; clrtype = pi[n].ParameterType; } + // this takes care of implicit conversions + var opImplicit = pi[n].ParameterType.GetMethod("op_Implicit", new[] { clrtype }); + if (opImplicit != null) + { + typematch = opImplicit.ReturnType == pi[n].ParameterType; + clrtype = pi[n].ParameterType; + } } Runtime.XDecref(pyoptype); if (!typematch) From e37266265c3bbce6e5dbeb3103d5097380d02bb0 Mon Sep 17 00:00:00 2001 From: AlexCatarino Date: Fri, 29 Sep 2017 13:57:59 +0100 Subject: [PATCH 10/12] Adds method name to "no method matches" error --- AUTHORS.md | 1 + CHANGELOG.md | 1 + src/runtime/methodbinder.cs | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/AUTHORS.md b/AUTHORS.md index 55aa69d11..f0b385fd0 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -12,6 +12,7 @@ ## Contributors +- Alexandre Catarino([@AlexCatarino](https://github.com/AlexCatarino)) - Arvid JB ([@ArvidJB](https://github.com/ArvidJB)) - Bradley Friedman ([@leith-bartrich](https://github.com/leith-bartrich)) - Callum Noble ([@callumnoble](https://github.com/callumnoble)) diff --git a/CHANGELOG.md b/CHANGELOG.md index 96461ca68..34e932132 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][]. - Fixed `clr.GetClrType` when iterating over `System` members (#607) - Fixed `LockRecursionException` when loading assemblies (#627) - Fixed errors breaking .NET Remoting on method invoke (#276) +- Fixed missing information on 'No method matches given arguments' by adding the method name ## [2.3.0][] - 2017-03-11 diff --git a/src/runtime/methodbinder.cs b/src/runtime/methodbinder.cs index b01480473..1f33447fd 100644 --- a/src/runtime/methodbinder.cs +++ b/src/runtime/methodbinder.cs @@ -520,7 +520,7 @@ internal virtual IntPtr Invoke(IntPtr inst, IntPtr args, IntPtr kw, MethodBase i if (binding == null) { - Exceptions.SetError(Exceptions.TypeError, "No method matches given arguments"); + Exceptions.SetError(Exceptions.TypeError, "No method matches given arguments for " + methodinfo[0].Name); return IntPtr.Zero; } From e680da5779cd23ae585c372db254f6aaa03a5c39 Mon Sep 17 00:00:00 2001 From: AlexCatarino Date: Tue, 28 Nov 2017 22:35:07 +0000 Subject: [PATCH 11/12] Fixes Decimal support It didn't have the proper handling of type correspondence --- src/runtime/converter.cs | 5 ++++- src/runtime/methodbinder.cs | 14 +++++++++++++- src/runtime/runtime.cs | 9 +++++++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/runtime/converter.cs b/src/runtime/converter.cs index 2762b77a5..c955a880f 100644 --- a/src/runtime/converter.cs +++ b/src/runtime/converter.cs @@ -106,6 +106,9 @@ internal static Type GetTypeByAlias(IntPtr op) if (op == Runtime.PyBoolType) return boolType; + if (op == Runtime.PyDecimalType) + return decimalType; + return null; } @@ -136,7 +139,7 @@ internal static IntPtr GetPythonTypeByAlias(Type op) return Runtime.PyBoolType; if (op == decimalType) - return Runtime.PyFloatType; + return Runtime.PyDecimalType; return IntPtr.Zero; } diff --git a/src/runtime/methodbinder.cs b/src/runtime/methodbinder.cs index 1f33447fd..1dcb8b801 100644 --- a/src/runtime/methodbinder.cs +++ b/src/runtime/methodbinder.cs @@ -186,9 +186,16 @@ internal static int GetPrecedence(MethodBase mi) val += ArgPrecedence(pi[i].ParameterType); } + var info = mi as MethodInfo; + if (info != null) + { + val += ArgPrecedence(info.ReturnType); + val += mi.DeclaringType == mi.ReflectedType ? 0 : 3000; + } + return val; } - + /// /// Return a precedence value for a particular Type object. /// @@ -200,6 +207,11 @@ internal static int ArgPrecedence(Type t) return 3000; } + if (t.IsAssignableFrom(typeof(PyObject))) + { + return -1; + } + TypeCode tc = Type.GetTypeCode(t); // TODO: Clean up switch (tc) diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs index abd0661a4..01e405b6f 100644 --- a/src/runtime/runtime.cs +++ b/src/runtime/runtime.cs @@ -299,6 +299,14 @@ internal static void Initialize() PyFloatType = PyObject_Type(op); XDecref(op); + IntPtr decimalMod = PyImport_ImportModule("decimal"); + IntPtr decimalCtor = PyObject_GetAttrString(decimalMod, "Decimal"); + op = PyObject_CallObject(decimalCtor, IntPtr.Zero); + PyDecimalType = PyObject_Type(op); + XDecref(op); + XDecref(decimalMod); + XDecref(decimalCtor); + #if PYTHON3 PyClassType = IntPtr.Zero; PyInstanceType = IntPtr.Zero; @@ -392,6 +400,7 @@ internal static int AtExit() internal static IntPtr PyBoolType; internal static IntPtr PyNoneType; internal static IntPtr PyTypeType; + internal static IntPtr PyDecimalType; #if PYTHON3 internal static IntPtr PyBytesType; From f5c501fbba3ec5500098667431882bc91dca6064 Mon Sep 17 00:00:00 2001 From: AlexCatarino Date: Thu, 28 Dec 2017 15:46:46 +0000 Subject: [PATCH 12/12] Method overload with decimal parameter accepts non-decimal numbers --- src/runtime/methodbinder.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/runtime/methodbinder.cs b/src/runtime/methodbinder.cs index 1dcb8b801..94be7cc78 100644 --- a/src/runtime/methodbinder.cs +++ b/src/runtime/methodbinder.cs @@ -420,6 +420,12 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, Meth typematch = true; clrtype = pi[n].ParameterType; } + // accepts non-decimal numbers in decimal parameters + if (underlyingType == typeof(decimal)) + { + clrtype = pi[n].ParameterType; + typematch = Converter.ToManaged(op, clrtype, out arg, false); + } // this takes care of implicit conversions var opImplicit = pi[n].ParameterType.GetMethod("op_Implicit", new[] { clrtype }); if (opImplicit != null)