X Tutup
using System; using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Reflection; using System.Runtime.InteropServices; using System.Security; using System.ComponentModel; namespace Python.Runtime { /// /// Performs data conversions between managed types and Python types. /// [SuppressUnmanagedCodeSecurity] internal class Converter { private Converter() { } private static NumberFormatInfo nfi; private static Type objectType; private static Type stringType; private static Type singleType; private static Type doubleType; private static Type decimalType; private static Type int16Type; private static Type int32Type; private static Type int64Type; private static Type flagsType; private static Type boolType; private static Type typeType; static Converter() { nfi = NumberFormatInfo.InvariantInfo; objectType = typeof(Object); stringType = typeof(String); int16Type = typeof(Int16); int32Type = typeof(Int32); int64Type = typeof(Int64); singleType = typeof(Single); doubleType = typeof(Double); decimalType = typeof(Decimal); flagsType = typeof(FlagsAttribute); boolType = typeof(Boolean); typeType = typeof(Type); } /// /// Given a builtin Python type, return the corresponding CLR type. /// internal static Type GetTypeByAlias(IntPtr op) { if (op == Runtime.PyStringType) return stringType; if (op == Runtime.PyUnicodeType) return stringType; if (op == Runtime.PyIntType) return int32Type; if (op == Runtime.PyLongType) return int64Type; if (op == Runtime.PyFloatType) return doubleType; if (op == Runtime.PyBoolType) return boolType; return null; } internal static IntPtr GetPythonTypeByAlias(Type op) { if (op == stringType) return Runtime.PyUnicodeType; if (op == int16Type) return Runtime.PyIntType; if (op == int32Type) return Runtime.PyIntType; #if PYTHON2 if (op == int64Type) return Runtime.PyLongType; #endif if (op == int64Type) return Runtime.PyIntType; if (op == doubleType) return Runtime.PyFloatType; if (op == singleType) return Runtime.PyFloatType; if (op == boolType) return Runtime.PyBoolType; return IntPtr.Zero; } /// /// Return a Python object for the given native object, converting /// basic types (string, int, etc.) into equivalent Python objects. /// This always returns a new reference. Note that the System.Decimal /// type has no Python equivalent and converts to a managed instance. /// internal static IntPtr ToPython(T value) { return ToPython(value, typeof(T)); } internal static IntPtr ToPython(object value, Type type) { if (value is PyObject) { IntPtr handle = ((PyObject)value).Handle; Runtime.XIncref(handle); return handle; } IntPtr result = IntPtr.Zero; // Null always converts to None in Python. if (value == null) { result = Runtime.PyNone; Runtime.XIncref(result); return result; } if (value is IList && !(value is INotifyPropertyChanged) && value.GetType().IsGenericType) { using (var resultlist = new PyList()) { foreach (object o in (IEnumerable)value) { using (var p = new PyObject(ToPython(o, o?.GetType()))) { resultlist.Append(p); } } Runtime.XIncref(resultlist.Handle); return resultlist.Handle; } } // it the type is a python subclass of a managed type then return the // underlying python object rather than construct a new wrapper object. var pyderived = value as IPythonDerivedType; if (null != pyderived) { return ClassDerivedObject.ToPython(pyderived); } // hmm - from Python, we almost never care what the declared // type is. we'd rather have the object bound to the actual // implementing class. type = value.GetType(); TypeCode tc = Type.GetTypeCode(type); switch (tc) { case TypeCode.Object: return CLRObject.GetInstHandle(value, type); case TypeCode.String: return Runtime.PyUnicode_FromString((string)value); case TypeCode.Int32: return Runtime.PyInt_FromInt32((int)value); case TypeCode.Boolean: if ((bool)value) { Runtime.XIncref(Runtime.PyTrue); return Runtime.PyTrue; } Runtime.XIncref(Runtime.PyFalse); return Runtime.PyFalse; case TypeCode.Byte: return Runtime.PyInt_FromInt32((int)((byte)value)); case TypeCode.Char: return Runtime.PyUnicode_FromOrdinal((int)((char)value)); case TypeCode.Int16: return Runtime.PyInt_FromInt32((int)((short)value)); case TypeCode.Int64: return Runtime.PyLong_FromLongLong((long)value); case TypeCode.Single: // return Runtime.PyFloat_FromDouble((double)((float)value)); string ss = ((float)value).ToString(nfi); IntPtr ps = Runtime.PyString_FromString(ss); IntPtr op = Runtime.PyFloat_FromString(ps, IntPtr.Zero); Runtime.XDecref(ps); return op; case TypeCode.Double: return Runtime.PyFloat_FromDouble((double)value); case TypeCode.SByte: return Runtime.PyInt_FromInt32((int)((sbyte)value)); case TypeCode.UInt16: return Runtime.PyInt_FromInt32((int)((ushort)value)); case TypeCode.UInt32: return Runtime.PyLong_FromUnsignedLong((uint)value); case TypeCode.UInt64: return Runtime.PyLong_FromUnsignedLongLong((ulong)value); default: if (value is IEnumerable) { using (var resultlist = new PyList()) { foreach (object o in (IEnumerable)value) { using (var p = new PyObject(ToPython(o, o?.GetType()))) { resultlist.Append(p); } } Runtime.XIncref(resultlist.Handle); return resultlist.Handle; } } result = CLRObject.GetInstHandle(value, type); return result; } } /// /// In a few situations, we don't have any advisory type information /// when we want to convert an object to Python. /// internal static IntPtr ToPythonImplicit(object value) { if (value == null) { IntPtr result = Runtime.PyNone; Runtime.XIncref(result); return result; } return ToPython(value, objectType); } /// /// Return a managed object for the given Python object, taking funny /// byref types into account. /// internal static bool ToManaged(IntPtr value, Type type, out object result, bool setError) { if (type.IsByRef) { type = type.GetElementType(); } return Converter.ToManagedValue(value, type, out result, setError); } internal static bool ToManagedValue(IntPtr value, Type obType, out object result, bool setError) { if (obType == typeof(PyObject)) { Runtime.XIncref(value); // PyObject() assumes ownership result = new PyObject(value); return true; } // Common case: if the Python value is a wrapped managed object // instance, just return the wrapped object. ManagedType mt = ManagedType.GetManagedObject(value); result = null; if (mt != null) { if (mt is CLRObject) { object tmp = ((CLRObject)mt).inst; if (obType.IsInstanceOfType(tmp)) { result = tmp; return true; } Exceptions.SetError(Exceptions.TypeError, $"value cannot be converted to {obType}"); return false; } if (mt is ClassBase) { result = ((ClassBase)mt).type; return true; } // shouldn't happen return false; } if (value == Runtime.PyNone && !obType.IsValueType) { result = null; return true; } if (obType.IsGenericType && obType.GetGenericTypeDefinition() == typeof(Nullable<>)) { if( value == Runtime.PyNone ) { result = null; return true; } // Set type to underlying type obType = obType.GetGenericArguments()[0]; } if (obType.IsArray) { return ToArray(value, obType, out result, setError); } if (obType.IsEnum) { return ToEnum(value, obType, out result, setError); } // Conversion to 'Object' is done based on some reasonable default // conversions (Python string -> managed string, Python int -> Int32 etc.). if (obType == objectType) { if (Runtime.IsStringType(value)) { return ToPrimitive(value, stringType, out result, setError); } if (Runtime.PyBool_Check(value)) { return ToPrimitive(value, boolType, out result, setError); } if (Runtime.PyInt_Check(value)) { return ToPrimitive(value, int32Type, out result, setError); } if (Runtime.PyLong_Check(value)) { return ToPrimitive(value, int64Type, out result, setError); } if (Runtime.PyFloat_Check(value)) { return ToPrimitive(value, doubleType, out result, setError); } if (Runtime.PySequence_Check(value)) { return ToArray(value, typeof(object[]), out result, setError); } if (setError) { Exceptions.SetError(Exceptions.TypeError, "value cannot be converted to Object"); } return false; } // Conversion to 'Type' is done using the same mappings as above for objects. if (obType == typeType) { if (value == Runtime.PyStringType) { result = stringType; return true; } if (value == Runtime.PyBoolType) { result = boolType; return true; } if (value == Runtime.PyIntType) { result = int32Type; return true; } if (value == Runtime.PyLongType) { result = int64Type; return true; } if (value == Runtime.PyFloatType) { result = doubleType; return true; } if (value == Runtime.PyListType || value == Runtime.PyTupleType) { result = typeof(object[]); return true; } if (setError) { Exceptions.SetError(Exceptions.TypeError, "value cannot be converted to Type"); } return false; } return ToPrimitive(value, obType, out result, setError); } /// /// Convert a Python value to an instance of a primitive managed type. /// private static bool ToPrimitive(IntPtr value, Type obType, out object result, bool setError) { IntPtr overflow = Exceptions.OverflowError; TypeCode tc = Type.GetTypeCode(obType); result = null; IntPtr op; int ival; switch (tc) { case TypeCode.String: string st = Runtime.GetManagedString(value); if (st == null) { goto type_error; } result = st; return true; case TypeCode.Int32: #if PYTHON2 // Trickery to support 64-bit platforms. if (Runtime.Is32Bit) { op = Runtime.PyNumber_Int(value); // As of Python 2.3, large ints magically convert :( if (Runtime.PyLong_Check(op)) { Runtime.XDecref(op); goto overflow; } if (op == IntPtr.Zero) { if (Exceptions.ExceptionMatches(overflow)) { goto overflow; } goto type_error; } ival = (int)Runtime.PyInt_AsLong(op); Runtime.XDecref(op); result = ival; return true; } #else // Python3 always use PyLong API { op = Runtime.PyNumber_Long(value); if (op == IntPtr.Zero) { Exceptions.Clear(); if (Exceptions.ExceptionMatches(overflow)) { goto overflow; } goto type_error; } long ll = (long)Runtime.PyLong_AsLongLong(op); Runtime.XDecref(op); if (ll == -1 && Exceptions.ErrorOccurred()) { goto overflow; } if (ll > Int32.MaxValue || ll < Int32.MinValue) { goto overflow; } result = (int)ll; return true; } #endif case TypeCode.Boolean: result = Runtime.PyObject_IsTrue(value) != 0; return true; case TypeCode.Byte: #if !PYTHON2 if (Runtime.PyObject_TypeCheck(value, Runtime.PyBytesType)) { if (Runtime.PyBytes_Size(value) == 1) { op = Runtime.PyBytes_AS_STRING(value); result = (byte)Marshal.ReadByte(op); return true; } goto type_error; } #else if (Runtime.PyObject_TypeCheck(value, Runtime.PyStringType)) { if (Runtime.PyString_Size(value) == 1) { op = Runtime.PyString_AsString(value); result = (byte)Marshal.ReadByte(op); return true; } goto type_error; } #endif op = Runtime.PyNumber_Int(value); if (op == IntPtr.Zero) { if (Exceptions.ExceptionMatches(overflow)) { goto overflow; } goto type_error; } ival = (int)Runtime.PyInt_AsLong(op); Runtime.XDecref(op); if (ival > Byte.MaxValue || ival < Byte.MinValue) { goto overflow; } byte b = (byte)ival; result = b; return true; case TypeCode.SByte: #if !PYTHON2 if (Runtime.PyObject_TypeCheck(value, Runtime.PyBytesType)) { if (Runtime.PyBytes_Size(value) == 1) { op = Runtime.PyBytes_AS_STRING(value); result = (byte)Marshal.ReadByte(op); return true; } goto type_error; } #else if (Runtime.PyObject_TypeCheck(value, Runtime.PyStringType)) { if (Runtime.PyString_Size(value) == 1) { op = Runtime.PyString_AsString(value); result = (sbyte)Marshal.ReadByte(op); return true; } goto type_error; } #endif op = Runtime.PyNumber_Int(value); if (op == IntPtr.Zero) { if (Exceptions.ExceptionMatches(overflow)) { goto overflow; } goto type_error; } ival = (int)Runtime.PyInt_AsLong(op); Runtime.XDecref(op); if (ival > SByte.MaxValue || ival < SByte.MinValue) { goto overflow; } sbyte sb = (sbyte)ival; result = sb; return true; case TypeCode.Char: #if !PYTHON2 if (Runtime.PyObject_TypeCheck(value, Runtime.PyBytesType)) { if (Runtime.PyBytes_Size(value) == 1) { op = Runtime.PyBytes_AS_STRING(value); result = (byte)Marshal.ReadByte(op); return true; } goto type_error; } #else if (Runtime.PyObject_TypeCheck(value, Runtime.PyStringType)) { if (Runtime.PyString_Size(value) == 1) { op = Runtime.PyString_AsString(value); result = (char)Marshal.ReadByte(op); return true; } goto type_error; } #endif else if (Runtime.PyObject_TypeCheck(value, Runtime.PyUnicodeType)) { if (Runtime.PyUnicode_GetSize(value) == 1) { op = Runtime.PyUnicode_AsUnicode(value); Char[] buff = new Char[1]; Marshal.Copy(op, buff, 0, 1); result = buff[0]; return true; } goto type_error; } op = Runtime.PyNumber_Int(value); if (op == IntPtr.Zero) { goto type_error; } ival = Runtime.PyInt_AsLong(op); Runtime.XDecref(op); if (ival > Char.MaxValue || ival < Char.MinValue) { goto overflow; } result = (char)ival; return true; case TypeCode.Int16: op = Runtime.PyNumber_Int(value); if (op == IntPtr.Zero) { if (Exceptions.ExceptionMatches(overflow)) { goto overflow; } goto type_error; } ival = (int)Runtime.PyInt_AsLong(op); Runtime.XDecref(op); if (ival > Int16.MaxValue || ival < Int16.MinValue) { goto overflow; } short s = (short)ival; result = s; return true; case TypeCode.Int64: op = Runtime.PyNumber_Long(value); if (op == IntPtr.Zero) { if (Exceptions.ExceptionMatches(overflow)) { goto overflow; } goto type_error; } long l = (long)Runtime.PyLong_AsLongLong(op); Runtime.XDecref(op); if ((l == -1) && Exceptions.ErrorOccurred()) { goto overflow; } result = l; return true; case TypeCode.UInt16: op = Runtime.PyNumber_Int(value); if (op == IntPtr.Zero) { if (Exceptions.ExceptionMatches(overflow)) { goto overflow; } goto type_error; } ival = (int)Runtime.PyInt_AsLong(op); Runtime.XDecref(op); if (ival > UInt16.MaxValue || ival < UInt16.MinValue) { goto overflow; } ushort us = (ushort)ival; result = us; return true; case TypeCode.UInt32: op = Runtime.PyNumber_Long(value); if (op == IntPtr.Zero) { if (Exceptions.ExceptionMatches(overflow)) { goto overflow; } goto type_error; } uint ui; try { ui = Convert.ToUInt32(Runtime.PyLong_AsUnsignedLong(op)); } catch (OverflowException) { // Probably wasn't an overflow in python but was in C# (e.g. if cpython // longs are 64 bit then 0xFFFFFFFF + 1 will not overflow in // PyLong_AsUnsignedLong) Runtime.XDecref(op); goto overflow; } if (Exceptions.ErrorOccurred()) { Runtime.XDecref(op); goto overflow; } IntPtr check = Runtime.PyLong_FromUnsignedLong(ui); int err = Runtime.PyObject_Compare(check, op); Runtime.XDecref(check); Runtime.XDecref(op); if (0 != err || Exceptions.ErrorOccurred()) { goto overflow; } result = ui; return true; case TypeCode.UInt64: op = Runtime.PyNumber_Long(value); if (op == IntPtr.Zero) { if (Exceptions.ExceptionMatches(overflow)) { goto overflow; } goto type_error; } ulong ul = (ulong)Runtime.PyLong_AsUnsignedLongLong(op); Runtime.XDecref(op); if (Exceptions.ErrorOccurred()) { goto overflow; } result = ul; return true; case TypeCode.Single: op = Runtime.PyNumber_Float(value); if (op == IntPtr.Zero) { if (Exceptions.ExceptionMatches(overflow)) { goto overflow; } goto type_error; } double dd = Runtime.PyFloat_AsDouble(op); Runtime.CheckExceptionOccurred(); Runtime.XDecref(op); if (dd > Single.MaxValue || dd < Single.MinValue) { if (!double.IsInfinity(dd)) { goto overflow; } } result = (float)dd; return true; case TypeCode.Double: op = Runtime.PyNumber_Float(value); if (op == IntPtr.Zero) { goto type_error; } double d = Runtime.PyFloat_AsDouble(op); Runtime.CheckExceptionOccurred(); Runtime.XDecref(op); result = d; return true; } type_error: if (setError) { string tpName = Runtime.PyObject_GetTypeName(value); Exceptions.SetError(Exceptions.TypeError, $"'{tpName}' value cannot be converted to {obType}"); } return false; overflow: if (setError) { Exceptions.SetError(Exceptions.OverflowError, "value too large to convert"); } return false; } private static void SetConversionError(IntPtr value, Type target) { IntPtr ob = Runtime.PyObject_Repr(value); string src = Runtime.GetManagedString(ob); Runtime.XDecref(ob); Exceptions.SetError(Exceptions.TypeError, $"Cannot convert {src} to {target}"); } /// /// Convert a Python value to a correctly typed managed array instance. /// The Python value must support the Python iterator protocol or and the /// items in the sequence must be convertible to the target array type. /// private static bool ToArray(IntPtr value, Type obType, out object result, bool setError) { Type elementType = obType.GetElementType(); result = null; bool IsSeqObj = Runtime.PySequence_Check(value); var len = IsSeqObj ? Runtime.PySequence_Size(value) : -1; IntPtr IterObject = Runtime.PyObject_GetIter(value); if(IterObject==IntPtr.Zero) { if (setError) { SetConversionError(value, obType); } return false; } Array items; var listType = typeof(List<>); var constructedListType = listType.MakeGenericType(elementType); IList list = IsSeqObj ? (IList) Activator.CreateInstance(constructedListType, new Object[] {(int) len}) : (IList) Activator.CreateInstance(constructedListType); IntPtr item; while ((item = Runtime.PyIter_Next(IterObject)) != IntPtr.Zero) { object obj = null; if (!Converter.ToManaged(item, elementType, out obj, true)) { Runtime.XDecref(item); return false; } list.Add(obj); Runtime.XDecref(item); } Runtime.XDecref(IterObject); items = Array.CreateInstance(elementType, list.Count); list.CopyTo(items, 0); result = items; return true; } /// /// Convert a Python value to a correctly typed managed enum instance. /// private static bool ToEnum(IntPtr value, Type obType, out object result, bool setError) { Type etype = Enum.GetUnderlyingType(obType); result = null; if (!ToPrimitive(value, etype, out result, setError)) { return false; } if (Enum.IsDefined(obType, result)) { result = Enum.ToObject(obType, result); return true; } if (obType.GetCustomAttributes(flagsType, true).Length > 0) { result = Enum.ToObject(obType, result); return true; } if (setError) { Exceptions.SetError(Exceptions.ValueError, "invalid enumeration value"); } return false; } } public static class ConverterExtension { public static PyObject ToPython(this object o) { return new PyObject(Converter.ToPython(o, o?.GetType())); } } }
X Tutup