[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