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.

 

© 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 additional .NET libraries

Task 1: Using System.Xml - AddReference

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

Exercise 4: Obtaining and Using the Python Standard Library

Task 1: Configuring IronPython to use the Python standard library

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: Use Word for Spell Checking

Task 1: Accessing Word and Checking Spelling

Task 2: Use Windows Form Dialog to Correct Spelling

Tutorial 4: Debugging IronPython programs

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 a simple class - constructor and ToString

Task 2: Making the object enumerable

Task 3: Adding a custom operator

Task 4: Adding a delegate

Exercise 2: Extending using Visual Basic.NET

Task 1: Implementing a simple class - constructor and ToString

Task 2: Making the object enumerable

Task 3: Adding a custom operator

Task 4: Adding a delegate

Tutorial 6: Using Visual Studio to Edit .py Files and Debug Them

Exercise 1: Setting up Visual Studio for IronPython Debugging

Task 1: Setting up Visual Studio for IronPython Debugging

Introduction

IronPython is the .NET implementation of the Python programming language (www.python.org).  It's a dynamically typed language with support for many programming paradigms such as imperative, object-oriented, and functional programming, and also allows you to seamlessly use existing .NET code.

 

The goal of this tutorial is to quickly familiarize you with the IronPython console and to show you how to make use of the extensive .NET libraries available.  This tutorial also shows you how to get started in more specialized areas such as interoperating with COM and extending IronPython with C# and/or Visual Basic.  While we do explain some finer points of Python's syntax, this tutorial is NOT meant to be an introduction to Python itself, and if you're looking for that, we recommend you start with the tutorial at www.python.org or the often recommended book Learning Python by Mark Lutz and David Ascher. In addition, IronPython in Action by Michael Foord and Christian Muirhead is a definitive guide that includes a wealth of IronPython-specific information.

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

 

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

This tutorial also assumes that you will launch the IronPython console (c:\ironpython\ipy.exe) from the tutorial directory.  When the tutorials direct you to start the IronPython console from the tutorial directory, you should change to the tutorial directory (>cd c:\ironpython\tutorial) and launch the console with the tutorial as your working directory (>..\ipy.exe).

 

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:

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 from the tutorial directory by changing to the tutorial directory (>cd c:\ironpython\tutorial) and launching the console c:\ironpython\ipy.exe executable (>..\ipy.exe).  This is how you should always launch the console for the tutorials, but from now on, we'll just direct you to "start the IronPython console from the tutorial directory".

IronPython 2.7 (2.7.10920.0) on .NET 4.0.30319.1
Type "help", "copyright", "credits" or "license" for more information.

>>> _

  1. IronPython's console interfaces with the user in a standard Read-Eval-Print Loop, or REPL. This means that the console repeatedly reads user input, evaluates the statement, prints the result (if any), and awaits more input. Try it out by executing the simple statements listed below. Note: The input line starting with 'for' requires an extra press of the enter key afterwards as a signal to the interpreter that there are no more statements in the 'for' loop.

2+2

print "Hello World!"

for i in range(3): print i

x = 10

print x

 

After this step, the console window will contain the 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

>>>

  1. The IronPython console supports multi-line statements, often used in function or class definitions.  IronPython prompts for additional lines in a multi-line statement using:

...

 

One of the more unique aspects of Python is its sensitivity to whitespace at the beginning of a line. Unlike C, C# or Java, where blocks of code are grouped by curly brackets "{...}", blocks of code in Python are grouped based on their level of indentation.  Every new block of code must be indented one more level than the previous block of code.  Blocks of code are used for many constructs, including function and class definitions, the bodies of loops, 'if'...'elif'...'else' clauses, and 'try'...'except'...'finally' blocks.

Define the 'add' function (note, you need to enter spaces before the 'return' statement):

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'

>>>

 

  1. To exit the IronPython interactive 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 from the tutorial directory (see Introduction for details).

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

dir()

The output in the console window will be:

>>> dir()

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

  1. IronPython comes with several built-in modules including 'sys', which is one of the most frequently used.  Load the 'sys' module using the 'import' keyword:

import sys

  1. 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 the global namespace to be accessed directly. Python’s import doesn’t do that automatically, unless it is used in conjunction with the 'from' keyword (more on this later). To access the names or attributes in an imported module, prefix them with the module's name:

sys.version

  1. 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']

  1. Use the dir() function to explore the contents of the 'sys' module:

dir(sys)

['__doc__', '__name__', '__package__', '__stderr__', '__stdin__', '__stdout__', 'api_version', 'argv', 'builtin_module_names', 'byteorder', 'call_tracing', 'callstats', 'copyright', 'displayhook', 'dllhandle', 'dont_write_bytecode', 'exc_clear', 'exc_info', 'exc_traceback', 'exc_type', 'exc_value', 'excepthook', 'exec_prefix', 'executable', 'exit', 'flags', 'float_info', 'getcheckinterval', 'getdefaultencoding', 'getfilesystemencoding', 'getrecursionlimit', 'getsizeof', 'gettrace', 'getwindowsversion', 'hexversion', 'maxint', 'maxsize', 'maxunicode', 'meta_path', 'modules', 'path', 'path_hooks', 'path_importer_cache', 'platform', 'prefix', 'ps1', 'ps2', 'py3kwarning', 'setcheckinterval', 'setdefaultencoding', 'setrecursionlimit', 'settrace', 'stderr', 'stdin', 'stdout', 'subversion', 'version', 'version_info', 'warnoptions', 'winver']

  1. Print the values of some of the 'sys' module attributes:

sys.path

['.', '%DLR_ROOT%\\Test', '%DLR_ROOT%\\Languages\\IronPython\\Public\\Tutorial', '%DLR_ROOT%\\Languages\\IronPython\\Tests', '%DLR_ROOT%\\Test\\IronPythonTutorial', '%DLR_ROOT%\\bin\\Debug\\Lib', '%DLR_ROOT%\\bin\\Debug\\DLLs', '%DLR_ROOT%\\bin\\Debug']

sys.executable

'%DLR_ROOT%\\bin\\Debug\\ipy.exe'

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 directory:

import first

Because you launched ipy.exe from there, the Tutorial directory appears in sys.path, telling IronPython to look there in its search for 'first.py'.

  1. Explore the 'first' module using dir():

dir(first)

>>> dir(first)

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

  1. Print the documentation for the 'add' and 'factorial' functions, using __doc__ attribute:

first.add.__doc__

first.factorial.__doc__

 

We will use the __doc__ attribute later to explore .NET methods and their parameter types.

>>> first.add.__doc__

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

>>> first.factorial.__doc__

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

  1. Call the methods in the '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!'

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

Exercise 2: Using the standard .NET libraries from IronPython

Much of IronPython's power arises from its 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 from the tutorial directory (see Introduction for details).

  2. Using the 'import' keyword, import the .NET System namespace:

import System

  1. 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 (with ellipsis for convenience):

>>> dir(System.Environment)

['CommandLine','CurrentDirectory','Equals','Exit','ExitCode','ExpandEnvironmentVariables','FailFast','GetCommandLineArgs','GetEnvironmentVariable','GetEnvironmentVariables','GetFolderPath','GetHashCode','GetLogicalDrives','GetType','HasShutdownStarted','Is64BitOperatingSystem','Is64BitProcess','MachineName','MemberwiseClone','NewLine','OSVersion','ProcessorCount','ReferenceEquals','SetEnvironmentVariable','SpecialFolder','SpecialFolderOption','StackTrace','SystemDirectory','SystemPageSize','TickCount','ToString','UserDomainName','UserInteractive','UserName','Version','WorkingSet','__all__','__class__','__delattr__','__doc__','__format__','__getattribute__','__hash__','__init__','__new__','__reduce__','__reduce_ex__','__repr__','__setattr__','__sizeof__','__str__','__subclasshook__']

['CommandLine', 'CurrentDirectory', ... '__subclasshook__']

>>> System.Environment.OSVersion

<System.OperatingSystem object at 0x000000000000002B [Microsoft Windows NT 6.0.6000.0]

>>> System.Environment.CommandLine

'"%DLR_ROOT%\\Bin\\Debug\\ipy.exe"'

'C:\\IronPython\\ipy.exe'

  1. The import statement can also be used to import contents of a class or module into the global namespace.  Use the "from ... import ..." flavor of the import statement to do this, then use dir() to explore the contents of the global namespace.

from System.Math import *

dir()

set1 = set(dir()) set2 = set(dir(object)) list(set1-set2)

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

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 *

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

h = Hashtable()

dir(h)

set1 = set(dir(h)) set2 = set(dir(object)) list(set1-set2)

['Keys','GetObjectData','Count','Contains','__getitem__','KeyEquals','hcp','Clone','SyncRoot','__setitem__','Remove','Clear','comparer','__len__','ContainsValue','Add','__add__','EqualityComparer','GetHash','IsFixedSize','ContainsKey','CopyTo','GetEnumerator','Synchronized','__iter__','IsReadOnly','__contains__','Item','Values','IsSynchronized','OnDeserialization']

  1. Insert a 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 indexable object (Arrays, Array lists etc):

h['a']

 

The output of this step will be:

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

>>> h['a']

'IronPython'

  1. 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 as follows. Note that the input line starting with 'for' requires an extra return or enter key press because the interpreter prompts for more statements in the loop's body.

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

a : IronPython
b : Tutorial

  1. You can initialize the collection classes using instances of the built-in list or tuple data types as arguments.  You can create a Python list by specifying the list of elements in square brackets: [1,2,3].  You create tuples 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 *

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

l = List[str]()

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

l.Add("Hello")

l.Add("Hi")

  1. 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 result in a type error:

>>> l.Add(3)

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: expected str, got int

>>> l.Add(2.5)

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: expected str, got float

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

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: expected str, got list

  1. Enumerate the generic collection:

for i in l: print i

 

The output will be:

>>> for i in l: print i
...

Hello
Hi

TypeError: expected str, got int TypeError: expected str, got float TypeError: expected str, got list

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

Exercise 3: Loading additional .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 (see clr.References in Task 1).  To add a reference to a .NET assembly, use the functions available in the built-in 'clr' module:

Task 1: Using System.Xml - AddReference

  1. Start the IronPython console from the tutorial directory (see Introduction for details).

  2. To import System.Xml, the reference to the assembly containing the Xml components must be first added to IronPython.  Reference System.Xml using the following code  (you can enter "clr.References" before and after the call to "clr.AddReference" to see it change if you want):

import clr

clr.AddReference('System.Xml')

from System.Xml import *

dir()

['ConformanceLevel','DtdProcessing','EntityHandling','Formatting','IHasXmlNode','IXmlLineInfo','IXmlNamespaceResolver','NameTable','NamespaceHandling','NewLineHandling','ReadState','Resolvers','Schema','Serialization','ValidationType','WhitespaceHandling','WriteState','XPath','XmlAttribute','XmlAttributeCollection','XmlCDataSection','XmlCharacterData','XmlComment','XmlConvert','XmlDateTimeSerializationMode','XmlDeclaration','XmlDocument','XmlDocumentFragment','XmlDocumentType','XmlElement','XmlEntity','XmlEntityReference','XmlException','XmlImplementation','XmlLinkedNode','XmlNameTable','XmlNamedNodeMap','XmlNamespaceManager','XmlNamespaceScope','XmlNode','XmlNodeChangedAction','XmlNodeChangedEventArgs','XmlNodeChangedEventHandler','XmlNodeList','XmlNodeOrder','XmlNodeReader','XmlNodeType','XmlNotation','XmlOutputMethod','XmlParserContext','XmlProcessingInstruction','XmlQualifiedName','XmlReader','XmlReaderSettings','XmlResolver','XmlSecureResolver','XmlSignificantWhitespace','XmlSpace','XmlText','XmlTextReader','XmlTextWriter','XmlTokenizedType','XmlUrlResolver','XmlValidatingReader','XmlWhitespace','XmlWriter','XmlWriterSettings','Xsl','__builtins__','__doc__','__name__','clr']

  1. Note that the clr.AddReference function accepts either System.Reflection.Assembly object or string as a parameter. The string parameter can be a full assembly name, a partial assembly name, or a file name. For more control over the assembly references, use the appropriate functions described above.

    For example, consider the 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')

  1. Load the XML file 'load.xml' into the XmlDocument. The xml file contains sample savegame data from the IronPython sample 'Puzzle' . For direct viewing, the load.xml file is located in the Tutorial folder

d = XmlDocument()

d.Load('load.xml')

  1. We can now query the document. Query for all the saved games using the query below:

n = d.SelectNodes('//Puzzle/SavedGames/Game/@caption')

for e in n: print e.Value

 

The output in the console window will be:

>>> n = d.SelectNodes('//Puzzle/SavedGames/Game/@caption')
>>> for e in n: print e.Value
...

Seattle (default game)
New York
World
North America

  1. (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

#document None #comment ************************************************************************* * * Copyright (c) Microsoft Corporation.  * * This source code is subject to terms and conditions of the Apache License, Version 2.0. A  * copy of the license can be found in the License.html file at the root of this distribution. If  * you cannot locate the Apache License, Version 2.0, please send an email to  * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound  * by the terms of the Apache License, Version 2.0. * * You must not remove this notice, or any other, from this software. * * * ***************************************************************************  Puzzle None SavedGames None Game None caption Seattle (default game) type a y 714 x 327 level 11 dimension 3 Game None caption New York type r y 1538 x 1205 level 12 dimension 3 Game None caption World type h y 0 x 0 level 2 dimension 4 Game None caption North America type a x 2 y 5 level 4 dimension 3 TopLeftPreviewTile None x -3 y -3 dimension 3 level 5 Cache None allow true

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 is iterating 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

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

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

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

  1. Start the IronPython console from the tutorial directory (see Introduction for details).

  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__', 'clr']

If you're having trouble getting this to work, make sure Mapack.dll is on the search path. One easy way to do this is to copy Mapack.dll into the Tutorial direcroty (or whichever directory you're running IronPython from).

  1. Instantiate the Matrix class:

m = Matrix()

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

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Matrix() takes at least 1 argument (0 given)

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

print Matrix.__new__.__doc__

>>> print Matrix.__new__.__doc__

__new__(cls: type, rows: int, columns: int)
__new__(cls:type, rows: int, columns: int, value: float)
__new__(cls:type, value: Array[Array[float]])

  1. Create instances of the 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

  1. (Optional) IronPython also supports overloaded operators. Matrix provides implementations of operators +, - (binary and unary), *, and equality. You can see the Python representation of these operators (__eq__, __add__, __mul__, __sub__, ...) using dir():

dir(m)

set1 = set(dir(m)) set2 = set(dir(object)) list(set1-set2)

['FrobeniusNorm','InfinityNorm','Rows','__neg__','__radd__','Columns','__getitem__','Symmetric','__rmul__','Inverse','__rsub__','Solve','Clone','__setitem__','Multiply','Submatrix','Subtract','Random','__eq__','Diagonal','Trace','Negate','Add','Determinant','Norm1','__sub__','__add__','Transpose','Square','Item','__mul__','__ne__']

  1. Make simple calculations with the matrices:

print m * n

print n.Transpose() * m

print m * 3

print n + -n

The expected output of this step is:

>>> print m * n

4.8
0

>>> print n.Transpose() * m

4.8 0

>>> print m * 3

3.6 0
0 3.6

>>> print n + -n

0
0

TypeError: Matrix() takes at least 1 argument (0 given)

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

Exercise 4: Obtaining and Using the Python Standard Library

Note: If you installed IronPython using the MSI installer file, then the Python standard library is already included, and this exercise does not apply. These steps are only necessary if you installed IronPython using the ZIP file, which does not include the library. In that case, this exercise explians how you can obtain Python and direct IronPython to the Python standard library.

If you're not sure how IronPython was installed, then attempt steps 3-4 below. If your output matches this tutorial's, then you already have access to the Python standard libraries, and this exercise may be skipped. If you get an ImportError message, then this exercise is necessary.

Task 1: Configuring IronPython to use the Python standard library

  1. Download the latest Python installer from http://www.python.org/download/ and install Python.  The rest of this exercise will assume you used the default installer settings (e.g. installing to c:\python27).

  2. Create a file named 'site.py' and place it in your IronPython 'Lib' directory.  There might be one there already, in which case you can simply edit it.  This 'site.py' file is automatically executed every time you run IronPython, making it a convenient way to tell IronPython the location of Python's standard library.  To do so, add the following code into IronPython's 'site.py'.  (Replace c:\python27\lib with your actual path to the Python lib directory):

import sys

sys.path.append(r"c:\python27\lib")

Syntax Note: Preceding a string literal with 'r' tells Python to parse the string in "raw" mode, which does not require backslashes (\) to be escaped. This is helpful for the readability of certain strings, such as Windows paths.

  1. Start the IronPython console from the tutorial directory (see Introduction for details).

  2. Now you can use the Python standard library from IronPython, for example to get the current working directory.

import os

os.getcwd()

Assuming you're using the standard IronPython installation directory, the output should look like this:

>>> import os
>>> os.getcwd()

'C:\\ironpython\\Tutorial'

'%DLR_ROOT%\\Test\\IronPythonTutorial'

Tutorial Summary

In this tutorial you performed the following exercises.

In this tutorial, you became acquainted with IronPython's interactive console and learned a powerful Python development technique -- the use of dir() and __doc__ to dynamically 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.  Finally, you learned how to access the standard Python libraries.

 

Tutorial 2: Advanced IronPython

A large part of IronPython's beauty lies in its capacity for 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 handle the user's pressing the button).

This tutorial will focus on creating delegates and handling events in IronPython, as well as creating Windows applications using Windows Forms and the new Windows Presentation Foundation, or WPF (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 to use that knowledge to build working Windows applications using Windows Forms and Windows Presentation Foundation.

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 from the tutorial directory (see Introduction for details).

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

from System.IO import *

  1. Create an instance of the FileSystemWatcher class and set its 'Path' property to watch over the current directory:

w = FileSystemWatcher()

dir(w)

set1 = set(dir(w)) set2 = set(dir(object)) list(set1-set2)

['SynchronizingObject','Deleted','EnableRaisingEvents','DesignMode','GetLifetimeService','OnRenamed','InitializeLifetimeService','IncludeSubdirectories','WaitForChanged','NotifyFilter','Filter','__enter__','Renamed','Container','Site','Changed','Path','Created','CreateObjRef','GetService','InternalBufferSize','OnChanged','CanRaiseEvents','Dispose','Disposed','__exit__','BeginInit','OnCreated','Error','Events','EndInit','OnDeleted','OnError']

w.Path = '.'

  1. 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:

def handle(*args): print args

Syntax Note: The * operator in front of a function parameter signifies the aggregation of any (remaining) input arguments into a list by that name. As such, * is only acceptable in front of a function's final parameter.

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

w.Changed += handle

w.Created += handle

w.Deleted += handle

  1. Enable the watcher to raise events:

w.EnableRaisingEvents = True

  1. 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.  Then finish by deleting the file to see the 'Deleted' event get raised.

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>)

  1. 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

  1. (Optional) You can try step 7 again to see that the events, while they are still being raised, are no longer being handled by our 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:

from System.IO import *

w = FileSystemWatcher()

w.Path = '.'

dir(FileSystemEventArgs)

set1 = set(dir(FileSystemEventArgs)) set2 = set(dir(object)) list(set1-set2)

The output in the console window will be:

>>> dir(FileSystemEventArgs)

['Name', 'ChangeType', 'FullPath', 'Empty']

  1. Now with more knowledge of the FileSystemEventArgs object's properties, we can create a better event handler that will display its ChangeType and FullPath properties:

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

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

w.Changed += handle

w.Created += handle

w.Deleted += handle

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

w.EnableRaisingEvents = True

  1. 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

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

w.Changed -= handle

w.Created -= handle

w.Deleted -= handle

  1. 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 hook (the object which allows connecting and disconnecting of handler delegates) and the callable event-firing object.


To create an event:


import pyevent

hook,caller = pyevent.make_event()


Syntax Note: Python supports the "unpacking" of iterable objects, which is similar to pattern-matching on F# tuples. This feature is used like normal variable assignment, except the left-hand side of the '=' is a comma-separated list of variables, each of which is assigned the corresponding value in the iterable. Unpacking can only be used when the number of items returned by the right-hand side's iterator is known and exactly matches the length of the comma-separated variable list.


This has returned created for 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 for the separation of these abilities just like .NET.  Now let's take this and put it into a class and see how it gets used.

  1. We'll define a class that has an event for

class MyClass(object):
    OnNewInstance,_NewInstance= pyevent.make_event()
    def __new__(cls):
        res = object.__new__(object)
        MyClass._NewInstance(res)

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

MyClass.OnNewInstance += NewInst

a = MyClass()

The console output should look like:

new inst:  <object object at 0x000000000000002B>

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

MyClass.OnNewInstance -= NewInst

Exercise 2: Windows Forms

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

To interactively develop Windows applications, IronPython must be initialized for that purpose.  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 blocked, or unable to process Windows messages.  Therefore, the application does not repaint itself or handle input to the UI.

We provide a .py file to initialize IronPython for properly multithreaded Windows Forms development. See the tasks below.

Task 1: Simple Windows Forms application

  1. Start the IronPython console from the tutorial directory (see Introduction for details).

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

import winforms

Syntax Note: Python modules get automatically initialized (executed) when imported, meaning that the Windows Forms initialization code in winforms.py has executed due to the import statement.

  1. 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 *

  1. Create an instance of the Form class and display it:

f = Form()

f.Show()

You may need to alt-tab or look for the running application since it may not have popped to the top level on your desktop.

  1. Now set the form's Text property:

This change should now appear in the form window's title bar.

f.Text = "My First Interactive Application"

  1. 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..

The output will be a tuple containing a Form object and a MouseEventArgs object, like so:

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

Now remove the click handler because we're going to further develop it.

f.Click -= click

  1. Use dir() function to explore MouseEventArgs

dir(MouseEventArgs)

set1 = set(dir(MouseEventArgs)) set2 = set(dir(object)) list(set1-set2)

['Button','Delta','Empty','Clicks','X','Y','Location']

  1. Knowing the contents of the MouseEventArgs, create an improved event handler for the Click event:

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

Syntax Note: Python functions support named parameters such as the 'Text' parameter in the above invocation of 'Label'. This allows us to specify function arguments by name rather than their ordering, and synergizes nicely with Python's capacity for optional parameters.

  1. Register the event handler:

f.Click += click

  1. 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"

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

f.Controls.Clear()

f.Close()

  1. 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)

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

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

Task 1: Simple Avalon Application

  1. Start the IronPython console from the tutorial directory (see Introduction for details).

  2. Initialize Windows Presentation Foundation:

from avalon import *

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

w = Window()

w.Show()

You may need to alt-tab or look for the running application since it may not have popped to the top level on your desktop.  Now, let's do more.

w.Title = "My Avalon Application"

w.SizeToContent = SizeToContent.WidthAndHeight

By setting the window property to "SizeToContent", the window shrinks.

  1. Let's add some content now:

w.Content = TextBlock()

w.Content.Text = "Hello IronPython!"

w.Content.FontSize = 50

  1. Remove the window content:

w.Content = None

  1. You may leave the interpreter open and continue on to Task 2.

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 Task 1, the relevant portions of which are repeated here:

from avalon import *

w = Window()

w.Show()

  1. Windows Presentation Foundation uses the XAML format to describe the graphical layout and basic behaviors of UI. Load the "calc.xaml" and display the resulting content:

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

Regardless of whether you flowed into this task from Task 1, w.SizeToContent has been reset to manual, so you will need to drag the window's border down and to the right to see the calculator UI.

  1. Let's walk the calculator's object model tree (function Walk is defined in the avalon.py file)

for n in Walk(w): print n

System.Windows.Window
<System.Windows.Controls.Canvas object at 0x000000000000002B [System.Windows.Controls.Canvas]>
<System.Windows.Controls.Canvas object at 0x000000000000002C [System.Windows.Controls.Canvas]>
<System.Windows.Shapes.Rectangle object at 0x000000000000002D [System.Windows.Shapes.Rectangle]>
<System.Windows.Controls.Canvas object at 0x000000000000002E [System.Windows.Controls.Canvas]>

System.Windows.Controls.TextBox System.Windows.Controls.RichTextBox System.Windows.Controls.Button: 1 1 System.Windows.Controls.Button: 9 9 System.Windows.Controls.Button: 8 8 System.Windows.Controls.Button: 5 5 System.Windows.Controls.Button: 4 4 System.Windows.Controls.Button: 2 2 System.Windows.Controls.Button: 3 3 System.Windows.Controls.Button: 6 6 System.Windows.Controls.Button: * * System.Windows.Controls.Button: 7 7 System.Windows.Controls.Button: - - System.Windows.Controls.Button: 0 0 System.Windows.Controls.Button: . . System.Windows.Controls.Button: = = System.Windows.Controls.Button: + + System.Windows.Controls.Button: / / System.Windows.Controls.Button: C C

  1. Using a generator, we can capture all buttons in the calculator:

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

System.Windows.Controls.Button

Syntax Note: Python's generator syntax is a concise way of enumerating, filtering (by way of the optional "if ..." portion), and mapping over (by way of the leftmost portion, an identity expression in this example) an iterable.

The console printed the list of all buttons. Let's store this list object in a variable for later use:

buttons = _

Syntax Note: Available only in the interpreter, the "_" variable stores the last non-None result value printed by the console.

  1. At this point we can make changes to all the buttons. For example, let's change the colors and fonts:

for b in buttons: b.FontSize *= 2

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

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

import calculator

  1. 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 (that is, register event handlers for the UI), enter:

calculator.enliven(w)

Registering self.on_One to handle One.Click Registering self.on_Nine to handle Nine.Click Registering self.on_Eight to handle Eight.Click Registering self.on_Five to handle Five.Click Registering self.on_Four to handle Four.Click Registering self.on_Two to handle Two.Click Registering self.on_Three to handle Three.Click Registering self.on_Six to handle Six.Click Registering self.on_Multiply to handle Multiply.Click Registering self.on_Seven to handle Seven.Click Registering self.on_Subtract to handle Subtract.Click Registering self.on_Zero to handle Zero.Click Registering self.on_DecimalPoint to handle DecimalPoint.Click Registering self.on_Equals to handle Equals.Click Registering self.on_Plus to handle Plus.Click Registering self.on_Divide to handle Divide.Click Registering self.on_Clear to handle Clear.Click

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

  2. Feel free to explore the calculator.py script file (located in Tutorial directory).  The point of interest is the Calculator.__init__ constructor 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 the 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 calculator attribute (method) with the corresponding name (for example, "on_One", "on_Multiply", and "on_Equals", respectively).  If such an attribute (method) is available, we hook the Click event event, using the name to fetch the attribute (line 7).

  1. The second point of interest is the on_Equals method, especially the highlighted line.  Python has a built-in function, "eval", which evaluates an expression passed as a string and returns the resulting value.  Calculator uses "eval" to evaluate the calculator expressions.  The "str" function will then convert the value into a 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 = ""

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

Tutorial Summary

IronPython provides a 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 in this manner with minimal setup. The advantage is that changes are visible immediately, and modifications are happening to a live system without requiring recompilation or even re-execution.

In this tutorial you performed the following exercises:

 

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 a simple interactive application in Windows Forms and 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.

Exercise 1: Use Word for Spell Checking

In this exercise you will create an instance of Microsoft Word and use it to check word spellings.

Task 1: Accessing Word and Checking Spelling

  1. Start the IronPython console from the tutorial directory (see Introduction for details).

  2. Import clr module and add a reference to the Word COM interop assembly.

import clr

clr.AddReferenceByPartialName("Microsoft.Office.Interop.Word")

from Microsoft.Office.Interop.Word import ApplicationClass

  1. Start an instance of Word as a COM server running.  You won't see it show up since it is hidden, but you can see it in the Windows Task Manager by typing ctrl-shift-escape and looking for the WINWORD.EXE process.

w = ApplicationClass()

  1. Define the following function to check the spelling.  Remember to indent the lines of the function's body extra spaces, and you have to hit an extra return or enter to complete the function's definition.

def check_word (word):
   return w.CheckSpelling(word)

check_word("foo")

False

check_word("food")

True

  1. You can try that out on a couple of words, but now lets define a function that will suggest corrections for us.  First, we need to add a document so that we can call GetSpellingSuggestions(), which gives a nice error message if you try to call it with no documents opened.

w.Documents.Add()

Microsoft.Office.Interop.Word.DocumentClass

  1. The first result of several return values from GetSpellingSuggestions() is a collection of items, each of which is a correction suggestion.  We use a Python list comprehension to iterate through the COM collection object and call the Name property on each item object in the collection.  Each item's Name property is a string that Word is suggesting as a correct spelling.

def suggestions(word):
   res_objects = w.GetSpellingSuggestions(word)
   return [x.Name for x in res_objects]

  1. Now, let's shut down Word and exit the IronPython console.  When you enter the next line and hit return, if you are watching the Windows Task Manager, you will see the WINWORD.EXE process go away.

w.Quit()

Task 2: Use Windows Form Dialog to Correct Spelling

In this task you will use an example already written that is very similar to what you did in Task 1, but it demonstrates two additional features.  It uses a Python standard module to register a clean up function so that when the IronPython console terminates, the Word process is closed.  The example also has a function, correct_word, that uses a Windows Form as a dialog to let users select a correct word to return.

  1. Start the IronPython console from the tutorial directory (see Introduction for details).

  2. Import spellcheck.

  3. Call spellcheck.correct_word on a correctly spelled word, and you'll see the function returns the word.

  4. Call spellcheck.correct_word on a misspelled word, and a Windows Form similar to Word's spelling correction dialog appears.  You can pick from the list and click the Replace button, double click a word in the suggestions list, enter custom text and click the Use Custom button, click an Ignore button to return the misspelled word, and so on.

  5. Open the spellcheck.py file to see the code for the the Windows Form as well as the functions.

 

Tutorial Summary

This tutorial was focused on using COM interop to control COM objects from the IronPython environment.

In this tutorial you performed the following exercises.

 

You used Word to check the spelling of words, learned how to supply missing optional arguments, and inspected code that builds a Windows Form and uses it as a dialog.

 

Tutorial 4: Debugging IronPython programs

This tutorial will walk you through a very simple debug session, debugging a Python script using the Microsoft CLR Debugger.  If you have a Visual Studio SKU, see using Visual Studio, and consider skipping this exercise.

Estimated time to complete this tutorial: 10 minutes

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

Exercise 1: Debugging IronPython programs

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

Task 1: Debugging IronPython programs 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 ipy.exe (located in the installaton directory)

  4. For Arguments, type in "-D debugging.py"

  5. Change Working directory to the Tutorial directory

  6. Click OK

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

debugging.py

first.py

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

print first.add(1, 2)

  1. Press F5 - Start Debugging.

IronPython will compile the debugging.py file and start executing it. You will hit the breakpoint you inserted at 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

  1. 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.

  2. End the debugging session and exit the debugger.

Tutorial Summary

In this tutorial you performed the following exercises.

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.

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. It’s worth noting that as Python does not contain protected class members, such members get promoted to public class members when imported into IronPython. Private class members, however, will be invisible in IronPython when imported.

Task 1: Implementing a simple class - constructor and ToString

  1. Open the "SDK Command Prompt" or "Visual Studio 2010 Command Prompt" from the start menu. A more recent Visual Studio command prompt should also work.

  2. Open the "csextend.cs" file in Notepad or your favorite text editor. The file is initially empty.

notepad csextend.cs

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

using System;
using System.Collections;

  1. 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);
    }
}

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

csx

  1. Start the IronPython console from the tutorial directory (see Introduction for details).

  1. 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")

clr.AddReferenceToFile("csextend_5.1.1.dll")

import Simple

dir(Simple)

set1 = set(dir(Simple)) set2 = set(dir(object)) list(set1-set2)

[]

You will see following output:

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

['Equals', 'GetHashCode', 'GetType', 'MemberwiseClone', 'ReferenceEquals', 'ToString', '__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']

  1. Instantiate the Simple class:

s = Simple(10)

print s

IronPython will output:

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

Simple<10>

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

Task 2: Making the object enumerable

  1. Back in Notepad, open "csextend.cs" to add more functionality to the Simple class.

  2. First, inherit the Simple class from IEnumerable and implement the 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);
        }
    }

}

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

import clr

clr.AddReferenceToFile("csextend.dll")

clr.AddReferenceToFile("csextend_5.1.2.dll")

import Simple

dir(Simple)

set1 = set(dir(Simple)) set2 = set(dir(object)) list(set1-set2)

['GetEnumerator','__iter__']

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', 'MemberwiseClone', 'ReferenceEquals', 'ToString', '__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__iter__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']

>>> 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 shows the GetEnumerator method.

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

Task 3: Adding a 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);
    }
}

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

import clr

clr.AddReferenceToFile("csextend.dll")

clr.AddReferenceToFile("csextend_5.1.3.dll")

import Simple

dir(Simple)

set1 = set(dir(Simple)) set2 = set(dir(object)) list(set1-set2)

['__radd__','__add__','GetEnumerator','__iter__']

a = Simple(10)

b = Simple(20)

a + b

Simple<30>

Notice in the output of dir() that you will now see the 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', 'MemberwiseClone', 'ReferenceEquals', 'ToString', '__add__', '__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__iter__', '__new__', '__radd__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']

>>> a = Simple(10)
>>> b = Simple(20)

>>> a + b

<Simple object at 0x000000000000002B [Simple<30>]>

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

Task 4: Adding a delegate

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

using System;
using System.Collections;
 
public delegate int Transformer(int input);

  1. Add the "Transform" method to the Simple class, which takes the delegate as a parameter and invokes the it:

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);
    }
}

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

import clr

clr.AddReferenceToFile("csextend.dll")

clr.AddReferenceToFile("csextend_5.1.4.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

  1. This concludes the C# extending example. An interesting optional exercise is to combine the debugging example with this one. If you are so inclined, try debugging the script that you just typed in. To do so, open the "debugcsx.py" script located in Tutorial directory and place a breakpoint on the function 'X'. You will see a mixed call stack between C# and IronPython.

Exercise 2: Extending using Visual Basic.NET

In this exercise you will use the Visual Basic .NET language to build a class that supports enumeration, custom operators and delegates, and you will use that class from IronPython. It’s worth noting that as Python does not contain protected class members, these members become public when imported into IronPython. Private class members, however, will be invisible from IronPython.

Task 1: Implementing a simple class - constructor and ToString

  1. Open the "SDK Command Prompt" or "Visual Studio 2010 Command Prompt" from the start menu. A more recent Visual Studio Command Prompt should also work.

  2. Open "vbextend.vb" in notepad or your favorite text editor. The file is initially empty.

notepad vbextend.vb

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

Imports System
Imports System.Collections

  1. 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

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

vbx

  1. Start the IronPython console from the tutorial directory (see Introduction for details).

  1. 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")

clr.AddReferenceToFile("vbextend_5.2.1.dll")

import Simple

dir(Simple)

set1 = set(dir(Simple)) set2 = set(dir(object)) list(set1-set2)

[]

You will see following output:

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

['Equals', 'GetHashCode', 'GetType', 'MemberwiseClone', 'ReferenceEquals', 'ToString', '__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']

  1. Instantiate and print a Simple object:

s = Simple(10)

print s

IronPython will output:

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

Simple<10>

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

Task 2: Making the object enumerable

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

  2. First, inherit the Simple class from IEnumerable and implement the 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

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

import clr

clr.AddReferenceToFile("vbextend.dll")

clr.AddReferenceToFile("vbextend_5.2.2.dll")

import Simple

dir(Simple)

set1 = set(dir(Simple)) set2 = set(dir(object)) list(set1-set2)

['GetEnumerator','__iter__']

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', 'MemberwiseClone', 'ReferenceEquals', 'ToString', '__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__iter__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']

>>> 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 shows the GetEnumerator method.

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

Task 3: Adding a 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

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

import clr

clr.AddReferenceToFile("vbextend.dll")

clr.AddReferenceToFile("vbextend_5.2.3.dll")

import Simple

dir(Simple)

set1 = set(dir(Simple)) set2 = set(dir(object)) list(set1-set2)

['__radd__','__add__','GetEnumerator','__iter__']

a = Simple(10)

b = Simple(20)

a + b

Simple<30

Notice in the output of dir() that you now see the 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', 'MemberwiseClone', 'ReferenceEquals', 'ToString', '__add__', '__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__iter__', '__new__', '__radd__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']

>>> a = Simple(10)
>>> b = Simple(20)

>>> a + b

<Simple object at 0x000000000000002B [Simple<30>]>

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

Task 4: Adding a delegate

  1. Add the declaration 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

  1. Add the "Transform" method to the Simple class, which takes the delegate as a parameter and invokes it:

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

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

import clr

clr.AddReferenceToFile("vbextend.dll")

clr.AddReferenceToFile("vbextend_5.2.4.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

  1. This concludes the Visual Basic extending example. An interesting optional exercise is to combine the debugging example with this one. If you are so inclined, try debugging the script that you just typed in. To do so, open the "debugvbx.py" script located in the Tutorial directory and place a breakpoint on the function 'X'. 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 Python and C# or Visual Basic code is located in the Python scripts debugcsx.py and debugvbx.py respectively.

In this tutorial you performed the following exercises.

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

Tutorial 6: Using Visual Studio to Edit .py Files and Debug Them

There is a community technology preview add-in for Visual Studio 2010, IronPython Tools, which supports development of Python code.  Not only does this include Python syntax highlighting, it also adds Python expression completion support to Visual Studio among other things!  You can download IronPython Tools from http://www.ironpython.net/tools/.

If you would like to see more features added to this tool, we would love your feedback! Please vote and/or leave a comment here.

This tutorial shows you how to quickly set up Visual Studio to work on Python scripts in a directory.

Estimated time to complete this tutorial: 5 minutes

The objective of this tutorial is to set up Visual Studio for some basic tool support for debugging.

Exercise 1: Setting up Visual Studio for IronPython Debugging

In this exercise you will set up a Visual Studio solution that you can use to edit .py files in a directory and debug them using the Visual Studio .NET debugger.  We will use the Tutorial directory as an example, but you could save your solution anywhere.

Task 1: Setting up Visual Studio for IronPython Debugging

  1. Open Visual Studio and close any solution or projects you might have open.

  2. Use the File->Open->Project/Solution ... command and browse to the ipy.exe application in your IronPython installation (c:\IronPython\ipy.exe is where the tutorial assumes you have it).

  3. We will use the Tutorial directory as an example, and we'll want to set it up as the working directory for loading .py scripts.  Right click on the ipy.exe project node in the Visual Studio's solution explorer and choose the Properties command.

  4. In the properties dialog, you will fill in two fields, the Command Arguments and Working Directory.  For the Command Arguments, enter "-D first.py".  For the Working Directory, enter the path to the Tutorial directory (c:\IronPython\Tutorial\ is where we assume you have it).  Click Apply and OK to confirm your changes.

  5. Now let's save the solution with the File->Save All ... command.  Save it to the Tutorial directory.

  6. To test out our set up, use Ctrl+O to open a file.  Browse to the "first.py" file in the Tutorial directory.  Place the caret on the line that reads "def add(a, b):", and press F9 to set a breakpoint.

  7. Press F5 to run the script, and you will hit the breakpoint.  If you Press F10, you will step through the file, but all it does is load the definitions and set some variables before terminating.

  8. To see more stepping, we'll add a line to the file and set another breakpoint.  Add a line to the end of the file to invoke the factorial function ("factorial(5)").  Add a breakpoint to the line that reads "if n <= 1: return 1".  Now Press F5 to run the script.  Press F10 repeatedly and notice the locals window where the parameter "n" decrements to 0 and then goes back up to 5 as the recursion unwinds.  You can also hover the mouse over the "n" in the editor window to get a data tip of its value each time you stop at the breakpoint.

To work on other scripts, just change the Command Arguments property for the ipy.exe project as we did above to name another script you want to load on F5.  You might also find it useful to go to the Tools->Options... dialog and change a property on the Environment->Documents page to show the open script files.  If you check the property "Show Miscellaneous Files in the Solution Explorer", then you will get a list of the .py files you are editing.  Each time you open this solution again at a later time, you will see all the .py files you developed while using this solution file.

Tutorial Summary

In this tutorial you performed the following exercises.

In this tutorial you set up Visual Studio to work on Python scripts in a directory and debug them.