8000 Merge pull request #167 from filmor/patch-interface-isinstance · pythonnet/pythonnet@1eec730 · GitHub
[go: up one dir, main page]

Skip to content

Commit 1eec730

Browse files
committed
Merge pull request #167 from filmor/patch-interface-isinstance
[WIP] Implement __instancecheck__.
2 parents 6438d0a + 49b01b0 commit 1eec730

File tree

7 files changed

+109
-37
lines changed

7 files changed

+109
-37
lines changed

src/runtime/converter.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ internal static Type GetTypeByAlias(IntPtr op) {
8080
// This always returns a new reference. Note that the System.Decimal
8181
// type has no Python equivalent and converts to a managed instance.
8282
//====================================================================
83+
internal static IntPtr ToPython<T>(T value)
84+
{
85+
return ToPython(value, typeof(T));
86+
}
8387

8488
internal static IntPtr ToPython(Object value, Type type) {
8589
IntPtr result = IntPtr.Zero;

src/runtime/importhook.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ internal static void Initialize() {
4545
IntPtr mod = Runtime.PyDict_GetItemString(dict, "__builtin__");
4646
py_import = Runtime.PyObject_GetAttrString(mod, "__import__");
4747
#endif
48-
hook = new MethodWrapper(typeof(ImportHook), "__import__");
49-
Runtime.PyObject_SetAttrString(mod, "__import__", hook.ptr);
48+
hook = new MethodWrapper(typeof(ImportHook), "__import__", "TernaryFunc");
49+
Runtime.PyObject_SetAttrString(mod, "__import__", hook.ptr);
5050
Runtime.Decref(hook.ptr);
5151

5252
root = new CLRModule();

src/runtime/interop.cs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ public static void FreeModuleDef(IntPtr ptr) {
249249
// } PyModuleDef
250250

251251
public static int name = 0;
252-
}
252+
}
253253
#endif // PYTHON3
254254

255255
/// <summary>
@@ -445,16 +445,19 @@ static Interop() {
445445
pmap["bf_getwritebuffer"] = p["IntObjArgFunc"];
446446
pmap["bf_getsegcount"] = p["ObjObjFunc"];
447447
pmap["bf_getcharbuffer"] = p["IntObjArgFunc"];
448-
449-
pmap["__import__"] = p["TernaryFunc"];
450448
}
451449

452450
internal static Type GetPrototype(string name) {
453451
return pmap[name] as Type;
454452
}
455453

456-
internal static IntPtr GetThunk(MethodInfo method) {
457-
Type dt = Interop.GetPrototype(method.Name);
454+
internal static IntPtr GetThunk(MethodInfo method, string funcType = null) {
455+
Type dt;
456+
if (funcType != null)
457+
dt = typeof(Interop).GetNestedType(funcType) as Type;
458+
else
459+
dt = GetPrototype(method.Name);
460+
458461
if (dt != null) {
459462
IntPtr tmp = Marshal.AllocHGlobal(IntPtr.Size);
460463
Delegate d = Delegate.CreateDelegate(dt, method);

src/runtime/metatype.cs

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -260,10 +260,44 @@ public static void tp_dealloc(IntPtr tp) {
260260
return;
261261
}
262262

263+
static IntPtr DoInstanceCheck(IntPtr tp, IntPtr args, bool checkType)
264+
{
265+
ClassBase cb = GetManagedObject(tp) as ClassBase;
266+
267+
if (cb == null)
268+
return Runtime.PyFalse;
269+
270+
using (PyList argsObj = new PyList(args))
271+
{
272+
if (argsObj.Length() != 1)
273+
return Exceptions.RaiseTypeError("Invalid parameter count");
274+
275+
PyObject arg = argsObj[0];
276+
PyObject otherType;
277+
if (checkType)
278+
otherType = arg;
279+
else
280+
otherType = arg.GetPythonType();
281+
282+
if (Runtime.PyObject_TYPE(otherType.Handle) != PyCLRMetaType)
283+
return Runtime.PyFalse;
284+
285+
ClassBase otherCb = GetManagedObject(otherType.Handle) as ClassBase;
286+
if (otherCb == null)
287+
return Runtime.PyFalse;
288+
289+
return Converter.ToPython(cb.type.IsAssignableFrom(otherCb.type));
290+
}
291+
}
263292

264-
265-
293+
public static IntPtr __instancecheck__(IntPtr tp, IntPtr args)
294+
{
295+
return DoInstanceCheck(tp, args, false);
296+
}
297+
298+
public static IntPtr __subclasscheck__(IntPtr tp, IntPtr args)
299+
{
300+
return DoInstanceCheck(tp, args, true);
301+
}
266302
}
267-
268-
269303
}

src/runtime/methodwrapper.cs

Lines changed: 4 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -25,41 +25,22 @@ internal class MethodWrapper {
2525
public IntPtr mdef;
2626
public IntPtr ptr;
2727

28-
public MethodWrapper(Type type, string name) {
28+
public MethodWrapper(Type type, string name, string funcType = null) {
2929

3030
// Turn the managed method into a function pointer
3131

32-
IntPtr fp = Interop.GetThunk(type.GetMethod(name));
33-
34-
// XXX - here we create a Python string object, then take the
35-
// char * of the internal string to pass to our methoddef
36-
// structure. Its a hack, and the name is leaked!
37-
#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35)
38-
IntPtr ps = Runtime.PyBytes_FromString(name);
39-
IntPtr sp = Runtime.PyBytes_AS_STRING(ps);
40-
#else
41-
IntPtr ps = Runtime.PyString_FromString(name);
42-
IntPtr sp = Runtime.PyString_AS_STRING(ps);
43-
#endif
32+
IntPtr fp = Interop.GetThunk(type.GetMethod(name), funcType);
4433

4534
// Allocate and initialize a PyMethodDef structure to represent
4635
// the managed method, then create a PyCFunction.
4736

4837
mdef = Runtime.PyMem_Malloc(4 * IntPtr.Size);
49-
Marshal.WriteIntPtr(mdef, sp);
50-
Marshal.WriteIntPtr(mdef, (1 * IntPtr.Size), fp);
51-
Marshal.WriteIntPtr(mdef, (2 * IntPtr.Size), (IntPtr)0x0003); // METH_VARARGS | METH_KEYWORDS
52-
Marshal.WriteIntPtr(mdef, (3 * IntPtr.Size), IntPtr.Zero);
38+
TypeManager.WriteMethodDef(mdef, name, fp, 0x0003);
5339
ptr = Runtime.PyCFunction_NewEx(mdef, IntPtr.Zero, IntPtr.Zero);
5440
}
5541

5642
public IntPtr Call(IntPtr args, IntPtr kw) {
5743
return Runtime.PyCFunction_Call(ptr, args, kw);
5844
}
59-
60-
6145
}
62-
63-
64-
}
65-
46+
}

src/runtime/typemanager.cs

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,28 @@ internal static IntPtr CreateSubType(IntPtr py_name, IntPtr py_base_type, IntPtr
267267
}
268268
}
269269

270+
internal static IntPtr WriteMethodDef(IntPtr mdef, IntPtr name, IntPtr func, int flags, IntPtr doc)
271+
{
272+
Marshal.WriteIntPtr(mdef, name);
273+
Marshal.WriteIntPtr(mdef, (1 * IntPtr.Size), func);
274+
Marshal.WriteInt32(mdef, (2 * IntPtr.Size), flags);
275+
Marshal.WriteIntPtr(mdef, (3 * IntPtr.Size), doc);
276+
return mdef + 4 * IntPtr.Size;
277+
}
278+
279+
internal static IntPtr WriteMethodDef(IntPtr mdef, string name, IntPtr func, int flags = 0x0001, string doc = null)
280+
{
281+
IntPtr namePtr = Marshal.StringToHGlobalAnsi(name);
282+
IntPtr docPtr = doc != null ? Marshal.StringToHGlobalAnsi(doc) : IntPtr.Zero;
283+
284+
return WriteMethodDef(mdef, namePtr, func, flags, docPtr);
285+
}
286+
287+
internal static IntPtr WriteMethodDefSentinel(IntPtr mdef)
288+
{
289+
return WriteMethodDef(mdef, IntPtr.Zero, IntPtr.Zero, 0, IntPtr.Zero);
290+
}
291+
270292
internal static IntPtr CreateMetaType(Type impl) {
271293

272294
// The managed metatype is functionally little different than the
@@ -300,7 +322,27 @@ internal static IntPtr CreateMetaType(Type impl) {
300322
flags |= TypeFlags.Managed;
301323
flags |= TypeFlags.HeapType;
302324
flags |= TypeFlags.HaveGC;
303-
Marshal.WriteIntPtr(type, TypeOffset.tp_flags, (IntPtr)flags);
325+
Marshal.WriteIntPtr(type, TypeOffset.tp_flags, (IntPtr)flags);
326+
327+
// We need space for 3 PyMethodDef structs, each of them
328+
// 4 int-ptrs in size.
329+
IntPtr mdef = Runtime.PyMem_Malloc(3 * (4 * IntPtr.Size));
330+
IntPtr mdefStart = mdef;
331+
mdef = WriteMethodDef(
332+
mdef,
333+
"__instancecheck__",
334+
Interop.GetThunk(typeof(MetaType).GetMethod("__instancecheck__"), "BinaryFunc")
335+
);
336+
337+
mdef = WriteMethodDef(
338+
mdef,
339+
"__subclasscheck__",
340+
Interop.GetThunk(typeof(MetaType).GetMethod("__subclasscheck__"), "BinaryFunc")
341+
);
342+
343+
mdef = WriteMethodDefSentinel(mdef);
344+
345+
Marshal.WriteIntPtr(type, TypeOffset.tp_methods, mdefStart);
304346

305347
Runtime.PyType_Ready(type);
306348

src/tests/test_exceptions.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -348,12 +348,20 @@ def testExceptionIsInstanceOfSystemObject(self):
348348
# without causing a crash in the CPython interpreter). This test is
349349
# here mainly to remind me to update the caveat in the documentation
350350
# one day when when exceptions can be new-style classes.
351+
352+
# This behaviour is now over-shadowed by the implementation of
353+
# __instancecheck__ (i.e., overloading isinstance), so for all Python
354+
# version >= 2.6 we expect isinstance(<managed exception>, Object) to
355+
# be true, even though it does not really subclass Object.
351356
from System import OverflowException
352357
from System import Object
353358

354359
o = OverflowException('error')
355-
self.assertFalse(isinstance(o, Object))
356-
360+
361+
if sys.version_info >= (2, 6):
362+
self.assertTrue(isinstance(o, Object))
363+
else:
364+
self.assertFalse(isinstance(o, Object))
357365

358366

359367
def test_suite():

0 commit comments

Comments
 (0)
0