public new static int tp_setattro(IntPtr ob, IntPtr key, IntPtr val) { var managedKey = Runtime.GetManagedString(key); if ((settableAttributes.Contains(managedKey)) || (ManagedType.GetManagedObject(val)?.GetType() == typeof(ModuleObject))) { var self = (ModuleObject)ManagedType.GetManagedObject(ob); return(Runtime.PyDict_SetItem(self.dict, key, val)); } return(ExtensionType.tp_setattro(ob, key, val)); }
internal static void RestoreRuntimeData(ImportHookState storage) { py_clr_module = storage.PyCLRModule; var rootHandle = storage.Root; root = new PyObject(rootHandle); clrModule = (CLRModule)ManagedType.GetManagedObject(rootHandle) !; BorrowedReference dict = Runtime.PyImport_GetModuleDict(); Runtime.PyDict_SetItemString(dict, "clr", ClrModuleReference); SetupNamespaceTracking(); RestoreDotNetModules(storage.Modules); }
//==================================================================== // Register a new Python object event handler with the event. //==================================================================== internal bool AddEventHandler(IntPtr target, IntPtr handler) { Object obj = null; if (target != IntPtr.Zero) { CLRObject co = (CLRObject)ManagedType.GetManagedObject(target); obj = co.inst; } // Create a true delegate instance of the appropriate type to // wrap the Python handler. Note that wrapper delegate creation // always succeeds, though calling the wrapper may fail. Type type = this.info.EventHandlerType; Delegate d = PythonEngine.DelegateManager.GetDelegate(type, handler); // Now register the handler in a mapping from instance to pairs // of (handler hash, delegate) so we can lookup to remove later. // All this is done lazily to avoid overhead until an event is // actually subscribed to by a Python event handler. if (reg == null) { reg = new Hashtable(); } object key = (obj != null) ? obj : this.info.ReflectedType; ArrayList list = reg[key] as ArrayList; if (list == null) { list = new ArrayList(); reg[key] = list; } list.Add(new Handler(Runtime.PyObject_Hash(handler), d)); // Note that AddEventHandler helper only works for public events, // so we have to get the underlying add method explicitly. object[] args = { d }; MethodInfo mi = this.info.GetAddMethod(true); mi.Invoke(obj, BindingFlags.Default, null, args, null); return(true); }
private static int TraverseTypeClear(IntPtr ob, IntPtr arg) { var visited = (HashSet <IntPtr>)GCHandle.FromIntPtr(arg).Target; if (!visited.Add(ob)) { return(0); } var clrObj = ManagedType.GetManagedObject(ob); if (clrObj != null) { clrObj.CallTypeTraverse(TraverseTypeClear, arg); clrObj.CallTypeClear(); } return(0); }
//==================================================================== // Implements __contains__ for array types. //==================================================================== public static int sq_contains(IntPtr ob, IntPtr v) { CLRObject obj = (CLRObject)ManagedType.GetManagedObject(ob); Type itemType = obj.inst.GetType().GetElementType(); IList items = obj.inst as IList; object value; if (!Converter.ToManaged(v, itemType, out value, false)) { return(0); } if (items.Contains(value)) { return(1); } return(0); }
private static Exception FromPyErr(BorrowedReference typeRef, BorrowedReference valRef, BorrowedReference tbRef, out ExceptionDispatchInfo?exceptionDispatchInfo) { if (valRef == null) { throw new ArgumentNullException(nameof(valRef)); } var type = PyType.FromReference(typeRef); var value = new PyObject(valRef); var traceback = PyObject.FromNullableReference(tbRef); exceptionDispatchInfo = TryGetDispatchInfo(valRef); if (exceptionDispatchInfo != null) { return(exceptionDispatchInfo.SourceException); } if (ManagedType.GetManagedObject(valRef) is CLRObject { inst : Exception e })
/// <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); } }
internal static IntPtr CreateSubType(IntPtr py_name, IntPtr py_base_type, IntPtr py_dict) { var dictRef = new BorrowedReference(py_dict); // Utility to create a subtype of a managed type with the ability for the // a python subtype able to override the managed implementation string name = Runtime.GetManagedString(py_name); // the derived class can have class attributes __assembly__ and __module__ which // control the name of the assembly and module the new type is created in. object assembly = null; object namespaceStr = null; using (var assemblyKey = new PyString("__assembly__")) { var assemblyPtr = Runtime.PyDict_GetItemWithError(dictRef, assemblyKey.Reference); if (assemblyPtr.IsNull) { if (Exceptions.ErrorOccurred()) { return(IntPtr.Zero); } } else if (!Converter.ToManagedValue(assemblyPtr, typeof(string), out assembly, true)) { return(Exceptions.RaiseTypeError("Couldn't convert __assembly__ value to string")); } using (var namespaceKey = new PyString("__namespace__")) { var pyNamespace = Runtime.PyDict_GetItemWithError(dictRef, namespaceKey.Reference); if (pyNamespace.IsNull) { if (Exceptions.ErrorOccurred()) { return(IntPtr.Zero); } } else if (!Converter.ToManagedValue(pyNamespace, typeof(string), out namespaceStr, true)) { return(Exceptions.RaiseTypeError("Couldn't convert __namespace__ value to string")); } } } // create the new managed type subclassing the base managed type var baseClass = ManagedType.GetManagedObject(py_base_type) as ClassBase; if (null == baseClass) { return(Exceptions.RaiseTypeError("invalid base class, expected CLR class type")); } try { Type subType = ClassDerivedObject.CreateDerivedType(name, baseClass.type.Value, py_dict, (string)namespaceStr, (string)assembly); // create the new ManagedType and python type ClassBase subClass = ClassManager.GetClass(subType); IntPtr py_type = GetTypeHandle(subClass, subType); // by default the class dict will have all the C# methods in it, but as this is a // derived class we want the python overrides in there instead if they exist. var cls_dict = new BorrowedReference(Marshal.ReadIntPtr(py_type, TypeOffset.tp_dict)); ThrowIfIsNotZero(Runtime.PyDict_Update(cls_dict, new BorrowedReference(py_dict))); Runtime.XIncref(py_type); // Update the __classcell__ if it exists BorrowedReference cell = Runtime.PyDict_GetItemString(cls_dict, "__classcell__"); if (!cell.IsNull) { ThrowIfIsNotZero(Runtime.PyCell_Set(cell, py_type)); ThrowIfIsNotZero(Runtime.PyDict_DelItemString(cls_dict, "__classcell__")); } return(py_type); } catch (Exception e) { return(Exceptions.RaiseTypeError(e.Message)); } }
internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info) { // loop to find match, return invoker w/ or /wo error MethodBase[] _methods = null; int nargs = Runtime.PyTuple_Size(args); object arg; 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]; ParameterInfo[] pi = mi.GetParameters(); int count = pi.Length; int outs = 0; if (nargs == count) { Object[] margs = new Object[count]; for (int n = 0; n < count; n++) { IntPtr 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; } margs[n] = arg; } if (margs == null) { continue; } Object target = null; if ((!mi.IsStatic) && (inst != IntPtr.Zero)) { CLRObject co = (CLRObject)ManagedType.GetManagedObject( inst ); target = co.inst; } return(new Binding(mi, target, margs, outs)); } } return(null); }
internal static IntPtr CreateSubType(IntPtr py_name, IntPtr py_base_type, IntPtr py_dict) { // Utility to create a subtype of a managed type with the ability for the // a python subtype able to override the managed implementation string name = Runtime.GetManagedString(py_name); // the derived class can have class attributes __assembly__ and __module__ which // control the name of the assembly and module the new type is created in. object assembly = null; object namespaceStr = null; var disposeList = new List <PyObject>(); try { var assemblyKey = new PyObject(Converter.ToPython("__assembly__", typeof(string))); disposeList.Add(assemblyKey); if (0 != Runtime.PyMapping_HasKey(py_dict, assemblyKey.Handle)) { var pyAssembly = new PyObject(Runtime.PyDict_GetItem(py_dict, assemblyKey.Handle)); Runtime.XIncref(pyAssembly.Handle); disposeList.Add(pyAssembly); if (!Converter.ToManagedValue(pyAssembly.Handle, typeof(string), out assembly, false)) { throw new InvalidCastException("Couldn't convert __assembly__ value to string"); } } var namespaceKey = new PyObject(Converter.ToPythonImplicit("__namespace__")); disposeList.Add(namespaceKey); if (0 != Runtime.PyMapping_HasKey(py_dict, namespaceKey.Handle)) { var pyNamespace = new PyObject(Runtime.PyDict_GetItem(py_dict, namespaceKey.Handle)); Runtime.XIncref(pyNamespace.Handle); disposeList.Add(pyNamespace); if (!Converter.ToManagedValue(pyNamespace.Handle, typeof(string), out namespaceStr, false)) { throw new InvalidCastException("Couldn't convert __namespace__ value to string"); } } } finally { foreach (PyObject o in disposeList) { o.Dispose(); } } // create the new managed type subclassing the base managed type var baseClass = ManagedType.GetManagedObject(py_base_type) as ClassBase; if (null == baseClass) { return(Exceptions.RaiseTypeError("invalid base class, expected CLR class type")); } try { Type subType = ClassDerivedObject.CreateDerivedType(name, baseClass.type, py_dict, (string)namespaceStr, (string)assembly); // create the new ManagedType and python type ClassBase subClass = ClassManager.GetClass(subType); IntPtr py_type = GetTypeHandle(subClass, subType); // by default the class dict will have all the C# methods in it, but as this is a // derived class we want the python overrides in there instead if they exist. IntPtr cls_dict = Marshal.ReadIntPtr(py_type, TypeOffset.tp_dict); Runtime.PyDict_Update(cls_dict, py_dict); return(py_type); } catch (Exception e) { return(Exceptions.RaiseTypeError(e.Message)); } }
/// <summary> /// Bind the given Python instance and arguments to a particular method /// overload in <see cref="list"/> and return a structure that contains the converted Python /// instance, converted arguments and the correct method to call. /// If unsuccessful, may set a Python error. /// </summary> /// <param name="inst">The Python target of the method invocation.</param> /// <param name="args">The Python arguments.</param> /// <param name="kw">The Python keyword arguments.</param> /// <param name="info">If not null, only bind to that method.</param> /// <param name="methodinfo">If not null, additionally attempt to bind to the generic methods in this array by inferring generic type parameters.</param> /// <returns>A Binding if successful. Otherwise null.</returns> internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, MethodInfo[] methodinfo) { // loop to find match, return invoker w/ or w/o error MethodBase[] _methods = null; var kwargDict = new Dictionary <string, IntPtr>(); if (kw != IntPtr.Zero) { var pynkwargs = (int)Runtime.PyDict_Size(kw); IntPtr keylist = Runtime.PyDict_Keys(kw); IntPtr valueList = Runtime.PyDict_Values(kw); for (int i = 0; i < pynkwargs; ++i) { var keyStr = Runtime.GetManagedString(Runtime.PyList_GetItem(new BorrowedReference(keylist), i)); kwargDict[keyStr] = Runtime.PyList_GetItem(new BorrowedReference(valueList), i).DangerousGetAddress(); } Runtime.XDecref(keylist); Runtime.XDecref(valueList); } var pynargs = (int)Runtime.PyTuple_Size(args); var isGeneric = false; if (info != null) { _methods = new MethodBase[1]; _methods.SetValue(info, 0); } else { _methods = GetMethods(); } var argMatchedMethods = new List <MatchedMethod>(_methods.Length); var mismatchedMethods = new List <MismatchedMethod>(); // TODO: Clean up foreach (MethodBase mi in _methods) { if (mi.IsGenericMethod) { isGeneric = true; } ParameterInfo[] pi = mi.GetParameters(); ArrayList defaultArgList; bool paramsArray; int kwargsMatched; int defaultsNeeded; bool isOperator = OperatorMethod.IsOperatorMethod(mi); // Binary operator methods will have 2 CLR args but only one Python arg // (unary operators will have 1 less each), since Python operator methods are bound. isOperator = isOperator && pynargs == pi.Length - 1; bool isReverse = isOperator && OperatorMethod.IsReverse((MethodInfo)mi); // Only cast if isOperator. if (isReverse && OperatorMethod.IsComparisonOp((MethodInfo)mi)) { continue; // Comparison operators in Python have no reverse mode. } if (!MatchesArgumentCount(pynargs, pi, kwargDict, out paramsArray, out defaultArgList, out kwargsMatched, out defaultsNeeded) && !isOperator) { continue; } // Preprocessing pi to remove either the first or second argument. if (isOperator && !isReverse) { // The first Python arg is the right operand, while the bound instance is the left. // We need to skip the first (left operand) CLR argument. pi = pi.Skip(1).ToArray(); } else if (isOperator && isReverse) { // The first Python arg is the left operand. // We need to take the first CLR argument. pi = pi.Take(1).ToArray(); } int outs; var margs = TryConvertArguments(pi, paramsArray, args, pynargs, kwargDict, defaultArgList, outs: out outs); if (margs == null) { var mismatchCause = PythonException.FetchCurrent(); mismatchedMethods.Add(new MismatchedMethod(mismatchCause, mi)); continue; } if (isOperator) { if (inst != IntPtr.Zero) { if (ManagedType.GetManagedObject(inst) is CLRObject co) { bool isUnary = pynargs == 0; // Postprocessing to extend margs. var margsTemp = isUnary ? new object[1] : new object[2]; // If reverse, the bound instance is the right operand. int boundOperandIndex = isReverse ? 1 : 0; // If reverse, the passed instance is the left operand. int passedOperandIndex = isReverse ? 0 : 1; margsTemp[boundOperandIndex] = co.inst; if (!isUnary) { margsTemp[passedOperandIndex] = margs[0]; } margs = margsTemp; } else { continue; } } } var matchedMethod = new MatchedMethod(kwargsMatched, defaultsNeeded, margs, outs, mi); argMatchedMethods.Add(matchedMethod); } if (argMatchedMethods.Count > 0) { var bestKwargMatchCount = argMatchedMethods.Max(x => x.KwargsMatched); var fewestDefaultsRequired = argMatchedMethods.Where(x => x.KwargsMatched == bestKwargMatchCount).Min(x => x.DefaultsNeeded); int bestCount = 0; int bestMatchIndex = -1; for (int index = 0; index < argMatchedMethods.Count; index++) { var testMatch = argMatchedMethods[index]; if (testMatch.DefaultsNeeded == fewestDefaultsRequired && testMatch.KwargsMatched == bestKwargMatchCount) { bestCount++; if (bestMatchIndex == -1) { bestMatchIndex = index; } } } if (bestCount > 1 && fewestDefaultsRequired > 0) { // Best effort for determining method to match on gives multiple possible // matches and we need at least one default argument - bail from this point StringBuilder stringBuilder = new StringBuilder("Not enough arguments provided to disambiguate the method. Found:"); foreach (var matchedMethod in argMatchedMethods) { stringBuilder.AppendLine(); stringBuilder.Append(matchedMethod.Method.ToString()); } Exceptions.SetError(Exceptions.TypeError, stringBuilder.ToString()); return(null); } // If we're here either: // (a) There is only one best match // (b) There are multiple best matches but none of them require // default arguments // in the case of (a) we're done by default. For (b) regardless of which // method we choose, all arguments are specified _and_ can be converted // from python to C# so picking any will suffice MatchedMethod bestMatch = argMatchedMethods[bestMatchIndex]; var margs = bestMatch.ManagedArgs; var outs = bestMatch.Outs; var mi = bestMatch.Method; 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) { Exceptions.SetError(Exceptions.TypeError, "Invoked a non-static method with an invalid instance"); return(null); } target = co.inst; } return(new Binding(mi, target, margs, outs)); } else if (isGeneric && info == null && methodinfo != null) { // 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. Type[] types = Runtime.PythonArgsToTypeArray(args, true); MethodInfo mi = MatchParameters(methodinfo, types); if (mi != null) { return(Bind(inst, args, kw, mi, null)); } } if (mismatchedMethods.Count > 0) { var aggregateException = GetAggregateException(mismatchedMethods); Exceptions.SetError(aggregateException); } return(null); }
//==================================================================== // Implements __getitem__ for array types. //==================================================================== public static IntPtr mp_subscript(IntPtr ob, IntPtr idx) { CLRObject obj = (CLRObject)ManagedType.GetManagedObject(ob); Array items = obj.inst as Array; Type itemType = obj.inst.GetType().GetElementType(); int rank = items.Rank; int index = 0; object value; // Note that CLR 1.0 only supports int indexes - methods to // support long indices were introduced in 1.1. We could // support long indices automatically, but given that long // indices are not backward compatible and a relative edge // case, we won't bother for now. // Single-dimensional arrays are the most common case and are // cheaper to deal with than multi-dimensional, so check first. if (rank == 1) { index = (int)Runtime.PyInt_AsLong(idx); if (Exceptions.ErrorOccurred()) { return(Exceptions.RaiseTypeError("invalid index value")); } if (index < 0) { index = items.Length + index; } try { value = items.GetValue(index); } catch (IndexOutOfRangeException) { Exceptions.SetError(Exceptions.IndexError, "array index out of range" ); return(IntPtr.Zero); } return(Converter.ToPython(items.GetValue(index), itemType)); } // Multi-dimensional arrays can be indexed a la: list[1, 2, 3]. if (!Runtime.PyTuple_Check(idx)) { Exceptions.SetError(Exceptions.TypeError, "invalid index value" ); return(IntPtr.Zero); } int count = Runtime.PyTuple_Size(idx); Array args = Array.CreateInstance(typeof(Int32), count); for (int i = 0; i < count; i++) { IntPtr op = Runtime.PyTuple_GetItem(idx, i); index = (int)Runtime.PyInt_AsLong(op); if (Exceptions.ErrorOccurred()) { return(Exceptions.RaiseTypeError("invalid index value")); } if (index < 0) { index = items.GetLength(i) + index; } args.SetValue(index, i); } try { value = items.GetValue((int[])args); } catch (IndexOutOfRangeException) { Exceptions.SetError(Exceptions.IndexError, "array index out of range" ); return(IntPtr.Zero); } return(Converter.ToPython(value, itemType)); }
//==================================================================== // This is the implementaion of the overriden methods in the derived // type. It looks for a python method with the same name as the method // on the managed base class and if it exists and isn't the managed // method binding (ie it has been overriden in the derived python // class) it calls it, otherwise it calls the base method. //==================================================================== public static T InvokeMethod <T>(IPythonDerivedType obj, string methodName, string origMethodName, Object[] args) { FieldInfo fi = obj.GetType().GetField("__pyobj__"); CLRObject self = (CLRObject)fi.GetValue(obj); if (null != self) { List <PyObject> disposeList = new List <PyObject>(); IntPtr gs = Runtime.PyGILState_Ensure(); try { Runtime.Incref(self.pyHandle); PyObject pyself = new PyObject(self.pyHandle); disposeList.Add(pyself); Runtime.Incref(Runtime.PyNone); PyObject pynone = new PyObject(Runtime.PyNone); disposeList.Add(pynone); PyObject method = pyself.GetAttr(methodName, pynone); disposeList.Add(method); if (method.Handle != Runtime.PyNone) { // if the method hasn't been overriden then it will be a managed object ManagedType managedMethod = ManagedType.GetManagedObject(method.Handle); if (null == managedMethod) { PyObject[] pyargs = new PyObject[args.Length]; for (int i = 0; i < args.Length; ++i) { pyargs[i] = new PyObject(Converter.ToPythonImplicit(args[i])); disposeList.Add(pyargs[i]); } PyObject py_result = method.Invoke(pyargs); disposeList.Add(py_result); return((T)py_result.AsManagedObject(typeof(T))); } } } finally { foreach (PyObject x in disposeList) { if (x != null) { x.Dispose(); } } Runtime.PyGILState_Release(gs); } } if (origMethodName == null) { throw new NullReferenceException("Python object does not have a '" + methodName + "' method"); } return((T)obj.GetType().InvokeMember(origMethodName, BindingFlags.InvokeMethod, null, obj, args)); }
//==================================================================== // Implements __setitem__ for array types. //==================================================================== public static int mp_ass_subscript(IntPtr ob, IntPtr idx, IntPtr v) { CLRObject obj = (CLRObject)ManagedType.GetManagedObject(ob); Array items = obj.inst as Array; Type itemType = obj.inst.GetType().GetElementType(); int rank = items.Rank; int index = 0; object value; if (items.IsReadOnly) { Exceptions.RaiseTypeError("array is read-only"); return(-1); } if (!Converter.ToManaged(v, itemType, out value, true)) { return(-1); } if (rank == 1) { index = (int)Runtime.PyInt_AsLong(idx); if (Exceptions.ErrorOccurred()) { Exceptions.RaiseTypeError("invalid index value"); return(-1); } if (index < 0) { index = items.Length + index; } try { items.SetValue(value, index); } catch (IndexOutOfRangeException) { Exceptions.SetError(Exceptions.IndexError, "array index out of range" ); return(-1); } return(0); } if (!Runtime.PyTuple_Check(idx)) { Exceptions.RaiseTypeError("invalid index value"); return(-1); } int count = Runtime.PyTuple_Size(idx); Array args = Array.CreateInstance(typeof(Int32), count); for (int i = 0; i < count; i++) { IntPtr op = Runtime.PyTuple_GetItem(idx, i); index = (int)Runtime.PyInt_AsLong(op); if (Exceptions.ErrorOccurred()) { Exceptions.RaiseTypeError("invalid index value"); return(-1); } if (index < 0) { index = items.GetLength(i) + index; } args.SetValue(index, i); } try { items.SetValue(value, (int[])args); } catch (IndexOutOfRangeException) { Exceptions.SetError(Exceptions.IndexError, "array index out of range" ); return(-1); } return(0); }
static IntPtr HandleParamsArray(IntPtr args, int arrayStart, int pyArgCount, out bool isNewReference) { isNewReference = false; IntPtr op; // for a params method, we may have a sequence or single/multiple items // here we look to see if the item at the paramIndex is there or not // and then if it is a sequence itself. if ((pyArgCount - arrayStart) == 1) { // Ee only have one argument left, so we need to check to see if it is a sequence or a single item // We also need to check if this object is possibly a wrapped C# Enumerable Object IntPtr item = Runtime.PyTuple_GetItem(args, arrayStart); if (!Runtime.PyString_Check(item) && (Runtime.PySequence_Check(item) || (ManagedType.GetManagedObject(item) as CLRObject)?.inst is IEnumerable)) { // it's a sequence (and not a string), so we use it as the op op = item; } else { // Its a single value that needs to be converted into the params array isNewReference = true; op = Runtime.PyTuple_GetSlice(args, arrayStart, pyArgCount); } } else { // Its a set of individual values, so we grab them as a tuple to be converted into the params array isNewReference = true; op = Runtime.PyTuple_GetSlice(args, arrayStart, pyArgCount); } return(op); }
internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, MethodInfo[] methodinfo) { // Relevant function variables used post conversion var isGeneric = false; Binding bindingUsingImplicitConversion = null; // If we have KWArgs create dictionary and collect them Dictionary <string, IntPtr> kwArgDict = null; if (kw != IntPtr.Zero) { var pyKwArgsCount = (int)Runtime.PyDict_Size(kw); kwArgDict = new Dictionary <string, IntPtr>(pyKwArgsCount); IntPtr keylist = Runtime.PyDict_Keys(kw); IntPtr valueList = Runtime.PyDict_Values(kw); for (int i = 0; i < pyKwArgsCount; ++i) { var keyStr = Runtime.GetManagedString(Runtime.PyList_GetItem(new BorrowedReference(keylist), i)); kwArgDict[keyStr] = Runtime.PyList_GetItem(new BorrowedReference(valueList), i).DangerousGetAddress(); } Runtime.XDecref(keylist); Runtime.XDecref(valueList); } // Fetch our methods we are going to attempt to match and bind too. var methods = info == null?GetMethods() : new List <MethodInformation>(1) { new MethodInformation(info, info.GetParameters()) }; foreach (var methodInformation in methods) { // Relevant method variables var mi = methodInformation.MethodBase; var pi = methodInformation.ParameterInfo; isGeneric = mi.IsGenericMethod; int pyArgCount = (int)Runtime.PyTuple_Size(args); // Special case for operators bool isOperator = OperatorMethod.IsOperatorMethod(mi); // Binary operator methods will have 2 CLR args but only one Python arg // (unary operators will have 1 less each), since Python operator methods are bound. isOperator = isOperator && pyArgCount == pi.Length - 1; bool isReverse = isOperator && OperatorMethod.IsReverse((MethodInfo)mi); // Only cast if isOperator. if (isReverse && OperatorMethod.IsComparisonOp((MethodInfo)mi)) { continue; // Comparison operators in Python have no reverse mode. } // Preprocessing pi to remove either the first or second argument. if (isOperator && !isReverse) { // The first Python arg is the right operand, while the bound instance is the left. // We need to skip the first (left operand) CLR argument. pi = pi.Skip(1).ToArray(); } else if (isOperator && isReverse) { // The first Python arg is the left operand. // We need to take the first CLR argument. pi = pi.Take(1).ToArray(); } // Must be done after IsOperator section int clrArgCount = pi.Length; if (CheckMethodArgumentsMatch(clrArgCount, pyArgCount, kwArgDict, pi, out bool paramsArray, out ArrayList defaultArgList)) { var outs = 0; var margs = new object[clrArgCount]; int paramsArrayIndex = paramsArray ? pi.Length - 1 : -1; // -1 indicates no paramsArray var usedImplicitConversion = false; // Conversion loop for each parameter for (int paramIndex = 0; paramIndex < clrArgCount; paramIndex++) { IntPtr op = IntPtr.Zero; // Python object to be converted; not yet set var parameter = pi[paramIndex]; // Clr parameter we are targeting object arg; // Python -> Clr argument // Check our KWargs for this parameter bool hasNamedParam = kwArgDict == null ? false : kwArgDict.TryGetValue(parameter.Name, out op); bool isNewReference = false; // Check if we are going to use default if (paramIndex >= pyArgCount && !(hasNamedParam || (paramsArray && paramIndex == paramsArrayIndex))) { if (defaultArgList != null) { margs[paramIndex] = defaultArgList[paramIndex - pyArgCount]; } continue; } // At this point, if op is IntPtr.Zero we don't have a KWArg and are not using default if (op == IntPtr.Zero) { // If we have reached the paramIndex if (paramsArrayIndex == paramIndex) { op = HandleParamsArray(args, paramsArrayIndex, pyArgCount, out isNewReference); } else { op = Runtime.PyTuple_GetItem(args, paramIndex); } } // 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 (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 ((parameter.ParameterType != typeof(object)) && (parameter.ParameterType != clrtype)) { IntPtr pytype = Converter.GetPythonTypeByAlias(parameter.ParameterType); pyoptype = Runtime.PyObject_Type(op); Exceptions.Clear(); if (pyoptype != IntPtr.Zero) { if (pytype != pyoptype) { typematch = false; } else { typematch = true; clrtype = parameter.ParameterType; } } if (!typematch) { // this takes care of nullables var underlyingType = Nullable.GetUnderlyingType(parameter.ParameterType); if (underlyingType == null) { underlyingType = parameter.ParameterType; } // this takes care of enum values TypeCode argtypecode = Type.GetTypeCode(underlyingType); TypeCode paramtypecode = Type.GetTypeCode(clrtype); if (argtypecode == paramtypecode) { typematch = true; clrtype = parameter.ParameterType; } // accepts non-decimal numbers in decimal parameters if (underlyingType == typeof(decimal)) { clrtype = parameter.ParameterType; typematch = Converter.ToManaged(op, clrtype, out arg, false); } // this takes care of implicit conversions var opImplicit = parameter.ParameterType.GetMethod("op_Implicit", new[] { clrtype }); if (opImplicit != null) { usedImplicitConversion = typematch = opImplicit.ReturnType == parameter.ParameterType; clrtype = parameter.ParameterType; } } Runtime.XDecref(pyoptype); if (!typematch) { margs = null; break; } } else { clrtype = parameter.ParameterType; } } else { clrtype = parameter.ParameterType; } if (parameter.IsOut || clrtype.IsByRef) { outs++; } if (!Converter.ToManaged(op, clrtype, out arg, false)) { Exceptions.Clear(); margs = null; break; } if (isNewReference) { // TODO: is this a bug? Should this happen even if the conversion fails? // GetSlice() creates a new reference but GetItem() // returns only a borrow reference. Runtime.XDecref(op); } margs[paramIndex] = arg; } if (margs == null) { continue; } if (isOperator) { if (inst != IntPtr.Zero) { if (ManagedType.GetManagedObject(inst) is CLRObject co) { bool isUnary = pyArgCount == 0; // Postprocessing to extend margs. var margsTemp = isUnary ? new object[1] : new object[2]; // If reverse, the bound instance is the right operand. int boundOperandIndex = isReverse ? 1 : 0; // If reverse, the passed instance is the left operand. int passedOperandIndex = isReverse ? 0 : 1; margsTemp[boundOperandIndex] = co.inst; if (!isUnary) { margsTemp[passedOperandIndex] = margs[0]; } margs = margsTemp; } else { 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); }
internal Binding Bind(IntPtr inst, IntPtr pythonParametersPtr, IntPtr kw, MethodBase info, MethodInfo[] methodInfoArray) { // loop to find match, return invoker w/ or /wo error MethodBase[] _methods = null; // NOTE: return the size of the args pointer (the number of parameter from the python call) int pythonParameterCount = Runtime.PyTuple_Size(pythonParametersPtr); //WHY: Why is that so widely scoped ? object pythonManagedParameterPtr; var isGeneric = false; ArrayList defaultParameterList = null; if (info != null) { // NOTE: If a MethodBase object has been provided, create an array with only it. // WHY a method base is provided some times and not other ? (ex: when call a genric with a type in []) _methods = new MethodBase[1]; _methods.SetValue(info, 0); } else { // NOTE: Create an array of MethodBase from teh called method/constructor // WHY not use the methodinfo provided? _methods = GetMethods(); } //WHY is it so widely scoped Type clrConvertedParameterType; // TODO: Clean up foreach (MethodBase methodInfo in _methods) { //WHY not just do isGeneric = mi.IsGenericMethod or use it directly ? if (methodInfo.IsGenericMethod) { isGeneric = true; } //NOTE: Get the parameter from the current MethodBase //NOTE: MethodInfo Ok ParameterInfo[] clrParameterInfoArray = methodInfo.GetParameters(); //NOTE: Get the number of clr parameters int clrParameterCount = clrParameterInfoArray.Length; var paramCountMatch = false; //REFACTOR: Use var like the other local variables var clrHasParamArray = false; var clrParamsArrayStart = -1; var byRefCount = 0; if (pythonParameterCount == clrParameterCount) { paramCountMatch = true; } else if (pythonParameterCount < clrParameterCount) { paramCountMatch = true; defaultParameterList = new ArrayList(); for (int v = pythonParameterCount; v < clrParameterCount; v++) { if (clrParameterInfoArray[v].DefaultValue == DBNull.Value) { paramCountMatch = false; } else { defaultParameterList.Add(clrParameterInfoArray[v].DefaultValue); } } } else if (pythonParameterCount > clrParameterCount && clrParameterCount > 0 && Attribute.IsDefined(clrParameterInfoArray[clrParameterCount - 1], typeof(ParamArrayAttribute))) { // This is a `foo({...}, params object[] bar)` style method paramCountMatch = true; clrHasParamArray = true; clrParamsArrayStart = clrParameterCount - 1; } if (paramCountMatch) { var methodParametersPtrArray = new object[clrParameterCount]; for (var n = 0; n < clrParameterCount; n++) { IntPtr pythonParameterPtr; if (n < pythonParameterCount) { if (clrParamsArrayStart == n) { // map remaining Python arguments to a tuple since // the managed function accepts it - hopefully :] //WHY: Hmmm it returns a lot of python paramter as one. isn't there a problem later ? pythonParameterPtr = Runtime.PyTuple_GetSlice(pythonParametersPtr, clrParamsArrayStart, pythonParameterCount); } else { pythonParameterPtr = Runtime.PyTuple_GetItem(pythonParametersPtr, n); } // this logic below handles cases when multiple overloading methods // are ambiguous, hence comparison between Python and CLR types // is necessary clrConvertedParameterType = null; IntPtr pythonParameterPtrType = IntPtr.Zero; if (_methods.Length > 1) { pythonParameterPtrType = Runtime.PyObject_Type(pythonParameterPtr); Exceptions.Clear(); if (pythonParameterPtrType != IntPtr.Zero) { clrConvertedParameterType = Converter.GetTypeByAlias(pythonParameterPtrType); } Runtime.XDecref(pythonParameterPtrType); } if (clrConvertedParameterType != null) { var parameterTypeMatch = false; if ((clrParameterInfoArray[n].ParameterType != typeof(object)) && (clrParameterInfoArray[n].ParameterType != clrConvertedParameterType)) { IntPtr pythonConvertedParameterPtrType = Converter.GetPythonTypeByAlias(clrParameterInfoArray[n].ParameterType); pythonParameterPtrType = Runtime.PyObject_Type(pythonParameterPtr); Exceptions.Clear(); if (pythonParameterPtrType != IntPtr.Zero) { if (pythonConvertedParameterPtrType != pythonParameterPtrType) { parameterTypeMatch = false; } else { parameterTypeMatch = true; clrConvertedParameterType = clrParameterInfoArray[n].ParameterType; } } if (!parameterTypeMatch) { // this takes care of enum values TypeCode clrParameterTypeCode = Type.GetTypeCode(clrParameterInfoArray[n].ParameterType); TypeCode clrConvertedParameterTypeCode = Type.GetTypeCode(clrConvertedParameterType); if (clrParameterTypeCode == clrConvertedParameterTypeCode) { parameterTypeMatch = true; clrConvertedParameterType = clrParameterInfoArray[n].ParameterType; } } Runtime.XDecref(pythonParameterPtrType); if (!parameterTypeMatch) { methodParametersPtrArray = null; break; } } else { parameterTypeMatch = true; clrConvertedParameterType = clrParameterInfoArray[n].ParameterType; } } else { clrConvertedParameterType = clrParameterInfoArray[n].ParameterType; } if (clrParameterInfoArray[n].IsOut || clrConvertedParameterType.IsByRef) { byRefCount++; } if (!Converter.ToManaged(pythonParameterPtr, clrConvertedParameterType, out pythonManagedParameterPtr, false)) { Exceptions.Clear(); methodParametersPtrArray = null; break; } if (clrParamsArrayStart == n) { // GetSlice() creates a new reference but GetItem() // returns only a borrow reference. Runtime.XDecref(pythonParameterPtr); } methodParametersPtrArray[n] = pythonParameterPtr; } else { if (defaultParameterList != null) { methodParametersPtrArray[n] = defaultParameterList[n - pythonParameterCount]; } } } if (methodParametersPtrArray == null) { continue; } object target = null; if (!methodInfo.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(methodInfo, target, methodParametersPtrArray, byRefCount)); } } // 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 && methodInfoArray != null) { Type[] types = Runtime.PythonArgsToTypeArray(pythonParametersPtr, true); MethodInfo mi = MatchParameters(methodInfoArray, types); return(Bind(inst, pythonParametersPtr, kw, mi, null)); } return(null); }
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); }
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); }
public static void InvokeMethodVoid(IPythonDerivedType obj, string methodName, string origMethodName, object[] args) { FieldInfo fi = obj.GetType().GetField("__pyobj__"); var self = (CLRObject)fi.GetValue(obj); if (null != self) { var disposeList = new List <PyObject>(); IntPtr gs = Runtime.PyGILState_Ensure(); try { Runtime.XIncref(self.pyHandle); var pyself = new PyObject(self.pyHandle); disposeList.Add(pyself); Runtime.XIncref(Runtime.PyNone); var pynone = new PyObject(Runtime.PyNone); disposeList.Add(pynone); PyObject method = pyself.GetAttr(methodName, pynone); disposeList.Add(method); if (method.Handle != Runtime.PyNone) { // if the method hasn't been overridden then it will be a managed object ManagedType managedMethod = ManagedType.GetManagedObject(method.Handle); if (null == managedMethod) { var pyargs = new PyObject[args.Length]; for (var i = 0; i < args.Length; ++i) { pyargs[i] = new PyObject(Converter.ToPythonImplicit(args[i])); disposeList.Add(pyargs[i]); } PyObject py_result = method.Invoke(pyargs); disposeList.Add(py_result); return; } } } finally { foreach (PyObject x in disposeList) { x?.Dispose(); } Runtime.PyGILState_Release(gs); } } if (origMethodName == null) { throw new NotImplementedException($"Python object does not have a '{methodName}' method"); } obj.GetType().InvokeMember(origMethodName, BindingFlags.InvokeMethod, null, obj, args); }
public static void InvokeCtor(IPythonDerivedType obj, string origCtorName, Object[] args) { // call the base constructor obj.GetType().InvokeMember(origCtorName, BindingFlags.InvokeMethod, null, obj, args); List <PyObject> disposeList = new List <PyObject>(); CLRObject self = null; IntPtr gs = Runtime.PyGILState_Ensure(); try { // create the python object IntPtr type = TypeManager.GetTypeHandle(obj.GetType()); self = new CLRObject(obj, type); // set __pyobj__ to self and deref the python object which will allow this // object to be collected. FieldInfo fi = obj.GetType().GetField("__pyobj__"); fi.SetValue(obj, self); Runtime.Incref(self.pyHandle); PyObject pyself = new PyObject(self.pyHandle); disposeList.Add(pyself); Runtime.Incref(Runtime.PyNone); PyObject pynone = new PyObject(Runtime.PyNone); disposeList.Add(pynone); // call __init__ PyObject init = pyself.GetAttr("__init__", pynone); disposeList.Add(init); if (init.Handle != Runtime.PyNone) { // if __init__ hasn't been overriden then it will be a managed object ManagedType managedMethod = ManagedType.GetManagedObject(init.Handle); if (null == managedMethod) { PyObject[] pyargs = new PyObject[args.Length]; for (int i = 0; i < args.Length; ++i) { pyargs[i] = new PyObject(Converter.ToPython(args[i], args[i].GetType())); disposeList.Add(pyargs[i]); } disposeList.Add(init.Invoke(pyargs)); } } } finally { foreach (PyObject x in disposeList) { if (x != null) { x.Dispose(); } } // Decrement the python object's reference count. // This doesn't actually destroy the object, it just sets the reference to this object // to be a weak reference and it will be destroyed when the C# object is destroyed. if (null != self) { Runtime.Decref(self.pyHandle); } Runtime.PyGILState_Release(gs); } }
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; var kwargDict = new Dictionary <string, IntPtr>(); if (kw != IntPtr.Zero) { var pynkwargs = (int)Runtime.PyDict_Size(kw); IntPtr keylist = Runtime.PyDict_Keys(kw); IntPtr valueList = Runtime.PyDict_Values(kw); for (int i = 0; i < pynkwargs; ++i) { var keyStr = Runtime.GetManagedString(Runtime.PyList_GetItem(new BorrowedReference(keylist), i)); kwargDict[keyStr] = Runtime.PyList_GetItem(new BorrowedReference(valueList), i).DangerousGetAddress(); } Runtime.XDecref(keylist); Runtime.XDecref(valueList); } var pynargs = (int)Runtime.PyTuple_Size(args); var isGeneric = false; if (info != null) { _methods = new MethodBase[1]; _methods.SetValue(info, 0); } else { _methods = GetMethods(); } var argMatchedMethods = new List <MatchedMethod>(_methods.Length); // TODO: Clean up foreach (MethodBase mi in _methods) { if (mi.IsGenericMethod) { isGeneric = true; } ParameterInfo[] pi = mi.GetParameters(); ArrayList defaultArgList; bool paramsArray; int kwargsMatched; int defaultsNeeded; if (!MatchesArgumentCount(pynargs, pi, kwargDict, out paramsArray, out defaultArgList, out kwargsMatched, out defaultsNeeded)) { continue; } var outs = 0; var margs = TryConvertArguments(pi, paramsArray, args, pynargs, kwargDict, defaultArgList, needsResolution: _methods.Length > 1, outs: out outs); if (margs == null) { continue; } var matchedMethod = new MatchedMethod(kwargsMatched, defaultsNeeded, margs, outs, mi); argMatchedMethods.Add(matchedMethod); } if (argMatchedMethods.Count > 0) { var bestKwargMatchCount = argMatchedMethods.Max(x => x.KwargsMatched); var fewestDefaultsRequired = argMatchedMethods.Where(x => x.KwargsMatched == bestKwargMatchCount).Min(x => x.DefaultsNeeded); int bestCount = 0; int bestMatchIndex = -1; for (int index = 0; index < argMatchedMethods.Count; index++) { var testMatch = argMatchedMethods[index]; if (testMatch.DefaultsNeeded == fewestDefaultsRequired && testMatch.KwargsMatched == bestKwargMatchCount) { bestCount++; if (bestMatchIndex == -1) { bestMatchIndex = index; } } } if (bestCount > 1 && fewestDefaultsRequired > 0) { // Best effort for determining method to match on gives multiple possible // matches and we need at least one default argument - bail from this point return(null); } // If we're here either: // (a) There is only one best match // (b) There are multiple best matches but none of them require // default arguments // in the case of (a) we're done by default. For (b) regardless of which // method we choose, all arguments are specified _and_ can be converted // from python to C# so picking any will suffice MatchedMethod bestMatch = argMatchedMethods[bestMatchIndex]; var margs = bestMatch.ManagedArgs; var outs = bestMatch.Outs; var mi = bestMatch.Method; 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); }
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; var kwargDict = new Dictionary <string, IntPtr>(); if (kw != IntPtr.Zero) { var pynkwargs = (int)Runtime.PyDict_Size(kw); IntPtr keylist = Runtime.PyDict_Keys(kw); IntPtr valueList = Runtime.PyDict_Values(kw); for (int i = 0; i < pynkwargs; ++i) { var keyStr = Runtime.GetManagedString(Runtime.PyList_GetItem(new BorrowedReference(keylist), i)); kwargDict[keyStr] = Runtime.PyList_GetItem(new BorrowedReference(valueList), i).DangerousGetAddress(); } Runtime.XDecref(keylist); Runtime.XDecref(valueList); } var pynargs = (int)Runtime.PyTuple_Size(args); var isGeneric = false; if (info != null) { _methods = new MethodBase[1]; _methods.SetValue(info, 0); } else { _methods = GetMethods(); } // TODO: Clean up foreach (MethodBase mi in _methods) { if (mi.IsGenericMethod) { isGeneric = true; } ParameterInfo[] pi = mi.GetParameters(); ArrayList defaultArgList; bool paramsArray; if (!MatchesArgumentCount(pynargs, pi, kwargDict, out paramsArray, out defaultArgList)) { continue; } var outs = 0; var margs = TryConvertArguments(pi, paramsArray, args, pynargs, kwargDict, defaultArgList, needsResolution: _methods.Length > 1, outs: out outs); 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); }
internal static bool ToManagedValue(BorrowedReference value, Type obType, out object?result, bool setError) { if (obType == typeof(PyObject)) { result = new PyObject(value); return(true); } if (obType.IsSubclassOf(typeof(PyObject)) && !obType.IsAbstract && obType.GetConstructor(new[] { typeof(PyObject) }) is { } ctor) { var untyped = new PyObject(value); result = ToPyObjectSubclass(ctor, untyped, setError); return(result is not null); } // Common case: if the Python value is a wrapped managed object // instance, just return the wrapped object. result = null; switch (ManagedType.GetManagedObject(value)) { case CLRObject co: object tmp = co.inst; if (obType.IsInstanceOfType(tmp)) { result = tmp; return(true); } if (setError) { string typeString = tmp is null ? "null" : tmp.GetType().ToString(); Exceptions.SetError(Exceptions.TypeError, $"{typeString} value cannot be converted to {obType}"); } return(false); case ClassBase cb: if (!cb.type.Valid) { Exceptions.SetError(Exceptions.TypeError, cb.type.DeletedMessage); return(false); } result = cb.type.Value; return(true); case null: break; default: throw new ArgumentException("We should never receive instances of other managed types"); } if (value == Runtime.PyNone && !obType.IsValueType) { result = null; return(true); } if (obType.IsGenericType && obType.GetGenericTypeDefinition() == typeof(Nullable <>)) { if (value == Runtime.PyNone) { result = null; return(true); } // Set type to underlying type obType = obType.GetGenericArguments()[0]; } if (obType.ContainsGenericParameters) { if (setError) { Exceptions.SetError(Exceptions.TypeError, $"Cannot create an instance of the open generic type {obType}"); } return(false); } if (obType.IsArray) { return(ToArray(value, obType, out result, setError)); } // Conversion to 'Object' is done based on some reasonable default // conversions (Python string -> managed string). if (obType == objectType) { if (Runtime.IsStringType(value)) { return(ToPrimitive(value, stringType, out result, setError)); } if (Runtime.PyBool_Check(value)) { return(ToPrimitive(value, boolType, out result, setError)); } if (Runtime.PyFloat_Check(value)) { return(ToPrimitive(value, doubleType, out result, setError)); } // give custom codecs a chance to take over conversion of ints and sequences BorrowedReference pyType = Runtime.PyObject_TYPE(value); if (PyObjectConversions.TryDecode(value, pyType, obType, out result)) { return(true); } if (Runtime.PyInt_Check(value)) { result = new PyInt(value); return(true); } if (Runtime.PySequence_Check(value)) { return(ToArray(value, typeof(object[]), out result, setError)); } result = new PyObject(value); return(true); } // Conversion to 'Type' is done using the same mappings as above for objects. if (obType == typeType) { if (value == Runtime.PyStringType) { result = stringType; return(true); } if (value == Runtime.PyBoolType) { result = boolType; return(true); } if (value == Runtime.PyLongType) { result = typeof(PyInt); return(true); } if (value == Runtime.PyFloatType) { result = doubleType; return(true); } if (value == Runtime.PyListType) { result = typeof(PyList); return(true); } if (value == Runtime.PyTupleType) { result = typeof(PyTuple); return(true); } if (setError) { Exceptions.SetError(Exceptions.TypeError, "value cannot be converted to Type"); } return(false); } if (DecodableByUser(obType)) { BorrowedReference pyType = Runtime.PyObject_TYPE(value); if (PyObjectConversions.TryDecode(value, pyType, obType, out result)) { return(true); } } return(ToPrimitive(value, obType, out result, setError)); }
internal List <Binding> Bind(IntPtr inst, IntPtr args, IntPtr kw, Type type, MethodBase info, MethodInfo[] methodinfo) { #region COM Binding List <Binding> bindings = new List <Binding>(); #endregion // loop to find match, return invoker w/ or /wo error MethodBase[] _methods = null; var pynargs = (int)Runtime.PyTuple_Size(args); var isGeneric = false; if (info != null) { _methods = new MethodBase[1]; _methods.SetValue(info, 0); } else { _methods = GetMethods(); } // TODO: Clean up foreach (MethodBase mi in _methods) { #region COM Binding try { // Enforcable return types if (type != null) { if (!(type.IsSubclassOf(((MethodInfo)mi).ReturnType) || type.Equals(((MethodInfo)mi).ReturnType))) { continue; } } } catch (InvalidCastException ex) { } #endregion if (mi.IsGenericMethod) { isGeneric = true; } ParameterInfo[] pi = mi.GetParameters(); ArrayList defaultArgList; bool paramsArray; if (!MatchesArgumentCount(pynargs, pi, out paramsArray, out defaultArgList)) { continue; } var outs = 0; var margs = TryConvertArguments(pi, paramsArray, args, pynargs, defaultArgList, needsResolution: _methods.Length > 1, outs: out outs); 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(new List <Binding>()); } target = co.inst; } #region COM Binding bindings.Add(new Binding(mi, target, margs, outs)); //return new Binding(mi, target, margs, outs); #endregion } #region COM Binding if (bindings.Count > 0) { /*Binding item = bindings.Where<Binding>(x => x.info.Name.EndsWith("_Item")).FirstOrDefault<Binding>(); * if (item != null) * return item; * * Binding def = bindings.Where<Binding>(x => x.info.Name.EndsWith("_Default")).FirstOrDefault<Binding>(); * if (def != null) * return def; * * return bindings.First<Binding>();*/ return(bindings.OrderBy(x => x.info.Name).ToList <Binding>()); } #endregion // 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, type, mi, null)); } return(new List <Binding>()); }
internal static bool ToManagedValue(IntPtr value, Type obType, out object result, bool setError, out bool usedImplicit) { usedImplicit = false; if (obType == typeof(PyObject)) { Runtime.XIncref(value); // PyObject() assumes ownership result = new PyObject(value); return(true); } if (obType.IsGenericType && Runtime.PyObject_TYPE(value) == Runtime.PyListType) { var typeDefinition = obType.GetGenericTypeDefinition(); if (typeDefinition == typeof(List <>)) { return(ToList(value, obType, out result, 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; if (mt != null) { if (mt is CLRObject co) { object tmp = co.inst; if (obType.IsInstanceOfType(tmp)) { result = tmp; return(true); } else { var type = tmp.GetType(); // check implicit conversions that receive tmp type and return obType var conversionMethod = type.GetMethod("op_Implicit", new[] { type }); if (conversionMethod != null && conversionMethod.ReturnType == obType) { result = conversionMethod.Invoke(null, new[] { tmp }); usedImplicit = true; return(true); } } if (setError) { string typeString = tmp is null ? "null" : tmp.GetType().ToString(); Exceptions.SetError(Exceptions.TypeError, $"{typeString} value cannot be converted to {obType}"); } return(false); } if (mt is ClassBase cb) { if (!cb.type.Valid) { Exceptions.SetError(Exceptions.TypeError, cb.type.DeletedMessage); return(false); } result = cb.type.Value; return(true); } // shouldn't happen return(false); } if (value == Runtime.PyNone && !obType.IsValueType) { result = null; return(true); } if (obType.IsGenericType && obType.GetGenericTypeDefinition() == typeof(Nullable <>)) { if (value == Runtime.PyNone) { result = null; return(true); } // Set type to underlying type obType = obType.GetGenericArguments()[0]; } if (obType.ContainsGenericParameters) { if (setError) { Exceptions.SetError(Exceptions.TypeError, $"Cannot create an instance of the open generic type {obType}"); } return(false); } if (obType.IsArray) { return(ToArray(value, obType, out result, setError)); } if (obType.IsEnum) { return(ToEnum(value, obType, out result, setError)); } // Conversion to 'Object' is done based on some reasonable default // conversions (Python string -> managed string, Python int -> Int32 etc.). if (obType == objectType) { if (Runtime.IsStringType(value)) { return(ToPrimitive(value, stringType, out result, setError)); } if (Runtime.PyBool_Check(value)) { return(ToPrimitive(value, boolType, out result, setError)); } if (Runtime.PyInt_Check(value)) { return(ToPrimitive(value, int32Type, out result, setError)); } if (Runtime.PyLong_Check(value)) { return(ToPrimitive(value, int64Type, out result, setError)); } if (Runtime.PyFloat_Check(value)) { return(ToPrimitive(value, doubleType, out result, setError)); } // give custom codecs a chance to take over conversion of sequences IntPtr pyType = Runtime.PyObject_TYPE(value); if (PyObjectConversions.TryDecode(value, pyType, obType, out result)) { return(true); } if (Runtime.PySequence_Check(value)) { return(ToArray(value, typeof(object[]), out result, setError)); } Runtime.XIncref(value); // PyObject() assumes ownership result = new PyObject(value); return(true); } // Conversion to 'Type' is done using the same mappings as above for objects. if (obType == typeType) { if (value == Runtime.PyStringType) { result = stringType; return(true); } if (value == Runtime.PyBoolType) { result = boolType; return(true); } if (value == Runtime.PyIntType) { result = int32Type; return(true); } if (value == Runtime.PyLongType) { result = int64Type; return(true); } if (value == Runtime.PyFloatType) { result = doubleType; return(true); } if (value == Runtime.PyListType || value == Runtime.PyTupleType) { result = typeof(object[]); return(true); } if (setError) { Exceptions.SetError(Exceptions.TypeError, "value cannot be converted to Type"); } return(false); } var underlyingType = Nullable.GetUnderlyingType(obType); if (underlyingType != null) { return(ToManagedValue(value, underlyingType, out result, setError, out usedImplicit)); } TypeCode typeCode = Type.GetTypeCode(obType); if (typeCode == TypeCode.Object) { IntPtr pyType = Runtime.PyObject_TYPE(value); if (PyObjectConversions.TryDecode(value, pyType, obType, out result)) { return(true); } } var opImplicit = obType.GetMethod("op_Implicit", new[] { obType }); if (opImplicit != null) { if (ToManagedValue(value, opImplicit.ReturnType, out result, setError, out usedImplicit)) { opImplicit = obType.GetMethod("op_Implicit", new[] { result.GetType() }); if (opImplicit != null) { result = opImplicit.Invoke(null, new[] { result }); } return(opImplicit != null); } } return(ToPrimitive(value, obType, out result, setError)); }
internal static bool ToManagedValue(IntPtr value, Type obType, out object result, bool setError) { if (obType == typeof(PyObject)) { Runtime.XIncref(value); // PyObject() assumes ownership result = new PyObject(value); return(true); } // Common case: if the Python value is a wrapped managed object // instance, just return the wrapped object. ManagedType mt = ManagedType.GetManagedObject(value); result = null; if (mt != null) { if (mt is CLRObject) { object tmp = ((CLRObject)mt).inst; if (obType.IsInstanceOfType(tmp)) { result = tmp; return(true); } Exceptions.SetError(Exceptions.TypeError, $"value cannot be converted to {obType}"); return(false); } if (mt is ClassBase) { result = ((ClassBase)mt).type; return(true); } // shouldn't happen return(false); } if (value == Runtime.PyNone && !obType.IsValueType) { result = null; return(true); } if (obType.IsGenericType && obType.GetGenericTypeDefinition() == typeof(Nullable <>)) { if (value == Runtime.PyNone) { result = null; return(true); } // Set type to underlying type obType = obType.GetGenericArguments()[0]; } if (obType.IsArray) { return(ToArray(value, obType, out result, setError)); } if (obType.IsEnum) { return(ToEnum(value, obType, out result, setError)); } // Conversion to 'Object' is done based on some reasonable default // conversions (Python string -> managed string, Python int -> Int32 etc.). if (obType == objectType) { if (Runtime.IsStringType(value)) { return(ToPrimitive(value, stringType, out result, setError)); } if (Runtime.PyBool_Check(value)) { return(ToPrimitive(value, boolType, out result, setError)); } if (Runtime.PyInt_Check(value)) { return(ToPrimitive(value, int32Type, out result, setError)); } if (Runtime.PyLong_Check(value)) { return(ToPrimitive(value, int64Type, out result, setError)); } if (Runtime.PyFloat_Check(value)) { return(ToPrimitive(value, doubleType, out result, setError)); } if (Runtime.Interop.PySequence_Check(value)) { return(ToArray(value, typeof(object[]), out result, setError)); } if (setError) { Exceptions.SetError(Exceptions.TypeError, "value cannot be converted to Object"); } return(false); } // Conversion to 'Type' is done using the same mappings as above for objects. if (obType == typeType) { if (value == Runtime.PyStringType) { result = stringType; return(true); } if (value == Runtime.PyBoolType) { result = boolType; return(true); } if (value == Runtime.PyIntType) { result = int32Type; return(true); } if (value == Runtime.PyLongType) { result = int64Type; return(true); } if (value == Runtime.PyFloatType) { result = doubleType; return(true); } if (value == Runtime.PyListType || value == Runtime.PyTupleType) { result = typeof(object[]); return(true); } if (setError) { Exceptions.SetError(Exceptions.TypeError, "value cannot be converted to Type"); } return(false); } return(ToPrimitive(value, obType, out result, setError)); }
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); } }
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)); }