Example of the Usefulness of IronPython

By | September 3, 2014

Previously I wrote about the basics of setting up and working with IronPython. In that post, I briefly talked about why one would want to use it in their .NET applications. I stated that the biggest reason to use IronPython would be to add dynamic business logic into your .NET application. However, my first post did not attempt to illustrate that point and instead was just a post where I played around with IronPython and documented the event. So in this sequel post, I will give a contrived example of where IronPython is best suited for use and that is injecting dynamically typed sections of code among the statically typed code. The full source code is available for download at the bottom of this post.

Background

Hypothetical Scenario: You work at a software company called Questionnaires R Us. Your flagship product EasyQuestions is a highly regarded desktop software application written using the .NET Framework which asks users questions and in turn logs the Question and Answer pair to a text file (I told you it was contrived example, didn’t I?) You currently have three clients all of which have slightly different needs. Here’s a listing of the clients and their requirements:

Client 1: The Trendy Tavern is a bar located in a very hip place of downtown and is only interested in people age 35 or younger. So they want their first question to be what a person’s age is and if it is over 35 or under 21 (legal drinking age in the US), then they want to immediately complete the survey and stop asking further questions.

Client 2: Joe’s Box Producers is a company which manufacturers cardboard boxes of all shapes and sizes for shipping and storage needs. Only problem is that their eccentric owner Joe hates the letter A and demands the that this forsaken letter be removed from all answers. Shhh… Don’t tell his wife Alice.

Client 3: Always On Time Delivery Service is a company which prides themselves on the timeliness of their delivery service. This survey is no exception so they demand a timestamp on every answer that the user provides.

Possible Solutions

So as a Software Developer on EasyQuestions how do you meet the needs of all your customers? After gathering and analyzing the requirements you find that all but a single component of your software is common to all customers. The variable part of the application is what questions are asked and how the application responds to the answers. How do you go about solving this?

Idea 1: Keep separate codebases of your software for each customer.

Not Quite… You are going to be carrying around loads of duplicate code for minor changes in requirements. Worst yet, you are going to be repeating yourself…

Idea 2: Split up the project into DLLs where all the types and user controls can be re-used. Leaving you only a small EXE that needs to be written per customer.

This approach is okay, but again we are still making a separate executable/ dedicated project for each customer. It would be nice to have it all down to one compilation, but if the needs of your customers may radically evolve and diverge further down the line then this may be an acceptable point to be at. Another bonus gained from this approach is that you don’t have to leave the world of type safety within the .NET Framework to do it. However for our pretend scenario this approach is not our best option.

Idea 3: Create a configuration file that can be loaded into the application at runtime to address the particular needs of each customer.

This is great and will work if it is only parameters which vary between customers. When business logic varies per application then this approach will not work on it’s own… Based on our imaginary scenario alone – we would need a configuration setting which would inspect the age of the user supplied via a response to a question that is unique to a single customer and then decided whether to continue the survey or not… Good luck writing an XML configuration for that…

Idea 4: Inject dynamically typed code into your application to address the areas of varying business logic.

Bingo was his name-o! Using this approach you inject runtime evaluated scripts into the areas of the application code where the business logic varies. Now your one software project can be configured with any special business logic it requires per customer requirement. One compilation of your software can service all three of your customers!

Writing the Application

So this project is made up of the following components:

  • PythonNet class
  • SurveyLogger class
  • Question Form
  • Python Scripts
  • XML Configuration File
  • Main Form

First thing is to recall the PythonNet class from the first post. If you didn’t read that post, then that’s okay. Just remember that the class has two fields. The first fields is pyEngine which is a instance of the PythonEngine class which allows you to create IronPython script source, modules, scopes, etc. The second field is pyScope which is an instance of the PythonScope class which is a container that holds the variables, functions, and classes. Additionally, the two main methods we care about in this example are:

AddToScope allows us to add an object from our C# code into the scope of our IronPython script, we also give it the name that our IronPython script will reference it by. ExecutePythonFile takes in a path to a *.py file and executes that file against the scope held within our PythonNet instance.

Next we define the SurveyLogger class which logs the user information, questions and the answers given by the participant.

The QuestionForm is a simple WinForm which displays a question to the user and accepts a response.

Screenshot of QuestionForm

Here is the code to the QuestionForm:

So now that leaves us with scripting out the varying business logic for each customer. I will just list two of them here. Again the full source code is available for download at the bottom of this post.

For the Trendy Tavern , the IronPython script will look something like this:

For Always On Time Delivery Service , the IronPython script will look something like this:

Now lets put this all together. In our MainForm code we load which ever Python file we want to use from a simple XML file called EasyQuestionsConfig.xml. Here is a sample where we load the script for Always On Time Delivery Service :

All that is left to do is to add a few lines of code to add our C# objects into the Python script (namely – first name, last name and email) and then execute the Python Script file based upon our configuration. The code is listed below:

Screenshot of the MainForm:

Screenshot of MainForm of EasyQuestions

You can easily switch between different customers just by switching out the value of the Script element in EasyQuestionsConfig.xml to a different Python file that is located inside the ‘Python Scripts’ folder found under IronPythonDemo2.exe. This is just a sample application and is obviously relaxed with the error handling. I tried to keep everything pretty sparse in order to not distract attention away from the main goal which was showcasing IronPython in an example where it really shines.

Source code is available for download here.

Leave a Reply

Your email address will not be published. Required fields are marked *