[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/patstew/pythonnet/python3/src/runtime/converter.cs [Back]  [Original]

// ==========================================================================
// This software is subject to the provisions of the Zope Public License,
// Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
// THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
// WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
// FOR A PARTICULAR PURPOSE.
// ==========================================================================

using System;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Globalization;
using System.Security;
using System.Collections;

namespace Python.Runtime {

    //========================================================================
    // Performs data conversions between managed types and Python types.
    //========================================================================

    [SuppressUnmanagedCodeSecurityAttribute()]

    internal class Converter {

        private Converter() {}

        static NumberFormatInfo nfi;
        static Type objectType;
        static Type stringType;
        static Type doubleType;
        static Type int32Type;
        static Type int64Type;
        static Type flagsType;
        static Type boolType;
        static Type typeType;

        static Converter () {
            nfi = NumberFormatInfo.InvariantInfo;
            objectType = typeof(Object);
            stringType = typeof(String);
            int32Type = typeof(Int32);
            int64Type = typeof(Int64);
            doubleType = typeof(Double);
            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) ||
                (op == Runtime.PyUnicodeType)) {
                return stringType;
            }
            else if (op == Runtime.PyIntType) {
                return int32Type;
            }
            else if (op == Runtime.PyLongType) {
                return int64Type;
            }
            else if (op == Runtime.PyFloatType) {
                return doubleType;
            }
            else if (op == Runtime.PyBoolType) {
                return boolType;
            }
            return null;
        }


        //====================================================================
        // 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(Object value, Type type) {
            IntPtr result = IntPtr.Zero;

            // Null always converts to None in Python.

            if (value == null) {
                result = Runtime.PyNone;
                Runtime.Incref(result);
                return result;
            }

            // it the type is a python subclass of a managed type then return the
            // underying python object rather than construct a new wrapper object.
            IPythonDerivedType 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:
                result = CLRObject.GetInstHandle(value, type);

                // XXX - hack to make sure we convert new-style class based
                // managed exception instances to wrappers ;(
                if (Runtime.wrap_exceptions) {
                    Exception e = value as Exception;
                    if (e != null) {
                        return Exceptions.GetExceptionInstanceWrapper(result);
                    }
                }

                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.Incref(resultlist.Handle);
                        return resultlist.Handle;
                    }
                }

                return result;

            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.Incref(Runtime.PyTrue);
                    return Runtime.PyTrue;
                }
                Runtime.Incref(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.Decref(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:
                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.Incref(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) {
            // Common case: if the Python value is a wrapped managed object
            // instance, just return the wrapped object.
            ManagedType mt = ManagedType.GetManagedObject(value);
            result = null;

            // XXX - hack to support objects wrapped in old-style classes
            // (such as exception objects).
            if (Runtime.wrap_exceptions) {
            if (mt == null) {
                if (Runtime.PyObject_IsInstance(
                            value, Exceptions.Exception
                            ) > 0) {
                    IntPtr p = Runtime.PyObject_GetAttrString(value, "_inner");
                    if (p != IntPtr.Zero) {
                        // This is safe because we know that the __dict__ of
                        // value holds a reference to _inner.
                        value = p;
                        Runtime.Decref(p);
                        mt = ManagedType.GetManagedObject(value);
                    }
                }
                IntPtr c = Exceptions.UnwrapExceptionClass(value);
                if ((c != IntPtr.Zero) && (c != value)) {
                    value = c;
                    Runtime.Decref(c);
                    mt = ManagedType.GetManagedObject(value);
                }
            }
            }

            if (mt != null) {
                if (mt is CLRObject) {
                    object tmp = ((CLRObject)mt).inst;
                    if (obType.IsInstanceOfType(tmp)) {
                        result = tmp;
                        return true;
                    }
                    string err = "value cannot be converted to {0}";
                    err = String.Format(err, obType);
                    Exceptions.SetError(Exceptions.TypeError, err);
                    return false;
                }
                if (mt is ClassBase) {
                    result = ((ClassBase)mt).type;
                    return true;
                }
                // shouldnt happen
                return false;
            }

            if (value == Runtime.PyNone && !obType.IsValueType) {
                result = null;
                return true;
            }

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

                else if (Runtime.PyBool_Check(value)) {
                    return ToPrimitive(value, boolType, out result, setError);
                }

                else if (Runtime.PyInt_Check(value)) {
                    return ToPrimitive(value, int32Type, out result, setError);
                }

                else if (Runtime.PyLong_Check(value)) {
                    return ToPrimitive(value, int64Type, out result, setError);
                }

                else if (Runtime.PyFloat_Check(value)) {
                    return ToPrimitive(value, doubleType, out result, setError);
                }

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

                else if (value == Runtime.PyBoolType)
                {
                    result = boolType;
                    return true;
                }

                else if (value == Runtime.PyIntType)
                {
                    result = int32Type;
                    return true;
                }

                else if (value == Runtime.PyLongType)
                {
                    result = int64Type;
                    return true;
                }

                else if (value == Runtime.PyFloatType)
                {
                    result = doubleType;
                    return true;
                }

                else 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.
        //====================================================================

        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:
                // Trickery to support 64-bit platforms.
                if (IntPtr.Size == 4) {
                    op = Runtime.PyNumber_Int(value);

                    // As of Python 2.3, large ints magically convert :(
                    if (Runtime.PyLong_Check(op) ) {
                        Runtime.Decref(op);
                        goto overflow;
                    }

                    if (op == IntPtr.Zero) {
                        if (Exceptions.ExceptionMatches(overflow)) {
                            goto overflow;
                        }
                      goto type_error;
                    }
                    ival = (int)Runtime.PyInt_AsLong(op);
                    Runtime.Decref(op);
                    result = ival;
                    return true;
                }
                else {
                    op = Runtime.PyNumber_Long(value);
                    if (op == IntPtr.Zero) {
                        if (Exceptions.ExceptionMatches(overflow)) {
                            goto overflow;
                        }
                        goto type_error;
                    }
                    long ll = (long)Runtime.PyLong_AsLongLong(op);
                    Runtime.Decref(op);
                    if ((ll == -1) && Exceptions.ErrorOccurred()) {
                        goto overflow;
                    }
                    if (ll > Int32.MaxValue || ll < Int32.MinValue) {
                        goto overflow;
                    }
                    result = (int)ll;
                    return true;
                }

            case TypeCode.Boolean:
                result = (Runtime.PyObject_IsTrue(value) != 0);
                return true;

            case TypeCode.Byte:
#if (PYTHON32 || PYTHON33 || PYTHON34)
                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_AS_STRING(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.Decref(op);

                if (ival > Byte.MaxValue || ival < Byte.MinValue) {
                    goto overflow;
                }
                byte b = (byte) ival;
                result = b;
                return true;

            case TypeCode.SByte:
#if (PYTHON32 || PYTHON33 || PYTHON34)
                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_AS_STRING(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.Decref(op);

                if (ival > SByte.MaxValue || ival < SByte.MinValue) {
                    goto overflow;
                }
                sbyte sb = (sbyte) ival;
                result = sb;
                return true;

            case TypeCode.Char:
#if (PYTHON32 || PYTHON33 || PYTHON34)
                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_AS_STRING(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_AS_UNICODE(value);
#if (!UCS4)
                        // 2011-01-02: Marshal as character array because the cast
                        // result = (char)Marshal.ReadInt16(op); throws an OverflowException
                        // on negative numbers with Check Overflow option set on the project
                        Char[] buff = new Char[1];
                        Marshal.Copy(op, buff, 0, 1);
                        result = buff[0];
#else
                        // XXX this is probably NOT correct?
                        result = (char)Marshal.ReadInt32(op);
#endif
                        return true;
                    }
                    goto type_error;
                }

                op = Runtime.PyNumber_Int(value);
                if (op == IntPtr.Zero) {
                    goto type_error;
                }
                ival = Runtime.PyInt_AsLong(op);
                if (ival > Char.MaxValue || ival < Char.MinValue) {
                    goto overflow;
                }
                Runtime.Decref(op);
                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.Decref(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.Decref(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.Decref(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 = (uint)Runtime.PyLong_AsUnsignedLong(op);
                Runtime.Decref(op);
                if (Exceptions.ErrorOccurred()) {
                    goto overflow;
                }

                IntPtr check = Runtime.PyLong_FromUnsignedLong(ui);
                int err = Runtime.PyObject_Compare(check, op);
                Runtime.Decref(check);
                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.Decref(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(value);
                if (dd > Single.MaxValue || dd < Single.MinValue) {
                    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.Decref(op);
                if (d > Double.MaxValue || d < Double.MinValue) {
                    goto overflow;
                }
                result = d;
                return true;

            }


        type_error:

            if (setError) {
                string format = "'{0}' value cannot be converted to {1}";
                string tpName = Runtime.PyObject_GetTypeName(value);
                string error = String.Format(format, tpName, obType);
                Exceptions.SetError(Exceptions.TypeError, error);
            }

            return false;

        overflow:

            if (setError) {
                string error = "value too large to convert";
                Exceptions.SetError(Exceptions.OverflowError, error);
            }

            return false;

        }


        static void SetConversionError(IntPtr value, Type target) {
            IntPtr ob = Runtime.PyObject_Repr(value);
            string src = Runtime.GetManagedString(ob);
            Runtime.Decref(ob);
            string error = String.Format(
                           "Cannot convert {0} to {1}", src, target
                           );
            Exceptions.SetError(Exceptions.TypeError, error);
        }


        //====================================================================
        // Convert a Python value to a correctly typed managed array instance.
        // The Python value must support the Python sequence protocol and the
        // items in the sequence must be convertible to the target array type.
        //====================================================================

        static bool ToArray(IntPtr value, Type obType, out Object result,
                           bool setError) {

            Type elementType = obType.GetElementType();
            int size = Runtime.PySequence_Size(value);
            result = null;

            if (size < 0) {
                if (setError) {
                    SetConversionError(value, obType);
                }
                return false;
            }

            Array items = Array.CreateInstance(elementType, size);

            // XXX - is there a better way to unwrap this if it is a real
            // array?
            for (int i = 0; i < size; i++) {
                Object obj = null;
                IntPtr item = Runtime.PySequence_GetItem(value, i);
                if (item == IntPtr.Zero) {
                    if (setError) {
                        SetConversionError(value, obType);
                        return false;
                    }
                }

                if (!Converter.ToManaged(item, elementType, out obj, true)) {
                    Runtime.Decref(item);
                    return false;
                }

                items.SetValue(obj, i);
                Runtime.Decref(item);
            }

            result = items;
            return true;
        }


        //====================================================================
        // Convert a Python value to a correctly typed managed enum instance.
        //====================================================================

        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) {
                string error = "invalid enumeration value";
                Exceptions.SetError(Exceptions.ValueError, error);
            }

            return false;

        }
    }

    public static class ConverterExtension
    {
        public static PyObject ToPython(this object o)
        {
            return new PyObject(Converter.ToPython(o, o.GetType()));
        }
    }
}

Web Proxy Viewer  |  New URL  |  Original Page