Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Implements System.Decimal and System.DateTime support #8

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Sep 29, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 150 additions & 1 deletion src/runtime/converter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ private Converter()
private static Type flagsType;
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()
{
Expand All @@ -45,6 +49,37 @@ static Converter()
flagsType = typeof(FlagsAttribute);
boolType = typeof(Boolean);
typeType = typeof(Type);

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();
}


Expand Down Expand Up @@ -100,6 +135,9 @@ internal static IntPtr GetPythonTypeByAlias(Type op)
if (op == boolType)
return Runtime.PyBoolType;

if (op == decimalType)
return Runtime.PyFloatType;

return IntPtr.Zero;
}

Expand Down Expand Up @@ -169,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:
Expand Down Expand Up @@ -221,6 +267,29 @@ 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);

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)
{
Expand All @@ -242,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);
}


/// <summary>
/// In a few situations, we don't have any advisory type information
Expand Down Expand Up @@ -428,6 +507,26 @@ 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);
}

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);
}

Expand All @@ -444,6 +543,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)
Expand Down Expand Up @@ -793,6 +918,30 @@ 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;

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;
}


Expand Down Expand Up @@ -837,7 +986,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)
{
Expand Down
15 changes: 14 additions & 1 deletion src/runtime/methodbinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -394,14 +394,27 @@ 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)
{
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)
Expand Down