IronPython Tutorial

A tour of Python on .NET

Information in this document is subject to change without notice. The example companies, organizations, products, people, and events depicted herein are fictitious. No association with any real company, organization, product, person or event is intended or should be inferred. Complying with all applicable copyright laws is the responsibility of the user. Without limiting the rights under copyright, no part of this document may be reproduced, stored in or introduced into a retrieval system, or transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or otherwise), or for any purpose, without the express written permission of Microsoft Corporation.

Microsoft may have patents, patent applications, trademarked, copyrights, or other intellectual property rights covering subject matter in this document. Except as expressly provided in any written license agreement from Microsoft, the furnishing of this document does not give you any license to these patents, trademarks, copyrights, or other intellectual property.

© 2005 Microsoft Corporation. All rights reserved.

Microsoft, MS-DOS, MS, Windows, Windows NT, MSDN, Active Directory, BizTalk, SQL Server, SharePoint, Outlook, PowerPoint, FrontPage, Visual Basic, Visual C++, Visual J++, Visual InterDev, Visual SourceSafe, Visual C#, Visual J#, and Visual Studio are either registered trademarks or trademarks of Microsoft Corporation in the U.S.A. and/or other countries.

Other product and company names herein may be the trademarks of their respective owners.

Contents

Introduction

Tutorial 1: Basic IronPython

Exercise 1: The IronPython interactive console

Task 1: IronPython console

Task 2: Built-in modules and interactive exploration

Task 3: External Python modules

Exercise 2: Using the standard .NET libraries from IronPython

Task 1: Basic .NET library use

Task 2: Working with .NET classes

Task 3: Generics

Exercise 3: Loading .NET libraries

Task 1: Using System.Xml - AddReference

Task 2: Mapack - Loading the .NET libraries - AddReferenceToFile

Tutorial 2: Advanced IronPython

Exercise 1: Events and Delegates

Task 1: File System Watcher

Task 2: Improving the event handler

Task 3: Defining events in Python

Exercise 2: Windows Forms

Task 1: Simple Windows Forms application

Exercise 3: Windows Presentation Foundation (Avalon)

Task 1: Simple Avalon Application

Task 2: Avalon calculator

Tutorial 3: IronPython and COM interoperability

Exercise 1: Merlin the Wizard

Task 1: Creating COM interop assemblies

Task 2: Using COM objects from IronPython

Tutorial 4: Debugging IronPython program

Exercise 1: Debugging IronPython programs

Task 1: Debugging IronPython programs using Microsoft CLR Debugger

Tutorial 5: Extending IronPython

Exercise 1: Extending using C#

Task 1: Implementing simple class - constructor and ToString

Task 2: Making the object enumerable

Task 3: Adding custom operator

Task 4: Adding delegate

Exercise 2: Extending using Visual Basic.NET

Task 1: Implementing simple class - constructor and ToString

Task 2: Making the object enumerable

Task 3: Adding custom operator

Task 4: Adding delegate

Tutorial 6: Embedding IronPython

Exercise 1: IronPython as an expression evaluator

Task 1: IronPython as an expression evaluator

Exercise 2: IronPython as application's scripting engine

Task 1: Embedding IronPython in an application

Task 2: Handling IronPython exceptions gracefully

Task 3: Redirecting the engine's output streams

# Introduction

Some of the exercises in this tutorial require prerequisites. The prerequisites to successfully complete the whole tutorial are:

- Microsoft .NET Framework Version 2.0 Redistributable Package (x86)
  - Required to run IronPython
  - Download from here.

- .NET Framework 2.0 Software Development Kit (SDK) (x86)
  - Required for the COM interoperability, extending and embedding tutorials.
  - Download from here.

- Microsoft WinFX Runtime Components (Avalon)
  - Required for Advanced IronPython and embedding tutorials.
  - Download from here.

- Mapack
  - Required for the basic IronPython tutorial, exercise "Loading .NET Libraries".
  - Download Mapack from here (direct link to the Mapack.zip download is here).
  - Extract Mapack.dll from the zip file directly into the Tutorial directory.

Visual Studio 2005 can be used in place of the Microsoft .NET Framework Version 2.0 and the .NET Framework 2.0 Software Development Kit (SDK). Since Visual Studio 2005 installs both the .NET Framework 2.0 and the .NET Framework SDK, there is no need to install those explicitly if you have Visual Studio 2005 available.

This tutorial assumes that the IronPython distribution was uncompressed into the directory C:\IronPython. Please note that your individual setup may vary.

# Tutorial 1: Basic IronPython

The emphasis of this tutorial is on the basic interaction with the IronPython interpreter and using the interactive environment to explore the .NET libraries.

Estimated time to complete this tutorial: 30 minutes

The objective of this tutorial is to launch the IronPython interpreter, explore the environment of the interactive console and use IronPython to interact with .NET libraries.

The exercises in this tutorial are:

- The IronPython interactive console

- Using the standard .NET libraries from IronPython

- Loading additional .NET libraries

## Exercise 1: The IronPython interactive console

In this exercise, you will start the IronPython interactive interpreter and perform simple tasks to become acquainted with the IronPython environment.

If you are familiar with using the Python interactive console, the import statement and exploring the Python interactive environment using dir() function and __doc__ attribute, you can skip this exercise.

### Task 1: IronPython console

1. Start the IronPython console (IronPythonConsole.bat or ip.bat)

IronPython 1.0 (Beta) on .NET 2.0.50727.42
Copyright (c) Microsoft Corporation. All rights reserved.

>>> _

2. Execute simple statements listed below. After each statement IronPython prints the result, if any, and awaits more input.

2+2

print "Hello World!"

for i in range(3): print i

x = 10

print x

After this step, the console window will contain following text:

>>> 2+2

4

>>> print "Hello World!"

Hello World!

>>> for i in range(3): print i

0
1
2

>>> x = 10
>>> print x

10

>>>

3. IronPython console supports multi-line statements, often used by function definitions. IronPython prompts for additional lines of multi-line statements using:

...

Define the "add" function:

def add(a, b):
return a + b

To complete the function definition, press Enter once more at this point

add(3, 2)

add("Iron", "Python")

After this step, the console contents will be:

>>> def add(a, b):
... return a + b
...
>>> add(3, 2)

5

>>> add("Iron", "Python")

'IronPython'

>>>

4. To exit the console, type Ctrl+Z and Enter (alternatively, press F6 followed by Enter).

^Z

### Task 2: Built-in modules and interactive exploration

1. Start the IronPython console (IronPythonConsole.bat or ip.bat).

2. Using the built-in dir() function, list the contents of the IronPython environment:

dir()

The output in the console window will be:

>>> dir()

['__builtins__', '__doc__', '__name__']
>>>

3. IronPython comes with several built-in modules, the most frequently used one being "sys". Import "sys" module using the "import" statement:

import sys

4. The Python import statement is similar to the "using" statement of C# or "Imports" statement of Visual Basic. The important difference is that the C# and VB statements bring the names from the imported namespace into to the global namespace to be accessed directly. Python’s import doesn’t do that. To access the attributes of the imported modules, prefix their names with the module name:

sys.version

5. Use the dir() function to explore the environment:

dir()

The environment (global namespace) has changed, now it contains the "sys" module:

>>> dir()

['_', '__builtins__', '__doc__', '__name__', 'sys']
>>>

Note also the "_" in the dir() output. The "_" represents the last result produced by the interactive interpreter. It is a very useful Python feature that lets you capture the result of the interactive exploration. We will use this feature later.

6. Use the dir() function to explore the contents of the "sys" module:

dir(sys)

7. Print the values of some of the "sys" module attributes:

sys.path

sys.executable

### Task 3: External Python modules

This task uses the module "first.py" located in the Tutorial folder.

1. Import the "first.py" module located in the Tutorial:

import first

2. Explore the module "first" using dir() function:

dir(first)

>>> dir(first)

['__builtins__', '__doc__', '__file__', '__name__', 'add', 'factorial', 'hi']

3. Print the documentation for the "add" and "factorial" functions, using __doc__ attribute:

first.add.__doc__

first.factorial.__doc__

The __doc__ attribute will be later used also for exploring .NET methods and their parameter types.

>>> first.add.__doc__

'add(a, b) -> returns a + b'

>>> first.factorial.__doc__

'factorial(n) -> returns factorial of n'
>>>

4. Call the methods in "first" module and print the contents of the "hi" attribute

first.add(1,2)

first.factorial(5)

first.hi

The expected output is:

>>> first.add(1,2)

3

>>> first.factorial(5)

120

>>> first.hi

'Hello from IronPython!'

5. Exit the IronPython Interactive console (Ctrl+Z or F6 followed by Enter)

## Exercise 2: Using the standard .NET libraries from IronPython

The power of IronPython lies within the ability to seamlessly access the wealth of .NET libraries. This exercise will demonstrate how the .NET libraries can be used from IronPython.

In this exercise, you will use the standard .NET libraries from IronPython.

### Task 1: Basic .NET library use

1. Start the IronPython console ("Iron Python Console.bat" or ip.bat)

2. Using the "import" statement, import the .NET System namespace:

import System

3. Explore the System.Environment class and access some of its properties:

dir(System.Environment)

System.Environment.OSVersion

System.Environment.CommandLine

The expected output of these commands is as follows:

>>> dir(System.Environment)

['CommandLine', ..., 'OSVersion', ... ]

>>> System.Environment.OSVersion

Microsoft Windows NT 5.2.3790 Service Pack 1

>>> System.Environment.CommandLine

'C:\\IronPython\\IronPythonConsole.exe '
>>>

4. Import statement can be used also to import contents of a class or module into the global namespace. Use the "from ... import ..." flavor of the import statement to do that and explore the contents of the global namespace using dir().

from System.Math import *

dir()

Now you can call Math methods without having to specify the namespace and class name prefix:

Sin(PI/2)

The expected output is:

>>> from System.Math import *

>>> dir()

['Abs', 'Acos', 'Asin', 'Atan', 'Atan2', 'BigMul', 'Ceiling', 'Cos', 'Cosh', 'DivRem', 'E', 'Equals', 'Exp', 'Floor', 'GetHashCode', 'GetType', 'IEEERemainder', 'Log', 'Log10', 'Max', 'Min', 'PI', 'Pow', 'Round', 'Sign', 'Sin', 'Sinh', 'Sqrt', 'System', 'Tan', 'Tanh', 'ToString', 'Truncate', '_', '__builtins__', '__doc__', '__name__']

>>> Sin(PI/2)

1.0

### Task 2: Working with .NET classes

1. Import the contents of the "System.Collections" namespace into the global namespace:

from System.Collections import *

2. Create instance of the Hashtable class and explore the instance using dir():

h = Hashtable()

dir(h)

3. Insert few elements into the hash table:

h["a"] = "IronPython"

h["b"] = "Tutorial"

IronPython supports the C# - style syntax for accessing the hash table elements. The same syntax applies to any index-able object (Arrays, Array lists etc):

h["a"]

The output of this step will be:

>>> h["a"] = "IronPython"
>>> h["b"] = "Tutorial"

>>> h["a"]

'IronPython'

4. Enumerate the contents of the hash table using the "for ... in ..." statement. The hash table elements are instances of "DictionaryEntry" class. Print the "Key" and "Value" properties of each entry:

for e in h: print e.Key, ":", e.Value

The expected output in the console is:

>>> for e in h: print e.Key, ":", e.Value

a : IronPython
b : Tutorial

5. You can initialize the collection classes by passing in the Python built-in list or tuple data types as arguments. Python list is created by specifying the list of elements in the square brackets: [1,2,3]. Tuple is defined by specifying elements in the parentheses: (1,2,3)

l = ArrayList([1,2,3])

for i in l: print i

s = Stack((1,2,3))

while s.Count: s.Pop()

The expected output is:

>>> l = ArrayList([1,2,3])
>>> for i in l: print i

1
2
3

>>> s = Stack((1,2,3))
>>> while s.Count: s.Pop()

3
2
1

### Task 3: Generics

1. Import the Generic collections from the System.Collections.Generic namespace

from System.Collections.Generic import *

2. To instantiate generic class, the generic type arguments must be specified. IronPython uses the following syntax to specify the type arguments: generic_type[type_argument, ...]. Create instance of generic list of string:

l = List[str]()

3. Add string values into the list. Since we created list of string, adding string is possible:

l.Add("Hello")

l.Add("Hi")

4. Try adding objects of types other than string:

l.Add(3)

l.Add(2.5)

l.Add([1,2,3])

Obviously, adding non-strings will fail with type error:

>>> l.Add(3)

Traceback (most recent call last):
at <shell>

ValueError: Bad args for the method <method# Add on ...>

5. Enumerate the generic collection:

for i in l: print i

The output will be:

>>> for i in l: print i

Hello
Hi

6. Exit the IronPython Interactive console (Ctrl+Z or F6 followed by Enter)

## Exercise 3: Loading .NET libraries

IronPython can directly import only some of the .NET libraries - the most commonly used ones. To use additional .NET libraries, they must be explicitly referenced. IronPython maintains a list of all referenced assemblies (clr.References). To add reference to the .NET assembly, use the functions available in the built-in "clr" module:

- clr.AddReference adds a reference to the .NET assembly either by passing the .NET assembly object directly, or specifying the file name or the assembly name (full or partial). This function is provided primarily for the interactive exploration. We recommend using the following functions in the code modules, since they provide more control over which assembly is being loaded.

- clr.AddReferenceToFile adds reference to the assembly specified by its file name. This function will load the assembly from the file regardless of the assembly version. As a result, it doesn't guarantee that the correct assembly version is being loaded. To guarantee that correct assembly version is being loaded, use clr.AddReferenceByName. Moreover, AddReferenceToFile requires that the assembly be located in the directory listed in sys.path.

- clr.AddReferenceToFileAndPath provides similar functionality to AddReferenceToFile. The difference is that it accepts absolute path and before loading the assembly, AddReferenceToFileAndPath adds the file path into sys.path.

- clr.AddReferenceByName adds reference to the assembly specified by its full assembly name, for example: 'System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.

- clr.AddReferenceByPartialName adds reference to the assembly by specifying partial assembly name. This function cannot guarantee that the correct version of the assembly is being loaded. Use clr.AddReferenceByName to add reference to specific version of the assembly.

### Task 1: Using System.Xml - AddReference

1. Start the IronPython console ("Iron Python Console.bat" or ip.bat).

2. To import System.Xml, the reference to the assembly containing the Xml components must be first added to IronPython. Add reference to System.Xml using the following code:

import clr

clr.AddReference("System.Xml")

from System.Xml import *

dir()

3. Note that clr.AddReference function accepts variety of parameters. The System.Reflection.Assembly object or string. The string parameter can be full assembly name, partial assembly name or file name. For more control over the assembly references, use the appropriate functions.

   For example, consider following alternatives for the statement clr.AddReference("System.Xml") above:

clr.AddReferenceByName('System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089')

clr.AddReferenceByPartialName("System.Xml")

4. Load the XML file "Releases.xml" into the XmlDocument. The xml file contains the release dates of all versions of IronPython between 0.7 and 0.9.4. For direct viewing, the Releases.xml file is located in "Releases.xml"

d = XmlDocument()

d.Load("Releases.xml")

5. We can now query the document. Query for all released versions of IronPython using the query below:

n = d.SelectNodes("//@Version")

for e in n: print e.Value

The output in the console window will be:

>>> n = d.SelectNodes("//@Version")
>>> for e in n: print e.Value

0.7
0.7.1
0.7.2
0.7.3
0.7.4
0.7.5
0.7.6
0.9
0.9.1
0.9.2
0.9.3
0.9.4

6. (Optional) Import the "xmlutil.py" module located in the Tutorial directory and use the function provided in the module to walk the contents of the Xml document:

import xmlutil

for e in xmlutil.Walk(d): print e.Name, e.Value

The Walk function is a generator (a Python function that contains a "yield" statement). As the Walk function executes, it returns (yields) the XML nodes one by one to the caller who iterates over the generator. The source for the Walk function is:

def Walk(xml):

yield xml

if hasattr(xml, "Attributes"):
attrs = xml.Attributes
if attrs:
for attr in attrs:

yield attr

for child in xml:
for c in Walk(child):

yield c

7. Exit the IronPython Interactive console (Ctrl+Z or F6 followed by Enter)

### Task 2: Mapack - Loading the .NET libraries - AddReferenceToFile

This task requires Mapack library for linear algebra computations the library is not part of IronPython distribution. See prerequisites for download details.

1. Start the IronPython console ("Iron Python Console.bat" or ip.bat).

2. Use the clr.AddReferenceToFile function to load the Matrix library "Mapack.dll":

import clr

clr.AddReferenceToFile("Mapack.dll")

from Mapack import *

dir()

The expected output will be:

>>> import clr
>>> clr.AddReferenceToFile("Mapack.dll")
>>> from Mapack import *
>>> dir()

['CholeskyDecomposition', 'EigenvalueDecomposition', 'LuDecomposition', 'Matrix', 'QrDecomposition', 'SingularValueDecomposition', '_', '__builtins__', '__doc__', '__name__', 'sys']

3. Create instance of Matrix class:

m = Matrix()

Oops, bad arguments for the constructor. In the next step you'll learn the way to discover the constructors available.

>>> m = Matrix()
Traceback (most recent call last):
at <shell>

ValueError: Bad args for the method <constructor# for Mapack.Matrix>

4. Using the __doc__ attribute, find out information about Matrix constructors:

print Matrix.__new__.__doc__

>>> print Matrix.__new__.__doc__

__new__(int, int)
__new__(int, int, float)
__new__(System.Double[][])

5. Create instances of Matrix class using the correct constructors and set some matrix elements manually. IronPython supports custom indexing on .NET classes.

m = Matrix(2, 2, 1.2)

n = Matrix(2,1)

n[0,0] = 4

print m

print n

>>> m = Matrix(2, 2, 1.2)
>>> n = Matrix(2,1)
>>> n[0,0] = 4
>>> print m

1.2 0
0 1.2

>>> print n

4
0

6. (Optional) IronPython also supports overloaded operators. Matrix does overload operators +, - (binary and unary) and *. You can see Python representation of the operators (__add__, __mul__, __sub__, ...) using dir():

dir(m)

7. Make simple calculations with the matrices:

m * n

n.Transpose() * m

m * 3

n + -n

The expected output of this step is:

>>> m * n

4.8
0

>>> n.Transpose() * m

4.8 0

>>> m * 3

3.6 0
0 3.6

>>> n + -n

0
0

8. Exit the IronPython Interactive console (Ctrl+Z or F6 followed by Enter)

Tutorial Summary

In this tutorial you performed the following exercises.

- The IronPython interactive console

- Using the standard .NET libraries from IronPython

- Loading .NET libraries

In this tutorial, you became acquainted with IronPython interactive console, the dynamic exploratory way of development using the dir() function and __doc__ attribute to explore the environment. You learned to load and import .NET libraries from IronPython (using the import statement), create instances of .NET classes (including generic classes), call methods, enumerate .NET collections and even use overloaded operators on .NET objects.

# Tutorial 2: Advanced IronPython

The large part of the beauty of IronPython lies within the dynamic-style development - modifying the live application by adding functioning elements to it. With Windows applications, this often requires delegates and event handling (i.e. adding a button to an existing form and adding functionality to the button to handle the user pressing the button).

This tutorial will focus on creating delegates and handling events in IronPython and creating windows applications using Windows Forms and the new Windows Presentation Foundation (formerly known as Avalon).

Estimated time to complete this tutorial: 25 minutes

The objective of this tutorial is to learn how to create delegates and handle events using IronPython and use that knowledge to build working Windows applications using Windows Forms and Windows Presentation Foundation.

- Events and Delegates

- Windows Forms

- Windows Presentation Foundation (Avalon)

## Exercise 1: Events and Delegates

In this exercise, you will create a simple event handler and learn how to explore event handler use. The event handler we'll use in this exercise is the FileSystemWatcher - a component that raises events on file system changes.

### Task 1: File System Watcher

1. Start the IronPython console ("Iron Python Console.bat" or ip.bat).

2. Import the contents of System.IO into the global namespace:

from System.IO import *

3. Create instance of the FileSystemWatcher class and set Path property to watch over the current directory:

w = FileSystemWatcher()

dir(w)

w.Path = "."

4. Create the function to handle the events. Because we don't know yet what arguments the delegate will have, let's accept any number of arguments (the *arg notation):

def handle(*args): print args

5. Register the event handler for the 'Changed', 'Created' and 'Deleted' events:

w.Changed += handle

w.Created += handle

w.Deleted += handle

6. Enable the watcher to raise events:

w.EnableRaisingEvents = True

7. Open the Tutorial folder and create a file. An easy way to create the file is to right-click with the mouse and select "New\Text Document". The file watcher will raise the "Created" event.

You can then open the file in Notepad, type in any text and save the file (this raises the "Changed" event) and finish by deleting the file ("Deleted" event).

At the end of this step, the output in the command window will be similar to the following:

(System.IO.FileSystemWatcher, <System.IO.FileSystemEventArgs object at 0x03CE0BB8>)
(System.IO.FileSystemWatcher, <System.IO.FileSystemEventArgs object at 0x039B3B4D>)
(System.IO.FileSystemWatcher, <System.IO.FileSystemEventArgs object at 0x039B3B4D>)
(System.IO.FileSystemWatcher, <System.IO.FileSystemEventArgs object at 0x014EF022>)

8. In the next task, we will create an improved event handler. For now, remove the current event handler from the file watcher events:

w.Changed -= handle

w.Created -= handle

w.Deleted -= handle

9. (Optional) You can try step 7 again to see that the events, while they are still being raised, are not being handled by the Python "handler" function.

### Task 2: Improving the event handler

1. In the previous task, step 7 we can see that the types of the parameters passed to all three events were the same:

FileSystemWatcher - the instance of the object that raised the event

FileSystemEventArgs - the information about the event raised

Use dir() to explore the event arguments class to find what information the event contains:

dir(FileSystemEventArgs)

The output in the console window will be:

>>> dir(FileSystemEventArgs)

['ChangeType', 'Equals', 'FullPath', 'GetHashCode', 'GetType', 'Name', 'ToString', '__new__']

2. Now with more knowledge of the event argument properties, we can create a better event handler that will print ChangeType and FullPath properties of the event argument object:

def handle(w, a): print a.ChangeType, a.FullPath

3. Register the new event handler for the 'Changed', 'Created' and 'Deleted' events:

w.Changed += handle

w.Created += handle

w.Deleted += handle

4. Make sure the raising of the events is enabled:

w.EnableRaisingEvents = True

5. Open the Tutorial folder again and create a new file ("Created" event), edit the file in Notepad and save it ("Changed" event) and finish by deleting the file ("Deleted" event).

At the end of this step, the output in the command window will be similar to the following:

Created .\New Text Document.txt
Changed .\New Text Document.txt
Changed .\New Text Document.txt
Deleted .\New Text Document.txt

6. Remove the event handler from the file watcher events:

w.Changed -= handle

w.Created -= handle

w.Deleted -= handle

7. Exit the IronPython Interactive console (Ctrl+Z or F6, followed by Enter).

### Task 3: Defining events in Python

1. Finally let's look at how we can get event handler syntax from Python code:

pyevent - a module providing Python event support

make_event - a function that returns an event tuple containing both the callable object and the object that allows connecting and unconnecting to the event.

To create an event:

>>> import pyevent

>>> hook,caller = pyevent.make_event()

2. This has returned back to us two objects. The first object allows a user to hook a function up to the event. The second object allows the owner of the event to cause the event to be raised. This allows seperation of these abilities just like .NET. Now let's take this and put it into a class and see how it gets used.

>>> class MyClass(object):

>>> OnNewInstance,_NewInstance= pyevent.make_event()

>>> def __new__(cls):

>>> res = object.__new__(object)

>>> MyClass._NewInstance(res)

>>> return res

>>>

>>> def NewInst(x): print 'new inst: ', x

>>>

>>> MyClass.OnNewInstance += NewInst

>>>

>>> a = MyClass()

new inst: (<object object at 0x000000000000002B>,)

3. Just like with CLR events you can also remove event handlers:

MyClass.OnNewInstance -= NewInst

## Exercise 2: Windows Forms

To interactively develop Windows applications, IronPython must be initialized. By default, the Python console executes on one thread only. While this thread awaits text input in the console window, the Windows application being dynamically created from the console is not able to process Windows messages and therefore repaint itself etc.

The code to initialize IronPython for Windows Forms is provided and will be referenced in the subsequent tasks.

In this exercise, you will create simple Windows Forms applications dynamically from the IronPython interactive console.

### Task 1: Simple Windows Forms application

1. Start the IronPython console ("Iron Python Console.bat" or ip.bat).

2. Initialize Windows Forms by loading the winforms module/script:

import winforms

The Python modules get automatically initialized (executed) upon import so the Windows Forms initialization code has executed as part of the import statement.

3. Import the contents of the System.Windows.Forms and System.Drawing namespaces into the global namespace:

from System.Windows.Forms import *

from System.Drawing import *

4. Create instance of the Form class and display it:

f = Form()

f.Show()

5. Now set the form Text property:

f.Text = "My First Interactive Application"

6. To bring the application alive, let's focus on the Click event of the form. Create an event handler for the Click event and click on the form to receive the event. Then remove the event handler

def click(*args): print args

f.Click += click

Click on the form to receive the event

f.Click -= click

The output will be similar to:

>>> (System.Windows.Forms.Form, Text: My First Interactive Application, <System.Windows.Forms.MouseEventArgs object at 0x02324551>)

7. Use dir() function to explore the MouseEventArgs

dir(MouseEventArgs)

8. Knowing the contents of the ouseEventArgs, create improved event handler for the Click event:

def click(f, a):
l = Label(Text = "Hello")
l.Location = a.Location
f.Controls.Add(l)

9. Register the event handler:

f.Click += click

10. Now clicking on the form with the mouse will add "Hello" labels. We can also access the controls we just added via mouse clicks and change them

for i in f.Controls: i.Font = Font("Verdana", 15)

for i in f.Controls: i.Text = "Hi"

11. After a few moments of clicking, the form will get quite crowded, co we can clear it out:

f.Controls.Clear()

12. Exit the IronPython Interactive console (Ctrl+Z or F6 followed by Enter)

The standalone version of this script is located in wfdemo.py in the Tutorial directory.

## Exercise 3: Windows Presentation Foundation (Avalon)

This exercise requires Windows Presentation Foundation installed. Please see prerequisites for more information.

Just like Windows Forms, the Windows Presentation Foundation also requires initialization to support interactive development. The initialization code is available in the "Tutorial\avalon.py" file.

In this exercise, you will interactively create simple interactive Windows Presentation Foundation application.

### Task 1: Simple Avalon Application

1. Start the IronPython console ("IronPythonConsole.bat" or ip.bat).

2. Initialize Windows Presentation Foundation:

from avalon import *

3. Create Avalon window, display it and set some of its properties:

w = Window()

w.Show()

w.Title = "My Avalon Application"

w.SizeToContent = SizeToContent.WidthAndHeight

4. By setting the window property to "size to content", the window shrinks. Let's add the content now:

w.Content = TextBlock()

w.Content.Text = "Hello IronPython!"

w.Content.FontSize = 50

5. Remove the window content:

w.Content = None

### Task 2: Avalon calculator

1. If you are continuing from the Task 1, proceed to step 2. Otherwise, please follow the steps 1-3 from the Task 1.

2. Windows Presentation Foundation uses XAML format to describe the graphical layout. Load the "calc.xaml" and display the resulting content:

w.Content = LoadXaml("calc.xaml")

w.SizeToContent = SizeToContent.WidthAndHeight

3. Now we can walk the calculator's object model (function Walk is defined in the avalon.py file)

for n in Walk(w): print n

4. Using Python's list comprehension syntax we can capture all buttons in the calculator:

[ n for n in Walk(w) if isinstance(n, Button) ]

The list of all buttons was printed on the console. To capture the button list in the variable, use the "_" (result of the last operation) identifier:

buttons = _

5. At this point we can make changes to all the buttons, for example, change the colors and fonts:

for b in buttons: b.FontSize *= 2

for b in buttons: b.Foreground = SolidColorBrush(Colors.Blue)

6. To bring the calculator alive, we need to provide event handlers for each button. These can be imported from the calculator.py file. Import the calculator.py:

import calculator

7. The calculator module contains the Calculator class that will be responsible for tracking the expression as it is being built by the calculator. To bring the calculator alive (register the proper event handlers), enter:

calculator.enliven(w)

8. At this point you can click on the calculator buttons and evaluate expressions.

9. Feel free to explore the calculator.py script file (located in Tutorial directory). The point of interest is the Calculator.__init__ method:

def __init__(self, controls): (1)
self.expression = "" (2)
(3)
for i in controls: (4)
if isinstance(i, Button): (5)
if hasattr(self, "on_" + i.Name): (6)
i.Click += getattr(self, "on_" + i.Name) (7)
elif isinstance(i, TextBox): (8)
if i.Name == "Result": (9)
self.result = i (10)
self.result.Text = self.expression (11)

The "controls" argument for the method is list of buttons and text boxes, very similar to the list of buttons created in steps 3 and 4 of this task. The initialization code enumerates the list (line 4), identifies buttons (line 5) and uses the name of the button ("One", "Multiply", " Equals" ...) to find the attribute (method) with the name "on_One", "on_Multiply", "on_Equals" respectively (line 6). If such attribute (event handler) is available, the event is registered via the code on the line 7.

10. Second point of interest is on_Equals method, especially the highlighted line. Python has a built-in function "eval" which evaluates expression passed as string and returns the value. Calculator uses "eval" to evaluate the expressions. "str" function will then convert the value into string for display.

def on_Equals(self, b, e):
try:

result = str(eval(self.expression))

self.result.Text = result
self.expression = result
except:
self.result.Text = "<<ERROR>>"
self.expression = ""

11. Exit the IronPython Interactive console (Ctrl+Z or F6 followed by Enter)

Tutorial Summary

IronPython provides very easy way to develop live applications in a dynamic and exploratory way. Both Windows Forms and Windows Presentation Foundation (Avalon) applications can be easily developed this way with minimal setup. The advantage is that changes are visible immediately and modifications are happening to live system and don't require recompilation.

In this tutorial you performed the following exercises:

- Events and Delegates

- Windows Forms

- Windows Presentation Foundation (Avalon)

In this tutorial, you became familiar with using delegates and handling events in IronPython - an essential part of interactive development of Windows applications using WinForms or Avalon. Then you dynamically created simple interactive application in Windows Forms and created two applications using Windows Presentation Foundation.

# Tutorial 3: IronPython and COM interoperability

COM interoperability is an important part of .NET Framework. To use COM objects from the .NET application, an interop assembly that contains the .NET metadata for the COM objects is required. This tutorial will outline how the interop assemblies can be created (in the case they are not provided by the creators of the COM object) and will demonstrate the ease with which COM objects can be used from IronPython.

Estimated time to complete this tutorial: 15 minutes

The objective of this tutorial is to explore COM interoperability from IronPython.

- Merlin the Wizard

## Exercise 1: Merlin the Wizard

This exercise consists of two tasks. In the first one we will create an interop assembly for a COM object library and explore it using the .NET reflection tools. In the second task we will use the COM object library from IronPython.

In this exercise, you will create a COM interop assembly and use it to drive COM object from IronPython.

### Task 1: Creating COM interop assemblies

1. Open the "SDK Command Prompt" or "Visual Studio 2005 Command Prompt" from the start menu.

2. .NET Frameworks SDK comes with the "tlbimp" tool which converts the COM type library into the .NET metadata information - the interop assembly. The COM object library we want to use from IronPython is the msagent, located in %SystemRoot%\msagent\agentsvr.exe. Use the tlbimp tool to generate the interop assembly for msagent:

tlbimp %SystemRoot%\msagent\agentsvr.exe

C:\IronPython\Tutorial>tlbimp %SystemRoot%\msagent\agentsvr.exe
Microsoft (R) .NET Framework Type Library to Assembly Converter 2.0.50727.42
Copyright (C) Microsoft Corporation. All rights reserved.

Type library imported to AgentServerObjects.dll

3. The .NET metadata information was saved in AgentServerObjects.dll. Use the ildasm tool to explore the generated DLL:

ildasm AgentServerObjects.dll

4. As you browse the metadata tree, notice that at the root of the hierarchy there is the "AgentServerObjects" namespace. The namespace contains many interfaces and a single class - AgentServerClass. Explore the methods on the class. Calling some of its methods will be the subject of the next task.

5. When you are done exploring the AgentServerObjects.dll, close the ildasm tool and the Windows command prompt.

### Task 2: Using COM objects from IronPython

1. Start the IronPython console ("Iron Python Console.bat" or ip.bat).

2. Using the "AddReferenceToFile" function in the sys module, load the AgentServerObjects.dll:

import clr

clr.AddReferenceToFile("AgentServerObjects.dll")

3. Import the contents of the AgentServerObjects namespace and explore the contents using dir():

from AgentServerObjects import *

dir()

4. Create instance of the AgentServerClass and explore its contents:

a = AgentServerClass()

dir(a)

The output in the console window will be:

>>> a = AgentServerClass()
>>> dir(a)

['CreateObjRef', 'Equals', 'GetCharacter', 'GetHashCode', 'GetLifetimeService', 'GetSuspended', 'GetType', 'InitializeLifetimeService', 'Load', 'Register', 'ToString', 'Unload', 'Unregister', '__new__']

5. Explore the parameters of the Load method on AgentServerClass:

a.Load.__doc__

>>> a.Load.__doc__

'System.Void Load(object, System.Int32&, System.Int32&)'

The Load methods has an input parameter (name of the character to load) and two output parameters. When calling this method, IronPython will turn the two output parameters into return values.

6. Load "Merlin the Wizard" character. (The character data is stored in c:\WINDOWS\msagent\chars). The Load method returns multiple values packed into a tuple - the first value is the actual character ID. Capture the character id in the variable:

cid = a.Load("Merlin.acs")[0]

7. Call GetCharacter method on the AgentServerClass instance to get the actual character, display the character and have him think about IronPython Tutorial:

c = a.GetCharacter(cid)

c.Show(0)

c.Think("IronPython Tutorial")

8. Merlin has a lot of animations he can play. Get list of all the animations by caling GetAnimationNames method. Play one of the animations - Read - by calling Play method:

for n in c.GetAnimationNames(): print n

c.Play("Read")

9. (Optional) To call series of animations, we could call c.Play(animation_name) for each animation. Python's lamda functions can simplify this process. We can create global functions and assign them names based on the animation name:

for n in c.GetAnimationNames(): globals()[n] = lambda name=n: c.Play(name)

At this point we can have Merlin play animations simply by
calling a function from global namespace:

Congratulate()

10. Stop Merlin's animations, hide him and exit IronPython console

c.StopAll(0)

c.Hide(0)

Tutorial Summary

This tutorial was focused on using COM interop assemblies generated via tlbimp tool (part of .NET Framework SDK) to control COM objects from the IronPython environment.

In this tutorial you performed the following exercises.

- Merlin the Wizard

In this tutorial, you created a COM interop assembly for the msagent COM object. You used tlbimp tool which is part of the .NET Framework SDK. Using ildasm tool you explored contents of the interop assembly.

Then, from IronPython you loaded the interop assembly, created instance of the COM object and instructed Merlin to play different animations.

# Tutorial 4: Debugging IronPython program

This tutorial will walk you through very simple debug session, debugging a Python script using Microsoft CLR Debugger.

Estimated time to complete this tutorial: 10 minutes

The objective of this tutorial is debugging simple Python script using Microsoft CLR Debugger.

- Debugging IronPython program

## Exercise 1: Debugging IronPython program

In this exercise, you will step through a simple Python script in the Microsoft CLR Debugger.

### Task 1: Debugging IronPython program using Microsoft CLR Debugger

1. Launch Microsoft CLR Debugger.

2. From the debugger Menu, select Debug / Program to Debug ...

3. For Program, browse to the IronPythonConsole.exe (located in the installaton directory)

4. For Arguments, type in debugging.py

5. Change Working directory to the Tutorial directory

6. Click OK

7. From Menu, select File / Open / File. Browse to the Tutorial directory and select two files to open:

debugging.py

first.py

8. Place breakpoint at the following line of the debugging.py file (Place cursor on the line and pres F9):

print first.add(1, 2)

9. Press F5 - Start Debugging.

IronPython will compile the debugging.py file and start executing it. You will hit the breakpoint at the line 3.

Note: If you get poor performance starting the debugging session, exit the debugger, open the Windows Explorer and delete the following directory:

%USERPROFILE%\Application Data\Microsoft\DbgClr

10. Pressing F11, step through the execution of the program, explore variables (even change the values of the variables in the watch window) and explore the call stack.

11. End the debugging session and exit the debugger.

Tutorial Summary

In this tutorial you performed the following exercises.

- Debugging IronPython program

In this tutorial, you walked through the simple debug session of IronPython program. You used Microsoft CLR Debugger, placed breakpoints and stepped through the Python program execution.

# Tutorial 5: Extending IronPython

Estimated time to complete this tutorial: 25- 60 minutes

The objective of this tutorial is to implement the class which will seamlessly fit into the IronPython environment. You can choose to follow Exercise 1 - C# implementation, Exercise 2 - Visual Basic, or both. Both tracks will result in the same functionality.

- Extending using C#

- Extending using Visual Basic.NET

## Exercise 1: Extending using C#

In this exercise you will use C# language to build a class that supports enumeration, custom operators and delegates and you will use that class from IronPython.

### Task 1: Implementing simple class - constructor and ToString

1. Open the "SDK Command Prompt" or "Visual Studio 2005 Command Prompt" from the start menu.

2. Open the "csextend.cs" file in notepad. The file is initially empty

notepad csextend.cs

3. Add using clauses at the beginning of the file:

using System;
using System.Collections;

4. Define a simple class:

public class Simple {
private int data;

public Simple(int data) {
this.data = data;
}

public override string ToString() {
return String.Format("Simple<{0}>", data);
}
}

5. Switch back to the SDK Command Prompt window and build the code using available csx.bat (C# extension):

csx

6. Start the IronPython console ("Iron Python Console.bat" or ip.bat):

ip

7. Load the dll that you just built (csextend.dll) into IronPython. Then explore the Simple class using built-in dir() function:

import clr

clr.AddReferenceToFile("csextend.dll")

import Simple

dir(Simple)

You will see following output:

>>> import clr
>>> clr.AddReferenceToFile("csextend.dll")
>>> import Simple
>>> dir(Simple)

['Equals', 'GetHashCode', 'GetType', 'ToString', '__new__', '__repr__']

8. Create instance of the Simple class:

s = Simple(10)

print s

IronPython will output:

>>> s = Simple(10)
>>> print s

Simple<10>

9. You have just explored all the functionality currently available in the Simple class. Exit IronPython and return back to the C# code to add more functionality.

### Task 2: Making the object enumerable

1. In Notepad, add more functionality to the Simple class.

2. First, inherit the Simple class from IEnumerable and implement GetEnumerator() method. Use the C#'s new "yield return" statement:

public class Simple : IEnumerable {

private int data;
public Simple(int data) {
this.data = data;
}
public override string ToString() {
return String.Format("Simple<{0}>", data);
}

public IEnumerator GetEnumerator() {
for (int i = 0; i < data; i ++) {
yield return new Simple(i);
}
}

}

3. Save the changes, ccompile the code (csx), launch IronPython Console (ip) and test the code:

import sys

clr.AddReferenceToFile("csextend.dll")

import Simple

dir(Simple)

s = Simple(10)

for i in s: print i

You will get the following output in the IronPython console window:

>>> import clr
>>> clr.AddReferenceToFile("csextend.dll")
>>> import Simple
>>> dir(Simple)
['Equals', 'GetEnumerator', 'GetHashCode', 'GetType', 'ToString', '__new__', '__repr__']
>>> s = Simple(10)

>>> for i in s: print i

Simple<0>
Simple<1>
Simple<2>
Simple<3>
Simple<4>
Simple<5>
Simple<6>
Simple<7>
Simple<8>
Simple<9>

Notice that the dir() function now returned the GetEnumerator method.

4. Exit IronPython and return back to the C# code to add more functionality.

### Task 3: Adding custom operator

1. Add the operator + method:

using System;
using System.Collections;

public class Simple : IEnumerable {
private int data;
public Simple(int data) {
this.data = data;
}
public override string ToString() {
return String.Format("Simple<{0}>", data);
}
public IEnumerator GetEnumerator() {
for (int i = 0; i < data; i ++) {
yield return new Simple(i);
}
}

public static Simple operator +(Simple a, Simple b) {
return new Simple(a.data + b.data);
}

}

2. Return to the command prompt, compile the code (csx), launch IronPython Console (ip) and test the code:

import clr

clr.AddReferenceToFile("csextend.dll")

import Simple

dir(Simple)

a = Simple(10)

b = Simple(20)

a + b

Notice that in the output of dir() call you will now get additional methods __add__ and __radd__, Python's notion of custom operator implementation:

>>> import clr
>>> clr.AddReferenceToFile("csextend.dll")
>>> import Simple
>>> dir(Simple)
['Equals', 'GetEnumerator', 'GetHashCode', 'GetType', 'ToString', '__add__', '__new__', '__radd__', '__repr__']
>>> a = Simple(10)
>>> b = Simple(20)

>>> a + b
Simple<30>

3. Exit IronPython and return back to the C# code to add more functionality.

### Task 4: Adding delegate

1. Add the definition of the delegate to the top of the C# source file:

using System;
using System.Collections;

public delegate int Transformer(int input);

2. Add "Transform" method to the Simple class that takes the delegate as a parameter and invokes the delegate:

using System;
using System.Collections;

public delegate int Transformer(int input);

public class Simple : IEnumerable {
private int data;
public Simple(int data) {
this.data = data;
}

public override string ToString() {
return String.Format("Simple<{0}>", data);
}

public IEnumerator GetEnumerator() {
for (int i = 0; i < data; i ++) {
yield return new Simple(i);
}
}

public int Transform(Transformer t) {
return t(data);
}

public static Simple operator +(Simple a, Simple b) {
return new Simple(a.data + b.data);
}
}

3. Return back to the SDK command prompt, compile the code (csx), launch IronPython Console (ip) and test the code:

import clr

clr.AddReferenceToFile("csextend.dll")

import Simple

a = Simple(10)

def X(i):
return i + 100

a.Transform(X)

The function X is getting passed as the delegate to the Simple's Transform method. You will get following output:

>>> import clr
>>> clr.AddReferenceToFile("csextend.dll")
>>> import Simple
>>> a = Simple(10)

>>> def X(i):
... return i + 100
...
>>> a.Transform(X)

110

4. This concludes the C# extending example. If you are interested, an interesting exercise is combining the debugging example and the extending one. If you are inspired to do so, try debugging the script that you just typed in. Debug the "debugcsx.py" script located in Tutorial directory and place breakpoint into the X function - you will see mixed call stack between Visual Basic and Python.

## Exercise 2: Extending using Visual Basic.NET

In this exercise you will use Visual Basic .NET language to build a class that supports enumeration, custom operators and delegates and you will use that class from IronPython.

### Task 1: Implementing simple class - constructor and ToString

1. Open the "SDK Command Prompt" or "Visual Studio 2005 Command Prompt" from the start menu.

2. Open the "vbextend.cs" file in notepad. The file is initially empty

notepad vbextend.cs

3. Add Imports clauses at the beginning of the file:

Imports System
Imports System.Collections

4. Define a simple class:

Public Class Simple
Private data As Integer
Public Sub New(ByVal data As Integer)
Me.data = data
End Sub
Overrides Function ToString() As String
Return String.Format("Simple<{0}>", data)
End Function

End Class

5. Switch back to the SDK Command Prompt window and build the code using the available vbx.bat (Visual Basic extension):

vbx

6. Start the IronPython console ("Iron Python Console.bat" or ip.bat):

ip

7. Load the dll that you just built (vbextend.dll) into IronPython. Then explore the Simple class using built-in dir() function:

import clr

clr.AddReferenceToFile("vbextend.dll")

import Simple

dir(Simple)

You will see following output:

>>> import clr
>>> clr.AddReferenceToFile("vbextend.dll")
>>> import Simple
>>> dir(Simple)

['Equals', 'GetHashCode', 'GetType', 'ToString', '__new__', '__repr__']

8. Create instance of the Simple class:

s = Simple(10)

print s

IronPython will output:

>>> s = Simple(10)
>>> print s

Simple<10>

9. You just have explored all the functionality available in the Simple class. Exit IronPython and return back to the Visual Basic code to add more functionality.

### Task 2: Making the object enumerable

1. Back in Notepad, add more functionality to the vbextend.vb, Simple class

2. First, inherit the Simple class from IEnumerable and implement GetEnumerator() method. Unlike C#, Visual Basic does not offer the yield keyword so implementing IEnumerable interface requires adding a helper class SimpleEnum:

Imports System
Imports System.Collections

Public Class SimpleEnum
Implements IEnumerator
Private data As Integer
Private curr As Integer
Public Sub New(ByVal data As Integer)
Me.data = data
Me.curr = -1
End Sub
Public ReadOnly Property Current() As Object _
Implements IEnumerator.Current
Get
Return New Simple(curr)
End Get
End Property
Public Function MoveNext() As Boolean _
Implements IEnumerator.MoveNext
curr += 1
Return curr < data
End Function
Public Sub Reset() Implements IEnumerator.Reset
curr = -1
End Sub
End Class

Public Class Simple

Implements IEnumerable

Private data As Integer
Public Sub New(ByVal data As Integer)
Me.data = data
End Sub
Overrides Function ToString() As String
Return String.Format("Simple<{0}>", data)
End Function

Function GetEnumerator() As IEnumerator _
Implements IEnumerable.GetEnumerator
Return New SimpleEnum(data)
End Function

End Class

3. Compile the code (vbx), launch IronPython Console (ip) and test the code:

import clr

clr.AddReferenceToFile("vbextend.dll")

import Simple

dir(Simple)

s = Simple(10)

for i in s: print i

You will get the following output in the IronPython console window:

>>> import clr
>>> clr.AddReferenceToFile("vbextend.dll")
>>> import Simple
>>> dir(Simple)
['Equals', 'GetEnumerator', 'GetHashCode', 'GetType', 'ToString', '__new__', '__repr__']
>>> s = Simple(10)

>>> for i in s: print i

Simple<0>
Simple<1>
Simple<2>
Simple<3>
Simple<4>
Simple<5>
Simple<6>
Simple<7>
Simple<8>
Simple<9>

Notice that the dir() function now returned the GetEnumerator method.

4. Exit IronPython and return back to the Visual Basic code to add more functionality.

### Task 3: Adding custom operator

1. Add the operator + method to the Simple class:

Public Class Simple
Implements IEnumerable
Private data As Integer
Public Sub New(ByVal data As Integer)
Me.data = data
End Sub
Overrides Function ToString() As String
Return String.Format("Simple<{0}>", data)
End Function
Function GetEnumerator() As IEnumerator _
Implements IEnumerable.GetEnumerator
Return New SimpleEnum(data)
End Function

Shared Operator +(ByVal a As Simple, ByVal b As Simple) As Simple
Return New Simple(a.data + b.data)
End Operator

End Class

2. Compile the code (vbx), launch IronPython Console (ip) and test the code:

import clr

clr.AddReferenceToFile("vbextend.dll")

import Simple

dir(Simple)

a = Simple(10)

b = Simple(20)

a + b

Notice that in the output of dir() call you will now get additional methods __add__ and __radd__, Python's notion of custom operator implementation:

>>> import clr
>>> clr.AddReferenceToFile("vbextend.dll")
>>> import Simple
>>> dir(Simple)
['Equals', 'GetEnumerator', 'GetHashCode', 'GetType', 'ToString', '__add__', '__new__', '__radd__', '__repr__']
>>> a = Simple(10)
>>> b = Simple(20)

>>> a + b
Simple<30>

3. Exit IronPython and return back to the Visual Basic .NET code to add more functionality.

### Task 4: Adding delegate

1. Add the definition of the delegate to the top of the Visual Basic source file:

Imports System
Imports System.Collections

Public Delegate Function Transformer(ByVal input As Integer) As Integer

2. Add "Transform" method to the Simple class that takes the delegate as a parameter and invokes the delegate:

Public Class Simple
Implements IEnumerable
Private data As Integer
Public Sub New(ByVal data As Integer)
Me.data = data
End Sub
Overrides Function ToString() As String
Return String.Format("Simple<{0}>", data)
End Function
Function GetEnumerator() As IEnumerator _
Implements IEnumerable.GetEnumerator
Return New SimpleEnum(data)
End Function

Function Transform(ByVal t As Transformer) As Integer
Return t(data)
End Function

Shared Operator +(ByVal a As Simple, ByVal b As Simple) As Simple
Return New Simple(a.data + b.data)
End Operator
End Class

3. Return back to the SDK command prompt, compile the code (vbx), launch IronPython Console (ip) and test the code:

import clr

clr.AddReferenceToFile("vbextend.dll")

import Simple

a = Simple(10)

def X(i):
return i + 100

a.Transform(X)

The function X is getting passed as the delegate to the Simple's Transform method. You will get following output:

>>> import clr
>>> clr.AddReferenceToFile("vbextend.dll")
>>> import Simple
>>> a = Simple(10)
>>> def X(i):

... return i + 100
...
>>> a.Transform(X)

110

4. This concludes the Visual Basic extending example. If you are interested, an interesting exercise is combining the debugging example and the extending one. If you are inspired to do so, try debugging the script that you just typed in. Debug the "debugvbx.py" script located in the Tutorial directory. Place breakpoint into the X function - you will see mixed call stack between Visual Basic and Python.

Tutorial Summary

The complete version of the csextend.cs and vbextend.vb files are located in the Tutorial\Extend directory along with files csxtest.py and vbxtest.py which contain the test code for the C# and Visual Basic .NET extensions respectively.

The code to experiment with debugging of Python and C# or Visual Basic code, use Python scripts debugcsx.py and debugvbx.py respectively.

In this tutorial you performed the following exercises.

- Extending IronPython using C# code

- Extending IronPython using Visual Basic .NET

In this tutorial, you implemented a simple class to be used from IronPython. You added the ability for IronPython to enumerate the class, added an operator to it and support for delegate. The implementation was in either C# or Visual Basic, or both.

# Tutorial 6: Embedding IronPython

IronPython can be very easily embedded in the .NET application - either as a simple engine to evaluate Python expressions, but much more powerfully to script the .NET application. This tutorial will explore both means of using IronPython.

Estimated time to complete this tutorial: 25 minutes

The objective of this tutorial is to use IronPython as part of .NET application to evaluate Python expressions and to script the .NET application.

- IronPython as an expression evaluator

- IronPython as application's scripting engine

## Exercise 1: IronPython as an expression evaluator

In this exercise, you will implement simple C# application that will embed IronPython and use it to evaluate expressions.

### Task 1: IronPython as an expression evaluator

1. Open the "SDK Command Prompt" or "Visual Studio 2005 Command Prompt" from the start menu.

2. Open the "eval.cs" file in notepad. The file is initially empty

notepad eval.cs

3. Insert the using statements at the beginning of the file. The PythonEngine, a class that provides the functionality we will leverage in this exercise, is located in the IronPython.Hosting namespace:

using System;
using IronPython.Hosting;

4. Implement the simple evaluator. The evaluator will create instance of PythonEngine class and call the Evaluate method, passing the command line argument as the expression string:

public class Eval {
public static void Main(string[] args) {
PythonEngine pe = new PythonEngine();
Console.WriteLine(pe.Evaluate(args[0]));
}
}

5. Save the changes and return to the SDK Command Prompt. Build the eval.cs using the provided batch file "cseval.bat". The batch script also copies the IronPython binaries to the current directory (they are not installed in Global Assembly Cache) so that the runtime can find them as the eval program executes:

cseval

6. Execute the eval program and pass simple expressions on the command line to be evaluated by the PythonEngine:

eval 2+2

eval 2**3

eval 5%3

The sample output:

C:\IronPython\Tutorial>eval 2+2

4

C:\IronPython\Tutorial>eval 2**3

8

C:\IronPython\Tutorial>eval 5%3

2

7. The complete code for the eval.cs is available in "Tutorial\Embed\eval.cs"

## Exercise 2: IronPython as application's scripting engine

The previous exercise demonstrated one-way interaction between IronPython and an application: the eval program used IronPython as a worker, and IronPython had no need to know anything about eval. From the IronPython engine’s perspective, there was no difference between commands sent from eval and those that someone sitting directly at the console might type.

However, IronPython can be embedded much more fully as a scripting engine for an application. The hosting application can make its own objects available to the IronPython engine, allowing users to write scripts in IronPython that manipulate these objects. Thus, the flexibility of IronPython can be used to automate complicated or frequently-performed tasks, and to provide an open-ended, extensible application.

In this exercise, you will:

1. Embed IronPython in an application as the scripting engine

2. Handle IronPython exceptions gracefully

3. Redirect the engine’s output streams

### Task 1: Embedding IronPython in an application

1. Open the file HostingApp\MonthAtAGlance.cs. (This is the only file in the project containing application logic; everything else is used by Forms only.) At the top of the file, add references to IronPython.Hosting and IronPython.Compiler:

using IronPython.Hosting;
using IronPython.Compiler;

1. Add a PythonEngine instance as a private member of the class MonthAtAGlance:

// Application variables
private Dictionary<System.DateTime, string> dateEntries;
private System.DateTime lastDate;

private PythonEngine engine;

3. Add a method to initialize the PythonEngine, import the site module, and make the dateEntries variable available to it under the name dateentries.

void InitializePythonEngine()
{
engine = new PythonEngine();
engine.ImportSite();
engine.SetVariable("dateentries", dateEntries);
}

4. Place a call to InitializePythonEngine() in the constructor for the App’s main form:

public MonthAtAGlance() {
InitializeComponent();
InitializeApp();

InitializePythonEngine();

}

5. In the monthCalendar_DateChanged method, add code to call the engine. Note that we now have two ways to invoke the IronPython engine: Evaluate, as we saw in Exercise 1; and Execute, which changes the state of the engine but doesn’t return any result. Also, if we know the type that an expression evaluates to, we can use the generic form Evaluate. Insert the following lines at the end of monthCalendar_DateChanged:

engine.Execute("n = dateentries.Count");
int nEntries = engine.Evaluate<int>("n");
MessageBox.Show("There are now "+nEntries.ToString()+" entries in the dictionary.");

6. Compile the application using the script makeha.bat. Run it inside the HostingApp directory, where it is found. The executable will be App.exe. The source file for this step is also available as HostingApp\MonthAtAGlance.1.cs

### Task 2: Handle IronPython exceptions gracefully

If an application’s users have IronPython available as a scripting engine, it’s a given that syntax and logic errors will sometimes occur, raising IronPython exceptions. To show how this can degrade usability, let’s first see what happens when an application doesn’t have any way of handling exceptions from IronPython scripts gracefully:

1. At the end of InitializePythonEngine, we'll add some Python code that generates an exception by attempting to reference an undefined variable, so:

   engine.Execute("print y");

Compile and run App.exe; again, the prepared source-file for this step is available, as MonthAtAGlance.2.1.cs. At once, you will see an exception dialog:
This dialogue appears because the default UncaughtExceptionHandler for the current AppDomain is invoked. By adding a new UncaughtExceptionHandler, you can perform various clean-up and reporting tasks before this dialogue appears. However, ideally you would like to handle the situation a little better than that. At the very least, your application can catch exceptions raised inside the PythonEngine and display the stack-trace to the user.

2. Wrap the call to Execute that you added in step 1 in a try/catch block:

try {

engine.Execute("print y");

} catch (Exception exc) {
MessageBox.Show(exc.ToString());
}

Compile and run to see the stack-trace displayed in the message-box; after dismissing it, you can interact with the application normally.

3. Since we know the type of exception that will be raised – and that every Python exception maps to a .NET exception – we can add more precise handling. Modify the try/catch block as follows:

try {
engine.Execute("print y");

} catch (IronPython.Runtime.PythonNameError exc) {
MessageBox.Show("IronPython engine complained: "+exc.Message);
} catch (Exception exc) {
MessageBox.Show("Unexpected exception: "+exc.ToString()+"\nShutting down!");
System.Environment.Exit(-1);
}

4. Compile and run again; note the different message-box that appears:

### Task 3: Redirect the engine’s output streams

In Task 2, we directed the PythonEnginer to execute a print statement. Although this direction failed, because we asked it to print an undefined variable, nonetheless it raises an important question: if we execute a print, or any other statement which has console output as a side-effect, where does this output go when we host the engine in a GUI application? Let’s see what happens…

1. Modify the print statement added in Task 2 so that it executes without an error:

engine.Execute("print 'Hello, world!'");

Now compile and run the program again. Look at the command window you launched the program from. You'll see 'Hello world!' printed there. By default, the IronPython engine writes to the standard output; but it's straightforward to redirect to a file so that all interactions with the engine are logged.

2. Modify InitializePythonEngine as follows:

engine = new PythonEngine();

System.IO.FileStream fs = new System.IO.FileStream("scripting-log.txt",
System.IO.FileMode.Create);
engine.SetStdout(fs);
engine.SetStderr(fs);

engine.SetVariable("dateentries", dateEntries);

Now compile and run.Look in the home directory of the executable for scripting-log.txt; it should contain the text ‘Hello, world!'.

3. Now let's use the IronPython engine to view information on the application objects. Add the following lines to the empty body of defaultEvtHandler:

engine.Execute("print '---------'");
engine.Execute("for i in dateentries.Keys: print 'Day %s: %s' % (i, dateentries[i])");

Now try compiling and running. Add a few entries to the calendar. You’ll see that, after you shut down, scripting-log.txt contains a text list of your calendar entries. (Any standard error output will be directed there as well.) Actually, it contains a complete record of the updates you have made to the calendar, since this Python code will be invoked whenever a date's entry is altered.

Tutorial Summary

In this tutorial you performed the following exercises.

- IronPython as an expression evaluator

- IronPython as application's scripting engine

In this tutorial you used IronPython as an embedded expression evaluator and as a scripting engine for an existing .NET application.