[Python.NET] example of calling Python from C# with simple object marshalling

MBarclay at cri-inc.com MBarclay at cri-inc.com
Fri Nov 14 12:31:19 EST 2003


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.




More information about the PythonDotNet mailing list