Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Integrating IronPython into your Applications

4.05/5 (8 votes)
16 Mar 2008CPOL6 min read 1   1.2K  
This article describes why scripting is useful and explains how to integrate IronPython as a scripting language in your application

Introduction

IronPython is an implementation of Python for .NET. It provides full access to .NET libraries through the Python programming language, while also providing access to standard Python libraries. While it can be used to write standalone applications, this article is about using it as a scripting language for your application.

Why a Scripting Language?

  1. Change functionality at run time

    There could be lots of reasons for this - maybe every one of your customers want your application to behave differently and writing and shipping plugins is too much work. Or your customers are developers and they want the ability to change how the app behaves and customize it with immediate feedback, without having to recompile the app or plugins. Or, you are testing some functionality and want the application to behave differently at different times, without breaking into a debugger.

  2. Provide "extra' functionality

    You're running your application and you notice that it's eating up memory. You decide to run it through a memory profiler, but before that, you want to run a full GC so that the profiler only picks up live objects. Wouldn't it be great if there was some way to call GC.Collect? Or let's say you have a complicated UI that requires a lot of navigation and you want to spare developers the trouble of going through the UI every single time. Wouldn't it be nice to have something that would automatically do whatever is necessary and help developers get to the interesting part quickly?

  3. Live testing

    Unit testing is great for testing out individual components separately, but wouldn't it be nice to have the ability to write tests that could run on a live system? Like say, when the user clicks button X and the software is in state Y, run test Z? Or do UI testing like filling out textboxes and clicking buttons automatically?

  4. Poking around live systems

    When you are stuck without a debugger and want to check the state of the system (like the value of a class member, for example), wouldn't it be great to have the ability to just type that into a command window and see the result right away?

Integrating IronPython

IronPython is incredibly easy to integrate. You'll need to:

  1. Add a reference to IronPython.dll from your application
  2. Instantiate a PythonEngine object
  3. Add the objects that you want to expose via scripting to pythonEngine.Globals
  4. Redirect standard output and error streams so you can show errors and script output

The following snippet of code shows how it has been done in the attached sample application:

C#
public Form1()
{
   InitializeComponent();
   InitializeIronPython();
}

private void InitializeIronPython()
{
   Options.PrivateBinding = true;
   pythonEngine = new PythonEngine();

   MessageBoxStream s = new MessageBoxStream();

   pythonEngine.SetStandardError(s);
   pythonEngine.SetStandardOutput(s);

   pythonEngine.AddToPath(AppDomain.CurrentDomain.BaseDirectory);

   pythonEngine.Globals.Add("form", this);
   pythonEngine.Globals.Add("bl", bl);
}

MessageBoxStream is a class deriving from System.IO.Stream that shows whatever is written to the stream using MessageBox.Show. The pythonEngine.Globals.Add method exposes your objects as global variables to the scripts. The first parameter is a string that translates to the variable name in the script, and the second parameter is the object corresponding to that variable. The sample application exposes the Form object and a business logic object to the script.

The sample application also has a "command prompt" at the bottom, which is actually a textbox, to execute live Python code. This is how that code looks like:

C#
private void commandPrompt_KeyPress(object sender, KeyPressEventArgs e)
{
   if (e.KeyChar == '\r')
   {
      try
      {
         pythonEngine.ExecuteToConsole(commandPrompt.Text);
         commandPrompt.Clear();
      }
      catch(Exception e)
      {
         MessageBox.Show(e.ToString());
      }
   }
}

It simply forwards the text to the ExecuteToConsole method on the pythonEngine object.

Using IronPython

The attached project demonstrates how to do things mentioned in the previous section. It's a pretty contrived application - it has two input textboxes and an output textbox. There are three buttons, "Add", "Subtract" and "Custom". As you would expect, the Add button adds the numbers in the input textboxes and writes it to the output, Subtract subtracts them and Custom allows people to write custom actions. Let's go through the points mentioned in the previous section and see how they can be done using IronPython.

Change Functionality at Run Time

The "Custom" button executes a script named custom.py in the application's directory. That script gets the input textboxes and a reference to the Form instance, which it can use to do whatever custom action it wants. IronPython allows you to control what objects the script can reference.

C#
private void customButton_Click(object sender, EventArgs e)
{
   Dictionary<string,object /> locals = new Dictionary<string,object />()
   {
      {"input1", input1},
      {"input2", input2},
      };

      pythonEngine.ExecuteFile("custom.py", pythonEngine.DefaultModule, locals);
   }

The custom.py script automatically gets the global objects added to the pythonEngine object. In addition to them, you can pass a dictionary of name/object pairs as local variables for that script. The script file can then access them like:

C#
def append(x, y):
    return ''.join([x,y])

form.ShowResult(int(append(input1.Text, input2.Text)))

This script simply concatenates the text in the input textboxes, converts the concatenated result to a number and calls the Form object's ShowResult.

As another example, type the following command:

C#
form.TransformResult = lambda x : '-'.join(str(x))

Then do an Add or Subtract - you'll notice that the result has hyphens between the digits.

If you look at the code for ShowResult, you'll see that it calls the TransformResult delegate (if available) before setting the resultTextBox's Text property. The above snippet of code creates an anonymous function that converts the passed number to a string and then inserts a hyphen between each character. That anonymous function is assigned to the TransformResult delegate of the Form object, which it then invoked when you clicked the Add Button.

Provide "Extra" Functionality

You can simply type GC.Collect in the command prompt textbox and hit Enter and have the CLR do a GC right away.

For a more interesting example, let's say that you as a developer would like to know the results generated in the current session. Assume that this is something that you wouldn't want to write as part of the application. With IronPython, you could do something like:

C#
resultTextBox = form.Controls.Find('result', True)[0]
results = []
def fn(arg1, arg2) : results.append(resultTextBox.Text)

resultTextBox.TextChanged += fn

You could then type...

results

... at any point in the command prompt to get the results calculated so far. The script basically subscribes to the TextChanged event of resultTextBox and appends the contents to a list.

Live Testing

Using the Controls.Find method shown in the previous section, you can get a reference to any UI object and then manipulate it.

C#
input1TextBox = form.Controls.Find('input1', True)[0]
input2TextBox = form.Controls.Find('input2', True)[0]
resultTextBox = form.Controls.Find('result', True)[0]

input1TextBox.Text = '1'
input2TextBox.Text = '2'

addButton = form.Controls.Find('addButton', True)[0]
addButton.PerformClick()

print int(resultTextBox.Text) == 3
print int(resultTextBox.Text) == 4

As you can see, this piece of code gets references to the UI objects, sets values, executes an action and then verifies the result. You could also choose to expose the UI objects by adding them to PythonEngine.Globals, instead of using Control.Find to get them.

Poking Around Live Systems

You have already seen that you can access UI objects and manipulate them. You can do...

C#
form.Text = 'IronPython'

... to change the form's title, for example. You can also access business logic components, if you added them to the PythonEngine. The sample application exposes the BusinessLogic object it holds with the name bl, so you can now access its state too. It holds the last operation's type as a member, so you can do...

C#
bl.LastOperation

... to view the last executed operation.

Conclusion

I hope this article got you interested in IronPython and integrating scripting into your application. The IronPython project is hosted at CodePlex, so that's the place to go to download and learn more about it. The command prompt in the sample application is very rudimentary and there are alternatives like IronTextBox that do a much better job. You could also add "meta" commands to your prompt that allow you to execute arbitrary python files. And please keep in mind that the attached application is a sample application, intended to demonstrate IronPython integration, so if the code is missing error checking, you know why.

History

  • 3:39 PM 3/16/2008: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)