[Zope-CVS] CVS: PythonNet/src/runtime - AssemblyManager.cs:1.3
EventBinding.cs:1.6 EventObject.cs:1.7 PropertyObject.cs:1.4
Brian Lloyd
cvs-admin at zope.org
Wed Nov 5 22:35:41 EST 2003
Update of /cvs-repository/PythonNet/src/runtime
In directory cvs.zope.org:/tmp/cvs-serv19508/src/runtime
Modified Files:
AssemblyManager.cs EventBinding.cs EventObject.cs
PropertyObject.cs
Log Message:
Fixed event support; added keyfile to buildout; fixed exception obsfucation
in property accessors.
=== PythonNet/src/runtime/AssemblyManager.cs 1.2 => 1.3 ===
--- PythonNet/src/runtime/AssemblyManager.cs:1.2 Tue Aug 12 17:27:16 2003
+++ PythonNet/src/runtime/AssemblyManager.cs Wed Nov 5 22:35:10 2003
@@ -10,6 +10,7 @@
// FOR A PARTICULAR PURPOSE.
using System;
+using System.IO;
using System.Collections;
using System.Reflection;
using System.Reflection.Emit;
@@ -25,7 +26,7 @@
internal class AssemblyManager {
static Hashtable namespaces;
- static ArrayList assemblies;
+ static ArrayList assemblies;
static Hashtable probed;
private AssemblyManager() {}
@@ -39,6 +40,10 @@
domain.AssemblyLoad += new AssemblyLoadEventHandler(
AssemblyLoadHandler
);
+
+// domain.AssemblyResolve += new ResolveEventHandler(
+// ResolveHandler
+// );
Assembly[] items = domain.GetAssemblies();
for (int i = 0; i < items.Length; i++) {
@@ -48,6 +53,7 @@
}
}
+
//===================================================================
// Event handler for assembly load events. At the time the Python
// runtime loads, we scan the app domain to map the assemblies that
@@ -62,22 +68,70 @@
ScanAssembly(assembly);
}
+
+ //===================================================================
+ // Event handler for assembly resolve events. This is needed because
+ // we augment the assembly search path with the PYTHONPATH when we
+ // load an assembly from Python. Because of that, we need to listen
+ // for failed loads, because they might be dependencies of something
+ // we loaded from Python which also needs to be found on PYTHONPATH.
+ //===================================================================
+
+ static Assembly ResolveHandler(Object ob, ResolveEventArgs args){
+ string name = args.Name.ToLower();
+
+ for (int i = 0; i < assemblies.Count; i++) {
+ Assembly a = (Assembly)assemblies[i];
+ string full = a.FullName.ToLower();
+ if (full.StartsWith(name)) {
+ return a;
+ }
+ }
+
+ return LoadAssembly(name);
+ }
+
+
+ //===================================================================
+ // Given an assembly name, try to find this assembly file using the
+ // PYTHONPATH. If not found, return null to indicate implicit load
+ // using standard load semantics (app base directory then GAC, etc.)
+ //===================================================================
+
+ internal string FindAssembly(string name) {
+ // TODO: finish
+ char sep = Path.DirectorySeparatorChar;
+ string path = name;
+ string[] list = {};
+
+ int count = list.Length;
+ for (int i = 0; i < count; i++) {
+ string temp = list[i] + sep + name;
+ if (File.Exists(temp)) {
+ return temp;
+ }
+ }
+
+ return null;
+ }
+
+
//===================================================================
// Loads an assembly from the application directory or the GAC
- // given a simple assembly name. Returns true if an assembly was
- // loaded, false if the assembly was not found or already loaded.
+ // given a simple assembly name. Returns the assembly if loaded.
//===================================================================
- public static bool LoadAssembly(string name) {
+ public static Assembly LoadAssembly(string name) {
Assembly assembly = null;
try {
assembly = Assembly.LoadWithPartialName(name);
}
catch {
}
- return (assembly != null);
+ return assembly;
}
+
//===================================================================
// Given a qualified name of the form A.B.C.D, attempt to load
// an assembly named after each of A.B.C.D, A.B.C, A.B, A. This
@@ -92,7 +146,7 @@
for (int i = 0; i < names.Length; i++) {
s = (i == 0) ? names[0] : s + "." + names[i];
if (probed[s] == null) {
- if (LoadAssembly(s)) {
+ if (LoadAssembly(s) != null) {
loaded = true;
}
probed[s] = 1;
@@ -101,6 +155,7 @@
return loaded;
}
+
//===================================================================
// Scans an assembly for exported namespaces, adding them to the
// mapping of valid namespaces. Note that for a given namespace
@@ -135,6 +190,7 @@
}
}
+
//===================================================================
// Returns true if the given qualified name matches a namespace
// exported by an assembly loaded in the current app domain.
@@ -143,6 +199,7 @@
public static bool IsValidNamespace(string name) {
return namespaces.ContainsKey(name);
}
+
//===================================================================
// Returns the System.Type object for a given qualified name,
=== PythonNet/src/runtime/EventBinding.cs 1.5 => 1.6 ===
--- PythonNet/src/runtime/EventBinding.cs:1.5 Mon Oct 27 21:07:00 2003
+++ PythonNet/src/runtime/EventBinding.cs Wed Nov 5 22:35:10 2003
@@ -63,7 +63,7 @@
if (Runtime.PyCallable_Check(arg) < 1) {
Exceptions.SetError(Exceptions.TypeError,
- "event handlers must be callable"
+ "invalid event handler"
);
return IntPtr.Zero;
}
@@ -74,48 +74,6 @@
Runtime.Incref(self.pyHandle);
return self.pyHandle;
- }
-
-
- //====================================================================
- // 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.
- //====================================================================
-
- [CallConvCdecl()]
- public static IntPtr tp_call(IntPtr ob, IntPtr args, IntPtr kw) {
- EventBinding self = (EventBinding)GetManagedObject(ob);
-
- // This supports calling an event 'unbound', passing the instance
- // as the first argument. Only meaningful for instance events.
-
- if ((self.target == IntPtr.Zero) && (!self.e.IsStatic())) {
- if (Runtime.PyTuple_Size(args) < 1) {
- Exceptions.SetError(Exceptions.TypeError,
- "not enough arguments"
- );
- return IntPtr.Zero;
- }
- int len = Runtime.PyTuple_Size(args);
- IntPtr uargs = Runtime.PyTuple_GetSlice(args, 1, len);
- IntPtr inst = Runtime.PyTuple_GetItem(args, 0);
- Runtime.Incref(inst);
- IntPtr r = self.e.Invoke(inst, uargs, kw);
- Runtime.Decref(inst);
- return r;
- }
-
- return self.e.Invoke(self.target, args, kw);
}
=== PythonNet/src/runtime/EventObject.cs 1.6 => 1.7 ===
--- PythonNet/src/runtime/EventObject.cs:1.6 Mon Oct 27 21:07:00 2003
+++ PythonNet/src/runtime/EventObject.cs Wed Nov 5 22:35:10 2003
@@ -24,20 +24,13 @@
internal string name;
internal EventBinding unbound;
internal EventInfo info;
- internal FieldInfo fi;
+ internal Hashtable reg;
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;
-
//====================================================================
// Register a new Python object event handler with the event.
@@ -57,6 +50,22 @@
Type type = this.info.EventHandlerType;
Delegate d = DelegateManager.GetDelegate(type, handler);
+ // Now register the handler in a mapping from instance to pairs
+ // of (handler hash, delegate) so we can lookup to remove later.
+ // All this is done lazily to avoid overhead until an event is
+ // actually subscribed to by a Python event handler.
+
+ if (reg == null) {
+ reg = new Hashtable();
+ }
+ object key = (obj != null) ? obj : this.info.ReflectedType;
+ ArrayList list = reg[key] as ArrayList;
+ if (list == null) {
+ list = new ArrayList();
+ reg[key] = list;
+ }
+ list.Add(new Handler(Runtime.PyObject_Hash(handler), d));
+
// Note that AddEventHandler helper only works for public events,
// so we have to get the underlying add method explicitly.
@@ -73,128 +82,59 @@
//====================================================================
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;
-
if (target != IntPtr.Zero) {
CLRObject co = (CLRObject)ManagedType.GetManagedObject(target);
obj = co.inst;
}
IntPtr hash = Runtime.PyObject_Hash(handler);
- if (Exceptions.ErrorOccurred()) {
+ if (Exceptions.ErrorOccurred() || (reg == null)) {
Exceptions.SetError(Exceptions.ValueError,
"unknown event handler"
);
return false;
}
- d = this.fi.GetValue(obj) as Delegate;
+ object key = (obj != null) ? obj : this.info.ReflectedType;
+ ArrayList list = reg[key] as ArrayList;
- if (d == null) {
+ if (list == 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;
- }
}
- Exceptions.Clear();
-
- if (found == null) {
- Exceptions.SetError(Exceptions.ValueError,
- "unknown event handler"
- );
- return false;
- }
-
- // Note that RemoveEventHandler only works for public events,
- // so we have to get the underlying remove method explicitly.
-
- object[] args = { found };
+ object[] args = { null };
MethodInfo mi = this.info.GetRemoveMethod(true);
- mi.Invoke(obj, BindingFlags.Default, null, args, null);
-
- return true;
- }
-
-
- //====================================================================
- // Invoke the event and return the result as a Python object.
- //====================================================================
-
- internal IntPtr Invoke(IntPtr target, IntPtr args, IntPtr kw) {
- CLRObject co = ManagedType.GetManagedObject(target) as CLRObject;
- object obj = (co != null) ? co.inst: null;
-
- Delegate d = this.fi.GetValue(obj) as Delegate;
- if (d == null) {
- IntPtr r = Runtime.PyNone;
- Runtime.Incref(r);
- return r;
- }
-
- MethodInfo method = d.GetType().GetMethod("Invoke", flags);
- IntPtr wrapped = CLRObject.GetInstHandle(d);
-
- MethodBinder binder = new MethodBinder(method);
- IntPtr op = binder.Invoke(wrapped, args, kw);
- Runtime.Decref(wrapped);
-
- return op;
- }
-
-
- //====================================================================
- // 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;
- }
-
-
- //====================================================================
- // Called by bindings to decide how to handle unbound event calls.
- //====================================================================
+ for (int i = 0; i < list.Count; i++) {
+ Handler item = (Handler)list[i];
+ if (item.hash != hash) {
+ continue;
+ }
+ args[0] = item.del;
+ try {
+ mi.Invoke(obj, BindingFlags.Default, null, args, null);
+ }
+ catch {
+ continue;
+ }
+ list.RemoveAt(i);
+ return true;
+ }
- internal bool IsStatic() {
- return fi.IsStatic;
+ Exceptions.SetError(Exceptions.ValueError,
+ "unknown event handler"
+ );
+ return false;
}
//====================================================================
// 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.
+ // a "bound" event that keeps a reference to the object instance.
//====================================================================
[CallConvCdecl()]
@@ -277,6 +217,20 @@
ExtensionType.FinalizeObject(self);
}
+
+ }
+
+
+
+ internal class Handler {
+
+ public IntPtr hash;
+ public Delegate del;
+
+ public Handler(IntPtr hash, Delegate d) {
+ this.hash = hash;
+ this.del = d;
+ }
}
=== PythonNet/src/runtime/PropertyObject.cs 1.3 => 1.4 ===
--- PythonNet/src/runtime/PropertyObject.cs:1.3 Mon Oct 20 23:05:14 2003
+++ PythonNet/src/runtime/PropertyObject.cs Wed Nov 5 22:35:10 2003
@@ -69,31 +69,32 @@
);
return IntPtr.Zero;
}
- //ts = PythonEngine.BeginAllowThreads();
+
try {
result = self.info.GetValue(null, null);
- // PythonEngine.EndAllowThreads(ts);
return Converter.ToPython(result, self.info.PropertyType);
}
catch(Exception e) {
- // PythonEngine.EndAllowThreads(ts);
Exceptions.SetError(Exceptions.TypeError, e.Message);
return IntPtr.Zero;
}
}
+ CLRObject co = GetManagedObject(ob) as CLRObject;
+ if (co == null) {
+ Exceptions.SetError(Exceptions.TypeError, "invalid target");
+ return IntPtr.Zero;
+ }
+
try {
- CLRObject co = (CLRObject)GetManagedObject(ob);
- //ts = PythonEngine.BeginAllowThreads();
result = self.info.GetValue(co.inst, null);
- // PythonEngine.EndAllowThreads(ts);
return Converter.ToPython(result, self.info.PropertyType);
}
catch(Exception e) {
- //if (ts != IntPtr.Zero) {
- // PythonEngine.EndAllowThreads(ts);
- //}
- Exceptions.SetError(Exceptions.TypeError, e.Message);
+ if (e.InnerException != null) {
+ e = e.InnerException;
+ }
+ Exceptions.SetError(e);
return IntPtr.Zero;
}
}
@@ -150,23 +151,24 @@
try {
if (!is_static) {
- CLRObject co = (CLRObject)GetManagedObject(ob);
- //ts = PythonEngine.BeginAllowThreads();
+ CLRObject co = GetManagedObject(ob) as CLRObject;
+ if (co == null) {
+ Exceptions.SetError(Exceptions.TypeError,
+ "invalid target");
+ return -1;
+ }
self.info.SetValue(co.inst, newval, null);
- //PythonEngine.EndAllowThreads(ts);
}
else {
- //ts = PythonEngine.BeginAllowThreads();
self.info.SetValue(null, newval, null);
- //PythonEngine.EndAllowThreads(ts);
}
return 0;
}
catch(Exception e) {
- //if (ts != IntPtr.Zero) {
- // PythonEngine.EndAllowThreads(ts);
- //}
- Exceptions.SetError(Exceptions.TypeError, e.Message);
+ if (e.InnerException != null) {
+ e = e.InnerException;
+ }
+ Exceptions.SetError(e);
return -1;
}
More information about the Zope-CVS
mailing list