forked from pythonnet/pythonnet
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathconstructorbinding.cs
More file actions
237 lines (217 loc) · 11.5 KB
/
constructorbinding.cs
File metadata and controls
237 lines (217 loc) · 11.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
// ==========================================================================
// This software is subject to the provisions of the Zope Public License,
// Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
// THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
// WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
// FOR A PARTICULAR PURPOSE.
// ==========================================================================
using System;
using System.Reflection;
namespace Python.Runtime {
/// <summary>
/// Implements a Python type that wraps a CLR ctor call. Constructor objects
/// support a .Overloads[] syntax to allow explicit ctor overload selection.
/// </summary>
/// <remarks>
/// ClassManager stores a ConstructorBinding instance in the class's __dict__['Overloads']
///
/// SomeType.Overloads[Type, ...] works like this:
/// 1) Python retreives 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 contructor overload using Type.GetConstructor()
/// [NOTE: I don't know why method overloads are not searched the same way.]
/// and creating the BoundContructor oject which contains ContructorInfo object.
/// 3) In tp_call, if ctorInfo is not null, ctorBinder.InvokeRaw() is called.
/// </remarks>
internal class ConstructorBinding : ExtensionType
{
Type type; // The managed Type being wrapped in a ClassObject
IntPtr pyTypeHndl; // The python type tells GetInstHandle which Type to create.
ConstructorBinder ctorBinder;
IntPtr repr;
public ConstructorBinding(Type type, IntPtr pyTypeHndl, ConstructorBinder ctorBinder) : base() {
this.type = type;
Runtime.Incref(pyTypeHndl);
this.pyTypeHndl = pyTypeHndl;
this.ctorBinder = ctorBinder;
repr = IntPtr.Zero;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="op"> PyObject* to a Constructors wrapper </param>
/// <param name="instance"> the instance that the attribute was accessed through,
/// or None when the attribute is accessed through the owner </param>
/// <param name="owner"> always the owner class </param>
/// <returns> a CtorMapper (that borrows a reference to this python type and the
/// ClassObject's ConstructorBinder) wrapper. </returns>
///
/// <remarks>
/// 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.
/// </remarks>
public static IntPtr tp_descr_get(IntPtr op, IntPtr instance, IntPtr owner)
{
ConstructorBinding 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.Incref(self.pyHandle); // Decref'd by the interpreter.
return self.pyHandle;
}
//====================================================================
// Implement explicit overload selection using subscript syntax ([]).
//====================================================================
/// <summary>
/// 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].
/// </summary>
/// <param name="tp"></param>
/// <param name="idx"></param>
/// <returns></returns>
public static IntPtr mp_subscript(IntPtr op, IntPtr key) {
ConstructorBinding 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) {
string msg = "No match found for constructor signature";
return Exceptions.RaiseTypeError(msg);
}
BoundContructor boundCtor = new BoundContructor(self.type, self.pyTypeHndl, self.ctorBinder, ci);
/* Since nothing's chached, do we need the increment???
Runtime.Incref(boundCtor.pyHandle); // Decref'd by the interpreter??? */
return boundCtor.pyHandle;
}
//====================================================================
// ConstructorBinding __repr__ implementation [borrowed from MethodObject].
//====================================================================
public static IntPtr tp_repr(IntPtr ob) {
ConstructorBinding self = (ConstructorBinding)GetManagedObject(ob);
if (self.repr != IntPtr.Zero) {
Runtime.Incref(self.repr);
return self.repr;
}
MethodBase[] methods = self.ctorBinder.GetMethods();
string name = self.type.FullName;
string doc = "";
for (int i = 0; i < methods.Length; i++) {
if (doc.Length > 0)
doc += "\n";
string str = methods[i].ToString();
int idx = str.IndexOf("(");
doc += String.Format("{0}{1}", name, str.Substring(idx));
}
self.repr = Runtime.PyString_FromString(doc);
Runtime.Incref(self.repr);
return self.repr;
}
//====================================================================
// ConstructorBinding dealloc implementation.
//====================================================================
public static new void tp_dealloc(IntPtr ob) {
ConstructorBinding self = (ConstructorBinding)GetManagedObject(ob);
Runtime.Decref(self.repr);
Runtime.Decref(self.pyTypeHndl);
ExtensionType.FinalizeObject(self);
}
}
/// <summary>
/// Implements a Python type that constucts the given Type given a particular ContructorInfo.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
internal class BoundContructor : ExtensionType {
Type type; // The managed Type being wrapped in a ClassObject
IntPtr pyTypeHndl; // The python type tells GetInstHandle which Type to create.
ConstructorBinder ctorBinder;
ConstructorInfo ctorInfo;
IntPtr repr;
public BoundContructor(Type type, IntPtr pyTypeHndl, ConstructorBinder ctorBinder, ConstructorInfo ci)
: base() {
this.type = type;
Runtime.Incref(pyTypeHndl);
this.pyTypeHndl = pyTypeHndl;
this.ctorBinder = ctorBinder;
ctorInfo = ci;
repr = IntPtr.Zero;
}
/// <summary>
/// BoundContructor.__call__(PyObject *callable_object, PyObject *args, PyObject *kw)
/// </summary>
/// <param name="ob"> PyObject *callable_object </param>
/// <param name="args"> PyObject *args </param>
/// <param name="kw"> PyObject *kw </param>
/// <returns> A reference to a new instance of the class by invoking the selected ctor(). </returns>
public static IntPtr tp_call(IntPtr op, IntPtr args, IntPtr kw) {
BoundContructor 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) {
BoundContructor self = (BoundContructor)GetManagedObject(ob);
if (self.repr != IntPtr.Zero) {
Runtime.Incref(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.Incref(self.repr);
return self.repr;
}
//====================================================================
// ConstructorBinding dealloc implementation.
//====================================================================
public static new void tp_dealloc(IntPtr ob) {
BoundContructor self = (BoundContructor)GetManagedObject(ob);
Runtime.Decref(self.repr);
Runtime.Decref(self.pyTypeHndl);
ExtensionType.FinalizeObject(self);
}
}
}