From e83a77591ebcd882e22ea15b49eab7c489ed3ee3 Mon Sep 17 00:00:00 2001 From: Victor Nova Date: Wed, 28 Jul 2021 14:48:16 -0700 Subject: [PATCH] fixed custom decoders not working for DateTime and Decimal --- src/embed_tests/Codecs.cs | 44 +++++++++++++++++++++++++++++++++++++++ src/runtime/converter.cs | 24 ++++++++++++++------- 2 files changed, 61 insertions(+), 7 deletions(-) diff --git a/src/embed_tests/Codecs.cs b/src/embed_tests/Codecs.cs index f0c00a6d8..e7303a8e4 100644 --- a/src/embed_tests/Codecs.cs +++ b/src/embed_tests/Codecs.cs @@ -355,6 +355,24 @@ public void ExceptionDecoded() Assert.AreEqual(TestExceptionMessage, error.Message); } + [Test] + public void DateTimeDecoded() + { + using var scope = Py.CreateScope(); + scope.Exec(@" +import clr +from datetime import datetime + + +from Python.EmbeddingTest import Codecs, DateTimeDecoder + +DateTimeDecoder.Setup() +"); + scope.Exec("Codecs.AcceptsDateTime(datetime(2021, 1, 22))"); + } + + public static void AcceptsDateTime(DateTime v) {} + class ValueErrorWrapper : Exception { public ValueErrorWrapper(string message) : base(message) { } @@ -419,4 +437,30 @@ public bool TryDecode(PyObject pyObj, out T value) return true; } } + + public class DateTimeDecoder : IPyObjectDecoder + { + public static void Setup() + { + PyObjectConversions.RegisterDecoder(new DateTimeDecoder()); + } + + public bool CanDecode(PyObject objectType, Type targetType) + { + return targetType == typeof(DateTime); + } + + public bool TryDecode(PyObject pyObj, out T value) + { + var dt = new DateTime( + pyObj.GetAttr("year").As(), + pyObj.GetAttr("month").As(), + pyObj.GetAttr("day").As(), + pyObj.GetAttr("hour").As(), + pyObj.GetAttr("minute").As(), + pyObj.GetAttr("second").As()); + value = (T)(object)dt; + return true; + } + } } diff --git a/src/runtime/converter.cs b/src/runtime/converter.cs index 936f8b248..420fc9435 100644 --- a/src/runtime/converter.cs +++ b/src/runtime/converter.cs @@ -149,11 +149,8 @@ internal static IntPtr ToPython(object value, Type type) return result; } - if (Type.GetTypeCode(type) == TypeCode.Object - && value.GetType() != typeof(object) - && value is not Type - || type.IsEnum - ) { + if (EncodableByUser(type, value)) + { var encoded = PyObjectConversions.TryEncode(value, type); if (encoded != null) { result = encoded.Handle; @@ -301,6 +298,13 @@ internal static IntPtr ToPython(object value, Type type) } } + static bool EncodableByUser(Type type, object value) + { + TypeCode typeCode = Type.GetTypeCode(type); + return type.IsEnum + || typeCode is TypeCode.DateTime or TypeCode.Decimal + || typeCode == TypeCode.Object && value.GetType() != typeof(object) && value is not Type; + } /// /// In a few situations, we don't have any advisory type information @@ -523,8 +527,7 @@ internal static bool ToManagedValue(IntPtr value, Type obType, return false; } - TypeCode typeCode = Type.GetTypeCode(obType); - if (typeCode == TypeCode.Object || obType.IsEnum) + if (DecodableByUser(obType)) { IntPtr pyType = Runtime.PyObject_TYPE(value); if (PyObjectConversions.TryDecode(value, pyType, obType, out result)) @@ -536,6 +539,13 @@ internal static bool ToManagedValue(IntPtr value, Type obType, return ToPrimitive(value, obType, out result, setError); } + static bool DecodableByUser(Type type) + { + TypeCode typeCode = Type.GetTypeCode(type); + return type.IsEnum + || typeCode is TypeCode.Object or TypeCode.Decimal or TypeCode.DateTime; + } + internal delegate bool TryConvertFromPythonDelegate(IntPtr pyObj, out object result); internal static int ToInt32(BorrowedReference value)