/// <summary> /// Exception __str__ implementation /// </summary> public new static IntPtr tp_str(IntPtr ob) { Exception e = ToException(ob); if (e == null) { return(Exceptions.RaiseTypeError("invalid object")); } string message = e.ToString(); string fullTypeName = e.GetType().FullName; string prefix = fullTypeName + ": "; if (message.StartsWith(prefix)) { message = message.Substring(prefix.Length); } else if (message.StartsWith(fullTypeName)) { message = message.Substring(fullTypeName.Length); } return(Runtime.PyUnicode_FromString(message)); }
/// <summary> /// Implement explicit overload selection using subscript syntax ([]). /// </summary> /// <remarks> /// ConstructorBinding.GetItem(PyObject *o, PyObject *key) /// Return element of o corresponding to the object key or NULL on failure. /// This is the equivalent of the Python expression o[key]. /// </remarks> public static IntPtr mp_subscript(IntPtr op, IntPtr key) { var self = (ConstructorBinding)GetManagedObject(op); Type[] types = Runtime.PythonArgsToTypeArray(key); if (types == null) { return(Exceptions.RaiseTypeError("type(s) expected")); } //MethodBase[] methBaseArray = self.ctorBinder.GetMethods(); //MethodBase ci = MatchSignature(methBaseArray, types); ConstructorInfo ci = self.type.GetConstructor(types); if (ci == null) { return(Exceptions.RaiseTypeError("No match found for constructor signature")); } var boundCtor = new BoundContructor(self.type, self.pyTypeHndl, self.ctorBinder, ci); /* Since nothing is cached, do we need the increment??? * Runtime.XIncref(boundCtor.pyHandle); // Decref'd by the interpreter??? */ return(boundCtor.pyHandle); }
/// <summary> /// Implements __new__ for reflected generic types. /// </summary> public static NewReference tp_new(BorrowedReference tp, BorrowedReference args, BorrowedReference kw) { var self = (GenericType)GetManagedObject(tp) !; if (!self.type.Valid) { return(Exceptions.RaiseTypeError(self.type.DeletedMessage)); } var type = self.type.Value; if (type.IsInterface && !type.IsConstructedGenericType) { var nargs = Runtime.PyTuple_Size(args); if (nargs == 1) { var instance = Runtime.PyTuple_GetItem(args, 0); return(AsGenericInterface(instance, type)); } } Exceptions.SetError(Exceptions.TypeError, "cannot instantiate an open generic type"); return(default);
//==================================================================== // helper methods for raising warnings //==================================================================== /// <summary> /// Alias for Python's warnings.warn() function. /// </summary> public static void warn(string message, BorrowedReference exception, int stacklevel) { if (exception == null || (Runtime.PyObject_IsSubclass(exception, Exceptions.Warning) != 1)) { Exceptions.RaiseTypeError("Invalid exception"); } using var warn = Runtime.PyObject_GetAttrString(warnings_module.obj, "warn"); Exceptions.ErrorCheck(warn.Borrow()); using var argsTemp = Runtime.PyTuple_New(3); BorrowedReference args = argsTemp.BorrowOrThrow(); using var msg = Runtime.PyString_FromString(message); Runtime.PyTuple_SetItem(args, 0, msg.StealOrThrow()); Runtime.PyTuple_SetItem(args, 1, exception); using var level = Runtime.PyInt_FromInt32(stacklevel); Runtime.PyTuple_SetItem(args, 2, level.StealOrThrow()); using var result = Runtime.PyObject_CallObject(warn.Borrow(), args); Exceptions.ErrorCheck(result.Borrow()); }
/// <summary> /// Implements __new__ for reflected interface types. /// </summary> public static NewReference tp_new(BorrowedReference tp, BorrowedReference args, BorrowedReference kw) { var self = (InterfaceObject)GetManagedObject(tp) !; if (!self.type.Valid) { return(Exceptions.RaiseTypeError(self.type.DeletedMessage)); } var nargs = Runtime.PyTuple_Size(args); Type type = self.type.Value; object obj; if (nargs == 1) { BorrowedReference inst = Runtime.PyTuple_GetItem(args, 0); if (GetManagedObject(inst) is CLRObject co && type.IsInstanceOfType(co.inst)) { obj = co.inst; } else { Exceptions.SetError(Exceptions.TypeError, $"object does not implement {type.Name}"); return(default);
/// <summary> /// Metatype __call__ implementation. This is needed to ensure correct /// initialization (__init__ support), because the tp_call we inherit /// from PyType_Type won't call __init__ for metatypes it doesn't know. /// </summary> public static IntPtr tp_call(IntPtr tp, IntPtr args, IntPtr kw) { IntPtr func = Marshal.ReadIntPtr(tp, TypeOffset.tp_new); if (func == IntPtr.Zero) { return(Exceptions.RaiseTypeError("invalid object")); } IntPtr obj = NativeCall.Call_3(func, tp, args, kw); if (obj == IntPtr.Zero) { return(IntPtr.Zero); } var init = Runtime.PyObject_GetAttrString(obj, "__init__"); Runtime.PyErr_Clear(); if (init != IntPtr.Zero) { IntPtr result = Runtime.PyObject_Call(init, args, kw); Runtime.XDecref(init); if (result == IntPtr.Zero) { Runtime.XDecref(obj); return(IntPtr.Zero); } Runtime.XDecref(result); } return(obj); }
/// <summary> /// Implement binding of generic methods using the subscript syntax []. /// </summary> public static IntPtr mp_subscript(IntPtr tp, IntPtr idx) { var self = (MethodBinding)GetManagedObject(tp); Type[] types = Runtime.PythonArgsToTypeArray(idx); if (types == null) { return(Exceptions.RaiseTypeError("type(s) expected")); } MethodInfo mi = MethodBinder.MatchParameters(self.m.info, types); if (mi == null) { return(Exceptions.RaiseTypeError("No match found for given type params")); } var mb = new MethodBinding(self.m, self.target) { info = mi }; return(mb.pyHandle); }
//==================================================================== // Default implementation of [] semantics for reflected types. //==================================================================== public virtual IntPtr type_subscript(IntPtr idx) { return(Exceptions.RaiseTypeError("unsubscriptable object")); }
/// <summary> /// Descriptor __get__ implementation. This method returns the /// value of the property on the given object. The returned value /// is converted to an appropriately typed Python object. /// </summary> public static IntPtr tp_descr_get(IntPtr ds, IntPtr ob, IntPtr tp) { var self = (PropertyObject)GetManagedObject(ds); MethodInfo getter = self.getter; object result; if (getter == null) { return(Exceptions.RaiseTypeError("property cannot be read")); } if (ob == IntPtr.Zero || ob == Runtime.PyNone) { if (!getter.IsStatic) { Exceptions.SetError(Exceptions.TypeError, "instance property must be accessed through a class instance"); return(IntPtr.Zero); } try { result = self.info.GetValue(null, null); return(Converter.ToPython(result, self.info.PropertyType)); } catch (Exception e) { return(Exceptions.RaiseTypeError(e.Message)); } } var co = GetManagedObject(ob) as CLRObject; if (co == null) { return(Exceptions.RaiseTypeError("invalid target")); } try { if (self.getterCache == null && !self.getterCacheFailed) { // if the getter is not public 'GetGetMethod' will not find it // but calling 'GetValue' will work, so for backwards compatibility // we will use it instead self.getterCache = BuildGetter(self.info); if (self.getterCache == null) { self.getterCacheFailed = true; } } result = self.getterCacheFailed ? self.info.GetValue(co.inst, null) : self.getterCache(co.inst); return(Converter.ToPython(result, self.info.PropertyType)); } catch (Exception e) { if (e.InnerException != null) { e = e.InnerException; } Exceptions.SetError(e); return(IntPtr.Zero); } }
/// <summary> /// Descriptor __set__ implementation. This method sets the value of /// a property based on the given Python value. The Python value must /// be convertible to the type of the property. /// </summary> public new static int tp_descr_set(IntPtr ds, IntPtr ob, IntPtr val) { var self = (PropertyObject)GetManagedObject(ds); MethodInfo setter = self.setter; object newval; if (val == IntPtr.Zero) { Exceptions.RaiseTypeError("cannot delete property"); return(-1); } if (setter == null) { Exceptions.RaiseTypeError("property is read-only"); return(-1); } if (!Converter.ToManaged(val, self.info.PropertyType, out newval, true)) { return(-1); } bool is_static = setter.IsStatic; if (ob == IntPtr.Zero || ob == Runtime.PyNone) { if (!is_static) { Exceptions.RaiseTypeError("instance property must be set on an instance"); return(-1); } } try { if (!is_static) { var co = GetManagedObject(ob) as CLRObject; if (co == null) { Exceptions.RaiseTypeError("invalid target"); return(-1); } if (self.setterCache == null && !self.setterCacheFailed) { // if the setter is not public 'GetSetMethod' will not find it // but calling 'SetValue' will work, so for backwards compatibility // we will use it instead self.setterCache = BuildSetter(self.info); if (self.setterCache == null) { self.setterCacheFailed = true; } } if (self.setterCacheFailed) { self.info.SetValue(co.inst, newval, null); } else { self.setterCache(co.inst, newval); } } else { self.info.SetValue(null, newval, null); } return(0); } catch (Exception e) { if (e.InnerException != null) { e = e.InnerException; } Exceptions.SetError(e); return(-1); } }
/// <summary> /// Descriptor __set__ implementation. This method sets the value of /// a property based on the given Python value. The Python value must /// be convertible to the type of the property. /// </summary> public new static int tp_descr_set(IntPtr ds, IntPtr ob, IntPtr val) { var self = (PropertyObject)GetManagedObject(ds); MethodInfo setter = self.setter; object newval; if (val == IntPtr.Zero) { Exceptions.RaiseTypeError("cannot delete property"); return(-1); } if (setter == null) { Exceptions.RaiseTypeError("property is read-only"); return(-1); } if (!Converter.ToManaged(val, self.info.PropertyType, out newval, true)) { return(-1); } bool is_static = setter.IsStatic; if (ob == IntPtr.Zero || ob == Runtime.PyNone) { if (!is_static) { Exceptions.RaiseTypeError("instance property must be set on an instance"); return(-1); } } try { if (!is_static) { var co = GetManagedObject(ob) as CLRObject; if (co == null) { Exceptions.RaiseTypeError("invalid target"); return(-1); } self.info.SetValue(co.inst, newval, null); } else { self.info.SetValue(null, newval, null); } return(0); } catch (Exception e) { if (e.InnerException != null) { e = e.InnerException; } Exceptions.SetError(e); return(-1); } }
public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw) { int len = Runtime.PyTuple_Size(args); if (len < 3) { return(Exceptions.RaiseTypeError("invalid argument list")); } IntPtr name = Runtime.PyTuple_GetItem(args, 0); IntPtr bases = Runtime.PyTuple_GetItem(args, 1); IntPtr dict = Runtime.PyTuple_GetItem(args, 2); // We do not support multiple inheritance, so the bases argument // should be a 1-item tuple containing the type we are subtyping. // That type must itself have a managed implementation. We check // that by making sure its metatype is the CLR metatype. if (Runtime.PyTuple_Size(bases) != 1) { return(Exceptions.RaiseTypeError( "cannot use multiple inheritance with managed classes" )); } IntPtr base_type = Runtime.PyTuple_GetItem(bases, 0); IntPtr mt = Runtime.PyObject_TYPE(base_type); if (!((mt == PyCLRMetaType) || (mt == Runtime.PyTypeType))) { return(Exceptions.RaiseTypeError("invalid metatype")); } // Ensure that the reflected type is appropriate for subclassing, // disallowing subclassing of delegates, enums and array types. ClassBase cb = GetManagedObject(base_type) as ClassBase; if (cb != null) { if (!cb.CanSubclass()) { return(Exceptions.RaiseTypeError( "delegates, enums and array types cannot be subclassed" )); } } IntPtr slots = Runtime.PyDict_GetItemString(dict, "__slots__"); if (slots != IntPtr.Zero) { return(Exceptions.RaiseTypeError( "subclasses of managed classes do not support __slots__" )); } // hack for now... fix for 1.0 //return TypeManager.CreateSubType(args); // right way IntPtr func = Marshal.ReadIntPtr(Runtime.PyTypeType, TypeOffset.tp_new); IntPtr type = NativeCall.Call_3(func, tp, args, kw); if (type == IntPtr.Zero) { return(IntPtr.Zero); } int flags = TypeFlags.Default; flags |= TypeFlags.Managed; flags |= TypeFlags.HeapType; flags |= TypeFlags.BaseType; flags |= TypeFlags.Subclass; flags |= TypeFlags.HaveGC; Marshal.WriteIntPtr(type, TypeOffset.tp_flags, (IntPtr)flags); TypeManager.CopySlot(base_type, type, TypeOffset.tp_dealloc); // Hmm - the standard subtype_traverse, clear look at ob_size to // do things, so to allow gc to work correctly we need to move // our hidden handle out of ob_size. Then, in theory we can // comment this out and still not crash. TypeManager.CopySlot(base_type, type, TypeOffset.tp_traverse); TypeManager.CopySlot(base_type, type, TypeOffset.tp_clear); // for now, move up hidden handle... IntPtr gc = Marshal.ReadIntPtr(base_type, TypeOffset.magic()); Marshal.WriteIntPtr(type, TypeOffset.magic(), gc); //DebugUtil.DumpType(base_type); //DebugUtil.DumpType(type); return(type); }
//=================================================================== // 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); if (mod_name == "CLR") { Exceptions.deprecation("The CLR module is deprecated. " + "Please use 'clr'."); root.InitializePreload(); Runtime.Incref(root.pyHandle); return(root.pyHandle); } if (mod_name == "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); } 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(); AssemblyManager.LoadImplicit(realname); if (!AssemblyManager.IsValidNamespace(realname)) { 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); }
/// <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> /// Standard comparison implementation for instances of reflected types. /// </summary> public static IntPtr tp_richcompare(IntPtr ob, IntPtr other, int op) { CLRObject co1; CLRObject co2; IntPtr tp = Runtime.PyObject_TYPE(ob); var cls = (ClassBase)GetManagedObject(tp); // C# operator methods take precedence over IComparable. // We first check if there's a comparison operator by looking up the richcompare table, // otherwise fallback to checking if an IComparable interface is handled. if (cls.richcompare.TryGetValue(op, out var methodObject)) { // Wrap the `other` argument of a binary comparison operator in a PyTuple. IntPtr args = Runtime.PyTuple_New(1); Runtime.XIncref(other); Runtime.PyTuple_SetItem(args, 0, other); IntPtr value; try { value = methodObject.Invoke(ob, args, IntPtr.Zero); } finally { Runtime.XDecref(args); // Free args pytuple } return(value); } switch (op) { case Runtime.Py_EQ: case Runtime.Py_NE: IntPtr pytrue = Runtime.PyTrue; IntPtr pyfalse = Runtime.PyFalse; // swap true and false for NE if (op != Runtime.Py_EQ) { pytrue = Runtime.PyFalse; pyfalse = Runtime.PyTrue; } if (ob == other) { Runtime.XIncref(pytrue); return(pytrue); } co1 = GetManagedObject(ob) as CLRObject; co2 = GetManagedObject(other) as CLRObject; if (null == co2) { Runtime.XIncref(pyfalse); return(pyfalse); } object o1 = co1.inst; object o2 = co2.inst; if (Equals(o1, o2)) { Runtime.XIncref(pytrue); return(pytrue); } Runtime.XIncref(pyfalse); return(pyfalse); case Runtime.Py_LT: case Runtime.Py_LE: case Runtime.Py_GT: case Runtime.Py_GE: co1 = GetManagedObject(ob) as CLRObject; co2 = GetManagedObject(other) as CLRObject; if (co1 == null || co2 == null) { return(Exceptions.RaiseTypeError("Cannot get managed object")); } var co1Comp = co1.inst as IComparable; if (co1Comp == null) { Type co1Type = co1.GetType(); return(Exceptions.RaiseTypeError($"Cannot convert object of type {co1Type} to IComparable")); } try { int cmp = co1Comp.CompareTo(co2.inst); IntPtr pyCmp; if (cmp < 0) { if (op == Runtime.Py_LT || op == Runtime.Py_LE) { pyCmp = Runtime.PyTrue; } else { pyCmp = Runtime.PyFalse; } } else if (cmp == 0) { if (op == Runtime.Py_LE || op == Runtime.Py_GE) { pyCmp = Runtime.PyTrue; } else { pyCmp = Runtime.PyFalse; } } else { if (op == Runtime.Py_GE || op == Runtime.Py_GT) { pyCmp = Runtime.PyTrue; } else { pyCmp = Runtime.PyFalse; } } Runtime.XIncref(pyCmp); return(pyCmp); } catch (ArgumentException e) { return(Exceptions.RaiseTypeError(e.Message)); } default: Runtime.XIncref(Runtime.PyNotImplemented); return(Runtime.PyNotImplemented); } }
/// <summary> /// Metatype __new__ implementation. This is called to create a new /// class / type when a reflected class is subclassed. /// </summary> public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw) { int len = Runtime.PyTuple_Size(args); if (len < 3) { return(Exceptions.RaiseTypeError("invalid argument list")); } IntPtr name = Runtime.PyTuple_GetItem(args, 0); IntPtr bases = Runtime.PyTuple_GetItem(args, 1); IntPtr dict = Runtime.PyTuple_GetItem(args, 2); // We do not support multiple inheritance, so the bases argument // should be a 1-item tuple containing the type we are subtyping. // That type must itself have a managed implementation. We check // that by making sure its metatype is the CLR metatype. if (Runtime.PyTuple_Size(bases) != 1) { return(Exceptions.RaiseTypeError("cannot use multiple inheritance with managed classes")); } IntPtr base_type = Runtime.PyTuple_GetItem(bases, 0); IntPtr mt = Runtime.PyObject_TYPE(base_type); if (!(mt == PyCLRMetaType || mt == Runtime.PyTypeType)) { return(Exceptions.RaiseTypeError("invalid metatype")); } // Ensure that the reflected type is appropriate for subclassing, // disallowing subclassing of delegates, enums and array types. var cb = GetManagedObject(base_type) as ClassBase; if (cb != null) { if (!cb.CanSubclass()) { return(Exceptions.RaiseTypeError("delegates, enums and array types cannot be subclassed")); } } IntPtr slots = Runtime.PyDict_GetItemString(dict, "__slots__"); if (slots != IntPtr.Zero) { return(Exceptions.RaiseTypeError("subclasses of managed classes do not support __slots__")); } // If __assembly__ or __namespace__ are in the class dictionary then create // a managed sub type. // This creates a new managed type that can be used from .net to call back // into python. if (IntPtr.Zero != dict) { Runtime.XIncref(dict); using (var clsDict = new PyDict(dict)) { if (clsDict.HasKey("__assembly__") || clsDict.HasKey("__namespace__")) { return(TypeManager.CreateSubType(name, base_type, dict)); } } } // otherwise just create a basic type without reflecting back into the managed side. IntPtr func = Marshal.ReadIntPtr(Runtime.PyTypeType, TypeOffset.tp_new); IntPtr type = NativeCall.Call_3(func, tp, args, kw); if (type == IntPtr.Zero) { return(IntPtr.Zero); } int flags = TypeFlags.Default; flags |= TypeFlags.Managed; flags |= TypeFlags.HeapType; flags |= TypeFlags.BaseType; flags |= TypeFlags.Subclass; flags |= TypeFlags.HaveGC; Util.WriteCLong(type, TypeOffset.tp_flags, flags); TypeManager.CopySlot(base_type, type, TypeOffset.tp_dealloc); // Hmm - the standard subtype_traverse, clear look at ob_size to // do things, so to allow gc to work correctly we need to move // our hidden handle out of ob_size. Then, in theory we can // comment this out and still not crash. TypeManager.CopySlot(base_type, type, TypeOffset.tp_traverse); TypeManager.CopySlot(base_type, type, TypeOffset.tp_clear); // for now, move up hidden handle... IntPtr gc = Marshal.ReadIntPtr(base_type, TypeOffset.magic()); Marshal.WriteIntPtr(type, TypeOffset.magic(), gc); return(type); }
/// <summary> /// Determine the managed type that a Python argument object needs to be converted into. /// </summary> /// <param name="parameterType">The parameter's managed type.</param> /// <param name="argument">Pointer to the Python argument object.</param> /// <param name="needsResolution">If true, there are multiple overloading methods that need resolution.</param> /// <returns>null if conversion is not possible</returns> 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); if (pyoptype != IntPtr.Zero) { clrtype = Converter.GetTypeByAlias(pyoptype); } Runtime.XDecref(pyoptype); } if (clrtype != null) { if ((parameterType != typeof(object)) && (parameterType != clrtype)) { IntPtr pytype = Converter.GetPythonTypeByAlias(parameterType); pyoptype = Runtime.PyObject_Type(argument); var typematch = false; if (pyoptype != IntPtr.Zero) { if (pytype != pyoptype) { typematch = false; } else { typematch = true; clrtype = parameterType; } } if (!typematch) { // this takes care of enum values TypeCode parameterTypeCode = Type.GetTypeCode(parameterType); TypeCode clrTypeCode = Type.GetTypeCode(clrtype); if (parameterTypeCode == clrTypeCode) { typematch = true; clrtype = parameterType; } else { Exceptions.RaiseTypeError($"Expected {parameterTypeCode}, got {clrTypeCode}"); } } Runtime.XDecref(pyoptype); if (!typematch) { return(null); } } else { clrtype = parameterType; } } else { clrtype = parameterType; } return(clrtype); }
internal virtual IntPtr Invoke(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, MethodInfo[] methodinfo) { Binding binding = Bind(inst, args, kw, info, methodinfo); object result; IntPtr ts = IntPtr.Zero; if (binding == null) { var value = new StringBuilder("No method matches given arguments"); if (methodinfo != null && methodinfo.Length > 0) { value.Append($" for {methodinfo[0].Name}"); } else if (list.Count > 0) { value.Append($" for {list[0].MethodBase.Name}"); } value.Append(": "); AppendArgumentTypes(to: value, args); Exceptions.RaiseTypeError(value.ToString()); return(IntPtr.Zero); } if (allow_threads) { ts = PythonEngine.BeginAllowThreads(); } try { result = binding.info.Invoke(binding.inst, BindingFlags.Default, null, binding.args, null); } catch (Exception e) { if (e.InnerException != null) { e = e.InnerException; } if (allow_threads) { PythonEngine.EndAllowThreads(ts); } Exceptions.SetError(e); return(IntPtr.Zero); } if (allow_threads) { PythonEngine.EndAllowThreads(ts); } // If there are out parameters, we return a tuple containing // the result followed by the out parameters. If there is only // one out parameter and the return type of the method is void, // we return the out parameter as the result to Python (for // code compatibility with ironpython). var mi = (MethodInfo)binding.info; if (binding.outs > 0) { ParameterInfo[] pi = mi.GetParameters(); int c = pi.Length; var n = 0; IntPtr t = Runtime.PyTuple_New(binding.outs + 1); IntPtr v = Converter.ToPython(result, mi.ReturnType); Runtime.PyTuple_SetItem(t, n, v); n++; for (var i = 0; i < c; i++) { Type pt = pi[i].ParameterType; if (pi[i].IsOut || pt.IsByRef) { v = Converter.ToPython(binding.args[i], pt); Runtime.PyTuple_SetItem(t, n, v); n++; } } if (binding.outs == 1 && mi.ReturnType == typeof(void)) { v = Runtime.PyTuple_GetItem(t, 1); Runtime.XIncref(v); Runtime.XDecref(t); return(v); } return(t); } return(Converter.ToPython(result, mi.ReturnType)); }
/// <summary> /// Allows ctor selection to be limited to a single attempt at a /// match by providing the MethodBase to use instead of searching /// the entire MethodBinder.list (generic ArrayList) /// </summary> /// <param name="inst"> (possibly null) instance </param> /// <param name="args"> PyObject* to the arg tuple </param> /// <param name="kw"> PyObject* to the keyword args dict </param> /// <param name="info"> The sole ContructorInfo to use or null </param> /// <returns> The result of the constructor call with converted params </returns> /// <remarks> /// 2010-07-24 BC: I added the info parameter to the call to Bind() /// Binding binding = this.Bind(inst, args, kw, info); /// to take advantage of Bind()'s ability to use a single MethodBase (CI or MI). /// </remarks> internal object InvokeRaw(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info) { if (!_containingType.Valid) { return(Exceptions.RaiseTypeError(_containingType.DeletedMessage)); } object result; Type tp = _containingType.Value; if (tp.IsValueType && !tp.IsPrimitive && !tp.IsEnum && tp != typeof(decimal) && Runtime.PyTuple_Size(args) == 0) { // If you are trying to construct an instance of a struct by // calling its default constructor, that ConstructorInfo // instance will not appear in reflection and the object must // instead be constructed via a call to // Activator.CreateInstance(). try { result = Activator.CreateInstance(tp); } catch (Exception e) { if (e.InnerException != null) { e = e.InnerException; } Exceptions.SetError(e); return(null); } return(result); } Binding binding = Bind(inst, args, kw, info); if (binding == null) { // It is possible for __new__ to be invoked on construction // of a Python subclass of a managed class, so args may // reflect more args than are required to instantiate the // class. So if we cant find a ctor that matches, we'll see // if there is a default constructor and, if so, assume that // any extra args are intended for the subclass' __init__. IntPtr eargs = Runtime.PyTuple_New(0); binding = Bind(inst, eargs, IntPtr.Zero); Runtime.XDecref(eargs); if (binding == null) { var errorMessage = new StringBuilder("No constructor matches given arguments"); if (info != null && info.IsConstructor && info.DeclaringType != null) { errorMessage.Append(" for ").Append(info.DeclaringType.Name); } errorMessage.Append(": "); Runtime.PyErr_Fetch(out var errType, out var errVal, out var errTrace); AppendArgumentTypes(to: errorMessage, args); Runtime.PyErr_Restore(errType.StealNullable(), errVal.StealNullable(), errTrace.StealNullable()); Exceptions.RaiseTypeError(errorMessage.ToString()); return(null); } } // Fire the selected ctor and catch errors... var ci = (ConstructorInfo)binding.info; // Object construction is presumed to be non-blocking and fast // enough that we shouldn't really need to release the GIL. try { result = ci.Invoke(binding.args); } catch (Exception e) { if (e.InnerException != null) { e = e.InnerException; } Exceptions.SetError(e); return(null); } return(result); }
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)); } }
/// <summary> /// Standard comparison implementation for instances of reflected types. /// </summary> public static IntPtr tp_richcompare(IntPtr ob, IntPtr other, int op) { CLRObject co1; CLRObject co2; switch (op) { case Runtime.Py_EQ: case Runtime.Py_NE: IntPtr pytrue = Runtime.PyTrue; IntPtr pyfalse = Runtime.PyFalse; // swap true and false for NE if (op != Runtime.Py_EQ) { pytrue = Runtime.PyFalse; pyfalse = Runtime.PyTrue; } if (ob == other) { Runtime.XIncref(pytrue); return(pytrue); } co1 = GetManagedObject(ob) as CLRObject; co2 = GetManagedObject(other) as CLRObject; if (null == co2) { Runtime.XIncref(pyfalse); return(pyfalse); } object o1 = co1.inst; object o2 = co2.inst; if (Equals(o1, o2)) { Runtime.XIncref(pytrue); return(pytrue); } Runtime.XIncref(pyfalse); return(pyfalse); case Runtime.Py_LT: case Runtime.Py_LE: case Runtime.Py_GT: case Runtime.Py_GE: co1 = GetManagedObject(ob) as CLRObject; co2 = GetManagedObject(other) as CLRObject; if (co1 == null || co2 == null) { return(Exceptions.RaiseTypeError("Cannot get managed object")); } var co1Comp = co1.inst as IComparable; if (co1Comp == null) { Type co1Type = co1.GetType(); return(Exceptions.RaiseTypeError($"Cannot convert object of type {co1Type} to IComparable")); } try { int cmp = co1Comp.CompareTo(co2.inst); IntPtr pyCmp; if (cmp < 0) { if (op == Runtime.Py_LT || op == Runtime.Py_LE) { pyCmp = Runtime.PyTrue; } else { pyCmp = Runtime.PyFalse; } } else if (cmp == 0) { if (op == Runtime.Py_LE || op == Runtime.Py_GE) { pyCmp = Runtime.PyTrue; } else { pyCmp = Runtime.PyFalse; } } else { if (op == Runtime.Py_GE || op == Runtime.Py_GT) { pyCmp = Runtime.PyTrue; } else { pyCmp = Runtime.PyFalse; } } Runtime.XIncref(pyCmp); return(pyCmp); } catch (ArgumentException e) { return(Exceptions.RaiseTypeError(e.Message)); } default: Runtime.XIncref(Runtime.PyNotImplemented); return(Runtime.PyNotImplemented); } }
//=================================================================== // 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); }
//==================================================================== // Implements __new__ for reflected classes and value types. //==================================================================== public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw) { ClassObject self = GetManagedObject(tp) as ClassObject; // Sanity check: this ensures a graceful error if someone does // something intentially wrong like use the managed metatype for // a class that is not really derived from a managed class. if (self == null) { return(Exceptions.RaiseTypeError("invalid object")); } Type type = self.type; // Primitive types do not have constructors, but they look like // they do from Python. If the ClassObject represents one of the // convertible primitive types, just convert the arg directly. if (type.IsPrimitive || type == typeof(String)) { if (Runtime.PyTuple_Size(args) != 1) { Exceptions.SetError(Exceptions.TypeError, "no constructors match given arguments" ); return(IntPtr.Zero); } IntPtr op = Runtime.PyTuple_GetItem(args, 0); Object result; if (!Converter.ToManaged(op, type, out result, true)) { return(IntPtr.Zero); } return(CLRObject.GetInstHandle(result, tp)); } if (type.IsAbstract) { Exceptions.SetError(Exceptions.TypeError, "cannot instantiate abstract class" ); return(IntPtr.Zero); } if (type.IsEnum) { Exceptions.SetError(Exceptions.TypeError, "cannot instantiate enumeration" ); return(IntPtr.Zero); } Object obj = self.binder.InvokeRaw(IntPtr.Zero, args, kw); if (obj == null) { // It is possible for __new__ to be invoked on construction // of a Python subclass of a managed class, so args may // reflect more args than are required to instantiate the // class. So if we cant find a ctor that matches, we'll see // if there is a default constructor and, if so, assume that // any extra args are intended for the subclass' __init__. IntPtr eargs = Runtime.PyTuple_New(0); obj = self.binder.InvokeRaw(IntPtr.Zero, eargs, kw); Runtime.Decref(eargs); if (obj == null) { return(IntPtr.Zero); } } return(CLRObject.GetInstHandle(obj, tp)); }
private object?TrueDispatch(object?[] args) { MethodInfo method = dtype.GetMethod("Invoke"); ParameterInfo[] pi = method.GetParameters(); Type rtype = method.ReturnType; NewReference callResult; using (var pyargs = Runtime.PyTuple_New(pi.Length)) { for (var i = 0; i < pi.Length; i++) { // Here we own the reference to the Python value, and we // give the ownership to the arg tuple. using var arg = Converter.ToPython(args[i], pi[i].ParameterType); int res = Runtime.PyTuple_SetItem(pyargs.Borrow(), i, arg.StealOrThrow()); if (res != 0) { throw PythonException.ThrowLastAsClrException(); } } callResult = Runtime.PyObject_Call(target, pyargs.Borrow(), null); } if (callResult.IsNull()) { throw PythonException.ThrowLastAsClrException(); } using (callResult) { BorrowedReference op = callResult.Borrow(); int byRefCount = pi.Count(parameterInfo => parameterInfo.ParameterType.IsByRef); if (byRefCount > 0) { // By symmetry with MethodBinder.Invoke, when there are out // parameters we expect to receive a tuple containing // the result, if any, followed by the out parameters. If there is only // one out parameter and the return type of the method is void, // we instead receive the out parameter as the result from Python. bool isVoid = rtype == typeof(void); int tupleSize = byRefCount + (isVoid ? 0 : 1); if (isVoid && byRefCount == 1) { // The return type is void and there is a single out parameter. for (int i = 0; i < pi.Length; i++) { Type t = pi[i].ParameterType; if (t.IsByRef) { if (!Converter.ToManaged(op, t, out args[i], true)) { Exceptions.RaiseTypeError($"The Python function did not return {t.GetElementType()} (the out parameter type)"); throw PythonException.ThrowLastAsClrException(); } break; } } return(null); } else if (Runtime.PyTuple_Check(op) && Runtime.PyTuple_Size(op) == tupleSize) { int index = isVoid ? 0 : 1; for (int i = 0; i < pi.Length; i++) { Type t = pi[i].ParameterType; if (t.IsByRef) { BorrowedReference item = Runtime.PyTuple_GetItem(op, index++); if (!Converter.ToManaged(item, t, out args[i], true)) { Exceptions.RaiseTypeError($"The Python function returned a tuple where element {i} was not {t.GetElementType()} (the out parameter type)"); throw PythonException.ThrowLastAsClrException(); } } } if (isVoid) { return(null); } BorrowedReference item0 = Runtime.PyTuple_GetItem(op, 0); if (!Converter.ToManaged(item0, rtype, out object?result0, true)) { Exceptions.RaiseTypeError($"The Python function returned a tuple where element 0 was not {rtype} (the return type)"); throw PythonException.ThrowLastAsClrException(); } return(result0); } else { string tpName = Runtime.PyObject_GetTypeName(op); if (Runtime.PyTuple_Check(op)) { tpName += $" of size {Runtime.PyTuple_Size(op)}"; } StringBuilder sb = new StringBuilder(); if (!isVoid) { sb.Append(rtype.FullName); } for (int i = 0; i < pi.Length; i++) { Type t = pi[i].ParameterType; if (t.IsByRef) { if (sb.Length > 0) { sb.Append(","); } sb.Append(t.GetElementType().FullName); } } string returnValueString = isVoid ? "" : "the return value and "; Exceptions.RaiseTypeError($"Expected a tuple ({sb}) of {returnValueString}the values for out and ref parameters, got {tpName}."); throw PythonException.ThrowLastAsClrException(); } } if (rtype == typeof(void)) { return(null); } if (!Converter.ToManaged(op, rtype, out object?result, true)) { throw PythonException.ThrowLastAsClrException(); } return(result); } }
internal virtual IntPtr Invoke(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, MethodInfo[] methodinfo) { // No valid methods, nothing to bind. if (GetMethods().Length == 0) { var msg = new StringBuilder("The underlying C# method(s) have been deleted"); if (list.Count > 0 && list[0].Name != null) { msg.Append($": {list[0]}"); } return(Exceptions.RaiseTypeError(msg.ToString())); } Binding binding = Bind(inst, args, kw, info, methodinfo); object result; IntPtr ts = IntPtr.Zero; if (binding == null) { var value = new StringBuilder("No method matches given arguments"); if (methodinfo != null && methodinfo.Length > 0) { value.Append($" for {methodinfo[0].Name}"); } else if (list.Count > 0 && list[0].Valid) { value.Append($" for {list[0].Value.Name}"); } value.Append(": "); Runtime.PyErr_Fetch(out var errType, out var errVal, out var errTrace); AppendArgumentTypes(to: value, args); Runtime.PyErr_Restore(errType.StealNullable(), errVal.StealNullable(), errTrace.StealNullable()); Exceptions.RaiseTypeError(value.ToString()); return(IntPtr.Zero); } if (allow_threads) { ts = PythonEngine.BeginAllowThreads(); } try { result = binding.info.Invoke(binding.inst, BindingFlags.Default, null, binding.args, null); } catch (Exception e) { if (e.InnerException != null) { e = e.InnerException; } if (allow_threads) { PythonEngine.EndAllowThreads(ts); } Exceptions.SetError(e); return(IntPtr.Zero); } if (allow_threads) { PythonEngine.EndAllowThreads(ts); } // If there are out parameters, we return a tuple containing // the result, if any, followed by the out parameters. If there is only // one out parameter and the return type of the method is void, // we return the out parameter as the result to Python (for // code compatibility with ironpython). var mi = (MethodInfo)binding.info; if (binding.outs > 0) { ParameterInfo[] pi = mi.GetParameters(); int c = pi.Length; var n = 0; bool isVoid = mi.ReturnType == typeof(void); int tupleSize = binding.outs + (isVoid ? 0 : 1); IntPtr t = Runtime.PyTuple_New(tupleSize); if (!isVoid) { IntPtr v = Converter.ToPython(result, mi.ReturnType); Runtime.PyTuple_SetItem(t, n, v); n++; } for (var i = 0; i < c; i++) { Type pt = pi[i].ParameterType; if (pt.IsByRef) { IntPtr v = Converter.ToPython(binding.args[i], pt.GetElementType()); Runtime.PyTuple_SetItem(t, n, v); n++; } } if (binding.outs == 1 && mi.ReturnType == typeof(void)) { IntPtr v = Runtime.PyTuple_GetItem(t, 0); Runtime.XIncref(v); Runtime.XDecref(t); return(v); } return(t); } return(Converter.ToPython(result, mi.ReturnType)); }
//==================================================================== // Implements __new__ for reflected classes and value types. //==================================================================== public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw) { ClassObject self = GetManagedObject(tp) as ClassObject; // Sanity check: this ensures a graceful error if someone does // something intentially wrong like use the managed metatype for // a class that is not really derived from a managed class. if (self == null) { return(Exceptions.RaiseTypeError("invalid object")); } Type type = self.type; // Primitive types do not have constructors, but they look like // they do from Python. If the ClassObject represents one of the // convertible primitive types, just convert the arg directly. if (type.IsPrimitive || type == typeof(String)) { if (Runtime.PyTuple_Size(args) != 1) { Exceptions.SetError(Exceptions.TypeError, "no constructors match given arguments" ); return(IntPtr.Zero); } IntPtr op = Runtime.PyTuple_GetItem(args, 0); Object result; if (!Converter.ToManaged(op, type, out result, true)) { return(IntPtr.Zero); } return(CLRObject.GetInstHandle(result, tp)); } if (type.IsAbstract) { Exceptions.SetError(Exceptions.TypeError, "cannot instantiate abstract class" ); return(IntPtr.Zero); } if (type.IsEnum) { Exceptions.SetError(Exceptions.TypeError, "cannot instantiate enumeration" ); return(IntPtr.Zero); } Object obj = self.binder.InvokeRaw(IntPtr.Zero, args, kw); if (obj == null) { return(IntPtr.Zero); } return(CLRObject.GetInstHandle(obj, tp)); }
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> /// Implements __setitem__ for array types. /// </summary> public static int mp_ass_subscript(IntPtr ob, IntPtr idx, IntPtr v) { var obj = (CLRObject)GetManagedObject(ob); var items = obj.inst as Array; Type itemType = obj.inst.GetType().GetElementType(); int rank = items.Rank; int index; 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 = Runtime.Interop.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.Interop.PyTuple_Size(idx); var args = new int[count]; for (var i = 0; i < count; i++) { IntPtr op = Runtime.Interop.PyTuple_GetItem(idx, i); index = Runtime.Interop.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, args); } catch (IndexOutOfRangeException) { Exceptions.SetError(Exceptions.IndexError, "array index out of range"); return(-1); } return(0); }
/// <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); } }
/// <summary> /// Implements __getitem__ for array types. /// </summary> public static IntPtr mp_subscript(IntPtr ob, IntPtr idx) { var obj = (CLRObject)GetManagedObject(ob); var items = obj.inst as Array; Type itemType = obj.inst.GetType().GetElementType(); int rank = items.Rank; int index; 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 = Runtime.Interop.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(value, 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.Interop.PyTuple_Size(idx); var args = new int[count]; for (var i = 0; i < count; i++) { IntPtr op = Runtime.Interop.PyTuple_GetItem(idx, i); index = Runtime.Interop.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(args); } catch (IndexOutOfRangeException) { Exceptions.SetError(Exceptions.IndexError, "array index out of range"); return(IntPtr.Zero); } return(Converter.ToPython(value, itemType)); }