• 1/2

Calling Python from C# using XML RPC

Posted by Christopher Diggins, 19 September 2012 5:14 pm

I wanted a host .NET application (in this case 3ds Max, but it applies equally to any .NET API like AutoCAD) to inform a Python script running on the same machine but in a different process whenever a particular event occured.

I first rejected a number of possible solutions that were low-level and that would prevent me from moving the client to another computer.

  • Named pipes
  • Memory mapped files
  • Windows messages
  • DLL function invocation 

Two remaining options seemed like possible candidates:

  • Web sockets
  • HTTP server running in the host

I liked the Web sockets approach, but the libraries for using them in C# and Python are not quite as mature or easy to use as I had hoped. Running an HTTP server and having the Python application poll it repeatedly seemed like the best option. However, I was concered about all of the wasted resource for the Python client to ping the host application repeatedly not to mention the potential for poor responsiveness.

After a bit of thought I realized that embedding the HTTP server in the Python application and treating the .NET application as the client would be a lot easier! 

With an HTTP server running in the Python application, I was with a new set of possibilities for communicating data from the host application to the Python client.

  • Simple HTTP API using query strings
  • REST API
  • SOAP API
  • XML RPC API

After further investigation I decided the simplest out-of-the-box solution would be to use the SimpleXmlRpcServer module in Python and the System.Net.WebRequest class in .NET. After quickly scanning the information on XML RPC on Wikipedia and hacking the example in the documentation for WebRequest and SimpleXmlRpcServer I ended up with a working prototype with almost no effort:

Here is the sample .NET client:

using System;
using System.Text;
using System.Net;
using System.IO;

namespace SimpleXmlRpcClient
{
    class Program
    {
        static void Main(string[] args)
        {
            WebRequest request = WebRequest.Create("http://localhost:8000/RPC2");
            request.Method = "POST";
            string postData = @"<?xml version=""1.0""?>
<methodCall>
  <methodName>add
  <params>
    <param>
        <value><i4>21</i4></value>
    </param>
    <param>
        <value>21
    </param>
  </params>
</methodCall>";
            byte[] byteArray = Encoding.UTF8.GetBytes(postData);
            request.ContentType = "application/x-www-form-urlencoded";
            request.ContentLength = byteArray.Length;
            Stream dataStream = request.GetRequestStream();
            dataStream.Write(byteArray, 0, byteArray.Length);
            dataStream.Close();
            WebResponse response = request.GetResponse();
            Console.WriteLine(((HttpWebResponse)response).StatusDescription);
            dataStream = response.GetResponseStream();
            StreamReader reader = new StreamReader(dataStream);
            string responseFromServer = reader.ReadToEnd();
            Console.WriteLine(responseFromServer);
            reader.Close();
            dataStream.Close();
            response.Close();
            Console.WriteLine("Press any key to continue ...");
            Console.ReadKey();
        }
    }
}

Here is the sample Python host:

from SimpleXMLRPCServer import SimpleXMLRPCServer
from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler

# Restrict to a particular path.
class RequestHandler(SimpleXMLRPCRequestHandler):
    rpc_paths = ('/RPC2',)

# Create server
server = SimpleXMLRPCServer(("localhost", 8000),requestHandler=RequestHandler)
server.register_introspection_functions()

# Register a function e
def adder_function(x,y):
    return x + y
server.register_function(adder_function, 'add')

# Run the server's main loop
server.serve_forever()

Hopefully this is enough to get your favorite .NET application talking to your favorite Python application. Happy hacking!

3 Comments

loocas

Posted 19 September 2012 6:56 pm

Very nice, Chris! Thanks.

Another option, though, might be to ask us for a copy of our homebrew Max Python plugin (well, it's more of a .NET pluging tailored specifically for 3ds Max): duberPython

Christopher Diggins

Posted 19 September 2012 7:36 pm

Thanks Loocas. So can duberPython run a Python script that relies on PyQt?

loocas

Posted 20 September 2012 2:14 pm

Hey, Chris,

unfortunately duberPython is based off of IronPython, thus anything that requires CPython is incompatible. I'm not sure there are libs for PyQt for .NET or IronPython in particular, unfortunately.

However, that being said, if you're not specifically tied to Qt, you can, of course, utilize the entire .NET framework, incl. its great GUI classes.

Add Your Comment

You must be logged in to post a comment.

Please only report comments that are spam or abusive.