Using IronPython from Other .NET Languages

Understanding the Relationship between Dynamic and Static Languages

Something that most developers fail to consider is that, at some point, all languages generate the same thing — machine code. Without machine code, the software doesn’t execute. Your computer cares nothing at all about the idiosyncrasies of human language and it doesn’t care about communicating with you at all. Computers are quite selfish when you think about it. The circuitry that makes up your computer relies on software to change the position of switches — trillions of them in some cases. So computers use machine code and only machine code; languages are for humans.

When it comes to dynamic and static languages, it’s the way that humans view the languages that make them useful. A dynamic language offers the developer freedom of choice, call it the creative solution. A static language offers a reliable and stable paradigm — call it the comfort solution, the one that everyone’s used. How you feel about the languages partly affects your use of them. In the end, both dynamic and static language output ends up as machine code. Dynamic and static languages end up being tools that help you create applications faster and with fewer errors. If you really wanted to do so, you could write any application today using assembler (a low-level language just above machine code, see http://www.bing.com/reference/semhtml/Assembly_language for more information), but assembler is hardly the correct tool any longer — humans need a better tool to put applications together. The point is that you should use the tool that works best for a particular development process and not think that the tool is doing anything for your computer.

Anytime you use multiple languages, you must consider issues that have nothing to do with the dynamic or static nature of that language. For example, you must consider the data types that the languages support and provide a method for marshaling data from one language to the other. In fact, marshaling data is an important element in many areas of coding. If you want to communicate with the Win32 API from a .NET-managed language such as C# or Visual Basic.NET, you must marshal the data between the two environments. It’s important not to confuse communication and infrastructure requirements with differences between dynamic and static languages. Many resources you find do confuse these issues, which makes it hard for anyone to truly understand how dynamic and static languages differ.

Before you can use IronPython from other languages, it’s important to consider the way in which IronPython performs tasks. When an IronPython session starts, nothing exists — the environment begins with an empty slate. You’ve discovered throughout this book that IronPython calls upon certain script files as it starts to configure the environment automatically. These configuration tasks aren’t part of the startup; they are part of the configuration — something that occurs after the startup. The dynamic nature of IronPython means that all activity begins and ends with adding, changing, and removing environment features. There aren’t any compiled bits that you can examine statically. Everything in IronPython is dynamic.

When a static language such as C# or Visual Basic.NET attempts to access IronPython, it must accommodate the constant change. If you got nothing else out of Chapter 14 but this one fact, then the chapter was worth reading. In order to do this, C# and Visual Basic.NET rely upon events because they can’t actually accommodate change as part of the language. An event signals a change — an IronPython application has modified a class to contain a new method or property. It isn’t just the idea that the output or value has changed, but the method or property itself is new. In some cases, C# or Visual Basic.NET will also need to deal with the situation where a method or property simply goes away as well. The underlying mechanism of events, delegates, and caches is inspired and all but invisible, but to be successful at using the languages together, you must know they’re present.

The differences between dynamic and static languages go further than simply not knowing what code will execute next in a dynamic language. There’s also the matter of data typing. A static language assigns a type to the data it manages, which means that the compiler can make assumptions about the data and optimize access to it. A dynamic language also assigns types to the data it manages, but only does so at run time and even then the data type can change. Now, consider how this changeability complicates the matter of marshaling data from one language to the other. Because the data no longer has a stable type, the marshaling code can’t assume anything about it and must constantly check type to ensure the data it marshals appears in the right form in the target language.

The difference between dynamic and static languages, at least from a programming perspective, comes down to flexible coding and data typing. Everything else you may have heard either relates to differences between any two languages (such as the need to marshal data) or the political drama of which tool works best. This book won’t endeavor to tell you what tool to use. Certainly, I don’t tell anyone that a hammer works best for driving screws or that screwdrivers make wonderful ice picks (not that I believe either of these statements myself). The tool you use for a particular task is the one you can use best or the one called for by a particular job requirement. The point of this chapter and the rest of the book is to demonstrate that dynamic and static languages can work together successfully and in more than one way. The tool you use is up to you.

Creating an Externally Accessible IronPython Module

The first requirement for building an application that allows external access is to create the IronPython script you want to use. Ideally, this script will contain code that is fully debugged. You also want to test the code before you try to use it within C# or Visual Basic.NET. The following sections provide you with the techniques you use to create an IronPython script that you access from C# or Visual Basic .NET.

Considering Requirements for Externally Accessible Modules

The mistake that many developers will make is to think they must do something special in IronPython to make the code accessible. What you really need to do is create an IronPython script using the same techniques as always, and then test it directly. After you test the script using IronPython code, work with the target static language to gain the required access. This pretesting process is important to ensure that you aren’t fighting with a bad script in addition to potential problems marshaling data or interacting with methods that change.

Creating the IronPython Script

The IronPython script used for this example is quite simple in approach. All that the example call really does is add two numbers together. You could perform the task with far less code, but the point of this class is to demonstrate access techniques, so it’s purposely simple. Listing 15-1 shows the external module code and the code used to test it. As previously mentioned, testing your IronPython script is essential if you want the application to work properly.

Listin g 15-1: A test IronPython class for use in the examples

[code]
# The class you want to access externally.
class DoCalculations():
# A method within the class that adds two numbers.
def DoAdd(self, First, Second):
# Provide a result.
return First + Second
# A test suite in IronPython.
def __test__():
# Create the object.
MyCalc = DoCalculations()
# Perform the test.
print MyCalc.DoAdd(5, 10)
# Pause after the test session.
raw_input(‘nPress any key to continue…’)
# Execute the test.
# Comment this call out when you finish testing the code.
__test__()
[/code]

The class used for this example is DoCalculations(). It contains a single method, DoAdd(), that returns the sum of two numbers, First and Second. Overall, the class is simple.

The TestClass.py file also contains a __test__() function. This function creates an instance of DoCalculations(), MyCalc. It then prints the result of calling the DoAdd() method with values of 5 and 10. The example waits until you press Enter to exit.

In __main__(), you see a call to __test__(). You can execute the example at the command line, as shown in Figure 15-1. Make sure you use the –D command line switch to place the interpreter in debug mode. You could also open IPY.EXE interactively, load the file, and execute it inside the interpreter. When you know that the code works properly, be sure to comment out the call to __test__() in __main__().

Test the external module before you use it with your application.
Figure 15-1: Test the external module before you use it with your application.

Accessing the Module from C#

Now that you have an external module to use, you’ll probably want to access from an application. This section considers the requirements for accessing IronPython from C#. Don’t worry; the section “Accessing the Module from Visual Basic.NET” later in this chapter discusses access from Visual Basic. NET as well. If you follow these steps, you’ll find that access is relatively straightforward, even if it does get a bit convoluted at times. Microsoft promises the future versions of C# will make dynamic language access even easier.

Adding the Required C# References

Any application you create requires access to the dynamic language assemblies. The IronPython assemblies appear in the Program FilesIronPython 2.6 folder on your machine. Right-click References and choose Add Reference from the content menu to display the Add Reference dialog box. Select the Browse tab. In most cases, you only need the three DLLs shown in Figure 15-2 to access any IronPython script. (You may also need to add the IronPython .Modules.DLL file to the list in some cases.)

Add the required references from your IronPython setup.
Figure 15-2: Add the required references from your IronPython setup.

Select the assemblies you require by Ctrlclicking them in the Add Reference dialog box. Click OK when you’re finished. You’ll see the assemblies added to the References folder in Solution Explorer.

Adding the Required References to the Host Language

You can perform a multitude of tasks with IronPython. In fact, later chapters in the book show how to perform tasks such as testing your static application code. IronPython really is quite flexible. However, most people will start by executing external scripts and only need a few of the namespaces in the IronPython assemblies to do it. The following using statements provide everything needed to execute and manage most IronPython scripts.

[code]
using System;
using IronPython.Hosting;
using IronPython.Runtime;
using Microsoft.Scripting.Hosting;
[/code]

Understanding the Use of ScriptEngine

You have many options for working with IronPython scripts. This first example takes an approach that works fine for Visual Studio 2008 developers, as well as those using Visual Studio 2010. It doesn’t require anything fancy and it works reliably for most scripts. Ease and flexibility concerns aside, this isn’t the shortest technique for working with IronPython scripts. This is the Method1 approach to working with IronPython scripts — the technique that nearly everyone can use and it appears in Listing 15-2.

Listin g 15-2: Using the script engine to access the script

[code]
static void Main(string[] args)
{
// Create an engine to access IronPython.
ScriptEngine Eng = Python.CreateEngine();
// Describe where to load the script.
ScriptSource Source = Eng.CreateScriptSourceFromFile(“TestClass.py”);
// Obtain the default scope for executing the script.
ScriptScope Scope = Eng.CreateScope();
// Create an object for performing tasks with the script.
ObjectOperations Ops = Eng.CreateOperations();
// Create the class object.
Source.Execute(Scope);
// Obtain the class object.
Object CalcClass = Scope.GetVariable(“DoCalculations”);
// Create an instance of the class.
Object CalcObj = Ops.Invoke(CalcClass);
// Get the method you want to use from the class instance.
Object AddMe = Ops.GetMember(CalcObj, “DoAdd”);
// Perform the add.
Int32 Result = (Int32)Ops.Invoke(AddMe, 5, 10);
// Display the result.
Console.WriteLine(“5 + 10 = {0}“, Result);
// Pause after running the test.
Console.WriteLine(“rnPress any key when ready…”);
Console.ReadKey();
}
[/code]

Now that you have access to Eng, you can use it to perform various tasks. For example, you must tell Eng what scope to use when executing code, so the example creates a ScriptScope object, Scope. In order to perform tasks, you must also have an ObjectOperations object, Ops. The example uses the defaults provided for each of these objects. However, in a production application, you might decide to change some properties to make the application execute faster or with better security.

At this point, you can execute the script. The act of executing the script using Source.Execute() loads the script into memory and compiles it in a form that the static application can use. The Source.Execute() method associates Scope with the execution environment. At this point, the parameters for executing the script are set in stone — you can’t change them.

The script is in memory, but you can’t access any of its features just yet. The script contains a DoCalculations class that you access by calling Scope.GetVariable() to create CalcObj. The code gains access to the class by creating an instance of it, CalcObj, using Ops.Invoke(). At this point, CalcObj contains an instance of DoCalculations() in the IronPython module, but you can’t use it directly. Remember that you must marshal data between C# and IronPython. In addition, C# has to have a way to deal with the potential changes in the IronPython script.

This seems like a lot of work just to gain access to DoAdd, but you can finally use AddMe to perform the addition. A call to Ops.Invoke() with AddMe and the arguments you want to use performs all of the required marshaling for you. You must coerce the output to an Int32 (something that C# understands). Finally, the application outputs the result, as shown in Figure 15-3.

FThe example application calls the DoAdd() method and displays the result onscreen.
Figure 15-3: The example application calls the DoAdd() method and displays the result onscreen.

Using the dynamic Keyword

One of the new ways in which you can access IronPython in C# 4.0 is to use the dynamic keyword. This keyword makes it possible for you to cut out a lot of the code shown in Listing 15-2 to perform tasks with IronPython. It’s still not perfect, but you’ll do a lot less work. Listing 15-3 shows a short example that accesses the __test__() function found in Listing 15-1.

Listin g 15-3: Accessing IronPython using the dynamic keyword

[code]
static void Main(string[] args)
{
// Obtain the runtime.
var IPY = Python.CreateRuntime();
// Create a dynamic object containing the script.
dynamic TestPy = IPY.UseFile(“TestClass.py”);
// Execute the __test__() method.
TestPy.__test__();
}
[/code]

The next step is to load the script. The dynamic type, TestPy, contains all the features of the TestClass.py script after you load it using IPY.UseFile(). Figure 15-4 shows how TestPy appears after the script loads. Notice that the Locals window correctly identifies all the IronPython types in the file.  (Visual Basic.NET developers will have to wait for an update).

In this case, the example calls the __test__() function. This function outputs the same information shown in Figure 15-1.

Loading the script provides access to all of the features it contains.
Figure 15-4: Loading the script provides access to all of the features it contains.

Working with the App.CONFIG File

In some cases, you might want to configure your application using an App.CONFIG file. Using the App.CONFIG file tends to ensure that your application works better between development machines. In addition, using the App.CONFIG file can make it easier to work with DLR using older versions of Visual Studio. Most important of all, using the App.CONFIG file ensures that anyone working with the application uses the correct version of the DLLs so that any DLL differences aren’t a problem.

Your project won’t contain an App.CONFIG file at the outset. To add this file, right-click the project entry in Solution Explorer and choose Add ➪ New Item from the context menu. You see the Add New Item dialog box shown in Figure 15-5. Highlight the Application Configuration File entry as shown and click Add. Visual Studio automatically opens the file for you.

The App.CONFIG file contains entries that describe the Microsoft scripting configuration. In most cases, you begin by defining a <section> element, which describes a <microsoft.scripting> element. The <microsoft.scripting> element contains a list of languages you want to use in a <languages> element, as shown in Listing 15-4.

Listin g 15-4: Defining the App.CONFIG file content

[code]
<?xml version=”1.0” encoding=”utf-8” ?>
<configuration>
<configSections>
<section name=”microsoft.scripting”
type=”Microsoft.Scripting.Hosting.Configuration.Section,
Microsoft.Scripting, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35”
requirePermission=”false” />
</configSections>
<microsoft.scripting>
<languages>
<language names=”IronPython,Python,py”
extensions=”.py”
displayName=”IronPython 2.0 Beta”
type=”IronPython.Runtime.PythonContext,IronPython,
Version=2.6.10920.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35” />
</languages>
</microsoft.scripting>
</configuration>
[/code]

Use an App.CONFIG file to hold DLR configuration information.
Figure 15-5: Use an App.CONFIG file to hold DLR configuration information.

The <section> element includes attributes for name, type, and requirePermission. The type attribute should appear on one line, even though it appears on multiple lines in the book. This attribute describes the Microsoft.Scripting.DLL attributes. Especially important is the Version and PublicKeyToken entries.

The <microsoft.scripting> element contains a <languages> element at a minimum. Within the <languages> element you find individual <language> elements that are descriptions of the languages you want to use in your application.

For this example, you create a <language> element for IronPython that starts with a names attribute. It’s important to define all the names you plan to use to access the language — the example defines three of them. The extensions attribute describes the file extensions associated with the language, which is .py in this case. The displayName attribute simply tells how to display the language. Finally, the type attribute contains a description of the IronPython.DLL file. As with the type element for Microsoft.Scripting.DLL. Again, you need to exercise special care with the Version and PublicKeyToken entries.

Now that you have the App.CONFIG file created, it’s time to look at the application code. Listing 15-5 contains the source for this example.

Listin g 15-5: Using the App.CONFIG file in an application

[code]
static void Main(string[] args)
{
// Read the configuration information from App.CONFIG.
ScriptRuntimeSetup srs = ScriptRuntimeSetup.ReadConfiguration();
// Create a ScriptRuntime object from the configuration
// information.
ScriptRuntime runtime = new ScriptRuntime(srs);
// Create an engine to access IronPython.
ScriptEngine Eng = runtime.GetEngine(“Python”);
// Describe where to load the script.
ScriptSource Source = Eng.CreateScriptSourceFromFile(“TestClass.py”);
// Obtain the default scope for executing the script.
ScriptScope Scope = Eng.CreateScope();
// Create an object for performing tasks with the script.
ObjectOperations Ops = Eng.CreateOperations();
// Create the class object.
Source.Execute(Scope);
// Obtain the class object.
Object CalcClass = Scope.GetVariable(“DoCalculations”);
// Create an instance of the class.
Object CalcObj = Ops.Invoke(CalcClass);
// Get the method you want to use from the class instance.
Object AddMe = Ops.GetMember(CalcObj, “DoAdd”);
// Perform the add.
Int32 Result = (Int32)Ops.Invoke(AddMe, 5, 10);
// Display the result.
Console.WriteLine(“5 + 10 = {0}“, Result);
// Pause after running the test.
Console.WriteLine(“rnPress any key when ready…”);
Console.ReadKey();
}
[/code]

The biggest difference between this example and the one shown in Listing 15-2 is that you don’t create the script engine immediately. Rather, the code begins by reading the configuration from the App.CONFIG file using ScriptRuntimeSetup.ReadConfiguration(). This information appears in srs and is used to create a ScriptRuntime object, runtime.

At this point, the code finally creates the ScriptEngine, Eng, as in the previous example. However, instead of using Python.CreateEngine(), this example relies on the runtime.GetEngine() method. For this example, the result is the same, except that you’ve had better control over how the ScriptEngine is created, which is the entire point of the example — exercising control over the IronPython environment. The rest of the example works the same as the example shown in Listing 15-2. The output is the same, as shown in Figure 15-3.

Accessing the Module from Visual Basic.NET

You might get the idea from the lack of Visual Basic.NET examples online that Microsoft has somehow forgotten Visual Basic.NET when it comes to DLR. Surprise! Just because the examples are nowhere to be seen (send me an e‑mail at [email protected] if you find a stash of Visual Basic.NET examples somewhere) doesn’t mean that you can’t work with IronPython from Visual Basic. In fact, the requirements for working with Visual Basic.NET are much the same as those for working with C#, as shown in the following sections.

Adding the Required Visual Basic.NET References

Visual Basic requires the same DLL references as C# does to work with IronPython. Figure 15-2 shows the assemblies you should add to your application to make it work properly. In this case, you right-click the project entry and choose Add Reference from the context menu to display an Add Reference dialog box similar to the one shown in Figure 15-2. Select the Browse tab and add the IronPython assemblies shown in Figure 15-2 by Ctrl-clicking on each of the assembly entries. Click OK. Visual Basic will add the references, but you won’t see them in Solution Explorer unless you click Show All Files at the top of the Solution Explorer window.

As with C#, you need to add some Imports statements to your code to access the various IronPython assemblies with ease. Most applications will require the following Imports statements at a minimum.

[code]
Imports System
Imports IronPython.Hosting
Imports IronPython.Runtime
Imports Microsoft.Scripting.Hosting
[/code]

Creating the Visual Basic.NET Code

As with all the other examples, you shouldn’t let the IronPython example dictate what you do in your own applications. You can obtain full access to any IronPython script from Visual Basic.NET and fully use every feature it provides.

Accessing IronPython scripts from Visual Basic.NET is much the same as accessing them from C# using the ScriptEngine object. Listing 15-6 shows the code you need to access the IronPython script used for all the examples

Listin g 15-6: Accessing IronPython from Visual Basic.NET

[code]
Sub Main()
‘ Create an engine to access IronPython.
Dim Eng As ScriptEngine = Python.CreateEngine()
‘ Describe where to load the script.
Dim Source As ScriptSource = Eng.CreateScriptSourceFromFile(“TestClass.py”)
‘ Obtain the default scope for executing the script.
Dim Scope As ScriptScope = Eng.CreateScope()
‘ Create an object for performing tasks with the script.
Dim Ops As ObjectOperations = Eng.CreateOperations()
‘ Create the class object.
Source.Execute(Scope)
‘ Obtain the class object.
Dim CalcClass As Object = Scope.GetVariable(“DoCalculations”)
‘ Create an instance of the class.
Dim CalcObj As Object = Ops.Invoke(CalcClass)
‘ Get the method you want to use from the class instance.
Dim AddMe As Object = Ops.GetMember(CalcObj, “DoAdd”)
‘ Perform the add.
Dim Result As Int32 = Ops.Invoke(AddMe, 5, 10)
‘ Display the result.
Console.WriteLine(“5 + 10 = {0}“, Result)
‘ Pause after running the test.
Console.WriteLine(vbCrLf + “Press any key when ready…”)
Console.ReadKey()
End Sub
[/code]

As you can see from the listing, Visual Basic.NET code uses precisely the same process as C# does to access IronPython scripts. In fact, you should compare this listing to the content of Listing 15-2. The two examples are similar so that you can compare them. The output is also precisely the same. You’ll see the output shown in Figure 15-3 when you execute this example.

Developing Test Procedures for External Modules

Many developers are beginning to realize the benefits of extensive application testing. There are entire product categories devoted to the testing process now because testing is so important. Most, if not all, developer tools now include some idea of application testing with them. In short, you should have all the testing tools you need to test the static portion of your IronPython application.

Unfortunately, the testing tools might not work particularly well with the dynamic portion of the application. Creating a test that goes from the static portion of the application to the dynamic portion of the application is hard. (Consequently, you need to include a test harness with your dynamic code and perform thorough testing of the dynamic code before you use it with the static application. (When you think about a test harness, think about a horse, your application that has a harness added externally for testing purposes. You add the harness for testing and remove it for production work without modifying the application.) Listing 15-1 shows an example of how you might perform this task.

The test harness you create has to test everything, which is a daunting task to say the least. In addition, you need to expend extra effort to make the test harness error free — nothing would be worse than to chase an error through your code, only to find out that the error is in the test harness. At a minimum, your test harness should perform the following checks on your dynamic code:

  • Outputs with good inputs
  • Outputs with erroneous inputs
  • Exception handling within methods
  • Property value handling
  • Exceptions that occur on public members that would normally be private

Of course, you want to check every method and property of every class within the dynamic code. To ensure you actually test everything, make sure you create a checklist to use to verify your test harness. Because IronPython isn’t compiled, you’ll find that you must manually perform some checks to ensure the code works precisely as planned, but use as much automation as possible.

Debugging the External Module

Debugging isn’t hard, but it also isn’t as straightforward as you might think when working with IronPython. The debugger won’t take you directly to an error. You can’t test variables using the debugger from within the static language. In short, you have to poke and prod the external script to discover what ails it. Fortunately, you do have three tools at your disposal for discovering errors.

  • Exceptions
  • print Statements
  • An ErrorListener object

Let’s begin with the easiest of the three tools. The static language application won’t ignore outright errors in the script code. For example, you might have the following error in the script:

[code]
# Introduce an error.
print 1/0
[/code]

If your code has this error (and it really shouldn’t), you’ll see an exception dialog box like the one shown in Figure 15-6. Unfortunately, when you click View Detail, the content of the View Detail dialog box is nearly useless. The exception information won’t tell you where to find the error in your script. In fact, it may very well lead you on a wild goose chase that ends in frustration.

The static language application displays exceptions for your script.
Figure 15-6: The static language application displays exceptions for your script.

The name of the exception will provide clues as to where the error might exist, but you can’t confirm your suspicions without help. The only tool, besides vigorous script testing, is to include print statements such as these in your code.

[code]
# Display the values of First and Second.
print ‘Values in IronPython Script’
print ‘First = ‘, First
print ‘Second = ‘, Second
[/code]

When you run the script, you see the output shown in Figure 15-7. Most developers view print statements as a bit old school, but they do work if you use them correctly. Make sure you provide enough information to know where the script is failing to perform as expected. Even so, using print statements may feel a bit like wandering around in the dark, so you should place an emphasis on testing the script before you use it and after each change you make.

Using print statements may seem old school, but they work.
Figure 15-7: Using print statements may seem old school, but they work.

In some cases, you might make a small change to a script and it stops running completely — you might not see a script exception, just an indicator that something’s wrong because the application raises an unrelated exception. Syntax errors and other problems where the interpreter simply fails can cause the developer a lot of woe. For example, your application might have the following syntax error:

[code]
# Create a syntax error.
while True print ‘This is an error!’
[/code]

This code obviously won’t run. Because of the nature of the error, you might even pass it by while looking through your code. The answer to this problem is to create an ErrorListener class like the one shown in Listing 15-7.

Listin g 15-7: Create an ErrorListener to hear script semantic errors

[code]
class MyListener : ErrorListener
{
public override void ErrorReported(ScriptSource source,
string message,
SourceSpan span,
int errorCode,
Severity severity)
{
Console.WriteLine(“Script Error {0}: {1}“, errorCode, message);
Console.WriteLine(“Source: {0}“, source.GetCodeLine(span.Start.Line));
Console.WriteLine(“Severity: {0}“, severity.ToString());
}
}
[/code]

The ErrorListener contains just one method, ErrorReported(). This method can contain anything you need to diagnose errors. The example provides an adequate amount of information for most needs. However, you might decide to provide additional information based on the kind of script you’re using.

In order to use this approach, you must compile the script before you execute it. The compilation process must include the ErrorListener, as shown here.

[code]
// Compile the script.
Source.Compile(new MyListener());
[/code]

When you run the application now, you get some useful information about the syntax error, as shown in Figure 15-8.

The ErrorListener provides useful output on syntax errors.
Figure 15-8: The ErrorListener provides useful output on syntax errors.

 

Interacting with the DLR

The Dynamic Language Runtime (DLR) is a new feature of the .NET platform. Its intended purpose is to support dynamic languages, such as Python (through IronPython) and Ruby (through IronRuby). Without DLR, the .NET Framework can’t really run dynamic languages. In addition, DLR provides interoperability between dynamic languages, the .NET Framework, and static languages such as C# and Visual Basic.NET. Without DLR, dynamic and static languages can’t communicate. In order to meet these goals, DLR must provide basic functionality that marshals data and code calls between the dynamic and static environments. This functionality comes in a number of forms that are discussed in this chapter. You might be surprised to find that you’ve already used many of these features throughout the book. Here’s the list of features that DLR supports in order to accomplish its goals.

  • Hosting Application Programming Interfaces (APIs): In order to run dynamic language scripts, the host language must have access to the scripting engine. The Hosting APIs provide the support needed to host the dynamic language within the host environment through the scripting engine. This marshaling of code and data makes it possible to seamlessly integrate static and dynamic languages.
  • Extensions to Language Integrated Query (LINQ) ExpressionTree: Normally, a language would need to convert data, objects, and code into Microsoft Intermediate Language (MSIL) before it could translate anything into another language. Because all .NET languages eventually end up as MSIL, MSIL is the common language for all higher-level .NET languages. These extensions make it possible for language compilers to create higher-level constructs for communication purposes, rather than always relying on MSIL. The result is that the marshaling process takes less time and the application runs faster.
  • DynamicSite: This feature provides a call-site cache that dynamic languages use in place of making constant calls to other .NET languages. Because the call-site cache is already in a form that the dynamic language can use, the overall speed of the dynamic language application improves.
  • IDynamicObject: An interface used to interact with dynamic objects directly. If you create a class that implements IDynamicObject, DLR lets the class perform the required conversions, rather than rely on the built-in functionality. Essentially, you create an object that can have methods, properties, and events added dynamically during run time. You use IDynamicObject when you want to implement custom behaviors in your class.
  • ActionBinder: The ActionBinder is a utility that helps support .NET interoperability. The ActionBinder is language specific. It ensures that conversions of variable data, return values, and arguments all follow language-specific behaviors so that the host language sees the data in a form it understands.

These are the main tasks that DLR performs. Of course, it also provides other compiler utilities that you need to know about. The final section in this chapter provides an overview of these other features.

DLR is a constantly changing technology today, so you’ll want to keep up with the additions and changes to DLR. One of the better places to find general DLR resources online is at http://blogs.msdn.com/ironpython/ archive/2008/03/16/dlr-resources.aspx. This chapter also provides a number of specific resources you can use to discover more about DLR. The point is to keep track of what’s going on with this exciting technology and review your code as things change.

Obtaining DLR

It’s important to remember that IronPython relies on DLR to perform just about every task that IronPython executes. Therefore, you already have access to a certain level of DLR, even if you don’t install anything or do anything special. In fact, you’re using DLR in the background every time you use IronPython. However, you’re using DLR without really knowing it exists and without understanding what DLR itself can do for your application. So while you can use the direct approach to DLR, it can prove frustrating and less than friendly.

In order to truly understand DLR, you at least need documentation. Better yet, you can download the entire DLR package and begin to understand the true impact of this product. If nothing else, spend some time viewing the available components at http://www.codeplex.com/dlr. The following sections describe various methods of gaining access to DLR so you can use it to perform some custom tasks.

Using the Direct Method

The direct method is the easiest way to obtain the benefits of DLR, but it’s also the most limited. You simply add a reference to the IronPython.DLL file located in the Program FilesIronPython 2.6 folder of your hard drive. This technique works fine for embedding IronPython scripts in your C# or Visual Basic.NET application. In fact, you gain access to the following classes:

  • IronPython
  • IronPython.Compiler
  • IronPython.Compiler.Ast
  • IronPython.Hosting
  • IronPython.Modules
  • IronPython.Runtime
  • IronPython.Runtime.Binding
  • IronPython.Runtime.Exceptions
  • IronPython.Runtime.Operations
  • IronPython.Runtime.Types

For many developers, this is all the DLR support you need, especially if your application only requires cross-language support through the Hosting APIs. (You’ll still want to download the documentation that’s available on the main DLR Web site — the section “Downloading the Documentation” later in this chapter explains how to perform this task.) The following steps describe how to add the required reference to gain access to these classes.

  1. Create the .NET project.
  2. Right-click References in Solution Explorer and choose Add Reference from the context menu. You see the Add Reference dialog box.
  3. Select the Browse tab and locate the IronPython.DLL file, as shown in Figure 14-1.
  4. Click OK. Visual Studio adds the required reference to your project.
Add the IronPython.DLL file to your project.
Figure 14-1: Add the IronPython.DLL file to your project.

You make use of IronPython.DLL as you would any other .NET assembly. Simply add the required use or Imports statement to your code. The examples throughout the book tell you about these requirements for the individual example.

Downloading the Full DLR

If you really want to experience DLR, you need the complete package. The full DLR consists of a number of components and even the source code weighs in at a hefty 10.5 MB.

Before you begin the download, check out the release notes at http://dlr.codeplex.com/ wikipage?title=0.92_release_notes for additional information about DLR. For example, you might decide to get an IronPython- or IronRuby-specific download. The full release includes both language products (which can be helpful, even if you use only one of them).

You obtain the full DLR from http://dlr.codeplex.com/Release/ProjectReleases .aspx?ReleaseId=34834. When you click the DLR-0.92-Src.zip link, you see a licensing dialog box. Click I Agree to begin the download process.

After the download completes, extract the resulting DLR-0.92-Src.ZIP file into its own folder. The resulting Codeplex-DLR-0.92 folder contains the following items.

  • License.HTML and License.RTF: You can read the same licensing information in two different formats. Use whichever form works best for you.
  • Docs: A folder containing the complete documentation for DLR. The best place to begin is the DLR-Overview.DOC file.
  • Samples: A folder containing a number of sample applications that demonstrate DLR features. There’s only one IronPython sample in the whole batch — you’ll find it in the Codeplex-DLR-0.92SamplesSilverlightAppPythonpython folder.
  • Source: A folder that contains the complete DLR source code that you need to compile in order to use DLR to create applications. This folder should be your first stop after you read the DLR-Overview.DOC file.

Building the Full DLR

Before you can use DLR, you must build it. The previous section explains how to download a copy of the DLR source. The following sections describe three methods you can use to build DLR. For most developers, the easiest and fastest method is the command line build. However, if you want to review the code before you use it, you might want to load the solution in Visual Studio and take a peek.

Performing a Command Line Build

The command line build option requires that you use the Visual Studio command line, not a standard command line (which doesn’t contain a path to the utilities you need). The following steps describe how to perform the command line build:

  1. Choose Start ➪ Programs ➪ Microsoft Visual Studio 2008 ➪ Visual Studio Tools ➪ Visual Studio 2008 Command Prompt or Start ➪ Programs ➪ Microsoft Visual Studio 2010 ➪ ➤ Visual Studio Tools ➪ Visual Studio Command Prompt (2010). You’ll see a command prompt.
  2. Type CD Codeplex-DLR-0.92Src and press Enter. This command places you in the DLR source code directory.
  3. Type MSBuild Codeplex-DLR.SLN (when using Visual Studio 2008) or MSBuild Codeplex-DLR-Dev10.SLN (when using Visual Studio 2010) and press Enter. By default, you get a debug build. Use the /p:Configuration=Release command line switch (as in MSBuild Codeplex-DLR.SLN /p:Configuration=Release or MSBuild Codeplex-DLR-Dev10.SLN /p:Configuration=Release) to obtain a release build. You see a lot of text appear onscreen as MSBuild creates the DLR DLLs for you. Some of the text will appear unreadable (Microsoft uses some odd color combinations). When the build process is complete, you should see 0 Error(s) as the output, along with a build time, as shown in Figure 14-2. (If you don’t see a 0 error output, you should probably download the files again because there is probably an error in the files you downloaded.)
The build process should show 0 Error(s) as the output message.
Figure 14-2: The build process should show 0 Error(s) as the output message.

Don’t look for the output in the source code folders. The output from the build process appears in the Codeplex-DLR-0.92Bin40 folder when working with Visual Studio 2010, no matter which technique you use to build DLR. Visual Studio 2008 developers will find their output in the Codeplex-DLR-0.92 BinDebug or Codeplex-DLR-0.92BinRelease folders, depending on the kind of build created. Visual Studio 2008 developers will also find a separate Codeplex-DLR-0.92BinSilverlight Debug or Codeplex-DLR-0.92Bin Silverlight Release folder for Silverlight use.

Performing a Visual Studio 2008 Build

Some developers will want to perform a build from within Visual Studio 2008. To perform this task, simply double-click the Codeplex-DLR.SLN icon in the Codeplex-DLR-0.92Src folder. Choose Build ➪ Build Solution or press Ctrl+Shift+B. You’ll see a series of messages in the Output window. When the process is complete, you should see, “Build: 23 succeeded or up-to-date, 0 failed, 1 skipped” as the output.

You must select each of the options in the Solution Configurations combo box in turn and perform a build to create a complete setup. Otherwise, you’ll end up with just the Release build or just the Debug build. If you need Silverlight or FxCop support, you must also create these builds individually.

Don’t worry if you see a number of messages stating

[code]
Project file contains ToolsVersion=”4.0”, which is not supported by this
version of MSBuild. Treating the project as if it had ToolsVersion=”3.5”.
[/code]

because this is normal when using Visual Studio 2008. You’ll also see a number of warning messages (a total of 59 for the current DLR build) in the Errors window, which you can ignore when using the current release.

Performing a Visual Studio 2010 Build

A release version of DLR will build better if you have a copy of Visual Studio 2010 on your system. To perform this task, simply double-click the Codeplex-DLR-Dev10.SLN icon in the Codeplex-DLR-0.92Src folder. Set the Solution Configurations option to Release or Debug as needed (there aren’t any options to build Silverlight or FxCop output). Choose Build ➪ Build Solution or press Ctrl+Shift+B. You’ll see a series of messages in the Output window. When the process is complete, you should see, “Build: 15 succeeded or up-to-date, 0 failed, 2 skipped” as the output. The Warnings tab of the Error List window should show 24 warnings.

Downloading the Documentation

The download you performed earlier provides code and documentation, but you might find that the documentation is outdated. As with everything else about DLR, the documentation is in a constant state of flux. If you want to use DLR directly, then you need the documentation found at http://dlr.codeplex.com/wikipage?title=Docs and specs&referringTitle=Home. Unfortunately, you have to download each document separately

Reporting Bugs and Other Issues

At some point, you’ll run into something that doesn’t work as expected. Of course, this problem even occurs with production code, but you’ll definitely run into problems when using the current release of DLR. In this case, check out the listing of issues at http://www.codeplex.com/dlr/WorkItem/List.aspx. If you don’t find an issue entry that matches the problem you’re experiencing, make sure you report the bug online so it gets fixed. Of course, reporting applies equally to code and documentation. Documentation errors are often harder to find and fix than coding errors — at least where developers are concerned — because it’s easier to see the coding error in many cases.

Working with Hosting APIs

In fact, you may have even wondered whether it’s possible to use IronPython as a scripting language for your next application. Fortunately, you can use IronPython as the scripting language for your next application by relying on the Hosting APIs. It turns out that a lot of people have considered IronPython an optimal language for the task. The following sections consider a number of Hosting API questions, such as how you can use it in an actual application, what the host application needs in order to use the Hosting APIs, and what you’d need to do to embed IronPython as a scripting language in an application.

Using the Hosting APIs

The DLR specification lists a number of hosting scenarios, such as operating on dynamic objects you create within C# or Visual Basic.NET applications. (See the section “Working with IDynamicObject” later in this chapter for details on dynamic objects in C# and Visual Basic.NET.) It’s also possible to use the Hosting APIs to create a scripting environment within Silverlight or other types of Web applications.

Whatever sort of host environment you create, you can use it to execute code snippets or entire applications found in files. The script run time can appear locally or within a remote application so you can use this functionality to create agent applications or scripting that relies on server support. The Hosting APIs make it possible to choose a specific scripting engine to execute the code or to let DLR choose the most appropriate scripting engine for the task. This second option might seem foolhardy, but it can actually let your code use the most recent scripting engine, even if that engine wasn’t available at the time you wrote the host environment code.

Chaos could result if you couldn’t control the extent (range) of the script execution in some way. For example, two developers could create variables with the same name in different areas of the application. The Hosting APIs make it possible to add scope to script execution. The scope acts much like a namespace does when writing code. Just as a namespace eliminates naming conflicts in assemblies, scoping eliminates them in the scripting environment. Executing the code within a scope also provides it with an execution context (controlled using a ScriptScope). Scopes are either public or private, with private scopes providing a measure of protection for the scripting environment. A script can also import scopes for use within the environment or require the host to support a certain scope to execute the script.

The Hosting APIs also provide support for other functionality. For example, you can employ reflection to obtain information about object members, obtain parameter information, and view documentation. You can also control how the scripting engine resolves file content when dynamic languages import code files.

Understanding the Hosting APIs Usage Levels

The DLR documentation specifies that most developers will use the Hosting APIs at one of three levels that are dictated by application requirements. Here are the three basic levels.

  • Basic code: The basic code level (Level 1 in the documentation) relies on a few basic types to execute code within scopes. The code can interact with variable bindings within those scopes.
  • Advanced code execution: The next level (Level 2 in the documentation) adds intermediate types that provide additional control over how code executes. In addition, this level supports using compiled code in various scopes and permits use of various code sources.
  • Support overrides: The final level (Level 3 in the documentation) provides methods to override how DLR resolves filenames. The application can also use custom source content readers, reflect over objects for design-time tool support, provide late bound variable values from the host, and use remote ScriptRuntime objects.

The concept of a ScriptRuntime object is central to working with the Hosting APIs. A host always begins a session by creating the ScriptRuntime object and then using that object to perform tasks. You can create a ScriptRuntime object using several methods. Of course, the easiest method is to use the standard constructor, which requires a ScriptRuntimeSetup object as input. It’s also possible to create a ScriptRuntime object using these methods

  • ScriptRuntime.CreateFromConfiguration(): A factory method that lets you use a preconfigured scope to create the ScriptRuntime object. In fact, this factor method is just short for new ScriptRuntime(ScriptRuntimeSetup.ReadConfiguration()).
  • ScriptRuntime.CreateRemote(): A factory method that helps you to create a ScriptRuntime object in another domain. The code must meet strict requirements to perform remote execution. See Section 4.1.3, “Create* Methods,” in the Hosting APIs specification for details.

At its name implies, a ScriptRuntimeSetup object gives a host full control over the ScriptRuntime object configuration. The ScriptRuntimeSetup object contains settings for debug mode, private execution, the host type, host arguments, and other setup features. Simply creating a ScriptRuntimeSetup object sets the defaults for executing a script. Once you use a ScriptRuntimeSetup object to create a ScriptRuntime object, you can’t change the settings — doing so will raise an exception.

The Hosting APIs actually support a number of objects that you use to create a scripting environment, load the code you want to execute, and control the execution process. The figure at http:// www.flickr.com/photos/john_lam/2220796647/ provides an overview of these objects and how you normally use them within the hosting session.

It’s important to isolate code during execution. The Hosting APIs provide three levels of isolation.

  • AppDomain: The highest isolation level, which affects the entire application. The AppDomain lets you execute code at different trust levels, and load and unload code as needed.
  • ScriptRuntime: Every AppDomain can have multiple ScriptRuntimes within it. Each ScriptRuntime object can have different name bindings, use different .NET assemblies, have different settings (one can be in debug mode, while another might not), and provide other settings and options support.
  • ScriptScope: Every ScriptRuntime can contain multiple ScriptScopes. A ScriptScope can provide variable binding isolation. In addition, you can use a ScriptScope to give executable code specific permissions.

Now that you have a better idea of how the pieces fit together, it’s important to consider which pieces you use to embed scripting support within an application. Generally, if you want basic code (Level 1) support, all you need are the objects shown in green at http://www.flickr.com/photos/ john_lam/2220796647/. In fact, if you want to use the default ScriptScope settings, all you really need to do is create the ScriptRuntime and then use the default ScriptScope.

Considering the Host Application

A host has to meet specific requirements before it can run IronPython as a scripting language. Chapter 15 discusses more of the details for C# and Visual Basic.NET developers. You’ll find that C# and Visual Basic.NET provide everything you need. However, it’s interesting to see just what the requirements are, especially if you’re using an older version of these languages. Section 3 of the DLR-Spec-Hosting.DOC file found in the Codeplex-DLR-0.92Docs folder contains complete information about the hosting requirements. Section 3.3 (and associated subsections) are especially important for most developers to read if they plan to use the Hosting APIs for anything special.

Embedding IronPython as a Scripting Language

Imagine that you’ve created a custom editor in your application where users can write IronPython scripts. They then save the script to disk (or you could read it from memory), and then they assign the script to a button or menu in your application. When the user selects the button or menu, your application executes the script. Creating this scenario isn’t as hard as you might imagine. DLR comes with most of the functionality you need built in.

Of course, you need a test script to start. Listing 14-1 shows the test script for this example. The example is purposely simple so that the example doesn’t become more focused on the IronPython code than the code that executes it. However, you could easily use any script you want as long as it’s a legitimate IronPython script.

Listin g 14-1: A simple IronPython script to execute

[code]
# A simple function call.
def mult(a, b):
return a * b
# Create a variable to hold the output.
Output = mult(5,10)
# Display the output.
print(‘5 * 10 =’),
print(Output)
# Pause after the debug session.
raw_input(‘nPress any key to continue…’)
[/code]

In this case, the example has a simple function, mult(), that multiplies two numbers together. The __main__() part of the script multiplies two numbers and displays the result using the print() function. In short, the script isn’t very complicated.

Now that you have a script, you need to create an application to execute it. The example is a simple console application. In order to create the IronPython ScriptRuntime object, you need access to some of the IronPython assemblies. Right-click References in Solution Explorer and choose Add Reference from the context menu. You see the Add Reference dialog box shown in Figure 14-3. Ctrl+click each of the entries shown in Figure 14-3, then click OK to add them to your project.

Add the required references from your IronPython setup.
Figure 14-3: Add the required references from your IronPython setup.

The example also requires that you add using statements for a number of the assemblies. Here are the using statements that you must add for this example.

[code]
using System;
using IronPython.Hosting;
using IronPython.Runtime;
using Microsoft.Scripting.Hosting;
[/code]

Now that the console project is set up, you can begin coding it. This example is very simple, but it actually works. You can execute an IronPython script using this technique. Of course, you can’t interact with it much. Chapter 15 provides more detailed examples, but this example is a good starting place. Listing 14-2 shows the minimum code you need to execute an IronPython script and display the result of executing it onscreen.

Listin g 14-2: Executing the IronPython script

[code]
static void Main(string[] args)
{
// Create an IronPython ScriptRuntime.
ScriptRuntime Runtime = IronPython.Hosting.Python.CreateRuntime();
// Execute the script file and return scope information about
// the task.
ScriptScope Scope = Runtime.ExecuteFile(“Test.py”);
// Display the name of the file executed.
Console.WriteLine(“rnExecuted {0}“,
Scope.GetVariable<string>(“__name__“));
// Keep the output visible.
Console.WriteLine(“rnPress any key…”);
Console.ReadLine();
}
[/code]

The code begins by creating the ScriptRuntime object, Runtime. Notice that you create this object by directly accessing the IronPython assemblies, rather than the DLR assemblies. There are many ways to accomplish this task, but using the technique shown is the simplest. The Runtime object contains default settings for everything. For example, this ScriptRuntime doesn’t provide debugging capability. Consequently, this technique is only useful when you have a debugged script to work with and may not do everything needed in a production environment where you let users execute their own scripts as part of an application.

The Runtime.ExecuteFile() method is just one of several ways to execute a script. You use it when a script appears in a file on disk, as is the case for this example. When you call the Runtime .ExecuteFile() method, your application actually calls on the IronPython interpreter to execute the code. The output from the script appears in Figure 14-4. As you can see, the code executes as you expect without any interference from the host. In fact, you can’t even tell that the application has a host.

The script output appears as you might expect.
Figure 14-4: The script output appears as you might expect.

When the Runtime.ExecuteFile() method call returns, the C# application that executed the script receives a ScriptScope object that it can use to interact with the application in various ways. This ScriptScope object, like the ScriptRuntime object, contains all the usual defaults. It’s a good idea to examine both Runtime and Scope in the debugger to see what these objects contain because you’ll find useful information in both.

The script is running in a host application. In fact, they share the same console window. To show how this works, the example writes output to the console window. It retrieves the __name__ property from Scope and displays it onscreen with the message, as shown in Figure 14-5. The point of this example is that the IronPython script truly is hosted and not running on its own. The technique shown here lets you perform simple interactions between C# or Visual Basic.NET and IronPython.

The output shows that the host and the IronPython environment share the same console.
Figure 14-5: The output shows that the host and the IronPython environment share the same console.

Understanding the Extensions to LINQ Expression Tree

Part of the premise behind DLR is that every .NET language eventually ends up in Microsoft Intermediate Language (MSIL) form. Whether you use C# or Visual Basic.NET, or even managed C++, the output from the compiler is MSIL. That’s how the various languages can get along. They rely on MSIL as an intermediary so that managed languages can work together.

The problem with compiling everything to MSIL is that MSIL doesn’t necessarily perform tasks quickly or easily when working with dynamic languages such as IronPython. It would be far easier if there were a mechanism for translating the code directly into something that C# or Visual Basic .NET could use. That’s where the LINQ Expression Tree (ET) comes into play. A LINQ ET can represent IronPython or other code (such as JavaScript) in a tree form that DLR can then translate into something that C# or Visual Basic.NET can understand. The result is a DLR tree that presents the code in an easily analyzable and mutable form. The example at http://blogs.msdn.com/hugunin/ archive/2007/05/15/dlr-trees-part-1.aspx explains how DRL trees work graphically. In this case, the author explains how a DLR tree can represent a JavaScript application — the same technique also applies to IronPython.

The LINQ ET originally appeared in the .NET Framework 3.5. In its original form, Microsoft used the LINQ ET to model LINQ expressions written in C# and Visual Basic.NET. In the .NET Framework 4.0, Microsoft added extensions for a number of reasons. For the purposes of this book, the most important reason to extend LINQ ETs is to accommodate the DLR semantics used to translate IronPython code into something that C# and Visual Basic.NET can understand.

DLR trees work in the background. It’s helpful to know they exist, but you generally won’t worry about them when working with IronPython so this section is short. However, let’s say you want to create a scripting language for your application that isn’t as complex as IronPython. Perhaps you want to implement an editor and everything that goes with it in your application. In this case, you may very well want to work with DLR trees. The examples found at http://weblogs.asp.net/ podwysocki/archive/2008/02/08/adventures-in-compilers-building-on-the-dlr.aspx show what you need to do to create your own language compiler. Once you have a compiler like this built, you could execute the code using a technique similar to the one shown in Listing 14-2.

It’s important to consider one word of warning, however, when working with the current version of DLR trees. As you scan through the specification, you’ll find that the authors have left behind copious notes about issues that aren’t resolved now or features that were left out of the current implementation due to a lack of time. The result is conversations such as the one at http://stackoverflow.com/ questions/250377/are-linq-expression-trees-turing-complete. If you look at section 2.4.1 of the specification, you find that a higher-level looping mechanism was indeed cut, but Microsoft is aware of the problem and plans to implement the feature in the future. In short, DLR trees have limits that you need to consider before implementing them in your application.

Considering DynamicSite

When working with a static language such as C# or Visual Basic.NET, the compiler knows what to emit in the form of MSIL based on the code the developer provides. However, dynamic code isn’t static — it can change based on any of a number of factors. One problem with dynamic languages is that DLR doesn’t always know what to emit during compile time because the real time event hasn’t occurred yet. Of course, the static language still needs some code in place because static languages need to know what to do at compile time. This seeming conundrum is handled by invoking a DynamicSite object. Using a DynamicSite object means that the static language knows what to call at compile time and DLR can fill the DynamicSite object with executable code during run time.

As with many parts of DLR, the action takes place behind the scenes — you don’t even know it occurs. However, it’s useful to know what happens so you at least know what to suspect when an error occurs. The act of invoking the DynamicSite method creates an operation to perform and a delegate. The delegate contains caching logic that is updated every time the arguments change. In short, as the dynamic language changes, DLR generates events that change the content of the cache as well.

At the center of working with DynamicSite is the UpdateBindingAndInvoke() method. The first time that application code calls the DynamicSite object, the UpdateBindingAndInvoke() method queries the arguments for the specified code. For example, the code might be something simple such as x + y, so the query would request the types of x and y. At this point, UpdateBindingAndInvoke() generates a delegate that contains the implementation of the code.

The next time the application invokes the DynamicSite object, the delegate checks the arguments in the call against those in the cache. If the argument types match, then the delegate simply uses the current implementation of the code. However, if the arguments are different, then the delegate calls UpdateBindingAndInvoke(), which creates a new delegate that contains a definition of the new code with the updated arguments. The new delegate contains checks for both sets of argument types and calls the appropriate implementation based on the arguments it receives. Of course, if none of the argument sets match the call, then the process starts over again with a call to UpdateBindingAndInvoke().

Working with IDynamicObject

This section discusses the IDynamicObject interface provided as part of DLR, which doesn’t affect IronPython directly, but could affect how you use other languages to interact with IronPython. You can easily skip this section and leave it for later reading if you plan to work exclusively with IronPython for the time being. This is a very short discussion of the topic that is meant to fill in the information you have about DLR and its use with IronPython.

As mentioned throughout the book, C# and Visual Basic.NET are both static languages. Microsoft doesn’t appear to have any desire to change this situation in upcoming versions of either language. Consequently, you can’t create dynamic types using C# or Visual Basic. There isn’t any technique for defining missing methods or dynamic classes using either language. However, you can consume dynamic types defined using a new interface, IDynamicObject.

The IDynamicObject interface tells DLR that the class knows how to dispatch operations on itself. In some respects, IDynamicInterface is a kind of managed form of the IQueryable interface that C++ developers use when creating COM objects. The concept isn’t new, but the implementation of it in the .NET environment is new.

There are many levels of complexity that you can build into your dynamic implementation. The example in this section is a very simple shell that you can build on when creating a fullfledged application. It’s designed to show a common implementation that you might use in an application. You can see another simple example at http://blogs.msdn.com/csharpfaq/ archive/2009/10/19/dynamic-in-c-4-0-creating-wrappers-with-dynamicobject.aspx.

The starting point for this example is a class that implements DynamicObject. In order to create such a class, you need to include the following using statements:

[code]
using System;
using System.Dynamic;
[/code]

The class is called ADynamicObject and appears in Listing 14-3.

Listin g 14-3: Creating a class to handle dynamic objects

[code]
// Any dynamic object you create must implement IDynamicObject.
public class ADynamicObject : DynamicObject
{
// Calls a method provided with the dynamic object.
public override bool TryInvokeMember(InvokeMemberBinder binder,
object[] args, out object result)
{
Console.WriteLine(“InvokeMember of method {0}.”, binder.Name);
if (args.Length > 0)
{
Console.WriteLine(“tMethod call has {0} arguments.”, args.Length);
for (int i = 0; i < args.Length; i++)
Console.WriteLine(“ttArgument {0} is {1}.”, i, args[i]);
}
result = binder.Name;
return true;
}
// Gets the property value.
public override bool TryGetMember(GetMemberBinder binder,
out object result)
{
Console.WriteLine(“GetMember of property {0}.”, binder.Name);
result = binder.Name;
return true;
}
// Sets the property value.
public override bool TrySetMember(SetMemberBinder binder, object value)
Console.WriteLine(“SetMember of property {0} to {1}.”,
binder.Name, value);
return true;
}
}
[/code]

In this case, the code provides the ability to call methods, get property values, and set property values. Amazingly, DLR automatically calls the correct method without any hints from you.

Notice that each of the methods uses a different binder class: InvokeMemberBinder, GetMemberBinder, or SetMemberBinder as needed. The binder provides you with information about the member of interest. In most cases, you use the member name to locate the member within the dynamic object. In this case, the code simply displays the member name onscreen so you can see that the code called the correct member.

Two of these methods, TryInvokeMember() and TryGetMember(), return something to the caller. It’s important to remember that the data is marshaled, so you must use the out keyword for the argument that returns a value or the application will complain later (the compiler may very well accept the error without comment). In both cases, the code simply returns the binder.Name value. If you were building this dynamic object class for an application, you’d use the binder.Name value to access the actual property or method.

When invoking a method, the TryInvokeMember() method receives an array of arguments to use with the method call. The code shows how you detect the presence of arguments and then displays them onscreen for this example. In an actual application, you’d need to compare the arguments provided by the caller against those required by the method to ensure the caller has supplied enough arguments of the right type.

All three methods return true. If the code were to return false instead, you’d see a RuntimeBinderException in the caller code. This exception tells the caller that the requested method or property doesn’t exist.

When a C# application desires to create a dynamic object, it simply creates an instance of the dynamic class. The instance can create properties, methods, or other constructs as needed. Listing 14-4 shows an example of how a test application might appear.

Listin g 14-4: Using the ADynamicObject class

[code]
class Test
{
static void Main()
{
// Create a new dynamic object.
dynamic DynObject = new ADynamicObject();
// Set a property to a specific value.
Console.WriteLine(“Setting a Property to a Value”);
DynObject.AProp = 5;
// Use one property to set another property.
// You would see a property get, followed by a property set.
Console.WriteLine(“rnSetting a Property to another Property”);
DynObject.Prop1 = DynObject.AProp;
// Call a method and set its output to a property.
// You would see a method call, followed by a property set.
Console.WriteLine(“rnSetting a Property to a Method Output”);
DynObject.Prop2 = DynObject.AMethod();
// Call a method with a property argument and set a new property.
// You would see a property get, a method call, and finally a
// property set.
Console.WriteLine(“rnSetting a Property to Method Output with Args”);
DynObject.Prop3 = DynObject.AMethod(DynObject.AProp);
// Wait to see the results.
Console.WriteLine(“rnPress any key when ready…”);
Console.ReadLine();
}
}
[/code]

Notice that the code begins by creating a new dynamic object using the dynamic keyword. At this point, you can begin adding properties and methods to the resulting DynObject. Properties can receive values directly, from other properties, or from methods. Methods can use arguments to change their output. Figure 14-6 shows the output from this example. The path that the code takes through the various objects helps you understand how dynamic objects work.

The DynamicObject class actually provides support for a number of members. You can use these members to provide a complete dynamic implementation for your application. Here’s a list of the DynamicObject members you can override.

  • GetDynamicMemberNames()
  • GetMetaObject()
  • TryBinaryOperation()
  • TryConvert()
  • TryDeleteIndex()
  • TryDeleteMember()
  • TryGetIndex()
  • TryGetMember()
  • TryInvoke()
  • TryInvokeMember()
  • TrySetIndex()
  • TrySetMember()
  • TryUnaryOperation()
The output shows the process used to work with dynamic objects.
Figure 14-6: The output shows the process used to work with dynamic objects.

The point of all this is that you can implement a kind of dynamic object strategy for static languages, but it’s cumbersome compared to IronPython. You might use this approach when you need to provide a dynamic strategy for something small within C# or Visual Basic. This technique is also useful for understanding how IronPython works, at a very basic level. IronPython is far more robust than the code shown in this example, but the theory is the same.

Understanding the ActionBinder

DLR makes it possible to invoke dynamic code from within a static environment using a DynamicSite object. The actual process for creating the method invocation call is to create an Abstract Syntax Tree (AST). The AST has functions assigned to it using an Assign() method. When DLR wants to assign a new function to AST, it supplies a function name and provides a calling syntax using the Call() method. The Call() method accepts four arguments.

  • An object used to hold the function. Normally, the code calls the Create() method of the host class using GetMethod(“Create”).
  • A constant containing the name of the function as it appears within the host object.
  • The array of arguments supplied to the function.
  • A delegate instance used to invoke the code later. It’s this argument that you consider when working with an ActionBinder.

At this point, you have an object that holds the parameters of the function call, as well as a delegate used to execute the function. The problem now is one of determining how to call the function. After all, the rest of your code knows nothing about the delegate if you create it during run time, as is the case when working with dynamic languages. If none of the code knows about the delegate, there must be some way to call it other than directly.

To make rules work, your code has to include a GetRule() method that returns a StandardRule object. Inside GetRule() is a switch that selects an action based on the kind of action that DLR requests, such as a call (DynamicActionKind.Call). When DLR makes this request, the code creates a StandardRule object that contains an ActionBinder. The ActionBinder determines what kind of action the call performs. For example, you might decide that the ActionBinder should be LanguageContext.Binder, which defines a language context for the function. The language context is a definition of the language’s properties, such as its name, identifier, version, and specialized features. (You can learn more about how a language context works at http://www.dotnetguru .org/us/dlrus/DLR2.htm.) The code then calls SetCallRule() with the StandardRule object, the ActionBinder, and a list of arguments for the function.

Now, here’s the important consideration for this section. The ActionBinder is actually part of the language design. If you wanted to create a new language, then part of the design process is to design an ActionBinder for it. The ActionBinder performs an immense amount of work. For example, a call to ActionBinder.ConvertExpression() provides conversion information about the data types that the language supports. Of course, IronPython already performs this task for you, but it’s important to know how things work under the hood in case you encounter problems.

Understanding the Other DLR Features

DLR is a moving target at the time of this writing. The latest release, 0.92, isn’t even considered production code as of yet. Consequently, you might find that the version of DLR that you use has features not described in this chapter because they weren’t available at the time of this writing.

An ExpandoObject is a dynamic property bag. Essentially, you fill it with data you want to move from one language to another. It works just like any other property bag you’ve used in the past. Because the ExpandoObject class implements IDynamicMetaObjectProvider, you can use it with dynamic languages such as IronPython. You use this object when moving data from C# or Visual Basic.NET to IronPython.

Developing ASP.NET Applications

Considering IronPyth on Web Application Compatibility

IronPython (and Python for that matter) have huge potential for making Web sites significantly easier to build and manage. In fact, there are a lot of options for using Python (and IronPython) to perform Web development tasks that you can see at http://wiki.python.org/ moin/WebProgramming and http://www.fredshack.com/docs/pythonweb.html. The problem with IronPython is that it can’t use the C/C++ libraries used by Python, which means that some of these solutions won’t work. For example, it’s unlikely that you could use IronPython in a Common Gateway Interface (CGI) application because CGI relies on modules written in C/ C++. Consequently, the first rule for Web development with IronPython is to make sure that IronPython can actually interact with the desired modules.

Unfortunately, IronPython isn’t integrated into Visual Studio. Because Visual Studio relies on some level of integration to provide full Web development support, you’ll almost certainly find that some projects are out of reach. It’s not that you can’t create them using IronPython; it’s that the process would be so horribly time consuming and error prone that using another language would be a better idea. You do have full .NET capability when working with IronPython, as demonstrated by the example in this chapter. All you really need is to add Dynamic Language Runtime (DLR) support to the picture and things will work out just fine.

The picture isn’t all gloom. IronPython actually proves to be a good development language for some tasks. As you progress through this chapter, you’ll find that IronPython actually makes code behind tasks exceptionally easy. It’s conceivable that you can build and test simple Web projects using IronPython considerably faster than using another language such as C# or Visual Basic.NET.

It’s important to remember that you don’t have to work with IronPython alone. Your Web application can include other technologies, such as Silverlight. You can also rely on other languages, such as C# or Visual Basic.NET, to fill in the gaps in IronPython coverage. Having another language at your disposal is all about flexibility, and IronPython is a great add-on language for any Web application project.

Obtaining ASP.NET Dynamic Language Support

Microsoft built ASP.NET with extensibility in mind, but the native support tends to focus more on static languages such as Visual Basic.NET and C#, rather than dynamic languages such as IronPython. The Dynamic Language Runtime (DLR) is an add-on for ASP.NET that makes it possible to use languages, such as IronPython, that make typing decisions at run time, rather than compile time. You must download and install this support before you can use IronPython to create a Web application. The following sections describe DLR in more detail.

DLR Limitations

The DLR is currently a work in progress. The overall feel is of an alpha product that shows promise, but still has more than a few warts. In addition, the product currently lacks these features (in order of their importance to you as a developer):

  • IntelliSense: Most developers depend on IntelliSense to provide clues as to what works and what doesn’t — in essence, what to write next. Without IntelliSense support, most developers find themselves peaking at the documentation and spending hours being frustrated with the development environment. Because a default relies so heavily on IntelliSense during the entire development process, its lack is keenly felt. Let’s hope that Microsoft will choose to add this feature sooner rather than later.
  • Limited designer support: If you worked through the examples in Chapter 8, you probably have a good idea of why designer support is so important. Sure, you can create a perfectly usable interface without a designer, but doing so becomes time consuming and many developers give up before they get their user interface completely right. The better the designer support, the faster you can work.
  • Project templates: The lack of templates isn’t anything new. You’ve created examples throughout this book without them, so not having them now isn’t that big a deal. However, it will be nice to have the convenience of project templates when Microsoft creates them.
  • ASP.NET Model-View-Controller (MVC) pattern: MVC is a development pattern that Microsoft is pushing very hard because it provides better control over the development process. You can learn more about ASP.NET MVC at http://www.asp.net/mvc/. Microsoft eventually plans to add MVC to DLR by extending MVC for IronRuby (see http:// github.com/jschementi/ironrubymvc for additional details).
  • Language Services Support: A new language feature that Microsoft plans to provide sometime in the future. Details about this feature are unavailable as of this writing, but it’s likely that Language Services Support will somehow make DLR more flexible and able to support a myriad of languages.

If you read the information at http://www.asp.net/DynamicLanguages/ carefully, you notice that it contains a wealth of caveats. The DLR is essentially an alpha version of a product that may not even appear as part of ASP .NET. Consequently, you need to use DLR to see what’s possible, rather than as a production tool for needs you have today. Anything you create using DLR today is likely to require updates and changes tomorrow (assuming you can use DLR at all). The lack of solid DLR commitment by Microsoft is one reason this chapter provides an overview of ASP.NET application development, rather than in-depth information.

Getting DLR

Before you can use DLR, you must download and install it. The files you need for the installation appear at http://aspnet.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=17613. Download both the documentation and binaries files so that you have a complete setup. The documentation file is very small, so you might not think you even received the download at first.

The files are simple ZIP files, so you can extract them to your root directory. The examples in this chapter use a source folder of C:ironpython-2.6-beta1-aspnet-200905 for the DLR-specific examples. You need to change the path in the examples to match the location you used to extract the files on your machine.

The ironpython-2.6-beta1-aspnet-200905.ZIP file contains two folders: bin and examples. The bin folder contains a complete set of IronPython files, including the files required to make an IronPython script work as an ASP.NET application.

Using the Sample Applications

The ironpython-2.6-beta1-aspnet-200905examples folder contains two example applications. You should run at least one of these examples to ensure you have a good installation (simple as it is, sometimes there’s a configuration on your machine that prevents the examples from working as intended). To start as simply as possible, use the following steps to run the basic Hello Web forms example:

  1. Open a command prompt. It doesn’t have to be a VS2010 command prompt — any command prompt will do. If you’re using Vista or above, make sure you open the command prompt with administrator rights by right-clicking the Command Prompt icon in the Start menu and choosing Run As Administrator from the context menu.
  2. Type CD WINDOWSMicrosoft.NETFrameworkv2.0.50727 and press Enter to change directories to the .NET Framework 2.0 folder on your system. If you don’t have the .NET Framework 2.0 installed on your system, then type CD Program FilesCommon Files Microsoft SharedDevServer10.0 and press Enter to gain access to the ASP.NET Development Server folder on your system.
  3. Type WebDev.WebServer /port:85 /path:C:ironpython-2.6-beta1-aspnet-200905 exampleshello-webforms and press Enter (change your path information if you need to do so). You may need to type WebDev.WebServer20 if you’re using an alternate folder location. This action will start the ASP.NET Development Server on port 85. You’ll see an ASP.NET Development Server icon appear in the Notification Area.
  4. Right-click the ASP.NET Development Server icon and choose Open in Web Browser from the context menu. Your Web browser should open up and you should see a simple Web page like the one shown in Figure 11-1.

    This simple Web page relies on IronPython for support.
    Figure 11-1: This simple Web page relies on IronPython for support.
  5. Test the Web page to see if it works. Type your name in the Enter Your Name field and click Submit. You should see your name appear in place of the Your Name Here label shown in Figure 11-1. It’s a really simple example, but it will tell you whether you‘re getting the right results.
  6. Right-click the ASP.NET Development Server icon and choose Stop from the context menu. The server is no longer available.

The DLR package includes a second example. To use it, simply type WebDev.WebServer /port:85 / path:C:ironpython-2.6-beta1-aspnet-200905examplesalbum-handler and press Enter at the command prompt (again, make sure you use a path that matches your machine setup). This example is a little more complicated. When you initially display the browser, you see a list of filenames, which isn’t particularly helpful. Click the album-ipy.aspx entry and wait a few seconds. Eventually, you’ll see the Test icon shown in Figure 11-2.

The second example is a little more interesting
Figure 11-2: The second example is a little more interesting — at least it has graphics.

Click the Test icon and you’ll see more graphics. You can click the La Flore icon to see some flowers, or click one of the scenic images, as shown in Figure 11-3. Spend a bit of time with this application and you’ll find that it really is pretty interesting. Now, consider that this application is written in IronPython. Even though DLR isn’t a fully supported technology yet, it does have some amazing capabilities.

Creating a Web Site

You might think that creating an IronPython Web site is going to be complex at first — especially with a lack of IDE support. However, an IronPython Web site doesn’t really contain many parts that you have to worry about. A basic Web site starts with the root folder, which contains three items:

  • An Active Server Page Framework (.ASPX) file containing the user interface
  • The code behind file for the .ASPX file containing IronPython code
  • The Web.CONFIG file that contains all of the required configuration entries (such as the location of the special binaries used to interpret the IronPython script)

In addition to these files, you need a bin folder that contains the executables for working with IronPython. These files won’t vary between projects, so you may as well copy the bin folder whenever you create a new project. The following list is the files found in the bin folder.

  • IronPython.DLL
  • IronPython.Modules.DLL
  • Microsoft.Dynamic.DLL
  • Microsoft.Scripting.Core.DLL
  • Microsoft.Scripting.DLL
  • Microsoft.Scripting.ExtensionAttribute.DLL
  • Microsoft.Web.Scripting.DLL

It’s important to remember that IronPython is scripted. As a consequence, much of the compiled language baggage that you might have had to consider in the past isn’t an issue when working with IronPython. Don’t let the seeming simplicity of the Web site fool you, however; IronPython is just as capable as any other language.

Building a Basic ASP.NET Site Using IronPython

You’ve seen someone else’s examples for working with IronPython on a Web site. Now it’s time to create an IronPython Web site of your own. The example in this section isn’t meant to do anything too impressive. This section focuses on a process you can use to build ASP.NET applications of your own instead. The following sections describe every step you need to take to create any ASP.NET application using IronPython.

Creating the Project

Neither Visual Studio nor DLR provides a project template for you to use. In addition, you have more work to do when creating an ASP.NET application, so creating a project isn’t quite as easy as it should be. The DLR instructions suggest copying the hello-webforms folder to another location and using it as a starting point, which will work but will prove cumbersome. The following steps create a different kind of setup, one that will prove better in the long run, but require more work on your part now.

  1. Create a folder for your application. The example uses Calculator, but you can use any name you want.
  2. Copy the hello-webformsbin folder to your project folder.
  3. Create a .BAT file (the example uses Start.BAT) with the following content:
    [code]
    @Echo Off
    REM Change this directory to the location of WebDev.Webserver
    CD WINDOWSMicrosoft.NETFrameworkv2.0.50727
    REM Start the Web server.
    Start /B WebDev.WebServer /port:%1 /path:%2
    REM Open the Web browser to the right Web site.
    Start “C:Program FilesMozilla FirefoxFirefox” http://localhost:%1/
    @Echo On
    [/code]
    This batch file changes directories to the correct location for the WebDev.Webserver utility. It then starts the WebDev.Webserver utility with the port and path information you provide as part of the project arguments. Finally, it starts your browser so you can see the results.
  4. Start Visual Studio and choose File ➪ Open ➪ Project/Solution. Locate CMD.EXE found in the WindowsSystem32 folder and click Open. This step creates a project based on the command processor.
  5. Right-click cmd in Solution Explorer and choose Properties from the context menu. You see the General tab of the cmd Properties window, as shown in Figure 11-4.

    Set the properties for your project.
    Figure 11-4: Set the properties for your project.
  6. Type /C Start.BAT 85 “C:255 – Source CodeChapter11Calculator“ in the Arguments field. The arguments start with the /C command line switch, which tells the command processor to process all of the commands you’ve requested, and then terminate itself. The Start.BAT argument is the file you created in Step 3. This batch file requires two input arguments, the port number you want to use and the location of the example application.
  7. Type the location of your project, such as C:255 – Source CodeChapter11 Calculator, in the Working Directory folder.
  8. Right-click the Solution entry in Solution Explorer and choose Add ➪ New Item from the context menu. You’ll see the Add New Item dialog box shown in Figure 11-5.
  9. Highlight the HTML Page template and type Default.ASPX in the Name field. Click Add. Visual Studio adds a new ASPX file to your project.
  10. Right-click the Solution entry in Solution Explorer and choose Add ➪ New Item from the context menu to display the Add New Item dialog box again.
  11. Highlight the Text File template and type Default.ASPX.py in the Name field. Click Add. Visual Studio adds the code behind file for the Default.ASPX page.
  12. Copy the Web.CONFIG file from the hello-webforms folder to your project folder.
  13. Right-click the Solution entry in Solution Explorer and choose Add ➪ Existing Item from the context menu to display the Add Existing Item dialog box shown in Figure 11-6.
    Select items to add from the Add New Item dialog box.
    Figure 11-5: Select items to add from the Add New Item dialog box.

    Copy the Web.CONFIG file from an existing project.
    Figure 11-6: Copy the Web.CONFIG file from an existing project.
  14. Locate and highlight the Web.CONFIG file as shown in Figure 11-6. Click Add. Your project is now ready to go.

Defining the User Interface

The template used for this example is actually an .HTM file so it doesn’t contain a few essential entries; you need to use it as an .ASPX file. First, you must tell the Web service which language to use and where to find the code behind file. Add the following code as the first line in the .ASPX file.

[code]
<%@ Page Language=”IronPython” CodeFile=”Default.aspx.py” %>
[/code]

When you complete this step, close and then reopen the Default.ASPX file. Otherwise, the IDE is going to spend a lot of time complaining. The next step is to change where the code runs. You want it to run at the server so you change the <head> tag, as shown in the following code:

[code]
<head runat=”server”>
[/code]

Now you need to create content for the Web page. In this case, the user interface provides a simple four-function calculator. Listing 11-1 shows the code needed to perform this task.

Listin g 11-1: Defining a user interface for the example application

[code]
<form ID=”form1” runat=”server”>
<div>
<asp:Label ID=”lblInput” runat=”server”
text=”Type an input value:”/>
<asp:TextBox ID=”txtInput” runat=”server” />
<asp:Label ID=”lblError” runat=”server”
text=”Type a number in the input field!”
style=”color:red”
visible=”false” />
</div>
<div>
<asp:Button ID=”btnAdd” runat=”server” text=”+”
OnClick=”btnAdd_Click” />
<asp:Button ID=”btnSub” runat=”server” text=”-“
OnClick=”btnSub_Click” />
<asp:Button ID=”btnMul” runat=”server” text=”*“
OnClick=”btnMul_Click” />
<asp:Button ID=”btnDiv” runat=”server” text=”/“
OnClick=”btnDiv_Click” />
</div>
<div>
<asp:Label ID=”lblResult” runat=”server”
text=”Current Value:” />
<asp:TextBox ID=”txtResult” runat=”server” text=”0”
readonly=”true” />
</div>
<div>
<asp:Button ID=”btnClear” runat=”server” text=”Clear”
OnClick=”btnClear_Click” />
</div>
</form>
[/code]

As the listing shows, you work with IronPython code in the same way that you work with any .ASPX file. Unfortunately, the designer has not a clue as to what to do with your code, so you have to write it all by hand. Theoretically, you could start a Web project and then simply move the .ASPX file from that project to your IronPython project, but that seems like a lot of work unless your interface is relatively complex. If you find that you can’t quite remember all the ASP.NET controls at your disposal, you can find a complete list at http://www.w3schools.com/ASPNET/aspnet_refwebcontrols.asp in an easily accessible form.

In this case, the controls appear in four separate groups: input, control, result, and clearing. Figure 11-7 shows a typical view of the example form. Some common errors that developers make are not including the runat=”server” attribute and not providing the proper connectivity to events in the code behind, such as OnClick=”btnClear_Click”. Notice that you can use styles, just as you normally do, with the style attribute. One of the attributes that developers can forget about is visible=”false”, which makes the control invisible.

The example form is a simple four-function calculator.
Figure 11-7: The example form is a simple four-function calculator.

Creating the Code Behind

The code behind for this example is in pure IronPython. So, while you won’t see much difference in Default.ASPX, you’ll find that Default.ASPX.py looks completely different from any Web project you’ve worked with in the past. Listing 11-2 shows the code for this example.

Listin g 11-2: Creating the code behind for the example

[code]
# Import the required assemblies.
from System import *
# Respond to an Add button click.
def btnAdd_Click(sender, e):
# Get the current value.
Value = Int32.Parse(txtResult.Text)
# Reset the error message label.
lblError.Visible = False
# Obtain the new input value.
try:
Addition = Int32.Parse(txtInput.Text)
except:
# Display an error message when necessary.
lblError.Visible = True
return
# Perform the task and return the result.
Value = Value + Addition
txtResult.Text = str(Value)
# Respond to a Subtraction button click.
def btnSub_Click(sender, e):
# Get the current value.
Value = Int32.Parse(txtResult.Text)
# Reset the error message label.
lblError.Visible = False
# Obtain the new input value.
try:
Subtract = Int32.Parse(txtInput.Text)
except:
# Display an error message when necessary.
lblError.Visible = True
return
# Perform the task and return the result.
Value = Value – Subtract
txtResult.Text = str(Value)
# Respond to a Multiplication button click.
def btnMul_Click(sender, e):
# Get the current value.
Value = Int32.Parse(txtResult.Text)
# Reset the error message label.
lblError.Visible = False
# Obtain the new input value.
try:
Multiply = Int32.Parse(txtInput.Text)
except:
# Display an error message when necessary.
lblError.Visible = True
return
# Perform the task and return the result.
Value = Value * Multiply
txtResult.Text = str(Value)
# Respond to a Division button click.
def btnDiv_Click(sender, e):
# Get the current value.
Value = Int32.Parse(txtResult.Text)
# Reset the error message label.
lblError.Visible = False
# Obtain the new input value.
try:
Divide = Int32.Parse(txtInput.Text)
except:
# Display an error message when necessary.
lblError.Visible = True
return
# Perform the task and return the result.
Value = Value / Divide
txtResult.Text = str(Value)
# Respond to a Clear button click.
def btnClear_Click(sender, e):
txtResult.Text = ‘0’
[/code]

The code begins by importing the required assemblies. As with any IronPython application, you can use a combination of Python modules and .NET assemblies to create your application. You also have full access to both Python and .NET functionality in your application, so the considerable flexibility that IronPython provides is still available in this environment.

Each of the event handlers must provide both the sender and e arguments as shown. You don’t include a self argument in this case, as you would with other IronPython code. As you might expect, the sender argument contains a reference to the control that called the event handler, while e contains a list of event arguments (normally set to None).

The four math buttons begin by obtaining the current value of txtResult (the output TextBox) as an Int32 value. Because txtResult is read-only, you don’t need to worry about someone putting an incorrect value into it. Consequently, this task doesn’t provide any error trapping code.

The next step is to obtain the new value for the math operation from txtInput. In this case, you’re relying on the user to provide the correct input value, which means that the application code could receive anything. Someone might even try to enter a script in order to fool your application into doing something improper. Using the Int32.Parse() method means that any input other than a number triggers an exception, which your code can handle by simply not processing the input. The try…except structure does just that. If the user inputs an incorrect value, the Web page displays an error message, rather than doing anything with the input.

Now that the code has two inputs to process, it performs the required math operation. After the math operation is complete, the code outputs the result to txtResult.Text.

The btnClear_Click() event handler is relatively simple. All it does is place a 0 in txtResult .Text. The next math operation starts with a zero value, which means that txtResult is cleared.

Starting the Visual Studio Built-In Web Server

It’s time to begin testing your application. Many developers don’t realize it, but the .NET Framework includes a special utility that makes it possible to host Web sites without having a full-fledged Web server. The WebDev.WebServer utility originally appeared as part of the .NET Framework 2.0. When you build an application for testing purposes with Visual Studio, you’re using this built-in Web server to execute the code.

You find the WebDev.WebServer.EXE file in the WINDOWSMicrosoft.NETFrameworkv2.0.50727 folder on your system. Alternatively, you can also find versions of this utility in the Program Files Common FilesMicrosoft SharedDevServer10.0 folder as WebDev.WebServer20.EXE or WebDev.WebServer40.EXE.

The amazing part of the built-in Web server is that it works fine for any Web site using any kind of code. If you want to test your standard HTML pages, that’s fine — just point the built-in Web server to the correct directory on your hard drive. Of course, you can’t run some types of applications because the built-in Web server isn’t designed to handle them. For example, you can’t execute your PHP code. This little deficiency doesn’t matter for your IronPython application, however, because the built-in Web server will see it as a standard ASP.NET application.

Unlike your full-fledged Web server, the built-in Web server doesn’t provide outside access, which is the reason you want to use it to test your unsecured, experimental IronPython Webforms application. You don’t have to worry about prying eyes seeing your tests and possibly using them as a means to gain entrance to your machine. More important, because this server is virtual, it’s less likely that a failed experiment will cause your system to crash. The following sections describe the WebDev.WebServer utility in more detail.

Understanding the WebDev.WebServer Command Line Syntax

The WebDev.WebServer utility provides only a few command line switches because you perform most configuration tasks using a special Notification Area icon. Here’s the command line syntax for this utility.

[code]
WebDev.WebServer /port:<PortNumber> /path:<PhysicalPath> [/vpath:<VirtualPath>]
WebDev.WebServer20 /port:<PortNumber> /path:<PhysicalPath>
[/vpath:<VirtualPath>]
WebDev.WebServer40 /port:<PortNumber> /path:<PhysicalPath>
[/vpath:<VirtualPath>]
[/code]

The following list provides an explanation of each of the command line switches.

  • /port:PortNumber: Defines the port number used to host the application. Because your application isn’t accessible to the outside world and you use a local browser to access the Web server, the port you use isn’t as important as it usually is. You can’t select port 80 if you also have IIS installed on the system. Any port number between 1 and 65,535 will work as long as the port you select isn’t in use.
  • /path:PhysicalPath: Specifies the physical location of the application you want to host in the browser. You must provide the full path, but you don’t include a filename.
  • /vpath:VirtualPath: Provides a virtual path for the application where VirtualPath is normally the application name, such as /MyApp. The default setting provides a virtual path of /.
  • ?: Displays the help information for the WebDev.WebServer utility.

Using the Built-In Web Server with a Batch File

When you use a batch (.BAT) file to start the application, use the Start utility to execute WebDev .WebServer. Otherwise, the Web server won’t start properly. In addition, you should include the Start utility’s /B command line switch (it isn’t mandatory). The /B command line switch tells Windows not to open a new window to start the application. If Windows opens a new window, the Web server will start, but it may not display the Web page. Here’s a modified command line for batch files.

[code]
Start /B WebDev.WebServer /port:7171 /path:”F:My Web Site”
[/code]

Interacting with the Built-In Web Server

The WebDev.WebServer utility creates an icon in the Notification Area when you start it. In fact, a popup message alerts you to the presence of this icon. Right-click this icon and you see three options:

  • Open in Web Browser: Tells the utility to start the default Web browser and navigate to the Web page hosted by the Web server. In most cases, WebDev.WebServer uses the same defaults as any full Web server you have set up on your machine. Otherwise, you can count on these defaults working:
    • Default.HTM
    • Default.ASP
    • Default.ASPX
    • Index.HTM
  • Stop: Stops the server and makes any Web pages inaccessible.
  • Show Details: Displays the ASP.NET Development Server dialog box shown in Figure 11-8 where you can see details about the Web server. In addition, this dialog box provides a link to access the default Web page.
The ASP.NET Development Server dialog box provides details about the Web server.
Figure 11-8: The ASP.NET Development Server dialog box provides details about the Web server.

Considering the Built-In Web Server Limitations

It’s important to realize that we’re not using the WebDev.WebServer utility for production purposes. The following list helps you better understand why you can’t use this utility for every purpose.

  • Functionality: The WebDev.WebServer utility doesn’t create a full-fledged Web server. Some of the functionality you rely on, such as the ability to add users or work with virtual directories, simply isn’t available.
  • Security: Any security that you want to set up has to appear as part of your application in a Web.CONFIG file. The WebDev.WebServer utility does tend to follow whatever rules you set for Internet Information Server (IIS) if you have it installed. However, you can’t count on this behavior when working on your own and you certainly can’t count on it when you send the application to other machines.
  • Administrative tools support: Anything you normally configure through the Internet Information Services console located in the Administrative Tools folder of the Control Panel is unavailable when working with the WebDev.WebServer utility. Consequently, if your application relies on a special ISAPI Filter, you won’t be able to execute it using the WebDev.WebServer utility. The same holds true for anything else that you normally have to add using the Internet Information Services console.
  • Single user Web server: It’s important to remember that this is a single-user Web server. No one outside the local machine can access the Web server because theoretically, it doesn’t exist — it’s virtual. This means that you can’t perform some types of testing using the built-in Web server. The feature that makes it so secure also prevents you from performing some kinds of real-world testing. Multi-user applications simply won’t work with the built-in Web server.

Savvy developers can get around some of the WebDev.WebServer configuration limitations through judicious use of the Web.CONFIG file and by creating resources locally. Make sure you don’t assume that an application won’t work simply because you have to configure it in a new way.

Performing Basic Testing

Your IronPython Web application works just like any other Web application you create. Because IronPython is fully .NET capable, you can use any control set you want within the application. Of course, you also have access to standard Web controls. All of this flexibility increases complexity and makes it necessary to test your application fully.

The example application is relatively simple, so testing isn’t cumbersome. When you first start the application by pressing Ctrl+F5, you see the Web page shown in Figure 11-7. When you type a value into the Type an Input Value field and click one of the math buttons (+, -, *, or /), the application performs the desired task. Trying to input an invalid value triggers an error message like the one shown in Figure 11-9.

Incorrect values trigger an error message.
Figure 11-9: Incorrect values trigger an error message.

Interestingly enough, the scripting nature of IronPython makes it possible to use IronPython to test your Web application. This gives you an advantage over compiled languages such as C# and Visual Basic.NET.

Considering Debugging

You may be wondering whether this project can provide any debugging. The fact is that you don’t get direct debugging when working with DLR, even if you use a full Web server. However, there are four ways in which you can debug your application.

  • Use print statements and other old standbys to determine what your application is doing.
  • Attach the debugger to the running process after the fact by choosing Debug ➪ Attach to Process within Visual Studio.
  • Import the Microsoft.Scripting.Debugging.DLL found in the Program Files IronPython 2.6 folder and add debugging information manually.
  • Rely on the output error message from the Web server, such as the one shown in Figure 11-10.

    The Web server provides you with error messages as needed.
    Figure 11-10: The Web server provides you with error messages as needed.

Even though Webforms use isn’t quite ready for prime time, you should still take time to experiment. At the very least, try the example programs that come with DLR to see that IronPython is a viable Web development language. Use the example application in this chapter as a starting point for your own experimentation. In short, have a bit of fun using IronPython to create Web pages. Consider how the dynamic nature of IronPython could help you in future Web application development, because DLR won’t remain in alpha status forever.

Understanding the Dynamic Language Runtime

IronPython is a dynamic language, yet the Common Language Runtime (CLR) is a static environment. While you can build a compiler that makes it possible to use a dynamic language with CLR, as was done for IronPython 1.0, you’ll find that certain functionality is missing because CLR simply doesn’t understand dynamic languages. Consequently, Microsoft started the Dynamic Language Runtime (DLR) project (see http://dlr.codeplex.com/ for additional information). DLR sits on top of CLR and performs a level of interpretation that offers additional functionality for dynamic languages. By relying on DLR, IronPython gains access to the following support:

  • Shared dynamic type support
  • Shared hosted model
  • Quick dynamic code generation
  • Interaction with other dynamic languages
  • Improved interaction with static languages such as C# and Visual Basic.NET
  • Shared sandbox security model and browse integration

DLR is now part of the .NET Framework 4.0. Consequently, you can begin accessing these features immediately when using Visual Studio 2010 without having to install any additional support. Microsoft currently supports these languages using DLR:

  • IronPython
  • IronRuby
  • JavaScript (EcmaScript 3.0)
  • Visual Basic

Silverlight also provides support for DLR and there’s even a special SDK for Silverlight DLR. You can discover more about this SDK at http://silverlight.net/learn/dynamic-languages/.

  • Makes it easier to port dynamic languages to the .NET Framework
  • Lets you include dynamic features in static languages
  • Creates an environment where sharing of objects and libraries between languages is possible
  • Makes it possible to perform fast dynamic dispatch and invocation of objects

This section provides a good overview of DLR. You’ll discover additional details about DLR as the book progresses. However, if you’d like to delve into some of the architectural details of DLR, check out the article at http://msdn.microsoft.com/library/dd233052.aspx.

 

Getting IronPython

Before you can use IronPython, you need to get a copy of your own, install it, and check to make sure it works. Theoretically, you might want to obtain the source code and build your own version of IronPython, but most developers simply download the binaries and begin working with IronPython right away. The first three sections that follow tell you what you need to work with IronPython, how to obtain the software, and how to install it. You’ll definitely want to read these sections.

The final two sections are completely optional. In fact, you may want to skip them for now and come back to them after you complete more chapters in the book. The first optional section tells you how to build your own copy of IronPython from the source. The second optional section tells you about third-party libraries.

Understanding the IronPython Requirements

As with any software, IronPython has basic system requirements you must meet before you can use it. It turns out that there are actually two versions of IronPython 2.6 — one for the .NET Framework 2.0, 3.0, and 3.5, and a second for the .NET Framework 4.0. Here are the requirements for the .NET Framework 2.0, 3.0, and 3.5 version.

  • The .NET Framework 2.0, 3.0, and 3.5
  • (Optional) Visual Studio 2005 or Visual Studio 2008 (your system must meet the prerequisites for this software)
  • (Optional) .NET Framework 2.0 Software Development Kit (SDK)

You need only the optional requirements if you plan to build IronPython 2.6 from the source code. Here are the requirements for the .NET Framework 4.0 version (again, the optional requirements are there if you want to build IronPython from source code).

  • The .NET Framework 4.0
  • (Optional) Visual Studio 2010

Getting the Software

As with most open source software, you have a number of choices when it comes to downloading IronPython. For the sake of your sanity, the best choice when starting with IronPython is to download the binary version of the product from http://ironpython.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=30315. You’ll see the Microsoft Installer (MSI) link right below the Recommended Download link as IronPython-2.6.msi. If you really must save the few seconds downloading the MSI version, select the IronPython-2.6-Bin.zip link instead.

It’s also possible to compile IronPython from the source code. If you want to use this option, select the IronPython-2.6-Src.zip link. You must have a copy of Visual Studio installed on your system to use this option. The “Building the Binaries from Scratch” section of the chapter describes how to build a version from scratch, but this process truly isn’t for the IronPython beginner and doesn’t serve much of a purpose unless you plan to add your own enhancements.

Performing the Installation

This section assumes that you’ve downloaded the MSI file to make life easy for yourself. This procedure works equally well for either version of IronPython 2.6 so you can use it for a DLR install as well. The following instructions help you get IronPython installed on your machine.

  1. Double-click the MSI file you downloaded from the CodePlex Web site. You’ll see the usual Welcome page — click Next to get past it.
  2. Read the licensing agreement, check I Accept the Terms in the License Agreement, and then click Next. You’ll see the Custom Setup dialog box shown in Figure 1-1 where you can select the IronPython features you want to install. At a minimum, you must install the Runtime. The Documentation, Standard Library, and Tools features are also strongly recommended. This book assumes that you’ve installed all the features. However, you might want to install just those features you actually need for a production setup (you might not actually need the samples).

    Choose the features you want to install.

  3. Select the features you want to install. Click Next. You’ll see a summary dialog box that simply states you’re ready to install IronPython.
  4. Click Install. MSI will begin the installation process. At some point, you’ll see an installation completion screen.
  5. Click Finish. You should be ready to begin working with IronPython at this point.

Building the Binaries from Scratch

You may eventually want to build the IronPython binaries from scratch. The normal reason to perform this task is to create a special version of IronPython that meets specific needs. A company may want to add extensions or special features to IronPython. Because you have the source code, it’s acceptable to create a custom version of IronPython for yourself — one that contains any feature set you deem necessary to get your work completed. So have fun molding IronPython and then sharing your modifications with others. In order to perform this task, you must have a copy of Visual Studio (you must have Visual Studio 2010 to build a DLR version of IronPython). The following steps tell you how to build the IronPython 2.6 binaries from scratch.

IronPython consists of multiple projects, so you must compile the entire solution.

  1. Download the source code file, such as IronPython-2.6-Src.zip.
  2. Extract the files into a folder. The example assumes that you extracted the files into the root directory of your hard drive into IronPython-2.6.
  3. Locate the IronPython-2.6Src directory and open the IronPython.sln solution using Visual Studio. Visual Studio will load the required files, and you’ll see them in Solution Explorer, as shown in Figure 1-2. Figure 1-2 shows that IronPython consists of a number of projects — you must compile the entire solution to obtain a workable group of DLLs.
  4. Make any required changes to the source code.
  5. Choose Build ➪ Build Solution. Visual Studio creates the required DLLs, ready for testing.

Using Third-Party Libraries

Python is an extremely flexible language and enjoys strong third-party support. In fact, you can find lists of these libraries in various places on the Internet. Here are a few places to check:

  • http://code.google.com/appengine/docs/python/tools/libraries.html
  • http://www.amaltas.org/show/third-party-python-libraries-and-frameworks.html
  • http://dakrauth.com/blog/entry/third-party-python-libraries-interest/

You should be able to use some third-party libraries with IronPython. At the time of this writing, you won’t actually find any usable third-party libraries. However, you should check http://www.ironpython.info/index.php/Third-Party_Library_Compatibility from time-to-time to discover whether there are any third-party libraries that do work with IronPython. It’s important to note that this list represents only tested libraries — you may find other third-party libraries that do work with the current version of IronPython.