example of calling Python from C# with simple object marshalling
In case anyone is trying to use the embedding aspect of this fine package (well-done, Brian!), here is a quick-and-dirty starting point that shows how one might call a Python function with a variable argument list of .NET simple value types (e.g. int, double, string) returning an object list, from a .NET application. The in and out lists are ordinary simple type objects in the NET wold, and ordinary Python objects in the Python world. Note that this is not meant to replace anything Brian has in the Runtime DLL. It is simply a proof of concept test I did to begin discovering what I can do in the C# -> Python direction. The following trivial python file has an invoke() method which can be called from C# in a very uncluttered way... """ PyPortal.py demonstration of passing simple type arguments and returning a tuple of simple types """ def invoke(a1, a2): print 'invoke() called with %s and %s.' % (a1, a2) # show that we can return a list of simple types... return ("[wrap(%s)]" % s, f*2) The C# code that calls this looks like this, including a traversal of the returned objects.... PythonEngine.Initialize(); PyObject pyportal = PythonEngine.ImportModule("PyPortal"); PyObject callable = pyportal.GetAttr("invoke"); Portal portal = new Portal(); object[] objects = portal.Invoke(callable, "Mark Barclay", 123.456); if (objects != null) { foreach (object o in objects) { if (o != null) Console.WriteLine(o.ToString()); else Console.WriteLine("o is null."); } } Note the object instantiation "Portal portal = new Portal()". For this to work, the following in-lined file, Portal.cs, needs to be added to the runtime (I created a project PyWrapper in VS2003 that includes all the sources from src\runtime), because it needs internal visibility into some of the classes there (e.g. Converter). There is almost certainly a better, simpler way, but for now this showed me that calling Pythoin with arbitrary value type args and retuning a list of Python objects onverted to .NET objects was feasible using Python for .NET. A class such as this (Portal) might be a handy addition to the runtime, or maybe Brian has something like that there already, or on the drawing board. If not, I'd be happy to take on the task of creating a more complete and robust implementation of this, if anyone thinks it would be useful. (Please forgive the lack of comments, error detection and general completeness in the following demonstration code) using System; namespace Python.Runtime { public class Portal { public Portal() { } public object[] Invoke(PyObject callable, params object[] objects) { if (!callable.IsCallable()) { throw new Exception("Invoke called with an un-callable"); } int size = objects.Length; IntPtr items = Runtime.PyTuple_New(size); for (int i=0; i<size; i++) { IntPtr po = Converter.ToPythonImplicit(objects[i]); Runtime.PyTuple_SetItem(items, i, po); } PyTuple tuple = new PyTuple(items); PyObject pyresult = callable.Invoke(tuple); if (pyresult.Handle == Runtime.PyNone) { return new object[] { "__none__" }; } if (PyTuple.IsTupleType(pyresult)) { int size_out = pyresult.Length(); object [] objects_out = new object[size_out]; for (int i=0; i<size_out; i++) { try { PyObject po = pyresult[i]; IntPtr obj = po.Handle; Type type = typeof(Object); if (Runtime.PyFloat_Check(obj)) { type = typeof(double); } else if (Runtime.PyInt_Check(obj)) { type = typeof(int); } else if (Runtime.PyString_Check(obj)) { type = typeof(string); } else { objects_out[i] = "[unknown type]"; break; } object mo = null; bool setError = true; bool ok = Converter.ToManaged(po.Handle, type, out mo, setError); if (ok) { objects_out[i] = mo; //Console.WriteLine(mo.ToString()); } } catch (Exception ex) { Console.WriteLine(ex.Message + "\r\n" + ex.StackTrace); objects_out[i] = "?"; } } return objects_out; } // just testing return new object[] { 123, "abc" }; } } } Please let me know if this is of any help, or if it is entirely redundant (perhaps I missed something in the API that does just this). Mark Barclay Sr. Software Engineer CRi Inc.
In case anyone is trying to use the embedding aspect of this fine package (well-done, Brian!), here is a quick-and-dirty starting point that shows how one might call a Python function with a variable argument list of .NET simple value types (e.g. int, double, string) returning an object list, from a .NET application. The in and out lists are ordinary simple type objects in the NET wold, and ordinary Python objects in the Python world.
Note that this is not meant to replace anything Brian has in the Runtime DLL. It is simply a proof of concept test I did to begin discovering what I can do in the C# -> Python direction.
The following trivial python file has an invoke() method which can be called from C# in a very uncluttered way...
<snip good stuff> I think this points out an area still wanting in the embedding interface (calling into Python and getting back managed objects rather than PyObject wrappers). I'm thinking that a good start would be a variation on what your example does as an overload for the Invoke method of PyObjects: object Invoke(...) object[] Invoke(...) I think it would also be a good idea to have a 'ToObject' method on PyObjects: object ToObject(); which would: - if the Python object represents a managed object, return that object - if the Python object is a primitive type that is convertible to a managed primitive type, return the converted value - otherwise throw an exception Another option could be to have PyObject implement IConvertible, though that would be more work, and might not buy you anything over using: int i = thing.ToObject() as Int32 thoughts? Brian Lloyd brian@zope.com V.P. Engineering 540.361.1716 Zope Corporation http://www.zope.com
participants (2)
-
Brian Lloyd
-
MBarclay@cri-inc.com