Skip to content

Commit c11be81

Browse files
committed
Reflect PR#8 MISSING CONVERTER.CS L516-528 Changes
1 parent 25a53bc commit c11be81

File tree

2 files changed

+128
-1
lines changed

2 files changed

+128
-1
lines changed

src/runtime/converter.cs

Lines changed: 113 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ private Converter()
3030
private static Type flagsType;
3131
private static Type boolType;
3232
private static Type typeType;
33+
private static IntPtr decimalCtor;
34+
private static IntPtr dateTimeCtor;
35+
private static IntPtr timeSpanCtor;
36+
private static IntPtr tzInfoCtor;
3337

3438
static Converter()
3539
{
@@ -45,6 +49,38 @@ static Converter()
4549
flagsType = typeof(FlagsAttribute);
4650
boolType = typeof(Boolean);
4751
typeType = typeof(Type);
52+
53+
54+
IntPtr decimalMod = Runtime.PyImport_ImportModule("decimal");
55+
if (decimalMod == null) throw new PythonException();
56+
57+
IntPtr dateTimeMod = Runtime.PyImport_ImportModule("datetime");
58+
if (dateTimeMod == null) throw new PythonException();
59+
60+
decimalCtor = Runtime.PyObject_GetAttrString(decimalMod, "Decimal");
61+
if (decimalCtor == null) throw new PythonException();
62+
63+
dateTimeCtor = Runtime.PyObject_GetAttrString(dateTimeMod, "datetime");
64+
if (dateTimeCtor == null) throw new PythonException();
65+
66+
timeSpanCtor = Runtime.PyObject_GetAttrString(dateTimeMod, "timedelta");
67+
if (timeSpanCtor == null) throw new PythonException();
68+
69+
IntPtr tzInfoMod = PythonEngine.ModuleFromString("custom_tzinfo",
70+
"from datetime import timedelta, tzinfo\n" +
71+
"class GMT(tzinfo):\n" +
72+
" def __init__(self, hours, minutes):\n" +
73+
" self.hours = hours\n" +
74+
" self.minutes = minutes\n" +
75+
" def utcoffset(self, dt):\n" +
76+
" return timedelta(hours=self.hours, minutes=self.minutes)\n" +
77+
" def tzname(self, dt):\n" +
78+
" return \"GMT {0:00}:{1:00}\".format(self.hours, self.minutes)\n" +
79+
" def dst (self, dt):\n" +
80+
" return timedelta(0)\n").Handle;
81+
82+
tzInfoCtor = Runtime.PyObject_GetAttrString(tzInfoMod, "GMT");
83+
if (tzInfoCtor == null) throw new PythonException();
4884
}
4985

5086

@@ -208,6 +244,14 @@ internal static IntPtr ToPython(object value, Type type)
208244
switch (tc)
209245
{
210246
case TypeCode.Object:
247+
if (value is TimeSpan)
248+
{
249+
var timespan = (TimeSpan)value;
250+
251+
IntPtr timeSpanArgs = Runtime.PyTuple_New(1);
252+
Runtime.PyTuple_SetItem(timeSpanArgs, 0, Runtime.PyFloat_FromDouble(timespan.TotalDays));
253+
return Runtime.PyObject_CallObject(timeSpanCtor, timeSpanArgs);
254+
}
211255
return CLRObject.GetInstHandle(value, type);
212256

213257
case TypeCode.String:
@@ -269,7 +313,23 @@ internal static IntPtr ToPython(object value, Type type)
269313
IntPtr args = Runtime.PyTuple_New(1);
270314
Runtime.PyTuple_SetItem(args, 0, d2p);
271315

272-
return Runtime.PyObject_CallObject(ctor, args);
316+
return Runtime.PyObject_CallObject(decimalCtor, args);
317+
318+
case TypeCode.DateTime:
319+
var datetime = (DateTime)value;
320+
321+
IntPtr dateTimeArgs = Runtime.PyTuple_New(8);
322+
Runtime.PyTuple_SetItem(dateTimeArgs, 0, Runtime.PyInt_FromInt32(datetime.Year));
323+
Runtime.PyTuple_SetItem(dateTimeArgs, 1, Runtime.PyInt_FromInt32(datetime.Month));
324+
Runtime.PyTuple_SetItem(dateTimeArgs, 2, Runtime.PyInt_FromInt32(datetime.Day));
325+
Runtime.PyTuple_SetItem(dateTimeArgs, 3, Runtime.PyInt_FromInt32(datetime.Hour));
326+
Runtime.PyTuple_SetItem(dateTimeArgs, 4, Runtime.PyInt_FromInt32(datetime.Minute));
327+
Runtime.PyTuple_SetItem(dateTimeArgs, 5, Runtime.PyInt_FromInt32(datetime.Second));
328+
Runtime.PyTuple_SetItem(dateTimeArgs, 6, Runtime.PyInt_FromInt32(datetime.Millisecond));
329+
Runtime.PyTuple_SetItem(dateTimeArgs, 7, TzInfo(datetime.Kind));
330+
331+
return Runtime.PyObject_CallObject(dateTimeCtor, dateTimeArgs);
332+
273333

274334
default:
275335
if (value is IEnumerable)
@@ -292,6 +352,15 @@ internal static IntPtr ToPython(object value, Type type)
292352
}
293353
}
294354

355+
private static IntPtr TzInfo(DateTimeKind kind)
356+
{
357+
if (kind == DateTimeKind.Unspecified) return Runtime.PyNone;
358+
var offset = kind == DateTimeKind.Local ? DateTimeOffset.Now.Offset : TimeSpan.Zero;
359+
IntPtr tzInfoArgs = Runtime.PyTuple_New(2);
360+
Runtime.PyTuple_SetItem(tzInfoArgs, 0, Runtime.PyFloat_FromDouble(offset.Hours));
361+
Runtime.PyTuple_SetItem(tzInfoArgs, 1, Runtime.PyFloat_FromDouble(offset.Minutes));
362+
return Runtime.PyObject_CallObject(tzInfoCtor, tzInfoArgs);
363+
}
295364

296365
/// <summary>
297366
/// In a few situations, we don't have any advisory type information
@@ -507,6 +576,12 @@ internal static bool ToManagedValue(IntPtr value, Type obType,
507576
return false;
508577
}
509578

579+
var underlyingType = Nullable.GetUnderlyingType(obType);
580+
if (underlyingType != null)
581+
{
582+
return ToManagedValue(value, underlyingType, out result, setError);
583+
}
584+
510585
TypeCode typeCode = Type.GetTypeCode(obType);
511586
if (typeCode == TypeCode.Object)
512587
{
@@ -533,6 +608,32 @@ private static bool ToPrimitive(IntPtr value, Type obType, out object result, bo
533608

534609
switch (tc)
535610
{
611+
case TypeCode.Object:
612+
if (obType == typeof(TimeSpan))
613+
{
614+
op = Runtime.PyObject_Str(value);
615+
TimeSpan ts;
616+
var arr = Runtime.GetManagedString(op).Split(',');
617+
string sts = arr.Length == 1 ? arr[0] : arr[1];
618+
if (!TimeSpan.TryParse(sts, out ts))
619+
{
620+
goto type_error;
621+
}
622+
Runtime.XDecref(op);
623+
624+
int days = 0;
625+
if (arr.Length > 1)
626+
{
627+
if (!int.TryParse(arr[0].Split(' ')[0].Trim(), out days))
628+
{
629+
goto type_error;
630+
}
631+
}
632+
result = ts.Add(TimeSpan.FromDays(days));
633+
return true;
634+
}
635+
break;
636+
536637
case TypeCode.String:
537638
string st = Runtime.GetManagedString(value);
538639
if (st == null)
@@ -774,6 +875,17 @@ private static bool ToPrimitive(IntPtr value, Type obType, out object result, bo
774875
Runtime.XDecref(op);
775876
result = m;
776877
return true;
878+
case TypeCode.DateTime:
879+
op = Runtime.PyObject_Str(value);
880+
DateTime dt;
881+
string sdt = Runtime.GetManagedString(op);
882+
if (!DateTime.TryParse(sdt, out dt))
883+
{
884+
goto type_error;
885+
}
886+
Runtime.XDecref(op);
887+
result = dt;
888+
return true;
777889
default:
778890
goto type_error;
779891
}

src/runtime/methodbinder.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -751,6 +751,13 @@ static Type TryComputeClrArgumentType(Type parameterType, IntPtr argument, bool
751751
}
752752
if (!typematch)
753753
{
754+
// this takes care of nullables
755+
var underlyingType = Nullable.GetUnderlyingType(parameterType);
756+
if (underlyingType != null)
757+
{
758+
parameterType = underlyingType;
759+
}
760+
754761
// this takes care of enum values
755762
TypeCode parameterTypeCode = Type.GetTypeCode(parameterType);
756763
TypeCode clrTypeCode = Type.GetTypeCode(clrtype);
@@ -763,6 +770,14 @@ static Type TryComputeClrArgumentType(Type parameterType, IntPtr argument, bool
763770
{
764771
Exceptions.RaiseTypeError($"Expected {parameterTypeCode}, got {clrTypeCode}");
765772
}
773+
774+
// this takes care of implicit conversions
775+
var opImplicit = parameterType.GetMethod("op_Implicit", new[] { clrtype });
776+
if (opImplicit != null)
777+
{
778+
typematch = opImplicit.ReturnType == parameterType;
779+
clrtype = parameterType;
780+
}
766781
}
767782
Runtime.XDecref(pyoptype);
768783
if (!typematch)

0 commit comments

Comments
 (0)