/// <summary> /// Given a sequence of MethodInfo and a sequence of type parameters, /// return the MethodInfo that represents the matching closed generic. /// If unsuccessful, returns null and may set a Python error. /// </summary> internal static MethodInfo MatchParameters(MethodInfo[] mi, Type[] tp) { if (tp == null) { return(null); } int count = tp.Length; foreach (MethodInfo t in mi) { if (!t.IsGenericMethodDefinition) { continue; } Type[] args = t.GetGenericArguments(); if (args.Length != count) { continue; } try { // MakeGenericMethod can throw ArgumentException if the type parameters do not obey the constraints. MethodInfo method = t.MakeGenericMethod(tp); Exceptions.Clear(); return(method); } catch (ArgumentException e) { Exceptions.SetError(e); // The error will remain set until cleared by a successful match. } } return(null); }
private static ExceptionDispatchInfo?TryGetDispatchInfo(BorrowedReference exception) { if (exception.IsNull) { return(null); } var pyInfo = Runtime.PyObject_GetAttrString(exception, Exceptions.DispatchInfoAttribute); if (pyInfo.IsNull()) { if (Exceptions.ExceptionMatches(Exceptions.AttributeError)) { Exceptions.Clear(); } return(null); } try { if (Converter.ToManagedValue(pyInfo, typeof(ExceptionDispatchInfo), out object result, setError: false)) { return((ExceptionDispatchInfo)result); } return(null); } finally { pyInfo.Dispose(); } }
/// <remarks> /// Unlike <see cref="ToManaged(BorrowedReference, Type, out object?, bool)"/>, /// this method does not have a <c>setError</c> parameter, because it should /// only be called after <see cref="ToManaged(BorrowedReference, Type, out object?, bool)"/>. /// </remarks> internal static bool ToManagedExplicit(BorrowedReference value, Type obType, out object?result) { result = null; // this method would potentially clean any existing error resulting in information loss Debug.Assert(Runtime.PyErr_Occurred() == null); string?converterName = IsInteger(obType) ? "__int__" : IsFloatingNumber(obType) ? "__float__" : null; if (converterName is null) { return(false); } Debug.Assert(obType.IsPrimitive); using var converter = Runtime.PyObject_GetAttrString(value, converterName); if (converter.IsNull()) { Exceptions.Clear(); return(false); } using var explicitlyCoerced = Runtime.PyObject_CallObject(converter.Borrow(), BorrowedReference.Null); if (explicitlyCoerced.IsNull()) { Exceptions.Clear(); return(false); } return(ToPrimitive(explicitlyCoerced.Borrow(), obType, out result, false)); }
/// <summary> /// Initialize Method /// </summary> /// <remarks> /// Initialize the Python runtime. It is safe to call this method /// more than once, though initialization will only happen on the /// first call. It is *not* necessary to hold the Python global /// interpreter lock (GIL) to call this method. /// initSigs can be set to 1 to do default python signal configuration. This will override the way signals are handled by the application. /// </remarks> public static void Initialize(IEnumerable <string> args, bool setSysArgv = true, bool initSigs = false) { if (!initialized) { // Creating the delegateManager MUST happen before Runtime.Initialize // is called. If it happens afterwards, DelegateManager's CodeGenerator // throws an exception in its ctor. This exception is eaten somehow // during an initial "import clr", and the world ends shortly thereafter. // This is probably masking some bad mojo happening somewhere in Runtime.Initialize(). delegateManager = new DelegateManager(); Runtime.Initialize(initSigs); initialized = true; Exceptions.Clear(); // Make sure we clean up properly on app domain unload. AppDomain.CurrentDomain.DomainUnload += OnDomainUnload; // Remember to shut down the runtime. AddShutdownHandler(Runtime.Shutdown); // The global scope gets used implicitly quite early on, remember // to clear it out when we shut down. AddShutdownHandler(PyScopeManager.Global.Clear); if (setSysArgv) { Py.SetArgv(args); } } }
/// <summary> /// Initialize Method /// </summary> /// /// <remarks> /// Initialize the Python runtime. It is safe to call this method /// more than once, though initialization will only happen on the /// first call. It is *not* necessary to hold the Python global /// interpreter lock (GIL) to call this method. /// </remarks> public static void Initialize() { if (!initialized) { Runtime.Initialize(); initialized = true; Exceptions.Clear(); } }
public static IntPtr tp_new(IntPtr tpRaw, IntPtr args, IntPtr kw) { if (kw != IntPtr.Zero) { return(Exceptions.RaiseTypeError("array constructor takes no keyword arguments")); } var tp = new BorrowedReference(tpRaw); var self = GetManagedObject(tp) as ArrayObject; if (!self.type.Valid) { return(Exceptions.RaiseTypeError(self.type.DeletedMessage)); } Type arrType = self.type.Value; long[] dimensions = new long[Runtime.PyTuple_Size(args)]; if (dimensions.Length == 0) { return(Exceptions.RaiseTypeError("array constructor requires at least one integer argument or an object convertible to array")); } if (dimensions.Length != 1) { return(CreateMultidimensional(arrType.GetElementType(), dimensions, shapeTuple: new BorrowedReference(args), pyType: tp) .DangerousMoveToPointerOrNull()); } IntPtr op = Runtime.PyTuple_GetItem(args, 0); // create single dimensional array if (Runtime.PyInt_Check(op)) { dimensions[0] = Runtime.PyLong_AsSignedSize_t(op); if (dimensions[0] == -1 && Exceptions.ErrorOccurred()) { Exceptions.Clear(); } else { return(NewInstance(arrType.GetElementType(), tp, dimensions) .DangerousMoveToPointerOrNull()); } } object result; // this implements casting to Array[T] if (!Converter.ToManaged(op, arrType, out result, true)) { return(IntPtr.Zero); } return(CLRObject.GetInstHandle(result, tp) .DangerousGetAddress()); }
public static NewReference tp_new(BorrowedReference tp, BorrowedReference args, BorrowedReference kw) { if (kw != null) { return(Exceptions.RaiseTypeError("array constructor takes no keyword arguments")); } var self = (ArrayObject)GetManagedObject(tp) !; if (!self.type.Valid) { return(Exceptions.RaiseTypeError(self.type.DeletedMessage)); } Type arrType = self.type.Value; long[] dimensions = new long[Runtime.PyTuple_Size(args)]; if (dimensions.Length == 0) { return(Exceptions.RaiseTypeError("array constructor requires at least one integer argument or an object convertible to array")); } if (dimensions.Length != 1) { return(CreateMultidimensional(arrType.GetElementType(), dimensions, shapeTuple: args, pyType: tp)); } BorrowedReference op = Runtime.PyTuple_GetItem(args, 0); // create single dimensional array if (Runtime.PyInt_Check(op)) { dimensions[0] = Runtime.PyLong_AsSignedSize_t(op); if (dimensions[0] == -1 && Exceptions.ErrorOccurred()) { Exceptions.Clear(); } else { return(NewInstance(arrType.GetElementType(), tp, dimensions)); } } // this implements casting to Array[T] if (Converter.ToManaged(op, arrType, out object?result, true)) { return(CLRObject.GetReference(result !, tp)); } else { return(default);
/// <summary> /// Initialize Method /// </summary> /// /// <remarks> /// Initialize the Python runtime. It is safe to call this method /// more than once, though initialization will only happen on the /// first call. It is *not* necessary to hold the Python global /// interpreter lock (GIL) to call this method. /// </remarks> public static void Initialize() { if (!initialized) { // Creating the delegateManager MUST happen before Runtime.Initialize // is called. If it happens afterwards, DelegateManager's CodeGenerator // throws an exception in its ctor. This exception is eaten somehow // during an initial "import clr", and the world ends shortly thereafter. // This is probably masking some bad mojo happening somewhere in Runtime.Initialize(). delegateManager = new DelegateManager(); Runtime.Initialize(); initialized = true; Exceptions.Clear(); } }
/// <summary> /// The initializing of the preload hook has to happen as late as /// possible since sys.ps1 is created after the CLR module is /// created. /// </summary> internal void InitializePreload() { if (interactive_preload) { interactive_preload = false; if (Runtime.PySys_GetObject("ps1") != IntPtr.Zero) { preload = true; } else { Exceptions.Clear(); preload = false; } } }
/// <summary> /// Equals Method /// </summary> /// <remarks> /// Return true if this object is equal to the given object. This /// method is based on Python equality semantics. /// </remarks> public override bool Equals(object o) { if (!(o is PyObject)) { return(false); } if (obj == ((PyObject)o).obj) { return(true); } int r = Runtime.PyObject_Compare(obj, ((PyObject)o).obj); if (Exceptions.ErrorOccurred()) { Exceptions.Clear(); // throw new PythonException(); } return(r == 0); }
private static void SetConversionError(BorrowedReference value, Type target) { // PyObject_Repr might clear the error Runtime.PyErr_Fetch(out var causeType, out var causeVal, out var causeTrace); var ob = Runtime.PyObject_Repr(value); string src = "'object has no repr'"; if (ob.IsNull()) { Exceptions.Clear(); } else { src = Runtime.GetManagedString(ob.Borrow()) ?? src; } ob.Dispose(); Runtime.PyErr_Restore(causeType.StealNullable(), causeVal.StealNullable(), causeTrace.StealNullable()); Exceptions.RaiseTypeError($"Cannot convert {src} to {target}"); }
static bool TryConvertArgument(IntPtr op, Type parameterType, bool needsResolution, out object arg, out bool isOut) { arg = null; isOut = false; var clrtype = TryComputeClrArgumentType(parameterType, op, needsResolution: needsResolution); if (clrtype == null) { return(false); } if (!Converter.ToManaged(op, clrtype, out arg, false)) { Exceptions.Clear(); return(false); } isOut = clrtype.IsByRef; return(true); }
internal static IntPtr UnwrapExceptionClass(IntPtr op) { // In some cases its necessary to recognize an exception *class*, // and obtain the inner (wrapped) exception class. This method // returns the inner class if found, or a null pointer. IntPtr d = Runtime.PyObject_GetAttrString(op, "__dict__"); if (d == IntPtr.Zero) { Exceptions.Clear(); return(IntPtr.Zero); } IntPtr c = Runtime.PyDict_GetItemString(d, "_class"); Runtime.Decref(d); if (c == IntPtr.Zero) { Exceptions.Clear(); } return(c); }
/// <summary> /// 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. /// </summary> private static bool ToArray(IntPtr value, Type obType, out object result, bool setError) { Type elementType = obType.GetElementType(); result = null; IntPtr IterObject = Runtime.PyObject_GetIter(value); if (IterObject == IntPtr.Zero || elementType.IsGenericType) { if (setError) { SetConversionError(value, obType); } else { // PyObject_GetIter will have set an error Exceptions.Clear(); } return(false); } var list = MakeList(value, IterObject, obType, elementType, setError); if (list == null) { return(false); } Array items = Array.CreateInstance(elementType, list.Count); list.CopyTo(items, 0); result = items; return(true); }
/// <summary> /// Convert a Python value to an instance of a primitive managed type. /// </summary> 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: // Trickery to support 64-bit platforms. if (Runtime.IsPython2 && Runtime.Is32Bit) { op = Runtime.Interop.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.Interop.PyInt_AsLong(op); Runtime.XDecref(op); result = ival; return(true); } else // Python3 always use PyLong API { op = Runtime.Interop.PyNumber_Long(value); if (op == IntPtr.Zero) { Exceptions.Clear(); if (Exceptions.ExceptionMatches(overflow)) { goto overflow; } goto type_error; } long ll = (long)Runtime.Interop.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); } case TypeCode.Boolean: result = Runtime.Interop.PyObject_IsTrue(value) != 0; return(true); case TypeCode.Byte: #if PYTHON3 if (Runtime.PyObject_TypeCheck(value, Runtime.PyBytesType)) { if (Runtime.Interop.PyBytes_Size(value) == 1) { op = Runtime.PyBytes_AS_STRING(value); result = (byte)Marshal.ReadByte(op); return(true); } goto type_error; } #elif PYTHON2 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.Interop.PyNumber_Int(value); if (op == IntPtr.Zero) { if (Exceptions.ExceptionMatches(overflow)) { goto overflow; } goto type_error; } ival = (int)Runtime.Interop.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 PYTHON3 if (Runtime.PyObject_TypeCheck(value, Runtime.PyBytesType)) { if (Runtime.Interop.PyBytes_Size(value) == 1) { op = Runtime.PyBytes_AS_STRING(value); result = (byte)Marshal.ReadByte(op); return(true); } goto type_error; } #elif PYTHON2 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.Interop.PyNumber_Int(value); if (op == IntPtr.Zero) { if (Exceptions.ExceptionMatches(overflow)) { goto overflow; } goto type_error; } ival = (int)Runtime.Interop.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 PYTHON3 if (Runtime.PyObject_TypeCheck(value, Runtime.PyBytesType)) { if (Runtime.Interop.PyBytes_Size(value) == 1) { op = Runtime.PyBytes_AS_STRING(value); result = (byte)Marshal.ReadByte(op); return(true); } goto type_error; } #elif PYTHON2 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.Interop.PyUnicode_GetSize(value) == 1) { op = Runtime.Interop.PyUnicode_AsUnicode(value); Char[] buff = new Char[1]; Marshal.Copy(op, buff, 0, 1); result = buff[0]; return(true); } goto type_error; } op = Runtime.Interop.PyNumber_Int(value); if (op == IntPtr.Zero) { goto type_error; } ival = Runtime.Interop.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.Interop.PyNumber_Int(value); if (op == IntPtr.Zero) { if (Exceptions.ExceptionMatches(overflow)) { goto overflow; } goto type_error; } ival = (int)Runtime.Interop.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.Interop.PyNumber_Long(value); if (op == IntPtr.Zero) { if (Exceptions.ExceptionMatches(overflow)) { goto overflow; } goto type_error; } long l = (long)Runtime.Interop.PyLong_AsLongLong(op); Runtime.XDecref(op); if ((l == -1) && Exceptions.ErrorOccurred()) { goto overflow; } result = l; return(true); case TypeCode.UInt16: op = Runtime.Interop.PyNumber_Int(value); if (op == IntPtr.Zero) { if (Exceptions.ExceptionMatches(overflow)) { goto overflow; } goto type_error; } ival = (int)Runtime.Interop.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.Interop.PyNumber_Long(value); if (op == IntPtr.Zero) { if (Exceptions.ExceptionMatches(overflow)) { goto overflow; } goto type_error; } uint ui = (uint)Runtime.Interop.PyLong_AsUnsignedLong(op); if (Exceptions.ErrorOccurred()) { Runtime.XDecref(op); goto overflow; } IntPtr check = Runtime.Interop.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.Interop.PyNumber_Long(value); if (op == IntPtr.Zero) { if (Exceptions.ExceptionMatches(overflow)) { goto overflow; } goto type_error; } ulong ul = (ulong)Runtime.Interop.PyLong_AsUnsignedLongLong(op); Runtime.XDecref(op); if (Exceptions.ErrorOccurred()) { goto overflow; } result = ul; return(true); case TypeCode.Single: op = Runtime.Interop.PyNumber_Float(value); if (op == IntPtr.Zero) { if (Exceptions.ExceptionMatches(overflow)) { goto overflow; } goto type_error; } double dd = Runtime.Interop.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.Interop.PyNumber_Float(value); if (op == IntPtr.Zero) { goto type_error; } double d = Runtime.Interop.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); }
/// <summary> /// Convert a Python value to an instance of a primitive managed type. /// </summary> private static bool ToPrimitive(IntPtr value, Type obType, out object result, bool setError) { TypeCode tc = Type.GetTypeCode(obType); result = null; IntPtr op = IntPtr.Zero; switch (tc) { case TypeCode.String: string st = Runtime.GetManagedString(value); if (st == null) { goto type_error; } result = st; return(true); case TypeCode.Int32: { // Python3 always use PyLong API nint num = Runtime.PyLong_AsSignedSize_t(value); if (num == -1 && Exceptions.ErrorOccurred()) { goto convert_error; } if (num > Int32.MaxValue || num < Int32.MinValue) { goto overflow; } result = (int)num; return(true); } case TypeCode.Boolean: result = Runtime.PyObject_IsTrue(value) != 0; return(true); case TypeCode.Byte: { 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; } nint num = Runtime.PyLong_AsSignedSize_t(value); if (num == -1 && Exceptions.ErrorOccurred()) { goto convert_error; } if (num > Byte.MaxValue || num < Byte.MinValue) { goto overflow; } result = (byte)num; return(true); } case TypeCode.SByte: { 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; } nint num = Runtime.PyLong_AsSignedSize_t(value); if (num == -1 && Exceptions.ErrorOccurred()) { goto convert_error; } if (num > SByte.MaxValue || num < SByte.MinValue) { goto overflow; } result = (sbyte)num; return(true); } case TypeCode.Char: { 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.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; } nint num = Runtime.PyLong_AsSignedSize_t(value); if (num == -1 && Exceptions.ErrorOccurred()) { goto convert_error; } if (num > Char.MaxValue || num < Char.MinValue) { goto overflow; } result = (char)num; return(true); } case TypeCode.Int16: { nint num = Runtime.PyLong_AsSignedSize_t(value); if (num == -1 && Exceptions.ErrorOccurred()) { goto convert_error; } if (num > Int16.MaxValue || num < Int16.MinValue) { goto overflow; } result = (short)num; return(true); } case TypeCode.Int64: { if (Runtime.Is32Bit) { if (!Runtime.PyLong_Check(value)) { goto type_error; } long num = Runtime.PyExplicitlyConvertToInt64(value); if (num == -1 && Exceptions.ErrorOccurred()) { goto convert_error; } result = num; return(true); } else { nint num = Runtime.PyLong_AsSignedSize_t(value); if (num == -1 && Exceptions.ErrorOccurred()) { goto convert_error; } result = (long)num; return(true); } } case TypeCode.UInt16: { nint num = Runtime.PyLong_AsSignedSize_t(value); if (num == -1 && Exceptions.ErrorOccurred()) { goto convert_error; } if (num > UInt16.MaxValue || num < UInt16.MinValue) { goto overflow; } result = (ushort)num; return(true); } case TypeCode.UInt32: { nuint num = Runtime.PyLong_AsUnsignedSize_t(value); if (num == unchecked ((nuint)(-1)) && Exceptions.ErrorOccurred()) { goto convert_error; } if (num > UInt32.MaxValue) { goto overflow; } result = (uint)num; return(true); } case TypeCode.UInt64: { ulong num = Runtime.PyLong_AsUnsignedLongLong(value); if (num == ulong.MaxValue && Exceptions.ErrorOccurred()) { goto convert_error; } result = num; return(true); } case TypeCode.Single: { double num = Runtime.PyFloat_AsDouble(value); if (num == -1.0 && Exceptions.ErrorOccurred()) { goto convert_error; } if (num > Single.MaxValue || num < Single.MinValue) { if (!double.IsInfinity(num)) { goto overflow; } } result = (float)num; return(true); } case TypeCode.Double: { double num = Runtime.PyFloat_AsDouble(value); if (num == -1.0 && Exceptions.ErrorOccurred()) { goto convert_error; } result = num; return(true); } default: goto type_error; } convert_error: if (op != value) { Runtime.XDecref(op); } if (!setError) { Exceptions.Clear(); } return(false); type_error: if (setError) { string tpName = Runtime.PyObject_GetTypeName(value); Exceptions.SetError(Exceptions.TypeError, $"'{tpName}' value cannot be converted to {obType}"); } return(false); overflow: // C# level overflow error if (op != value) { Runtime.XDecref(op); } if (setError) { Exceptions.SetError(Exceptions.OverflowError, "value too large to convert"); } return(false); }
internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, MethodInfo[] methodinfo) { // loop to find match, return invoker w/ or /wo error int pynargs = Runtime.PyTuple_Size(args); object arg; var isGeneric = false; ArrayList defaultArgList; Type clrtype; Binding bindingUsingImplicitConversion = null; var methods = info == null?GetMethods() : new List <MethodInformation>(1) { new MethodInformation(info, info.GetParameters()) }; // TODO: Clean up foreach (var methodInformation in methods) { var mi = methodInformation.MethodBase; var pi = methodInformation.ParameterInfo; if (mi.IsGenericMethod) { isGeneric = true; } int clrnargs = pi.Length; int arrayStart; if (CheckMethodArgumentsMatch(clrnargs, pynargs, pi, out arrayStart, out defaultArgList)) { var outs = 0; var margs = new object[clrnargs]; var usedImplicitConversion = false; for (var n = 0; n < clrnargs; n++) { IntPtr op; if (n < pynargs) { if (arrayStart == n) { // map remaining Python arguments to a tuple since // the managed function accepts it - hopefully :] op = Runtime.PyTuple_GetSlice(args, arrayStart, pynargs); } else { op = Runtime.PyTuple_GetItem(args, n); } // this logic below handles cases when multiple overloading methods // are ambiguous, hence comparison between Python and CLR types // is necessary clrtype = null; IntPtr pyoptype; if (methods.Count > 1) { pyoptype = IntPtr.Zero; pyoptype = Runtime.PyObject_Type(op); Exceptions.Clear(); if (pyoptype != IntPtr.Zero) { clrtype = Converter.GetTypeByAlias(pyoptype); } Runtime.XDecref(pyoptype); } if (clrtype != null) { var typematch = false; if ((pi[n].ParameterType != typeof(object)) && (pi[n].ParameterType != clrtype)) { IntPtr pytype = Converter.GetPythonTypeByAlias(pi[n].ParameterType); pyoptype = Runtime.PyObject_Type(op); Exceptions.Clear(); if (pyoptype != IntPtr.Zero) { if (pytype != pyoptype) { typematch = false; } else { typematch = true; clrtype = pi[n].ParameterType; } } 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(underlyingType); TypeCode paramtypecode = Type.GetTypeCode(clrtype); if (argtypecode == paramtypecode) { typematch = true; clrtype = pi[n].ParameterType; } // accepts non-decimal numbers in decimal parameters if (underlyingType == typeof(decimal)) { clrtype = pi[n].ParameterType; typematch = Converter.ToManaged(op, clrtype, out arg, false); } // this takes care of implicit conversions var opImplicit = pi[n].ParameterType.GetMethod("op_Implicit", new[] { clrtype }); if (opImplicit != null) { usedImplicitConversion = typematch = opImplicit.ReturnType == pi[n].ParameterType; clrtype = pi[n].ParameterType; } } Runtime.XDecref(pyoptype); if (!typematch) { margs = null; break; } } else { typematch = true; clrtype = pi[n].ParameterType; } } else { clrtype = pi[n].ParameterType; } if (pi[n].IsOut || clrtype.IsByRef) { outs++; } if (!Converter.ToManaged(op, clrtype, out arg, false)) { Exceptions.Clear(); margs = null; break; } if (arrayStart == n) { // GetSlice() creates a new reference but GetItem() // returns only a borrow reference. Runtime.XDecref(op); } margs[n] = arg; } else { if (defaultArgList != null) { margs[n] = defaultArgList[n - pynargs]; } } } if (margs == null) { continue; } object target = null; if (!mi.IsStatic && inst != IntPtr.Zero) { //CLRObject co = (CLRObject)ManagedType.GetManagedObject(inst); // InvalidCastException: Unable to cast object of type // 'Python.Runtime.ClassObject' to type 'Python.Runtime.CLRObject' var co = ManagedType.GetManagedObject(inst) as CLRObject; // Sanity check: this ensures a graceful exit if someone does // something intentionally wrong like call a non-static method // on the class rather than on an instance of the class. // XXX maybe better to do this before all the other rigmarole. if (co == null) { return(null); } target = co.inst; } var binding = new Binding(mi, target, margs, outs); if (usedImplicitConversion) { // lets just keep the first binding using implicit conversion // this is to respect method order/precedence if (bindingUsingImplicitConversion == null) { // in this case we will not return the binding yet in case there is a match // which does not use implicit conversions, which will return directly bindingUsingImplicitConversion = binding; } } else { return(binding); } } } // if we generated a binding using implicit conversion return it if (bindingUsingImplicitConversion != null) { return(bindingUsingImplicitConversion); } // We weren't able to find a matching method but at least one // is a generic method and info is null. That happens when a generic // method was not called using the [] syntax. Let's introspect the // type of the arguments and use it to construct the correct method. if (isGeneric && info == null && methodinfo != null) { Type[] types = Runtime.PythonArgsToTypeArray(args, true); MethodInfo mi = MatchParameters(methodinfo, types); return(Bind(inst, args, kw, mi, null)); } return(null); }
/// <summary> /// Convert a Python value to an instance of a primitive managed type. /// </summary> private static bool ToPrimitive(IntPtr value, Type obType, out object result, bool setError) { TypeCode tc = Type.GetTypeCode(obType); result = null; IntPtr op = IntPtr.Zero; switch (tc) { case TypeCode.String: string st = Runtime.GetManagedString(value); if (st == null) { goto type_error; } result = st; return(true); case TypeCode.Int32: { // Python3 always use PyLong API long num = Runtime.PyLong_AsLongLong(value); if (num == -1 && Exceptions.ErrorOccurred()) { goto convert_error; } if (num > Int32.MaxValue || num < Int32.MinValue) { goto overflow; } result = (int)num; return(true); } case TypeCode.Boolean: result = Runtime.PyObject_IsTrue(value) != 0; return(true); case TypeCode.Byte: { 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; } int num = Runtime.PyLong_AsLong(value); if (num == -1 && Exceptions.ErrorOccurred()) { goto convert_error; } if (num > Byte.MaxValue || num < Byte.MinValue) { goto overflow; } result = (byte)num; return(true); } case TypeCode.SByte: { 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; } int num = Runtime.PyLong_AsLong(value); if (num == -1 && Exceptions.ErrorOccurred()) { goto convert_error; } if (num > SByte.MaxValue || num < SByte.MinValue) { goto overflow; } result = (sbyte)num; return(true); } case TypeCode.Char: { 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.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; } int num = Runtime.PyLong_AsLong(value); if (num == -1 && Exceptions.ErrorOccurred()) { goto convert_error; } if (num > Char.MaxValue || num < Char.MinValue) { goto overflow; } result = (char)num; return(true); } case TypeCode.Int16: { int num = Runtime.PyLong_AsLong(value); if (num == -1 && Exceptions.ErrorOccurred()) { goto convert_error; } if (num > Int16.MaxValue || num < Int16.MinValue) { goto overflow; } result = (short)num; return(true); } case TypeCode.Int64: { long num = (long)Runtime.PyLong_AsLongLong(value); if (num == -1 && Exceptions.ErrorOccurred()) { goto convert_error; } result = num; return(true); } case TypeCode.UInt16: { long num = Runtime.PyLong_AsLong(value); if (num == -1 && Exceptions.ErrorOccurred()) { goto convert_error; } if (num > UInt16.MaxValue || num < UInt16.MinValue) { goto overflow; } result = (ushort)num; return(true); } case TypeCode.UInt32: { op = value; if (Runtime.PyObject_TYPE(value) != Runtime.PyLongType) { op = Runtime.PyNumber_Long(value); if (op == IntPtr.Zero) { goto convert_error; } } if (Runtime.Is32Bit || Runtime.IsWindows) { uint num = Runtime.PyLong_AsUnsignedLong32(op); if (num == uint.MaxValue && Exceptions.ErrorOccurred()) { goto convert_error; } result = num; } else { ulong num = Runtime.PyLong_AsUnsignedLong64(op); if (num == ulong.MaxValue && Exceptions.ErrorOccurred()) { goto convert_error; } try { result = Convert.ToUInt32(num); } 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) goto overflow; } } return(true); } case TypeCode.UInt64: { op = value; if (Runtime.PyObject_TYPE(value) != Runtime.PyLongType) { op = Runtime.PyNumber_Long(value); if (op == IntPtr.Zero) { goto convert_error; } } ulong num = Runtime.PyLong_AsUnsignedLongLong(op); if (num == ulong.MaxValue && Exceptions.ErrorOccurred()) { goto overflow; } result = num; return(true); } case TypeCode.Single: { double num = Runtime.PyFloat_AsDouble(value); if (num == -1.0 && Exceptions.ErrorOccurred()) { goto convert_error; } if (num > Single.MaxValue || num < Single.MinValue) { if (!double.IsInfinity(num)) { goto overflow; } } result = (float)num; return(true); } case TypeCode.Double: { double num = Runtime.PyFloat_AsDouble(value); if (num == -1.0 && Exceptions.ErrorOccurred()) { goto convert_error; } result = num; return(true); } default: goto type_error; } convert_error: if (op != value) { Runtime.XDecref(op); } if (!setError) { Exceptions.Clear(); } return(false); type_error: if (setError) { string tpName = Runtime.PyObject_GetTypeName(value); Exceptions.SetError(Exceptions.TypeError, $"'{tpName}' value cannot be converted to {obType}"); } return(false); overflow: // C# level overflow error if (op != value) { Runtime.XDecref(op); } if (setError) { Exceptions.SetError(Exceptions.OverflowError, "value too large to convert"); } return(false); }
static Type TryComputeClrArgumentType(Type parameterType, IntPtr argument, bool needsResolution) { // this logic below handles cases when multiple overloading methods // are ambiguous, hence comparison between Python and CLR types // is necessary Type clrtype = null; IntPtr pyoptype; if (needsResolution) { // HACK: each overload should be weighted in some way instead pyoptype = Runtime.PyObject_Type(argument); Exceptions.Clear(); if (pyoptype != IntPtr.Zero) { clrtype = Converter.GetTypeByAlias(pyoptype); } Runtime.XDecref(pyoptype); } if (clrtype != null) { var typematch = false; if ((parameterType != typeof(object)) && (parameterType != clrtype)) { IntPtr pytype = Converter.GetPythonTypeByAlias(parameterType); pyoptype = Runtime.PyObject_Type(argument); Exceptions.Clear(); if (pyoptype != IntPtr.Zero) { if (pytype != pyoptype) { typematch = false; } else { typematch = true; clrtype = parameterType; } } if (!typematch) { // this takes care of enum values TypeCode argtypecode = Type.GetTypeCode(parameterType); TypeCode paramtypecode = Type.GetTypeCode(clrtype); if (argtypecode == paramtypecode) { typematch = true; clrtype = parameterType; } } Runtime.XDecref(pyoptype); if (!typematch) { return(null); } } else { typematch = true; clrtype = parameterType; } } else { clrtype = parameterType; } return(clrtype); }
/// <summary> /// Initialize Method /// </summary> /// /// <remarks> /// Initialize the Python runtime. It is safe to call this method /// more than once, though initialization will only happen on the /// first call. It is *not* necessary to hold the Python global /// interpreter lock (GIL) to call this method. /// </remarks> public static void Initialize() { if (!initialized) { // Creating the delegateManager MUST happen before Runtime.Initialize // is called. If it happens afterwards, DelegateManager's CodeGenerator // throws an exception in its ctor. This exception is eaten somehow // during an initial "import clr", and the world ends shortly thereafter. // This is probably masking some bad mojo happening somewhere in Runtime.Initialize(). delegateManager = new DelegateManager(); Runtime.Initialize(); initialized = true; Exceptions.Clear(); // register the atexit callback (this doesn't use Py_AtExit as the C atexit // callbacks are called after python is fully finalized but the python ones // are called while the python engine is still running). string code = "import atexit, clr\n" + "atexit.register(clr._AtExit)\n"; PyObject r = PythonEngine.RunString(code); if (r != null) { r.Dispose(); } // Load the clr.py resource into the clr module IntPtr clr = Python.Runtime.ImportHook.GetCLRModule(); IntPtr clr_dict = Runtime.PyModule_GetDict(clr); PyDict locals = new PyDict(); try { IntPtr module = Runtime.PyImport_AddModule("clr._extras"); IntPtr module_globals = Runtime.PyModule_GetDict(module); IntPtr builtins = Runtime.PyEval_GetBuiltins(); Runtime.PyDict_SetItemString(module_globals, "__builtins__", builtins); var assembly = Assembly.GetExecutingAssembly(); using (Stream stream = assembly.GetManifestResourceStream("clr.py")) using (StreamReader reader = new StreamReader(stream)) { // add the contents of clr.py to the module string clr_py = reader.ReadToEnd(); PyObject result = RunString(clr_py, module_globals, locals.Handle); if (null == result) { throw new PythonException(); } result.Dispose(); } // add the imported module to the clr module, and copy the API functions // and decorators into the main clr module. Runtime.PyDict_SetItemString(clr_dict, "_extras", module); foreach (PyObject key in locals.Keys()) { if (!key.ToString().StartsWith("_")) { PyObject value = locals[key]; Runtime.PyDict_SetItem(clr_dict, key.Handle, value.Handle); value.Dispose(); } key.Dispose(); } } finally { locals.Dispose(); } } }
/// <summary> /// Convert a Python value to an instance of a primitive managed type. /// </summary> private static bool ToPrimitive(BorrowedReference value, Type obType, out object?result, bool setError) { result = null; if (obType.IsEnum) { if (setError) { Exceptions.SetError(Exceptions.TypeError, "since Python.NET 3.0 int can not be converted to Enum implicitly. Use Enum(int_value)"); } return(false); } TypeCode tc = Type.GetTypeCode(obType); switch (tc) { case TypeCode.String: string?st = Runtime.GetManagedString(value); if (st == null) { goto type_error; } result = st; return(true); case TypeCode.Int32: { // Python3 always use PyLong API nint num = Runtime.PyLong_AsSignedSize_t(value); if (num == -1 && Exceptions.ErrorOccurred()) { goto convert_error; } if (num > Int32.MaxValue || num < Int32.MinValue) { goto overflow; } result = (int)num; return(true); } case TypeCode.Boolean: if (value == Runtime.PyTrue) { result = true; return(true); } if (value == Runtime.PyFalse) { result = false; return(true); } if (setError) { goto type_error; } return(false); case TypeCode.Byte: { if (Runtime.PyObject_TypeCheck(value, Runtime.PyBytesType)) { if (Runtime.PyBytes_Size(value) == 1) { IntPtr bytePtr = Runtime.PyBytes_AsString(value); result = (byte)Marshal.ReadByte(bytePtr); return(true); } goto type_error; } nint num = Runtime.PyLong_AsSignedSize_t(value); if (num == -1 && Exceptions.ErrorOccurred()) { goto convert_error; } if (num > Byte.MaxValue || num < Byte.MinValue) { goto overflow; } result = (byte)num; return(true); } case TypeCode.SByte: { if (Runtime.PyObject_TypeCheck(value, Runtime.PyBytesType)) { if (Runtime.PyBytes_Size(value) == 1) { IntPtr bytePtr = Runtime.PyBytes_AsString(value); result = (sbyte)Marshal.ReadByte(bytePtr); return(true); } goto type_error; } nint num = Runtime.PyLong_AsSignedSize_t(value); if (num == -1 && Exceptions.ErrorOccurred()) { goto convert_error; } if (num > SByte.MaxValue || num < SByte.MinValue) { goto overflow; } result = (sbyte)num; return(true); } case TypeCode.Char: { if (Runtime.PyObject_TypeCheck(value, Runtime.PyBytesType)) { if (Runtime.PyBytes_Size(value) == 1) { IntPtr bytePtr = Runtime.PyBytes_AsString(value); result = (char)Marshal.ReadByte(bytePtr); return(true); } goto type_error; } else if (Runtime.PyObject_TypeCheck(value, Runtime.PyUnicodeType)) { if (Runtime.PyUnicode_GetLength(value) == 1) { IntPtr unicodePtr = Runtime.PyUnicode_AsUnicode(value); Char[] buff = new Char[1]; Marshal.Copy(unicodePtr, buff, 0, 1); result = buff[0]; return(true); } goto type_error; } nint num = Runtime.PyLong_AsSignedSize_t(value); if (num == -1 && Exceptions.ErrorOccurred()) { goto convert_error; } if (num > Char.MaxValue || num < Char.MinValue) { goto overflow; } result = (char)num; return(true); } case TypeCode.Int16: { nint num = Runtime.PyLong_AsSignedSize_t(value); if (num == -1 && Exceptions.ErrorOccurred()) { goto convert_error; } if (num > Int16.MaxValue || num < Int16.MinValue) { goto overflow; } result = (short)num; return(true); } case TypeCode.Int64: { if (Runtime.Is32Bit) { if (!Runtime.PyLong_Check(value)) { goto type_error; } long?num = Runtime.PyLong_AsLongLong(value); if (num is null) { goto convert_error; } result = num.Value; return(true); } else { nint num = Runtime.PyLong_AsSignedSize_t(value); if (num == -1 && Exceptions.ErrorOccurred()) { goto convert_error; } result = (long)num; return(true); } } case TypeCode.UInt16: { nint num = Runtime.PyLong_AsSignedSize_t(value); if (num == -1 && Exceptions.ErrorOccurred()) { goto convert_error; } if (num > UInt16.MaxValue || num < UInt16.MinValue) { goto overflow; } result = (ushort)num; return(true); } case TypeCode.UInt32: { nuint num = Runtime.PyLong_AsUnsignedSize_t(value); if (num == unchecked ((nuint)(-1)) && Exceptions.ErrorOccurred()) { goto convert_error; } if (num > UInt32.MaxValue) { goto overflow; } result = (uint)num; return(true); } case TypeCode.UInt64: { ulong?num = Runtime.PyLong_AsUnsignedLongLong(value); if (num is null) { goto convert_error; } result = num.Value; return(true); } case TypeCode.Single: { if (!Runtime.PyFloat_Check(value) && !Runtime.PyInt_Check(value)) { goto type_error; } double num = Runtime.PyFloat_AsDouble(value); if (num == -1.0 && Exceptions.ErrorOccurred()) { goto convert_error; } if (num > Single.MaxValue || num < Single.MinValue) { if (!double.IsInfinity(num)) { goto overflow; } } result = (float)num; return(true); } case TypeCode.Double: { if (!Runtime.PyFloat_Check(value) && !Runtime.PyInt_Check(value)) { goto type_error; } double num = Runtime.PyFloat_AsDouble(value); if (num == -1.0 && Exceptions.ErrorOccurred()) { goto convert_error; } result = num; return(true); } default: goto type_error; } convert_error: if (!setError) { Exceptions.Clear(); } return(false); type_error: if (setError) { string tpName = Runtime.PyObject_GetTypeName(value); Exceptions.SetError(Exceptions.TypeError, $"'{tpName}' value cannot be converted to {obType}"); } return(false); overflow: // C# level overflow error if (setError) { Exceptions.SetError(Exceptions.OverflowError, "value too large to convert"); } return(false); }
/// <summary> /// 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. /// </summary> private static bool ToArray(BorrowedReference value, Type obType, out object?result, bool setError) { Type elementType = obType.GetElementType(); result = null; using var IterObject = Runtime.PyObject_GetIter(value); if (IterObject.IsNull()) { if (setError) { SetConversionError(value, obType); } else { // PyObject_GetIter will have set an error Exceptions.Clear(); } return(false); } IList list; try { // MakeGenericType can throw because elementType may not be a valid generic argument even though elementType[] is a valid array type. // For example, if elementType is a pointer type. // See https://docs.microsoft.com/en-us/dotnet/api/system.type.makegenerictype#System_Type_MakeGenericType_System_Type var constructedListType = typeof(List <>).MakeGenericType(elementType); bool IsSeqObj = Runtime.PySequence_Check(value); object[] constructorArgs = Array.Empty <object>(); if (IsSeqObj) { var len = Runtime.PySequence_Size(value); if (len >= 0) { if (len <= int.MaxValue) { constructorArgs = new object[] { (int)len }; } } else { // for the sequences, that explicitly deny calling __len__() Exceptions.Clear(); } } // CreateInstance can throw even if MakeGenericType succeeded. // See https://docs.microsoft.com/en-us/dotnet/api/system.activator.createinstance#System_Activator_CreateInstance_System_Type_ list = (IList)Activator.CreateInstance(constructedListType, args: constructorArgs); } catch (Exception e) { if (setError) { Exceptions.SetError(e); SetConversionError(value, obType); } return(false); } while (true) { using var item = Runtime.PyIter_Next(IterObject.Borrow()); if (item.IsNull()) { break; } if (!Converter.ToManaged(item.Borrow(), elementType, out var obj, setError)) { return(false); } list.Add(obj); } if (Exceptions.ErrorOccurred()) { if (!setError) { Exceptions.Clear(); } return(false); } Array items = Array.CreateInstance(elementType, list.Count); list.CopyTo(items, 0); result = items; return(true); }
internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, MethodInfo[] methodinfo) { // loop to find match, return invoker w/ or /wo error MethodBase[] _methods = null; int pynargs = Runtime.PyTuple_Size(args); object arg; bool isGeneric = false; if (info != null) { _methods = (MethodBase[])Array.CreateInstance( typeof(MethodBase), 1 ); _methods.SetValue(info, 0); } else { _methods = GetMethods(); } for (int i = 0; i < _methods.Length; i++) { MethodBase mi = _methods[i]; if (mi.IsGenericMethod) { isGeneric = true; } ParameterInfo[] pi = mi.GetParameters(); int clrnargs = pi.Length; bool match = false; int arrayStart = -1; int outs = 0; if (pynargs == clrnargs) { match = true; } else if ((pynargs > clrnargs) && (clrnargs > 0) && (pi[clrnargs - 1].ParameterType.IsArray)) { // The last argument of the mananged functions seems to // accept multiple arguments as a array. Hopefully it's a // spam(params object[] egg) style method match = true; arrayStart = clrnargs - 1; } if (match) { Object[] margs = new Object[clrnargs]; for (int n = 0; n < clrnargs; n++) { IntPtr op; if (arrayStart == n) { // map remaining Python arguments to a tuple since // the managed function accepts it - hopefully :] op = Runtime.PyTuple_GetSlice(args, arrayStart, pynargs); } else { op = Runtime.PyTuple_GetItem(args, n); } Type type = pi[n].ParameterType; if (pi[n].IsOut || type.IsByRef) { outs++; } if (!Converter.ToManaged(op, type, out arg, false)) { Exceptions.Clear(); margs = null; break; } if (arrayStart == n) { // GetSlice() creates a new reference but GetItem() // returns only a borrow reference. Runtime.Decref(op); } margs[n] = arg; } if (margs == null) { continue; } Object target = null; if ((!mi.IsStatic) && (inst != IntPtr.Zero)) { //CLRObject co = (CLRObject)ManagedType.GetManagedObject(inst); // InvalidCastException: Unable to cast object of type // 'Python.Runtime.ClassObject' to type 'Python.Runtime.CLRObject' CLRObject co = ManagedType.GetManagedObject(inst) as CLRObject; // Sanity check: this ensures a graceful exit if someone does // something intentionally wrong like call a non-static method // on the class rather than on an instance of the class. // XXX maybe better to do this before all the other rigmarole. if (co == null) { return(null); } target = co.inst; } return(new Binding(mi, target, margs, outs)); } } // We weren't able to find a matching method but at least one // is a generic method and info is null. That happens when a generic // method was not called using the [] syntax. Let's introspect the // type of the arguments and use it to construct the correct method. if (isGeneric && (info == null) && (methodinfo != null)) { Type[] types = Runtime.PythonArgsToTypeArray(args, true); MethodInfo mi = MethodBinder.MatchParameters(methodinfo, types); return(Bind(inst, args, kw, mi, null)); } return(null); }
/// <summary> /// The actual import hook that ties Python to the managed world. /// </summary> public static IntPtr __import__(IntPtr self, IntPtr args, IntPtr kw) { // Replacement for the builtin __import__. The original import // hook is saved as this.py_import. This version handles CLR // import and defers to the normal builtin for everything else. var num_args = Runtime.PyTuple_Size(args); if (num_args < 1) { return(Exceptions.RaiseTypeError("__import__() takes at least 1 argument (0 given)")); } // borrowed reference IntPtr py_mod_name = Runtime.PyTuple_GetItem(args, 0); if (py_mod_name == IntPtr.Zero || !Runtime.IsStringType(py_mod_name)) { return(Exceptions.RaiseTypeError("string expected")); } // Check whether the import is of the form 'from x import y'. // This determines whether we return the head or tail module. IntPtr fromList = IntPtr.Zero; var fromlist = false; if (num_args >= 4) { fromList = Runtime.PyTuple_GetItem(args, 3); if (fromList != IntPtr.Zero && Runtime.PyObject_IsTrue(fromList) == 1) { fromlist = true; } } string mod_name = Runtime.GetManagedString(py_mod_name); // Check these BEFORE the built-in import runs; may as well // do the Incref()ed return here, since we've already found // the module. if (mod_name == "clr" || mod_name == "CLR") { if (mod_name == "CLR") { Exceptions.deprecation("The CLR module is deprecated. Please use 'clr'."); } IntPtr clr_module = GetCLRModule(fromList); if (clr_module != IntPtr.Zero) { IntPtr sys_modules = Runtime.PyImport_GetModuleDict(); if (sys_modules != IntPtr.Zero) { Runtime.PyDict_SetItemString(sys_modules, "clr", clr_module); } } return(clr_module); } string realname = mod_name; string clr_prefix = null; if (mod_name.StartsWith("CLR.")) { clr_prefix = "CLR."; // prepend when adding the module to sys.modules realname = mod_name.Substring(4); string msg = $"Importing from the CLR.* namespace is deprecated. Please import '{realname}' directly."; Exceptions.deprecation(msg); } else { // 2010-08-15: Always seemed smart to let python try first... // This shaves off a few tenths of a second on test_module.py // and works around a quirk where 'sys' is found by the // LoadImplicit() deprecation logic. // Turns out that the AssemblyManager.ResolveHandler() checks to see if any // Assembly's FullName.ToLower().StartsWith(name.ToLower()), which makes very // little sense to me. IntPtr res = Runtime.PyObject_Call(py_import, args, kw); if (res != IntPtr.Zero) { // There was no error. if (fromlist && IsLoadAll(fromList)) { var mod = ManagedType.GetManagedObject(res) as ModuleObject; mod?.LoadNames(); } return(res); } // There was an error if (!Exceptions.ExceptionMatches(Exceptions.ImportError)) { // and it was NOT an ImportError; bail out here. return(IntPtr.Zero); } if (mod_name == string.Empty) { // Most likely a missing relative import. // For example site-packages\bs4\builder\__init__.py uses it to check if a package exists: // from . import _html5lib // We don't support them anyway return(IntPtr.Zero); } // Otherwise, just clear the it. Exceptions.Clear(); } string[] names = realname.Split('.'); // Now we need to decide if the name refers to a CLR module, // and may have to do an implicit load (for b/w compatibility) // using the AssemblyManager. The assembly manager tries // really hard not to use Python objects or APIs, because // parts of it can run recursively and on strange threads. // // It does need an opportunity from time to time to check to // see if sys.path has changed, in a context that is safe. Here // we know we have the GIL, so we'll let it update if needed. AssemblyManager.UpdatePath(); if (!AssemblyManager.IsValidNamespace(realname)) { var loadExceptions = new List <Exception>(); if (!AssemblyManager.LoadImplicit(realname, assemblyLoadErrorHandler: loadExceptions.Add)) { // May be called when a module being imported imports a module. // In particular, I've seen decimal import copy import org.python.core IntPtr importResult = Runtime.PyObject_Call(py_import, args, kw); // TODO: use ModuleNotFoundError in Python 3.6+ if (importResult == IntPtr.Zero && loadExceptions.Count > 0 && Exceptions.ExceptionMatches(Exceptions.ImportError)) { loadExceptions.Add(new PythonException()); var importError = new PyObject(new BorrowedReference(Exceptions.ImportError)); importError.SetAttr("__cause__", new AggregateException(loadExceptions).ToPython()); Runtime.PyErr_SetObject(new BorrowedReference(Exceptions.ImportError), importError.Reference); } return(importResult); } } // See if sys.modules for this interpreter already has the // requested module. If so, just return the existing module. IntPtr modules = Runtime.PyImport_GetModuleDict(); IntPtr module = Runtime.PyDict_GetItem(modules, py_mod_name); if (module != IntPtr.Zero) { if (fromlist) { if (IsLoadAll(fromList)) { var mod = ManagedType.GetManagedObject(module) as ModuleObject; mod?.LoadNames(); } Runtime.XIncref(module); return(module); } if (clr_prefix != null) { return(GetCLRModule(fromList)); } module = Runtime.PyDict_GetItemString(modules, names[0]); Runtime.XIncref(module); return(module); } Exceptions.Clear(); // Traverse the qualified module name to get the named module // and place references in sys.modules as we go. Note that if // we are running in interactive mode we pre-load the names in // each module, which is often useful for introspection. If we // are not interactive, we stick to just-in-time creation of // objects at lookup time, which is much more efficient. // NEW: The clr got a new module variable preload. You can // enable preloading in a non-interactive python processing by // setting clr.preload = True ModuleObject head = mod_name == realname ? null : root; ModuleObject tail = root; root.InitializePreload(); foreach (string name in names) { ManagedType mt = tail.GetAttribute(name, true); if (!(mt is ModuleObject)) { Exceptions.SetError(Exceptions.ImportError, $"No module named {name}"); return(IntPtr.Zero); } if (head == null) { head = (ModuleObject)mt; } tail = (ModuleObject)mt; if (CLRModule.preload) { tail.LoadNames(); } // Add the module to sys.modules Runtime.PyDict_SetItemString(modules, tail.moduleName, tail.pyHandle); // If imported from CLR add CLR.<modulename> to sys.modules as well if (clr_prefix != null) { Runtime.PyDict_SetItemString(modules, clr_prefix + tail.moduleName, tail.pyHandle); } } { var mod = fromlist ? tail : head; if (fromlist && IsLoadAll(fromList)) { mod.LoadNames(); } Runtime.XIncref(mod.pyHandle); return(mod.pyHandle); } }
/// <summary> /// Convert a Python value to an instance of a primitive managed type. /// </summary> private static bool ToPrimitive(IntPtr value, Type obType, out object result, bool setError) { TypeCode tc = Type.GetTypeCode(obType); result = null; IntPtr op = IntPtr.Zero; 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) { goto type_error; } result = st; return(true); case TypeCode.Int32: { // Python3 always use PyLong API op = Runtime.PyNumber_Long(value); if (op == IntPtr.Zero && Exceptions.ErrorOccurred()) { goto convert_error; } nint num = Runtime.PyLong_AsSignedSize_t(op); if (num == -1 && Exceptions.ErrorOccurred()) { goto convert_error; } if (num > Int32.MaxValue || num < Int32.MinValue) { goto overflow; } result = (int)num; return(true); } case TypeCode.Boolean: result = Runtime.PyObject_IsTrue(value) != 0; return(true); case TypeCode.Byte: { 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; } nint num = Runtime.PyLong_AsSignedSize_t(value); if (num == -1 && Exceptions.ErrorOccurred()) { goto convert_error; } if (num > Byte.MaxValue || num < Byte.MinValue) { goto overflow; } result = (byte)num; return(true); } case TypeCode.SByte: { 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; } nint num = Runtime.PyLong_AsSignedSize_t(value); if (num == -1 && Exceptions.ErrorOccurred()) { goto convert_error; } if (num > SByte.MaxValue || num < SByte.MinValue) { goto overflow; } result = (sbyte)num; return(true); } case TypeCode.Char: { 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.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; } nint num = Runtime.PyLong_AsSignedSize_t(value); if (num == -1 && Exceptions.ErrorOccurred()) { goto convert_error; } if (num > Char.MaxValue || num < Char.MinValue) { goto overflow; } result = (char)num; return(true); } case TypeCode.Int16: { op = Runtime.PyNumber_Long(value); if (op == IntPtr.Zero && Exceptions.ErrorOccurred()) { goto convert_error; } nint num = Runtime.PyLong_AsSignedSize_t(op); if (num == -1 && Exceptions.ErrorOccurred()) { goto convert_error; } if (num > Int16.MaxValue || num < Int16.MinValue) { goto overflow; } result = (short)num; return(true); } case TypeCode.Int64: { if (Runtime.Is32Bit) { if (!Runtime.PyLong_Check(value)) { goto type_error; } long num = Runtime.PyExplicitlyConvertToInt64(value); if (num == -1 && Exceptions.ErrorOccurred()) { goto convert_error; } result = num; return(true); } else { op = Runtime.PyNumber_Long(value); if (op == IntPtr.Zero && Exceptions.ErrorOccurred()) { goto convert_error; } nint num = Runtime.PyLong_AsSignedSize_t(op); if (num == -1 && Exceptions.ErrorOccurred()) { goto convert_error; } result = (long)num; return(true); } } case TypeCode.UInt16: { op = Runtime.PyNumber_Long(value); if (op == IntPtr.Zero && Exceptions.ErrorOccurred()) { goto convert_error; } nint num = Runtime.PyLong_AsSignedSize_t(op); if (num == -1 && Exceptions.ErrorOccurred()) { goto convert_error; } if (num > UInt16.MaxValue || num < UInt16.MinValue) { goto overflow; } result = (ushort)num; return(true); } case TypeCode.UInt32: { op = Runtime.PyNumber_Long(value); if (op == IntPtr.Zero && Exceptions.ErrorOccurred()) { goto convert_error; } nuint num = Runtime.PyLong_AsUnsignedSize_t(op); if (num == unchecked ((nuint)(-1)) && Exceptions.ErrorOccurred()) { goto convert_error; } if (num > UInt32.MaxValue) { goto overflow; } result = (uint)num; return(true); } case TypeCode.UInt64: { op = Runtime.PyNumber_Long(value); if (op == IntPtr.Zero && Exceptions.ErrorOccurred()) { goto convert_error; } ulong num = Runtime.PyLong_AsUnsignedLongLong(op); if (num == ulong.MaxValue && Exceptions.ErrorOccurred()) { goto convert_error; } result = num; return(true); } case TypeCode.Single: { double num = Runtime.PyFloat_AsDouble(value); if (num == -1.0 && Exceptions.ErrorOccurred()) { goto convert_error; } if (num > Single.MaxValue || num < Single.MinValue) { if (!double.IsInfinity(num)) { goto overflow; } } result = (float)num; return(true); } case TypeCode.Double: { double num = Runtime.PyFloat_AsDouble(value); if (num == -1.0 && Exceptions.ErrorOccurred()) { goto convert_error; } result = num; return(true); } case TypeCode.Decimal: op = Runtime.PyObject_Str(value); decimal m; string sm = Runtime.GetManagedString(op); if (!Decimal.TryParse(sm, NumberStyles.Number | NumberStyles.AllowExponent, 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 = sdt.EndsWith("+00:00") ? dt.ToUniversalTime() : dt; return(true); default: goto type_error; } convert_error: if (op != value) { Runtime.XDecref(op); } if (!setError) { Exceptions.Clear(); } return(false); type_error: if (setError) { string tpName = Runtime.PyObject_GetTypeName(value); Exceptions.SetError(Exceptions.TypeError, $"'{tpName}' value cannot be converted to {obType}"); } return(false); overflow: // C# level overflow error if (op != value) { Runtime.XDecref(op); } if (setError) { Exceptions.SetError(Exceptions.OverflowError, "value too large to convert"); } return(false); }
/// <summary> /// Initialize Method /// </summary> /// <remarks> /// Initialize the Python runtime. It is safe to call this method /// more than once, though initialization will only happen on the /// first call. It is *not* necessary to hold the Python global /// interpreter lock (GIL) to call this method. /// initSigs can be set to 1 to do default python signal configuration. This will override the way signals are handled by the application. /// </remarks> public static void Initialize(IEnumerable <string> args, bool setSysArgv = true, bool initSigs = false) { if (!initialized) { // Creating the delegateManager MUST happen before Runtime.Initialize // is called. If it happens afterwards, DelegateManager's CodeGenerator // throws an exception in its ctor. This exception is eaten somehow // during an initial "import clr", and the world ends shortly thereafter. // This is probably masking some bad mojo happening somewhere in Runtime.Initialize(). delegateManager = new DelegateManager(); Runtime.Initialize(initSigs); initialized = true; Exceptions.Clear(); // Make sure we clean up properly on app domain unload. AppDomain.CurrentDomain.DomainUnload += OnDomainUnload; // Remember to shut down the runtime. AddShutdownHandler(Runtime.Shutdown); // The global scope gets used implicitly quite early on, remember // to clear it out when we shut down. AddShutdownHandler(PyScopeManager.Global.Clear); if (setSysArgv) { Py.SetArgv(args); } // register the atexit callback (this doesn't use Py_AtExit as the C atexit // callbacks are called after python is fully finalized but the python ones // are called while the python engine is still running). string code = "import atexit, clr\n" + "atexit.register(clr._AtExit)\n"; PythonEngine.Exec(code); // Load the clr.py resource into the clr module IntPtr clr = Python.Runtime.ImportHook.GetCLRModule(); IntPtr clr_dict = Runtime.PyModule_GetDict(clr); var locals = new PyDict(); try { IntPtr module = Runtime.PyImport_AddModule("clr._extras"); IntPtr module_globals = Runtime.PyModule_GetDict(module); IntPtr builtins = Runtime.PyEval_GetBuiltins(); Runtime.PyDict_SetItemString(module_globals, "__builtins__", builtins); Assembly assembly = Assembly.GetExecutingAssembly(); using (Stream stream = assembly.GetManifestResourceStream("clr.py")) using (var reader = new StreamReader(stream)) { // add the contents of clr.py to the module string clr_py = reader.ReadToEnd(); Exec(clr_py, module_globals, locals.Handle); } // add the imported module to the clr module, and copy the API functions // and decorators into the main clr module. Runtime.PyDict_SetItemString(clr_dict, "_extras", module); foreach (PyObject key in locals.Keys()) { if (!key.ToString().StartsWith("_") || key.ToString().Equals("__version__")) { PyObject value = locals[key]; Runtime.PyDict_SetItem(clr_dict, key.Handle, value.Handle); value.Dispose(); } key.Dispose(); } } finally { locals.Dispose(); } } }
//=================================================================== // The actual import hook that ties Python to the managed world. //=================================================================== public static IntPtr __import__(IntPtr self, IntPtr args, IntPtr kw) { // Replacement for the builtin __import__. The original import // hook is saved as this.py_import. This version handles CLR // import and defers to the normal builtin for everything else. int num_args = Runtime.PyTuple_Size(args); if (num_args < 1) { return(Exceptions.RaiseTypeError( "__import__() takes at least 1 argument (0 given)" )); } // borrowed reference IntPtr py_mod_name = Runtime.PyTuple_GetItem(args, 0); if ((py_mod_name == IntPtr.Zero) || (!Runtime.IsStringType(py_mod_name))) { return(Exceptions.RaiseTypeError("string expected")); } // Check whether the import is of the form 'from x import y'. // This determines whether we return the head or tail module. IntPtr fromList = IntPtr.Zero; bool fromlist = false; if (num_args >= 4) { fromList = Runtime.PyTuple_GetItem(args, 3); if ((fromList != IntPtr.Zero) && (Runtime.PyObject_IsTrue(fromList) == 1)) { fromlist = true; } } string mod_name = Runtime.GetManagedString(py_mod_name); // Check these BEFORE the built-in import runs; may as well // do the Incref()ed return here, since we've already found // the module. if (mod_name == "clr") { root.InitializePreload(); Runtime.Incref(root.pyHandle); return(root.pyHandle); } if (mod_name == "CLR") { Exceptions.deprecation("The CLR module is deprecated. " + "Please use 'clr'."); root.InitializePreload(); Runtime.Incref(root.pyHandle); return(root.pyHandle); } string realname = mod_name; if (mod_name.StartsWith("CLR.")) { realname = mod_name.Substring(4); string msg = String.Format("Importing from the CLR.* namespace " + "is deprecated. Please import '{0}' directly.", realname); Exceptions.deprecation(msg); } else { // 2010-08-15: Always seemed smart to let python try first... // This shaves off a few tenths of a second on test_module.py // and works around a quirk where 'sys' is found by the // LoadImplicit() deprecation logic. // Turns out that the AssemblyManager.ResolveHandler() checks to see if any // Assembly's FullName.ToLower().StartsWith(name.ToLower()), which makes very // little sense to me. IntPtr res = Runtime.PyObject_Call(py_import, args, kw); if (res != IntPtr.Zero) { // There was no error. return(res); } // There was an error if (!Exceptions.ExceptionMatches(Exceptions.ImportError)) { // and it was NOT an ImportError; bail out here. return(IntPtr.Zero); } // Otherwise, just clear the it. Exceptions.Clear(); } string[] names = realname.Split('.'); // Now we need to decide if the name refers to a CLR module, // and may have to do an implicit load (for b/w compatibility) // using the AssemblyManager. The assembly manager tries // really hard not to use Python objects or APIs, because // parts of it can run recursively and on strange threads. // // It does need an opportunity from time to time to check to // see if sys.path has changed, in a context that is safe. Here // we know we have the GIL, so we'll let it update if needed. AssemblyManager.UpdatePath(); if (!AssemblyManager.IsValidNamespace(realname)) { bool fromFile = false; if (AssemblyManager.LoadImplicit(realname, out fromFile)) { if (true == fromFile) { string deprWarning = String.Format("\nThe module was found, but not in a referenced namespace.\n" + "Implicit loading is deprecated. Please use clr.AddReference(\"{0}\").", realname); Exceptions.deprecation(deprWarning); } } else { // May be called when a module being imported imports a module. // In particular, I've seen decimal import copy import org.python.core return(Runtime.PyObject_Call(py_import, args, kw)); } } // See if sys.modules for this interpreter already has the // requested module. If so, just return the exising module. IntPtr modules = Runtime.PyImport_GetModuleDict(); IntPtr module = Runtime.PyDict_GetItem(modules, py_mod_name); if (module != IntPtr.Zero) { if (fromlist) { Runtime.Incref(module); return(module); } module = Runtime.PyDict_GetItemString(modules, names[0]); Runtime.Incref(module); return(module); } Exceptions.Clear(); // Traverse the qualified module name to get the named module // and place references in sys.modules as we go. Note that if // we are running in interactive mode we pre-load the names in // each module, which is often useful for introspection. If we // are not interactive, we stick to just-in-time creation of // objects at lookup time, which is much more efficient. // NEW: The clr got a new module variable preload. You can // enable preloading in a non-interactive python processing by // setting clr.preload = True ModuleObject head = (mod_name == realname) ? null : root; ModuleObject tail = root; root.InitializePreload(); for (int i = 0; i < names.Length; i++) { string name = names[i]; ManagedType mt = tail.GetAttribute(name, true); if (!(mt is ModuleObject)) { string error = String.Format("No module named {0}", name); Exceptions.SetError(Exceptions.ImportError, error); return(IntPtr.Zero); } if (head == null) { head = (ModuleObject)mt; } tail = (ModuleObject)mt; if (CLRModule.preload) { tail.LoadNames(); } Runtime.PyDict_SetItemString(modules, tail.moduleName, tail.pyHandle ); } ModuleObject mod = fromlist ? tail : head; if (fromlist && Runtime.PySequence_Size(fromList) == 1) { IntPtr fp = Runtime.PySequence_GetItem(fromList, 0); if ((!CLRModule.preload) && Runtime.GetManagedString(fp) == "*") { mod.LoadNames(); } Runtime.Decref(fp); } Runtime.Incref(mod.pyHandle); return(mod.pyHandle); }
internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, MethodInfo[] methodinfo) { // loop to find match, return invoker w/ or /wo error MethodBase[] _methods = null; int pynargs = Runtime.PyTuple_Size(args); object arg; var isGeneric = false; ArrayList defaultArgList = null; if (info != null) { _methods = new MethodBase[1]; _methods.SetValue(info, 0); } else { _methods = GetMethods(); } Type clrtype; // TODO: Clean up foreach (MethodBase mi in _methods) { if (mi.IsGenericMethod) { isGeneric = true; } ParameterInfo[] pi = mi.GetParameters(); int clrnargs = pi.Length; var match = false; int arrayStart = -1; var outs = 0; if (pynargs == clrnargs) { match = true; } else if (pynargs < clrnargs) { match = true; defaultArgList = new ArrayList(); for (int v = pynargs; v < clrnargs; v++) { if (pi[v].DefaultValue == DBNull.Value) { match = false; } else { defaultArgList.Add(pi[v].DefaultValue); } } } else if (pynargs > clrnargs && clrnargs > 0 && Attribute.IsDefined(pi[clrnargs - 1], typeof(ParamArrayAttribute))) { // This is a `foo(params object[] bar)` style method match = true; arrayStart = clrnargs - 1; } if (match) { var margs = new object[clrnargs]; for (var n = 0; n < clrnargs; n++) { IntPtr op; if (n < pynargs) { if (arrayStart == n) { // map remaining Python arguments to a tuple since // the managed function accepts it - hopefully :] op = Runtime.PyTuple_GetSlice(args, arrayStart, pynargs); } else { op = Runtime.PyTuple_GetItem(args, n); } // this logic below handles cases when multiple overloading methods // are ambiguous, hence comparison between Python and CLR types // is necessary clrtype = null; IntPtr pyoptype; if (_methods.Length > 1) { pyoptype = IntPtr.Zero; pyoptype = Runtime.PyObject_Type(op); Exceptions.Clear(); if (pyoptype != IntPtr.Zero) { clrtype = Converter.GetTypeByAlias(pyoptype); } Runtime.XDecref(pyoptype); } if (clrtype != null) { var typematch = false; if ((pi[n].ParameterType != typeof(object)) && (pi[n].ParameterType != clrtype)) { IntPtr pytype = Converter.GetPythonTypeByAlias(pi[n].ParameterType); pyoptype = Runtime.PyObject_Type(op); Exceptions.Clear(); if (pyoptype != IntPtr.Zero) { if (pytype != pyoptype) { typematch = false; } else { typematch = true; clrtype = pi[n].ParameterType; } } 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(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) { margs = null; break; } } else { typematch = true; clrtype = pi[n].ParameterType; } } else { clrtype = pi[n].ParameterType; } if (pi[n].IsOut || clrtype.IsByRef) { outs++; } if (!Converter.ToManaged(op, clrtype, out arg, false)) { Exceptions.Clear(); margs = null; break; } if (arrayStart == n) { // GetSlice() creates a new reference but GetItem() // returns only a borrow reference. Runtime.XDecref(op); } margs[n] = arg; } else { if (defaultArgList != null) { margs[n] = defaultArgList[n - pynargs]; } } } if (margs == null) { continue; } object target = null; if (!mi.IsStatic && inst != IntPtr.Zero) { //CLRObject co = (CLRObject)ManagedType.GetManagedObject(inst); // InvalidCastException: Unable to cast object of type // 'Python.Runtime.ClassObject' to type 'Python.Runtime.CLRObject' var co = ManagedType.GetManagedObject(inst) as CLRObject; // Sanity check: this ensures a graceful exit if someone does // something intentionally wrong like call a non-static method // on the class rather than on an instance of the class. // XXX maybe better to do this before all the other rigmarole. if (co == null) { return(null); } target = co.inst; } return(new Binding(mi, target, margs, outs)); } } // We weren't able to find a matching method but at least one // is a generic method and info is null. That happens when a generic // method was not called using the [] syntax. Let's introspect the // type of the arguments and use it to construct the correct method. if (isGeneric && info == null && methodinfo != null) { Type[] types = Runtime.PythonArgsToTypeArray(args, true); MethodInfo mi = MatchParameters(methodinfo, types); return(Bind(inst, args, kw, mi, null)); } return(null); }
/// <summary> /// The actual import hook that ties Python to the managed world. /// </summary> public static IntPtr __import__(IntPtr self, IntPtr argsRaw, IntPtr kw) { var args = new BorrowedReference(argsRaw); // Replacement for the builtin __import__. The original import // hook is saved as this.py_import. This version handles CLR // import and defers to the normal builtin for everything else. var num_args = Runtime.PyTuple_Size(args); if (num_args < 1) { return(Exceptions.RaiseTypeError("__import__() takes at least 1 argument (0 given)")); } BorrowedReference py_mod_name = Runtime.PyTuple_GetItem(args, 0); if (py_mod_name.IsNull || !Runtime.IsStringType(py_mod_name)) { return(Exceptions.RaiseTypeError("string expected")); } // Check whether the import is of the form 'from x import y'. // This determines whether we return the head or tail module. BorrowedReference fromList = default; var fromlist = false; if (num_args >= 4) { fromList = Runtime.PyTuple_GetItem(args, 3); if (fromList != null && Runtime.PyObject_IsTrue(fromList) == 1) { fromlist = true; } } string mod_name = Runtime.GetManagedString(py_mod_name); // Check these BEFORE the built-in import runs; may as well // do the Incref()ed return here, since we've already found // the module. if (mod_name == "clr") { NewReference clr_module = GetCLRModule(fromList); if (!clr_module.IsNull()) { BorrowedReference sys_modules = Runtime.PyImport_GetModuleDict(); if (!sys_modules.IsNull) { Runtime.PyDict_SetItemString(sys_modules, "clr", clr_module); } } return(clr_module.DangerousMoveToPointerOrNull()); } string realname = mod_name; // 2010-08-15: Always seemed smart to let python try first... // This shaves off a few tenths of a second on test_module.py // and works around a quirk where 'sys' is found by the // LoadImplicit() deprecation logic. // Turns out that the AssemblyManager.ResolveHandler() checks to see if any // Assembly's FullName.ToLower().StartsWith(name.ToLower()), which makes very // little sense to me. IntPtr res = Runtime.PyObject_Call(py_import, args.DangerousGetAddress(), kw); if (res != IntPtr.Zero) { // There was no error. if (fromlist && IsLoadAll(fromList)) { var mod = ManagedType.GetManagedObject(res) as ModuleObject; mod?.LoadNames(); } return(res); } // There was an error if (!Exceptions.ExceptionMatches(Exceptions.ImportError)) { // and it was NOT an ImportError; bail out here. return(IntPtr.Zero); } if (mod_name == string.Empty) { // Most likely a missing relative import. // For example site-packages\bs4\builder\__init__.py uses it to check if a package exists: // from . import _html5lib // We don't support them anyway return(IntPtr.Zero); } // Save the exception var originalException = new PythonException(); // Otherwise, just clear the it. Exceptions.Clear(); string[] names = realname.Split('.'); // See if sys.modules for this interpreter already has the // requested module. If so, just return the existing module. BorrowedReference modules = Runtime.PyImport_GetModuleDict(); BorrowedReference module = Runtime.PyDict_GetItem(modules, py_mod_name); if (module != null) { if (fromlist) { if (IsLoadAll(fromList)) { var mod = ManagedType.GetManagedObject(module) as ModuleObject; mod?.LoadNames(); } return(new NewReference(module).DangerousMoveToPointer()); } module = Runtime.PyDict_GetItemString(modules, names[0]); return(new NewReference(module, canBeNull: true).DangerousMoveToPointer()); } Exceptions.Clear(); // Traverse the qualified module name to get the named module // and place references in sys.modules as we go. Note that if // we are running in interactive mode we pre-load the names in // each module, which is often useful for introspection. If we // are not interactive, we stick to just-in-time creation of // objects at lookup time, which is much more efficient. // NEW: The clr got a new module variable preload. You can // enable preloading in a non-interactive python processing by // setting clr.preload = True ModuleObject head = mod_name == realname ? null : root; ModuleObject tail = root; root.InitializePreload(); foreach (string name in names) { ManagedType mt = tail.GetAttribute(name, true); if (!(mt is ModuleObject)) { originalException.Restore(); return(IntPtr.Zero); } if (head == null) { head = (ModuleObject)mt; } tail = (ModuleObject)mt; if (CLRModule.preload) { tail.LoadNames(); } // Add the module to sys.modules Runtime.PyDict_SetItemString(modules, tail.moduleName, tail.ObjectReference); } { var mod = fromlist ? tail : head; if (fromlist && IsLoadAll(fromList)) { mod.LoadNames(); } Runtime.XIncref(mod.pyHandle); return(mod.pyHandle); } }
//==================================================================== // 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: #if !(PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) // 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 { #else // When using Python3 always use the PyLong API { #endif 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.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 || PYTHON35) 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 || PYTHON35) 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 || PYTHON35) 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); Runtime.Decref(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.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); if (Exceptions.ErrorOccurred()) { Runtime.Decref(op); goto overflow; } IntPtr check = Runtime.PyLong_FromUnsignedLong(ui); int err = Runtime.PyObject_Compare(check, op); Runtime.Decref(check); Runtime.Decref(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.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(op); Runtime.Decref(op); 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); }