See More

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

/// Implements a Python type that wraps a CLR ctor call. Constructor objects /// support a .Overloads[] syntax to allow explicit ctor overload selection. /// /// /// ClassManager stores a ConstructorBinding instance in the class's __dict__['Overloads'] /// SomeType.Overloads[Type, ...] works like this: /// 1) Python retrieves the Overloads attribute from this ClassObject's dictionary normally /// and finds a non-null tp_descr_get slot which is called by the interpreter /// and returns an IncRef()ed pyHandle to itself. /// 2) The ConstructorBinding object handles the [] syntax in its mp_subscript by matching /// the Type object parameters to a constructor overload using Type.GetConstructor() /// [NOTE: I don't know why method overloads are not searched the same way.] /// and creating the BoundContructor object which contains ContructorInfo object. /// 3) In tp_call, if ctorInfo is not null, ctorBinder.InvokeRaw() is called. /// [Serializable] internal class ConstructorBinding : ExtensionType { private Type type; // The managed Type being wrapped in a ClassObject private IntPtr pyTypeHndl; // The python type tells GetInstHandle which Type to create. private ConstructorBinder ctorBinder; [NonSerialized] private IntPtr repr; public ConstructorBinding(Type type, IntPtr pyTypeHndl, ConstructorBinder ctorBinder) { this.type = type; this.pyTypeHndl = pyTypeHndl; // steal a type reference this.ctorBinder = ctorBinder; repr = IntPtr.Zero; } /// /// Descriptor __get__ implementation. /// Implements a Python type that wraps a CLR ctor call that requires the use /// of a .Overloads[pyTypeOrType...] syntax to allow explicit ctor overload /// selection. /// /// PyObject* to a Constructors wrapper /// /// the instance that the attribute was accessed through, /// or None when the attribute is accessed through the owner /// /// always the owner class /// /// a CtorMapper (that borrows a reference to this python type and the /// ClassObject's ConstructorBinder) wrapper. /// /// /// Python 2.6.5 docs: /// object.__get__(self, instance, owner) /// Called to get the attribute of the owner class (class attribute access) /// or of an instance of that class (instance attribute access). /// owner is always the owner class, while instance is the instance that /// the attribute was accessed through, or None when the attribute is accessed through the owner. /// This method should return the (computed) attribute value or raise an AttributeError exception. /// public static IntPtr tp_descr_get(IntPtr op, IntPtr instance, IntPtr owner) { var self = (ConstructorBinding)GetManagedObject(op); if (self == null) { return IntPtr.Zero; } // It doesn't seem to matter if it's accessed through an instance (rather than via the type). /*if (instance != IntPtr.Zero) { // This is ugly! PyObject_IsInstance() returns 1 for true, 0 for false, -1 for error... if (Runtime.PyObject_IsInstance(instance, owner) < 1) { return Exceptions.RaiseTypeError("How in the world could that happen!"); } }*/ Runtime.XIncref(self.pyHandle); return self.pyHandle; } /// /// Implement explicit overload selection using subscript syntax ([]). /// /// /// 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]. /// 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); return boundCtor.pyHandle; } /// /// ConstructorBinding __repr__ implementation [borrowed from MethodObject]. /// public static IntPtr tp_repr(IntPtr ob) { var self = (ConstructorBinding)GetManagedObject(ob); if (self.repr != IntPtr.Zero) { Runtime.XIncref(self.repr); return self.repr; } MethodBase[] methods = self.ctorBinder.GetMethods(); string name = self.type.FullName; var doc = ""; foreach (MethodBase t in methods) { if (doc.Length > 0) { doc += "\n"; } string str = t.ToString(); int idx = str.IndexOf("("); doc += string.Format("{0}{1}", name, str.Substring(idx)); } self.repr = Runtime.PyString_FromString(doc); Runtime.XIncref(self.repr); return self.repr; } /// /// ConstructorBinding dealloc implementation. /// public new static void tp_dealloc(IntPtr ob) { var self = (ConstructorBinding)GetManagedObject(ob); Runtime.XDecref(self.repr); self.Dealloc(); } public static int tp_clear(IntPtr ob) { var self = (ConstructorBinding)GetManagedObject(ob); Runtime.Py_CLEAR(ref self.repr); return 0; } public static int tp_traverse(IntPtr ob, IntPtr visit, IntPtr arg) { var self = (ConstructorBinding)GetManagedObject(ob); int res = PyVisit(self.pyTypeHndl, visit, arg); if (res != 0) return res; res = PyVisit(self.repr, visit, arg); if (res != 0) return res; return 0; } } /// /// Implements a Python type that constructs the given Type given a particular ContructorInfo. /// /// /// Here mostly because I wanted a new __repr__ function for the selected constructor. /// An earlier implementation hung the __call__ on the ContructorBinding class and /// returned an Incref()ed self.pyHandle from the __get__ function. /// [Serializable] internal class BoundContructor : ExtensionType { private Type type; // The managed Type being wrapped in a ClassObject private IntPtr pyTypeHndl; // The python type tells GetInstHandle which Type to create. private ConstructorBinder ctorBinder; private ConstructorInfo ctorInfo; private IntPtr repr; public BoundContructor(Type type, IntPtr pyTypeHndl, ConstructorBinder ctorBinder, ConstructorInfo ci) { this.type = type; this.pyTypeHndl = pyTypeHndl; // steal a type reference this.ctorBinder = ctorBinder; ctorInfo = ci; repr = IntPtr.Zero; } /// /// BoundContructor.__call__(PyObject *callable_object, PyObject *args, PyObject *kw) /// /// PyObject *callable_object /// PyObject *args /// PyObject *kw /// A reference to a new instance of the class by invoking the selected ctor(). public static IntPtr tp_call(IntPtr op, IntPtr args, IntPtr kw) { var self = (BoundContructor)GetManagedObject(op); // Even though a call with null ctorInfo just produces the old behavior /*if (self.ctorInfo == null) { string msg = "Usage: Class.Overloads[CLR_or_python_Type, ...]"; return Exceptions.RaiseTypeError(msg); }*/ // Bind using ConstructorBinder.Bind and invoke the ctor providing a null instancePtr // which will fire self.ctorInfo using ConstructorInfo.Invoke(). object obj = self.ctorBinder.InvokeRaw(IntPtr.Zero, args, kw, self.ctorInfo); if (obj == null) { // XXX set an error return IntPtr.Zero; } // Instantiate the python object that wraps the result of the method call // and return the PyObject* to it. return CLRObject.GetInstHandle(obj, self.pyTypeHndl); } /// /// BoundContructor __repr__ implementation [borrowed from MethodObject]. /// public static IntPtr tp_repr(IntPtr ob) { var self = (BoundContructor)GetManagedObject(ob); if (self.repr != IntPtr.Zero) { Runtime.XIncref(self.repr); return self.repr; } string name = self.type.FullName; string str = self.ctorInfo.ToString(); int idx = str.IndexOf("("); str = string.Format("returns a new {0}{1}", name, str.Substring(idx)); self.repr = Runtime.PyString_FromString(str); Runtime.XIncref(self.repr); return self.repr; } /// /// ConstructorBinding dealloc implementation. /// public new static void tp_dealloc(IntPtr ob) { var self = (BoundContructor)GetManagedObject(ob); Runtime.XDecref(self.repr); self.Dealloc(); } public static int tp_clear(IntPtr ob) { var self = (BoundContructor)GetManagedObject(ob); Runtime.Py_CLEAR(ref self.repr); return 0; } public static int tp_traverse(IntPtr ob, IntPtr visit, IntPtr arg) { var self = (BoundContructor)GetManagedObject(ob); int res = PyVisit(self.pyTypeHndl, visit, arg); if (res != 0) return res; res = PyVisit(self.repr, visit, arg); if (res != 0) return res; return 0; } } }