See More

using System; using System.Reflection; namespace Python.Runtime { ///

/// Implements a Python type that represents a CLR method. Method objects /// support a subscript syntax [] to allow explicit overload selection. /// /// /// TODO: ForbidPythonThreadsAttribute per method info /// [Serializable] internal class MethodObject : ExtensionType { internal MethodInfo[] info; internal string name; internal MethodBinding unbound; internal MethodBinder binder; internal bool is_static = false; internal IntPtr doc; internal Type type; public MethodObject(Type type, string name, MethodInfo[] info) { _MethodObject(type, name, info); } public MethodObject(Type type, string name, MethodInfo[] info, bool allow_threads) { _MethodObject(type, name, info); binder.allow_threads = allow_threads; } private void _MethodObject(Type type, string name, MethodInfo[] info) { this.type = type; this.name = name; this.info = info; binder = new MethodBinder(); foreach (MethodInfo item in info) { binder.AddMethod(item); if (item.IsStatic) { this.is_static = true; } } } public virtual IntPtr Invoke(IntPtr inst, IntPtr args, IntPtr kw) { return Invoke(inst, args, kw, null); } public virtual IntPtr Invoke(IntPtr target, IntPtr args, IntPtr kw, MethodBase info) { return binder.Invoke(target, args, kw, info, this.info); } /// /// Helper to get docstrings from reflected method / param info. /// internal IntPtr GetDocString() { if (doc != IntPtr.Zero) { return doc; } var str = ""; Type marker = typeof(DocStringAttribute); MethodBase[] methods = binder.GetMethods(); foreach (MethodBase method in methods) { if (str.Length > 0) { str += Environment.NewLine; } var attrs = (Attribute[])method.GetCustomAttributes(marker, false); if (attrs.Length == 0) { str += method.ToString(); } else { var attr = (DocStringAttribute)attrs[0]; str += attr.DocString; } } doc = Runtime.PyString_FromString(str); return doc; } /// /// This is a little tricky: a class can actually have a static method /// and instance methods all with the same name. That makes it tough /// to support calling a method 'unbound' (passing the instance as the /// first argument), because in this case we can't know whether to call /// the instance method unbound or call the static method. /// /// /// The rule we is that if there are both instance and static methods /// with the same name, then we always call the static method. So this /// method returns true if any of the methods that are represented by /// the descriptor are static methods (called by MethodBinding). /// internal bool IsStatic() { return is_static; } private void ClearMembers() { Runtime.Py_CLEAR(ref doc); if (unbound != null) { Runtime.XDecref(unbound.pyHandle); unbound = null; } } /// /// Descriptor __getattribute__ implementation. /// public static IntPtr tp_getattro(IntPtr ob, IntPtr key) { var self = (MethodObject)GetManagedObject(ob); if (!Runtime.PyString_Check(key)) { return Exceptions.RaiseTypeError("string expected"); } if (Runtime.PyUnicode_Compare(key, PyIdentifier.__doc__) == 0) { IntPtr doc = self.GetDocString(); Runtime.XIncref(doc); return doc; } return Runtime.PyObject_GenericGetAttr(ob, key); } /// /// Descriptor __get__ implementation. Accessing a CLR method returns /// a "bound" method similar to a Python bound method. /// public static IntPtr tp_descr_get(IntPtr ds, IntPtr ob, IntPtr tp) { var self = (MethodObject)GetManagedObject(ds); MethodBinding binding; // If the method is accessed through its type (rather than via // an instance) we return an 'unbound' MethodBinding that will // cached for future accesses through the type. if (ob == IntPtr.Zero) { if (self.unbound == null) { self.unbound = new MethodBinding(self, IntPtr.Zero, tp); } binding = self.unbound; Runtime.XIncref(binding.pyHandle); ; return binding.pyHandle; } if (Runtime.PyObject_IsInstance(ob, tp) < 1) { return Exceptions.RaiseTypeError("invalid argument"); } // If the object this descriptor is being called with is a subclass of the type // this descriptor was defined on then it will be because the base class method // is being called via super(Derived, self).method(...). // In which case create a MethodBinding bound to the base class. var obj = GetManagedObject(ob) as CLRObject; if (obj != null && obj.inst.GetType() != self.type && obj.inst is IPythonDerivedType && self.type.IsInstanceOfType(obj.inst)) { ClassBase basecls = ClassManager.GetClass(self.type); binding = new MethodBinding(self, ob, basecls.pyHandle); return binding.pyHandle; } binding = new MethodBinding(self, ob, tp); return binding.pyHandle; } /// /// Descriptor __repr__ implementation. /// public static IntPtr tp_repr(IntPtr ob) { var self = (MethodObject)GetManagedObject(ob); return Runtime.PyString_FromString($""); } /// /// Descriptor dealloc implementation. /// public new static void tp_dealloc(IntPtr ob) { var self = (MethodObject)GetManagedObject(ob); self.ClearMembers(); ClearObjectDict(ob); self.Dealloc(); } public static int tp_clear(IntPtr ob) { var self = (MethodObject)GetManagedObject(ob); self.ClearMembers(); ClearObjectDict(ob); return 0; } protected override void OnSave(InterDomainContext context) { base.OnSave(context); if (unbound != null) { Runtime.XIncref(unbound.pyHandle); } Runtime.XIncref(doc); } } }