Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 94260ae

Browse files
author
Barton Cline
committed
Implements the .Overloads[PyOrCsType, ...] syntax on class objects as described in the docs. __overloads__[...] is a valid alias in the sort term, but deprecation is planned for the near future. It works very much like MethodObject __overloads__ (which was also aliased as "Overloads" in an earlier update). These new names support the current state of IronPython.
When used like a property .Overloads returns a __doc__-like string from its __repr__ method. GetItem() returns a bound constructor that also has a pretty cool __repr__ method.
1 parent 122d3af commit 94260ae

File tree

2 files changed

+245
-0
lines changed

2 files changed

+245
-0
lines changed

pythonnet/src/runtime/classmanager.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,13 @@ private static ClassBase CreateClass(Type type) {
133133
// required that the ClassObject.ctors be changed to internal
134134
if (co != null) {
135135
if (co.ctors.Length > 0) {
136+
// Implement Overloads on the class object
137+
ConstructorBinding ctors = new ConstructorBinding(type, tp, co.binder);
138+
// ExtensionType types are untracked, so don't Incref() them.
139+
// XXX deprecate __overloads__ soon...
140+
Runtime.PyDict_SetItemString(dict, "__overloads__", ctors.pyHandle);
141+
Runtime.PyDict_SetItemString(dict, "Overloads", ctors.pyHandle);
142+
136143
IntPtr doc = co.GetDocString();
137144
Runtime.PyDict_SetItemString(dict, "__doc__", doc);
138145
Runtime.Decref(doc);
Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
// ==========================================================================
2+
// This software is subject to the provisions of the Zope Public License,
3+
// Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
4+
// THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
5+
// WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
6+
// WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
7+
// FOR A PARTICULAR PURPOSE.
8+
// ==========================================================================
9+
10+
using System;
11+
using System.Reflection;
12+
13+
namespace Python.Runtime {
14+
15+
/// <summary>
16+
/// Implements a Python type that wraps a CLR ctor call. Constructor objects
17+
/// support a .Overloads[] syntax to allow explicit ctor overload selection.
18+
/// </summary>
19+
/// <remarks>
20+
/// ClassManager stores a ConstructorBinding instance in the class's __dict__['Overloads']
21+
///
22+
/// SomeType.Overloads[Type, ...] works like this:
23+
/// 1) Python retreives the Overloads attribute from this ClassObject's dictionary normally
24+
/// and finds a non-null tp_descr_get slot which is called by the interpreter
25+
/// and returns an IncRef()ed pyHandle to itself.
26+
/// 2) The ConstructorBinding object handles the [] syntax in its mp_subscript by matching
27+
/// the Type object parameters to a contructor overload using Type.GetConstructor()
28+
/// [NOTE: I don't know why method overloads are not searched the same way.]
29+
/// and creating the BoundContructor oject which contains ContructorInfo object.
30+
/// 3) In tp_call, if ctorInfo is not null, ctorBinder.InvokeRaw() is called.
31+
/// </remarks>
32+
internal class ConstructorBinding : ExtensionType
33+
{
34+
Type type; // The managed Type being wrapped in a ClassObject
35+
IntPtr pyTypeHndl; // The python type tells GetInstHandle which Type to create.
36+
ConstructorBinder ctorBinder;
37+
IntPtr repr;
38+
39+
public ConstructorBinding(Type type, IntPtr pyTypeHndl, ConstructorBinder ctorBinder) : base() {
40+
this.type = type;
41+
Runtime.Incref(pyTypeHndl);
42+
this.pyTypeHndl = pyTypeHndl;
43+
this.ctorBinder = ctorBinder;
44+
repr = IntPtr.Zero;
45+
}
46+
47+
/// <summary>
48+
/// Descriptor __get__ implementation.
49+
/// Implements a Python type that wraps a CLR ctor call that requires the use
50+
/// of a .Overloads[pyTypeOrType...] syntax to allow explicit ctor overload
51+
/// selection.
52+
/// </summary>
53+
/// <param name="op"> PyObject* to a Constructors wrapper </param>
54+
/// <param name="instance"> the instance that the attribute was accessed through,
55+
/// or None when the attribute is accessed through the owner </param>
56+
/// <param name="owner"> always the owner class </param>
57+
/// <returns> a CtorMapper (that borrows a reference to this python type and the
58+
/// ClassObject's ConstructorBinder) wrapper. </returns>
59+
///
60+
/// <remarks>
61+
/// Python 2.6.5 docs:
62+
/// object.__get__(self, instance, owner)
63+
/// Called to get the attribute of the owner class (class attribute access)
64+
/// or of an instance of that class (instance attribute access).
65+
/// owner is always the owner class, while instance is the instance that
66+
/// the attribute was accessed through, or None when the attribute is accessed through the owner.
67+
/// This method should return the (computed) attribute value or raise an AttributeError exception.
68+
/// </remarks>
69+
public static IntPtr tp_descr_get(IntPtr op, IntPtr instance, IntPtr owner)
70+
{
71+
ConstructorBinding self = (ConstructorBinding)GetManagedObject(op);
72+
if (self == null) {
73+
return IntPtr.Zero;
74+
}
75+
76+
// It doesn't seem to matter if it's accessed through an instance (rather than via the type).
77+
/*if (instance != IntPtr.Zero) {
78+
// This is ugly! PyObject_IsInstance() returns 1 for true, 0 for false, -1 for error...
79+
if (Runtime.PyObject_IsInstance(instance, owner) < 1) {
80+
return Exceptions.RaiseTypeError("How in the world could that happen!");
81+
}
82+
}*/
83+
/* Since ExtensionType calls Runtime.PyObject_GC_UnTrack(py), don't
84+
Runtime.Incref(self.pyHandle); // Decref'd by the interpreter??? */
85+
return self.pyHandle;
86+
}
87+
88+
//====================================================================
89+
// Implement explicit overload selection using subscript syntax ([]).
90+
//====================================================================
91+
/// <summary>
92+
/// ConstructorBinding.GetItem(PyObject *o, PyObject *key)
93+
/// Return element of o corresponding to the object key or NULL on failure.
94+
/// This is the equivalent of the Python expression o[key].
95+
/// </summary>
96+
/// <param name="tp"></param>
97+
/// <param name="idx"></param>
98+
/// <returns></returns>
99+
public static IntPtr mp_subscript(IntPtr op, IntPtr key) {
100+
ConstructorBinding self = (ConstructorBinding)GetManagedObject(op);
101+
102+
Type[] types = Runtime.PythonArgsToTypeArray(key);
103+
if (types == null) {
104+
return Exceptions.RaiseTypeError("type(s) expected");
105+
}
106+
//MethodBase[] methBaseArray = self.ctorBinder.GetMethods();
107+
//MethodBase ci = MatchSignature(methBaseArray, types);
108+
ConstructorInfo ci = self.type.GetConstructor(types);
109+
if (ci == null) {
110+
string msg = "No match found for constructor signature";
111+
return Exceptions.RaiseTypeError(msg);
112+
}
113+
BoundContructor boundCtor = new BoundContructor(self.type, self.pyTypeHndl, self.ctorBinder, ci);
114+
115+
/* Since ExtensionType calls Runtime.PyObject_GC_UnTrack(py), don't
116+
Runtime.Incref(boundCtor.pyHandle); // Decref'd by the interpreter??? */
117+
return boundCtor.pyHandle;
118+
}
119+
120+
//====================================================================
121+
// ConstructorBinding __repr__ implementation [borrowed from MethodObject].
122+
//====================================================================
123+
124+
public static IntPtr tp_repr(IntPtr ob) {
125+
ConstructorBinding self = (ConstructorBinding)GetManagedObject(ob);
126+
if (self.repr != IntPtr.Zero) {
127+
Runtime.Incref(self.repr);
128+
return self.repr;
129+
}
130+
MethodBase[] methods = self.ctorBinder.GetMethods();
131+
string name = self.type.Name;
132+
string doc = "";
133+
for (int i = 0; i < methods.Length; i++) {
134+
if (doc.Length > 0)
135+
doc += "\n";
136+
string str = methods[i].ToString();
137+
int idx = str.IndexOf("(");
138+
doc += String.Format("{0}{1}", name, str.Substring(idx));
139+
}
140+
self.repr = Runtime.PyString_FromString(doc);
141+
Runtime.Incref(self.repr);
142+
return self.repr;
143+
}
144+
145+
//====================================================================
146+
// ConstructorBinding dealloc implementation.
147+
//====================================================================
148+
149+
public static new void tp_dealloc(IntPtr ob) {
150+
ConstructorBinding self = (ConstructorBinding)GetManagedObject(ob);
151+
Runtime.Decref(self.repr);
152+
Runtime.Decref(self.pyTypeHndl);
153+
ExtensionType.FinalizeObject(self);
154+
}
155+
}
156+
157+
/// <summary>
158+
/// Implements a Python type that constucts the given Type given a particular ContructorInfo.
159+
/// </summary>
160+
/// <remarks>
161+
/// Here mostly because I wanted a new __repr__ function for the selected constructor.
162+
/// An earlier implementation hung the __call__ on the ContructorBinding class and
163+
/// returned an Incref()ed self.pyHandle from the __get__ function.
164+
/// </remarks>
165+
internal class BoundContructor : ExtensionType {
166+
Type type; // The managed Type being wrapped in a ClassObject
167+
IntPtr pyTypeHndl; // The python type tells GetInstHandle which Type to create.
168+
ConstructorBinder ctorBinder;
169+
ConstructorInfo ctorInfo;
170+
IntPtr repr;
171+
172+
public BoundContructor(Type type, IntPtr pyTypeHndl, ConstructorBinder ctorBinder, ConstructorInfo ci)
173+
: base() {
174+
this.type = type;
175+
Runtime.Incref(pyTypeHndl);
176+
this.pyTypeHndl = pyTypeHndl;
177+
this.ctorBinder = ctorBinder;
178+
ctorInfo = ci;
179+
repr = IntPtr.Zero;
180+
}
181+
182+
/// <summary>
183+
/// BoundContructor.__call__(PyObject *callable_object, PyObject *args, PyObject *kw)
184+
/// </summary>
185+
/// <param name="ob"> PyObject *callable_object </param>
186+
/// <param name="args"> PyObject *args </param>
187+
/// <param name="kw"> PyObject *kw </param>
188+
/// <returns> A reference to a new instance of the class by invoking the selected ctor(). </returns>
189+
public static IntPtr tp_call(IntPtr op, IntPtr args, IntPtr kw) {
190+
BoundContructor self = (BoundContructor)GetManagedObject(op);
191+
// Even though a call with null ctorInfo just produces the old behavior
192+
/*if (self.ctorInfo == null) {
193+
string msg = "Usage: Class.Overloads[CLR_or_python_Type, ...]";
194+
return Exceptions.RaiseTypeError(msg);
195+
}*/
196+
// Bind using ConstructorBinder.Bind and invoke the ctor providing a null instancePtr
197+
// which will fire self.ctorInfo using ConstructorInfo.Invoke().
198+
Object obj = self.ctorBinder.InvokeRaw(IntPtr.Zero, args, kw, self.ctorInfo);
199+
if (obj == null) {
200+
// XXX set an error
201+
return IntPtr.Zero;
202+
}
203+
// Instantiate the python object that wraps the result of the method call
204+
// and return the PyObject* to it.
205+
return CLRObject.GetInstHandle(obj, self.pyTypeHndl);
206+
}
207+
208+
//====================================================================
209+
// BoundContructor __repr__ implementation [borrowed from MethodObject].
210+
//====================================================================
211+
212+
public static IntPtr tp_repr(IntPtr ob) {
213+
BoundContructor self = (BoundContructor)GetManagedObject(ob);
214+
if (self.repr != IntPtr.Zero) {
215+
Runtime.Incref(self.repr);
216+
return self.repr;
217+
}
218+
string name = self.type.FullName;
219+
string str = self.ctorInfo.ToString();
220+
int idx = str.IndexOf("(");
221+
str = String.Format("returns a new {0}{1}", name, str.Substring(idx));
222+
self.repr = Runtime.PyString_FromString(str);
223+
Runtime.Incref(self.repr);
224+
return self.repr;
225+
}
226+
227+
//====================================================================
228+
// ConstructorBinding dealloc implementation.
229+
//====================================================================
230+
231+
public static new void tp_dealloc(IntPtr ob) {
232+
BoundContructor self = (BoundContructor)GetManagedObject(ob);
233+
Runtime.Decref(self.repr);
234+
Runtime.Decref(self.pyTypeHndl);
235+
ExtensionType.FinalizeObject(self);
236+
}
237+
}
238+
}

0 commit comments

Comments
 (0)