[Zope-CVS] CVS: PythonNet/src/runtime - ClassBase.cs:1.6 ClassObject.cs:1.6 DelegateManager.cs:1.4 EventBinding.cs:1.3 EventObject.cs:1.3 MethodBinding.cs:1.2 MethodObject.cs:1.2

Brian Lloyd brian at zope.com
Tue Oct 7 22:29:48 EDT 2003


Update of /cvs-repository/PythonNet/src/runtime
In directory cvs.zope.org:/tmp/cvs-serv8705/src/runtime

Modified Files:
	ClassBase.cs ClassObject.cs DelegateManager.cs EventBinding.cs 
	EventObject.cs MethodBinding.cs MethodObject.cs 
Log Message:
Finally finished event / delegate tests, refactoring and fixing. Now 
its a race to see if I can get out a preview 3 before the baby comes ;)


=== PythonNet/src/runtime/ClassBase.cs 1.5 => 1.6 ===
--- PythonNet/src/runtime/ClassBase.cs:1.5	Tue Sep 30 19:54:11 2003
+++ PythonNet/src/runtime/ClassBase.cs	Tue Oct  7 22:29:17 2003
@@ -92,6 +92,23 @@
 
 
 	//====================================================================
+	// Standard __hash__ implementation for instances of reflected types.
+	//====================================================================
+
+	[CallConvCdecl()]
+	public static IntPtr tp_hash(IntPtr ob) {
+	    CLRObject co = GetManagedObject(ob) as CLRObject;
+
+	    if (co == null) {
+		Exceptions.SetError(Exceptions.TypeError, "unhashable type");
+		return IntPtr.Zero;
+	    }
+
+	    return new IntPtr(co.inst.GetHashCode());
+	}
+
+
+	//====================================================================
 	// Standard dealloc implementation for instances of reflected types.
 	//====================================================================
 


=== PythonNet/src/runtime/ClassObject.cs 1.5 => 1.6 ===
--- PythonNet/src/runtime/ClassObject.cs:1.5	Fri Aug  8 15:49:21 2003
+++ PythonNet/src/runtime/ClassObject.cs	Tue Oct  7 22:29:17 2003
@@ -247,6 +247,38 @@
 	}
 
 
+	//====================================================================
+	// This is a hack. Generally, no managed class is considered callable
+	// from Python - with the exception of System.Delegate. It is useful
+	// to be able to call a System.Delegate instance directly, especially
+	// when working with multicast delegates.
+	//====================================================================
+
+	[CallConvCdecl()]
+	public static IntPtr tp_call(IntPtr ob, IntPtr args, IntPtr kw) {
+	    ManagedType self = GetManagedObject(ob);
+	    IntPtr tp = Runtime.PyObject_TYPE(ob);
+	    ClassBase cb = (ClassBase)GetManagedObject(tp);
+
+	    if (cb.type != typeof(System.Delegate)) {
+		Exceptions.SetError(Exceptions.TypeError, 
+				    "object is not callable");
+		return IntPtr.Zero;
+	    }
+
+	    CLRObject co = (CLRObject)ManagedType.GetManagedObject(ob);
+	    Delegate d = co.inst as Delegate;
+	    BindingFlags flags = BindingFlags.Public | 
+		                 BindingFlags.NonPublic |
+		                 BindingFlags.Instance |
+		                 BindingFlags.Static;
+
+	    MethodInfo method = d.GetType().GetMethod("Invoke", flags);
+  	    MethodBinder binder = new MethodBinder(method);
+ 	    return binder.Invoke(ob, args, kw);
+	}
+
+
     }	
 
 }


=== PythonNet/src/runtime/DelegateManager.cs 1.3 => 1.4 ===
--- PythonNet/src/runtime/DelegateManager.cs:1.3	Fri Aug  1 10:30:13 2003
+++ PythonNet/src/runtime/DelegateManager.cs	Tue Oct  7 22:29:17 2003
@@ -20,8 +20,8 @@
 namespace Python.Runtime {
 
     /// <summary>
-    /// The DelegateManager class manages the creation of delegate instances
-    /// that dispatch calls to Python methods and other callable objects.
+    /// The DelegateManager class manages the creation of true managed 
+    /// delegate instances that dispatch calls to Python methods.
     /// </summary>
 
     internal class DelegateManager {
@@ -212,7 +212,6 @@
 	}
 
 	public object Dispatch(ArrayList args) {
-	    // check refs
 
 	    // Delegates with Python implementations may be invoked on a 
 	    // thread from the system thread pool, so we need to ensure


=== PythonNet/src/runtime/EventBinding.cs 1.2 => 1.3 ===
--- PythonNet/src/runtime/EventBinding.cs:1.2	Thu Oct  2 23:06:17 2003
+++ PythonNet/src/runtime/EventBinding.cs	Tue Oct  7 22:29:17 2003
@@ -28,6 +28,7 @@
 	    this.e = e;
 	}
 
+
 	//====================================================================
 	// EventBinding += operator implementation.
 	//====================================================================
@@ -35,14 +36,23 @@
 	[CallConvCdecl()]
 	public static IntPtr nb_inplace_add(IntPtr ob, IntPtr arg) {
 	    EventBinding self = (EventBinding)GetManagedObject(ob);
-	    int result = self.e.AddEventHandler(self.target, arg);
-	    if (result == -1) {
+
+	    if (Runtime.PyCallable_Check(arg) < 1) {
+		Exceptions.SetError(Exceptions.TypeError, 
+				    "event handlers must be callable"
+				    );
+		return IntPtr.Zero;
+	    }
+
+	    if(!self.e.AddEventHandler(self.target, arg)) {
 		return IntPtr.Zero;
 	    }
+
 	    Runtime.Incref(self.Handle);
 	    return self.Handle;
 	}
 
+
 	//====================================================================
 	// EventBinding -= operator implementation.
 	//====================================================================
@@ -50,14 +60,34 @@
 	[CallConvCdecl()]
 	public static IntPtr nb_inplace_subtract(IntPtr ob, IntPtr arg) {
 	    EventBinding self = (EventBinding)GetManagedObject(ob);
-	    int result = self.e.RemoveEventHandler(self.target, arg);
-	    if (result == -1) {
+
+	    if (Runtime.PyCallable_Check(arg) < 1) {
+		Exceptions.SetError(Exceptions.TypeError, 
+				    "event handlers must be callable"
+				    );
 		return IntPtr.Zero;
 	    }
+
+	    if (!self.e.RemoveEventHandler(self.target, arg)) {
+		return IntPtr.Zero;
+	    }
+
 	    Runtime.Incref(self.Handle);
 	    return self.Handle;
 	}
 
+
+	//====================================================================
+	// EventBinding __nonzero__ implementation.
+	//====================================================================
+
+	[CallConvCdecl()]
+	public static int nb_nonzero(IntPtr ob) {
+	    EventBinding self = (EventBinding)GetManagedObject(ob);
+	    return self.e.IsNonEmpty(self.target);
+	}
+
+
 	//====================================================================
 	// EventBinding __call__ implementation.
 	//====================================================================
@@ -68,6 +98,39 @@
 	    return self.e.Invoke(self.target, args, kw);
 	}
 
+
+	//====================================================================
+	// EventBinding  __hash__ implementation.
+	//====================================================================
+
+	[CallConvCdecl()]
+	public static IntPtr tp_hash(IntPtr ob) {
+	    EventBinding self = (EventBinding)GetManagedObject(ob);
+	    long x = 0;
+	    long y = 0;
+
+	    if (self.target != IntPtr.Zero) {
+		x = Runtime.PyObject_Hash(self.target).ToInt64();
+		if (x == -1) {
+		    return new IntPtr(-1);
+		}
+	    }
+ 
+	    y = Runtime.PyObject_Hash(self.e.pyHandle).ToInt64();
+	    if (y == -1) {
+		return new IntPtr(-1);
+	    }
+
+	    x ^= y;
+
+	    if (x == -1) {
+		x = -1;
+	    }
+
+	    return new IntPtr(x);
+	}
+
+
 	//====================================================================
 	// EventBinding __repr__ implementation.
 	//====================================================================
@@ -79,6 +142,7 @@
 	    string s = String.Format("<{0} event '{1}'>", type, self.e.name);
 	    return Runtime.PyString_FromString(s);
 	}
+
 
 	//====================================================================
 	// EventBinding dealloc implementation.


=== PythonNet/src/runtime/EventObject.cs 1.2 => 1.3 ===
--- PythonNet/src/runtime/EventObject.cs:1.2	Thu Oct  2 23:06:17 2003
+++ PythonNet/src/runtime/EventObject.cs	Tue Oct  7 22:29:17 2003
@@ -22,64 +22,53 @@
     internal class EventObject : ExtensionType {
 
 	internal string name;
-	Hashtable mapping;
 	EventBinding unbound;
 	EventInfo info;
+	FieldInfo fi;
 
 	public EventObject(EventInfo info) : base() {
+	    Type t = info.DeclaringType;
+	    this.fi = t.GetField(info.Name, flags);
 	    this.name = info.Name;
 	    this.info = info;
 	}
 
+	static BindingFlags flags = BindingFlags.Public | 
+                                    BindingFlags.NonPublic |
+		                    BindingFlags.Instance |
+		                    BindingFlags.Static;
 
-	private string GetMethodToken(object ob, IntPtr handler) {
 
-	    // Any callable Python object can be used as an event handler.
-	    // When a Python event handler is added, we generate a delegate
-	    // that hands off to the Python implementation. To support the 
-	    // ability to remove Python event handlers, we need to recognize 
-	    // the Python version of the hander when it is presented for 
-	    // removal and map that to the delegate that we generated.
-	    //
-	    // This is easier said than done, since some callable objects 
-	    // (bound methods in particular) dont have a stable identity.
-	    // This method generates a (hopefully unique) key for mapping.
-
-	    string s = (ob == null) ? "null" : ob.GetHashCode().ToString();
-	    if (Runtime.PyObject_TYPE(handler) == Runtime.PyMethodType) {
-		return s + Runtime.PyMethod_Self(handler).ToString() +
-		       Runtime.PyMethod_Function(handler).ToString();
-	    }
-	    return s + handler.ToString();
-	}
-
-
-	public int AddEventHandler(IntPtr target, IntPtr handler) {
+	internal bool AddEventHandler(IntPtr target, IntPtr handler) {
 	    Object obj = null;
 	    if (target != IntPtr.Zero) {
 		CLRObject co = (CLRObject)ManagedType.GetManagedObject(target);
 		obj = co.inst;
 	    }
 
+	    // Create a true delegate instance of the appropriate type to
+	    // wrap the Python handler. Note that wrapper delegate creation
+	    // always succeeds, though calling the wrapper may fail.
+
 	    Type type = this.info.EventHandlerType;
 	    Delegate d = DelegateManager.GetDelegate(type, handler);
-	    if (mapping == null) {
-		mapping = new Hashtable();
-	    }
-	    mapping[GetMethodToken(obj, handler)] = d;
-
-	    // Note: AddEventHandler doesn't work for non-public events.
 
-	    object[] args = new object[1];
-	    args[0] = d;
+	    object[] args = { d };
 	    MethodInfo mi = this.info.GetAddMethod(true);
 	    mi.Invoke(obj, BindingFlags.Default, null, args, null);
 
-	    return 0;
+	    return true;
 	}
 
 
-	public int RemoveEventHandler(IntPtr target, IntPtr handler) {
+	internal bool RemoveEventHandler(IntPtr target, IntPtr handler) {
+
+	    // Removing a Python event handler is potentially expensive if
+	    // the event has a lot of registered handlers. This is because
+	    // we need to sniff each of the registered handlers to see if
+	    // it is a wrapper for a Python delegate. In practice this is
+	    // probably not really an issue - handlers are rarely removed.
+
 	    Object obj = null;
 	    Delegate d = null;
 
@@ -88,42 +77,58 @@
 		obj = co.inst;
 	    }
 
-	    if (mapping != null) {
-		string token = GetMethodToken(obj, handler);
-		d = mapping[token] as Delegate;
-		if (d != null) {
-		    mapping.Remove(token);
-		}
+	    IntPtr hash = Runtime.PyObject_Hash(handler);
+	    if (Exceptions.ErrorOccurred()) {
+		Exceptions.SetError(Exceptions.ValueError, 
+				    "unknown event handler"
+				    ); 
+		return false;
 	    }
 
-	    if (d == null) {
-		Exceptions.SetError(Exceptions.ValueError, "unknown method");
-		return -1;
+	    d = this.fi.GetValue(obj) as Delegate;
+
+  	    if (d == null) {
+		Exceptions.SetError(Exceptions.ValueError, 
+				    "unknown event handler"
+				    ); 
+		return false;
+  	    }
+
+	    Delegate[] list = d.GetInvocationList();
+	    Delegate found = null;
+
+	    for (int i=0; i < list.Length; i++) {
+		IntPtr py = DelegateManager.GetPythonHandle(list[i]);
+		if (hash == Runtime.PyObject_Hash(py)) {
+		    found = list[i];
+		    break;
+		}
 	    }
 
-	    // Note: RemoveEventHandler doesn't work for non-public events.
+	    Exceptions.Clear();
+
+  	    if (found == null) {
+		Exceptions.SetError(Exceptions.ValueError, 
+				    "unknown event handler"
+				    ); 
+		return false;
+  	    }
 
-	    object[] args = new object[1];
-	    args[0] = d;
+	    // RemoveEventHandler doesn't work for non-public events.
+
+	    object[] args = { found };
 	    MethodInfo mi = this.info.GetRemoveMethod(true);
 	    mi.Invoke(obj, BindingFlags.Default, null, args, null);
 
-	    return 0;
+	    return true;
 	}
 
 
-	public IntPtr Invoke(IntPtr target, IntPtr args, IntPtr kw) {
+	internal IntPtr Invoke(IntPtr target, IntPtr args, IntPtr kw) {
 	    CLRObject co = ManagedType.GetManagedObject(target) as CLRObject;
 	    object obj = (co != null) ? co.inst: null;
 
-	    BindingFlags flags = BindingFlags.Public | 
-                                 BindingFlags.NonPublic |
-		                 BindingFlags.Instance |
-		                 BindingFlags.Static;
-
-	    Type type = info.DeclaringType;
-	    FieldInfo fi = type.GetField(name, flags);
-	    Delegate d = fi.GetValue(obj) as Delegate;
+	    Delegate d = this.fi.GetValue(obj) as Delegate;
 
   	    if (d == null) {
   		IntPtr r = Runtime.PyNone;
@@ -131,8 +136,6 @@
   		return r;
   	    }
 
-	    // The method target in this case is actually the delegate.
-
 	    MethodInfo method = d.GetType().GetMethod("Invoke", flags);
 	    IntPtr wrapped = CLRObject.GetInstHandle(d);
 	    Runtime.Incref(wrapped);
@@ -145,6 +148,25 @@
 
 
 	//====================================================================
+	// Helper to support truth testing on event descriptors in Python.
+	// The standard if(event != null) idiom does not work for Python,
+	// because events never appear to be null / None - the descriptor
+	// always exists even if the actual event (delegate) is null. To
+	// provide help for Python, event descriptors support __nonzero__
+	// so that the Python can use 'if event:' to test events.
+	//====================================================================
+
+	internal int IsNonEmpty(IntPtr target) {
+	    CLRObject co = ManagedType.GetManagedObject(target) as CLRObject;
+	    object obj = (co != null) ? co.inst: null;
+	    Type type = info.DeclaringType;
+	    FieldInfo fi = type.GetField(name, flags);
+	    Delegate d = fi.GetValue(obj) as Delegate;
+	    return (d == null) ? 0 : 1;
+	}
+
+
+	//====================================================================
 	// Descriptor __get__ implementation. A getattr on an event returns
 	// a "bound" event that keeps a reference to the object instance,
 	// making events first-class Python objects similar to methods.
@@ -152,9 +174,14 @@
 
 	[CallConvCdecl()]
 	public static IntPtr tp_descr_get(IntPtr ds, IntPtr ob, IntPtr tp) {
-	    EventObject self = (EventObject)GetManagedObject(ds);
+	    EventObject self = GetManagedObject(ds) as EventObject;
 	    EventBinding binding;
 
+	    if (self == null) {
+		Exceptions.SetError(Exceptions.TypeError, "invalid argument"); 
+		return IntPtr.Zero;
+	    }
+
 	    // If the event is accessed through its type (rather than via
 	    // an instance) we return an 'unbound' EventBinding that will
 	    // be cached for future accesses through the type.
@@ -168,10 +195,8 @@
 		return binding.Handle;
 	    }
 
-	    if (Runtime.PyObject_IsInstance(ob, tp) == 0) {
-		Exceptions.SetError(Exceptions.TypeError, 
-				    "invalid descriptor argument"
-				    );
+	    if (Runtime.PyObject_IsInstance(ob, tp) < 1) {
+		Exceptions.SetError(Exceptions.TypeError, "invalid argument");
 		return IntPtr.Zero;
 	    }
 
@@ -190,12 +215,12 @@
 
 	[CallConvCdecl()]
 	public static int tp_descr_set(IntPtr ds, IntPtr ob, IntPtr val) {
-	    EventObject self = (EventObject)GetManagedObject(ds);
-	    ManagedType m = GetManagedObject(val);
-	    // fix to make this a simple type check...
-	    if (m is EventBinding) {
+	    EventBinding e = GetManagedObject(val) as EventBinding;
+
+	    if (e != null) {
 		return 0;
 	    }
+
 	    string message = "cannot set event attributes";
 	    Exceptions.SetError(Exceptions.TypeError, message);
 	    return -1;


=== PythonNet/src/runtime/MethodBinding.cs 1.1 => 1.2 ===
--- PythonNet/src/runtime/MethodBinding.cs:1.1	Mon Jul 14 15:59:51 2003
+++ PythonNet/src/runtime/MethodBinding.cs	Tue Oct  7 22:29:17 2003
@@ -40,6 +40,39 @@
 	    return self.m.Invoke(self.target, args, kw);
 	}
 
+
+	//====================================================================
+	// MethodBinding  __hash__ implementation.
+	//====================================================================
+
+	[CallConvCdecl()]
+	public static IntPtr tp_hash(IntPtr ob) {
+	    MethodBinding self = (MethodBinding)GetManagedObject(ob);
+	    long x = 0;
+	    long y = 0;
+
+	    if (self.target != IntPtr.Zero) {
+		x = Runtime.PyObject_Hash(self.target).ToInt64();
+		if (x == -1) {
+		    return new IntPtr(-1);
+		}
+	    }
+ 
+	    y = Runtime.PyObject_Hash(self.m.pyHandle).ToInt64();
+	    if (y == -1) {
+		return new IntPtr(-1);
+	    }
+
+	    x ^= y;
+
+	    if (x == -1) {
+		x = -1;
+	    }
+
+	    return new IntPtr(x);
+	}
+
+
 	//====================================================================
 	// MethodBinding  __repr__ implementation.
 	//====================================================================


=== PythonNet/src/runtime/MethodObject.cs 1.1 => 1.2 ===
--- PythonNet/src/runtime/MethodObject.cs:1.1	Mon Jul 14 15:59:51 2003
+++ PythonNet/src/runtime/MethodObject.cs	Tue Oct  7 22:29:17 2003
@@ -63,10 +63,8 @@
 		return binding.Handle;
 	    }
 
-	    if (Runtime.PyObject_IsInstance(ob, tp) != 1) {
-		Exceptions.SetError(Exceptions.TypeError, 
-				    "invalid descriptor argument"
-				    );
+	    if (Runtime.PyObject_IsInstance(ob, tp) < 1) {
+		Exceptions.SetError(Exceptions.TypeError, "invalid argument");
 		return IntPtr.Zero;
 	    }
 




More information about the Zope-CVS mailing list