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
object-oriented programming, and also allows you to seamlessly use .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, extending IronPython with C#,
and embedding IronPython. 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.
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.
- Mapack (example assembly found on the
internet)
- 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.
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).
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:
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.
-
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 1.0 on .NET 2.0.50727.42
Copyright (c)
Microsoft Corporation. All rights reserved.
>>> _
-
Execute simple statements listed below. After each statement
IronPython prints the result, if any, and awaits more input. (The input line
starting with "for" requires an extra return or enter key press because the
interpreter prompts for 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
>>>
-
IronPython console supports multi-line statements, often used by
function definitions. IronPython prompts for additional lines of multi-line
statements using:
...
Unlike 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 function and class
definitions as well as 'if' statements and loops.
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'
>>>
-
To exit the IronPython interactive console, type Ctrl+Z and Enter
(alternatively, press F6 followed by Enter).
^Z
-
Start the IronPython console from the tutorial directory (see Introduction for details).
-
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__']
-
IronPython comes with several built-in modules, the most
frequently used one being "sys". Import "sys" module using the "import"
statement:
import sys
-
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. To access
the names or attributes in an imported module, prefix the names with the
module's name:
sys.version
-
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']
-
Use the dir() function to explore the contents of the "sys"
module:
dir(sys)
['PerformModuleReload', '__name__', '__stderr__',
'__stdin__', '__stdout__', '_getframe', 'api_version', 'argv',
'builtin_module_names', 'byteorder', 'copyright', 'displayhook', 'exc_clear',
'exc_info', 'exc_traceback', 'exc_type', 'exc_value', 'excepthook',
'exec_prefix', 'executable', 'exit', 'getcheckinterval', 'getdefaultencoding',
'getfilesystemencoding', 'getrecursionlimit', 'hexversion', 'maxint',
'maxunicode', 'meta_path', 'modules', 'path', 'path_hooks',
'path_importer_cache', 'platform', 'prefix', 'ps1', 'setcheckinterval',
'setdefaultencoding', 'setrecursionlimit', 'settrace', 'stderr', 'stdin',
'stdout', 'version', 'version_info', 'warnoptions', 'winver']
-
Print the values of some of the "sys" module
attributes:
sys.path
['%MERLIN_ROOT%\\Test\\IronPythonTutorial',
'%MERLIN_ROOT%\\Test', '%MERLIN_ROOT%\\Languages\\IronPython\\Tutorial',
'%MERLIN_ROOT%\\Languages\\IronPython\\Tests',
'%MERLIN_ROOT%\\Bin\\Debug\\Lib']
sys.executable
'%MERLIN_ROOT%\\Bin\\Debug\\ipy.exe'
This task uses the module "first.py" located in the Tutorial
folder.
-
Import the "first.py" module located in the Tutorial (because you
launched ipy.exe from the Tutorial directory, "first" will be found on your
sys.path):
import first
-
Explore the module "first" using dir() function:
dir(first)
>>> dir(first)
['__builtins__', '__file__', '__name__', 'add',
'factorial', 'hi']
-
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'
-
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!'
-
Exit the IronPython Interactive console (Ctrl+Z or F6 followed by
Enter)
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.
-
Start the IronPython console from the tutorial directory (see Introduction for details).
-
Using the "import" statement, import the .NET System
namespace:
import System
-
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 some ellipsis for convenience):
>>> dir(System.Environment)
['CommandLine', 'CurrentDirectory', 'Equals', 'Exit',
'ExitCode', 'ExpandEnvironmentVariables', 'FailFast', 'Finalize',
'GetCommandLineArgs', 'GetEnvironmentVariable', 'GetEnvironmentVariables',
'GetFolderPath', 'GetHashCode', 'GetLogicalDrives', 'GetType',
'HasShutdownStarted', 'MachineName', 'MemberwiseClone', 'NewLine', 'OSVersion',
'ProcessorCount', 'ReferenceEquals', 'SetEnvironmentVariable', 'SpecialFolder',
'StackTrace', 'SystemDirectory', 'TickCount', 'ToString', 'UserDomainName',
'UserInteractive', 'UserName', 'Version', 'WorkingSet', '__class__',
'__delattr__', '__doc__', '__getattribute__', '__hash__', '__init__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__',
'get_CommandLine', 'get_CurrentDirectory', 'get_ExitCode',
'get_HasShutdownStarted', 'get_MachineName', 'get_NewLine', 'get_OSVersion',
'get_ProcessorCount', 'get_StackTrace', 'get_SystemDirectory', 'get_TickCount',
'get_UserDomainName', 'get_UserInteractive', 'get_UserName', 'get_Version',
'get_WorkingSet', 'set_CurrentDirectory', 'set_ExitCode']
>>> System.Environment.OSVersion
<System.OperatingSystem object at
0x000000000000002B [Microsoft Windows NT 6.0.6000.0]
>>> System.Environment.CommandLine
'C:\\IronPython\\ipy.exe'
'"%MERLIN_ROOT%\\Bin\\Debug\\ipy.exe"'
-
The 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()
set1 = set(dir()) set2 = set(dir(object)) list(set1-set2)
['Tan', 'Tanh', 'Sin', 'Sinh', 'Ceiling', '__name__',
'Atan', 'Pow', 'Cos', 'Cosh', 'Round', 'Abs', 'Atan2', 'Acos', 'BigMul',
'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
-
Import the contents of the "System.Collections" namespace into the
global namespace:
from System.Collections import *
-
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)
['get_Values', 'ContainsValue', 'Keys', 'get_SyncRoot',
'GetObjectData', 'Count', 'get_IsFixedSize', 'get_Keys', 'Contains', 'Add',
'__getitem__', 'get_IsReadOnly', 'get_comparer', 'get_IsSynchronized',
'EqualityComparer', 'get_EqualityComparer', 'GetHash', 'IsFixedSize',
'KeyEquals', 'ContainsKey', 'CopyTo', 'get_Count', 'hcp', 'Clone', 'SyncRoot',
'__setitem__', 'GetEnumerator', 'set_comparer', 'Remove', 'Synchronized',
'Clear', '__iter__', 'IsReadOnly', 'comparer', 'Values', 'get_Item', 'get_hcp',
'IsSynchronized', 'OnDeserialization', 'set_Item', 'set_hcp']
-
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 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'
-
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 (the input line
starting with "for" requires an extra return or enter key press because the
interpreter prompts for more statements in the 'for' loop.):
>>> for e in h: print e.Key, ":",
e.Value
...
a : IronPython
b : Tutorial
-
You can initialize the collection classes by passing in the Python
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
-
Import the Generic collections from the System.Collections.Generic
namespace:
from System.Collections.Generic import *
-
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]()
-
Add string values into the list. Since we created a list of
string, adding strings is possible:
l.Add("Hello")
l.Add("Hi")
-
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):
File , line
unknown, in Initialize##40
TypeError: expected str, got int
-
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
-
Exit the IronPython Interactive console (Ctrl+Z or F6 followed by
Enter)
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:
-
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 a 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 a 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.
-
Start the IronPython console from the tutorial directory (see Introduction for details).
-
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 (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', 'EntityHandling', 'Formatting',
'IHasXmlNode', 'IXmlLineInfo', 'IXmlNamespaceResolver', 'NameTable',
'NewLineHandling', 'ReadState', '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']
-
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")
-
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")
-
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
-
(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 Microsoft Public License. A * copy of the license can be
found in the License.html file at the root of this distribution. If * you
cannot locate the Microsoft Public License, 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 Microsoft Public License. * * 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 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
-
Exit the IronPython Interactive console (Ctrl+Z or F6 followed by
Enter)
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.
-
Start the IronPython console from the tutorial directory (see Introduction for details).
-
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']
-
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.
Traceback (most recent call last):
File , line
unknown, in Initialize##30
TypeError: Matrix() takes at least 1 argument (0
given)
-
Using the __doc__ attribute, find out information about Matrix
constructors:
print Matrix.__new__.__doc__
>>> print Matrix.__new__.__doc__
__new__(cls, int rows, int columns)
__new__(cls, int
rows, int columns, float value)
__new__(cls, Array[Array[float]] value)
-
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
-
(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)
set1 = set(dir(m)) set2 = set(dir(object))
list(set1-set2)
['FrobeniusNorm', 'InfinityNorm', 'Rows', '__neg__',
'__radd__', 'Columns', '__getitem__', 'Symmetric', 'get_Norm1', '__rmul__',
'__rsub__', 'Inverse', 'op_UnaryNegation', 'Solve', 'Clone', '__setitem__',
'Multiply', 'get_Square', 'Submatrix', 'Subtract', 'Random', 'get_Trace',
'op_Subtraction', '__eq__', 'get_InfinityNorm', 'Diagonal', 'Trace', 'Negate',
'get_Columns', 'Add', '__sub__', 'get_FrobeniusNorm', 'Determinant', 'Norm1',
'__add__', 'get_Symmetric', 'get_Inverse', 'op_Addition', 'op_Equality',
'op_Inequality', 'Transpose', 'get_Determinant', 'Square', 'get_Rows',
'get_Item', '__mul__', '__ne__', 'op_Multiply', 'set_Item']
-
Make simple calculations with the matrices:
m * n
n.Transpose() * m
m * 3
n + -n
The expected output of this step is:
>>> m * n
Mapack.Matrix
>>> n.Transpose() * m
Mapack.Matrix
>>> m * 3
Mapack.Matrix
>>> n + -n
Mapack.Matrix
TypeError: Matrix() takes at least 1 argument (0
given)
-
Exit the IronPython Interactive console (Ctrl+Z or F6 followed by
Enter)
In this exercise, you will obtain Python and point IronPython at
the Python standard library.
-
Download the latest Python installer from http://www.python.org/download/ and
install Python. The rest of this exercise will assume you used defaults (and
installed to c:\python25, for example).
-
Create a file named "site.py" and place it into your IronPython
"Lib" directory. There might be one there already, so you can just edit it.
The "site.py" file is executed every time you run IronPython. To tell
IronPython where the Python standard library is, you can add the "lib" directory
of Python to IronPython's path. To do so, put the following code into
IronPython's "site.py" file (replace c:\python25\lib with your actual path to
the Python lib directory):
import sys
sys.path.append(r"c:\python25\lib")
-
Start the IronPython console from the tutorial directory (see Introduction for details).
-
Now you can use the Python standard library from IronPython, for
example to get the current working directory (output uses assumed location of
your IronPython installation):
import os
os.getcwd()
'C:\\ironpython\\Tutorial'
'%MERLIN_ROOT%\\Test\\IronPythonTutorial'
Tutorial Summary
In this tutorial you performed the following exercises.
In this tutorial, you became acquainted with IronPython
interactive console, including 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. Finally, you learned how to access standard Python libraries.
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, 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 to use that knowledge to build working
Windows applications using Windows Forms and Windows Presentation
Foundation.
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.
-
Start the IronPython console from the tutorial directory (see Introduction for details).
-
Import the contents of System.IO into the global
namespace:
from System.IO import *
-
Create instance of the FileSystemWatcher class and set Path
property to watch over the current directory:
w = FileSystemWatcher()
dir(w)
set1 = set(dir(w)) set2 = set(dir(object))
list(set1-set2)
['SynchronizingObject', 'get_CanRaiseEvents', 'Deleted',
'remove_Error', 'get_SynchronizingObject', 'get_NotifyFilter',
'EnableRaisingEvents', 'set_SynchronizingObject', 'get_Events', 'DesignMode',
'get_DesignMode', 'GetLifetimeService', 'get_Site', 'set_NotifyFilter',
'OnRenamed', 'get_Path', 'remove_Disposed', 'remove_Created',
'InitializeLifetimeService', 'WaitForChanged', 'IncludeSubdirectories',
'remove_Deleted', 'NotifyFilter', 'Filter', 'Renamed', 'Container', 'Site',
'add_Deleted', 'remove_Renamed', 'Changed', 'set_Filter', 'Path', 'Created',
'CreateObjRef', 'set_EnableRaisingEvents', 'GetService', 'add_Created',
'get_Container', 'InternalBufferSize', 'set_InternalBufferSize',
'get_EnableRaisingEvents', 'set_Site', 'add_Error', 'set_Path', 'get_Filter',
'get_InternalBufferSize', 'OnChanged', 'add_Disposed',
'set_IncludeSubdirectories', 'CanRaiseEvents', 'Dispose', 'Disposed',
'BeginInit', 'OnCreated', 'Error', 'get_IncludeSubdirectories', 'add_Changed',
'Events', 'remove_Changed', 'EndInit', 'add_Renamed', 'OnDeleted',
'OnError']
w.Path = "."
-
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
-
Register the event handler for the 'Changed', 'Created', and
'Deleted' events:
w.Changed += handle
w.Created += handle
w.Deleted += handle
-
Enable the watcher to raise events:
w.EnableRaisingEvents = True
-
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>)
-
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
-
(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.
-
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)
['ChangeType', 'Empty', 'Equals', 'Finalize',
'FullPath', 'GetHashCode', 'GetType', 'MemberwiseClone', 'Name',
'ReferenceEquals', 'ToString', '__class__', '__delattr__', '__doc__',
'__getattribute__', '__hash__', '__init__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__str__', 'get_ChangeType',
'get_FullPath', 'get_Name']
['ChangeType', 'get_ChangeType', 'get_Name', 'FullPath',
'get_FullPath', 'Empty', 'Name']
-
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
-
Register the new event handler for the 'Changed', 'Created' and
'Deleted' events:
w.Changed += handle
w.Created += handle
w.Deleted += handle
-
Make sure the raising of the events is enabled:
w.EnableRaisingEvents = True
-
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
-
Remove the event handler from the file watcher
events:
w.Changed -= handle
w.Created -= handle
w.Deleted -= handle
-
Exit the IronPython Interactive console (Ctrl+Z or F6, followed by
Enter).
-
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()
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 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.
-
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()
new inst
-
Just like with CLR events you can also remove event
handlers:
MyClass.OnNewInstance -= NewInst
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 not able 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 Windows Forms
development, see tasks below.
In this exercise, you will create simple Windows Forms
applications dynamically from the IronPython interactive console.
-
Start the IronPython console from the tutorial directory (see Introduction for details).
-
Initialize Windows Forms by loading the winforms
module/script:
import winforms
Python modules get automatically initialized (executed)
upon import so the Windows Forms initialization code has executed as part of the
import statement.
-
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 *
-
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.
-
Now set the form Text property:
f.Text = "My First Interactive Application"
-
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 similar to:
>>> (System.Windows.Forms.Form, Text: My
First Interactive Application, <System.Windows.Forms.MouseEventArgs object at
0x02324551>)
Now remove the click handler because we're going to
further develop it.
f.Click -= click
-
Use dir() function to explore the MouseEventArgs
dir(MouseEventArgs)
set1 = set(dir(MouseEventArgs)) set2 = set(dir(object))
list(set1-set2)
['get_Clicks', 'Button', 'get_Delta', 'get_Button',
'get_Location', 'get_X', 'get_Y', 'Delta', 'Empty', 'Clicks', 'Y', 'X',
'Location']
-
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)
-
Register the event handler:
f.Click += click
-
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"
-
After a few moments of clicking, the form will get quite crowded,
so we can clear it out:
f.Controls.Clear()
f.Close()
-
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.
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.
-
Start the IronPython console from the tutorial directory (see Introduction for details).
-
Initialize Windows Presentation Foundation:
from avalon import *
-
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 "size to content",
the window shrinks.
-
Let's add the content now:
w.Content = TextBlock()
w.Content.Text = "Hello IronPython!"
w.Content.FontSize = 50
-
Remove the window content:
w.Content = None
-
If you are continuing from the Task 1, proceed to step 2.
Otherwise, please follow the steps 1-3 from the Task 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:
from avalon import *
w = Window()
w.Show()
w.Content = LoadXaml("calc.xaml")
If you flowed into this task from Task 1, then
w.SizeToContent may have been reset to manual, but in either case, you may need
to manually drag the window's border down and to the right to see the calculator
UI.
-
Let's walk the calculator's object model (function Walk is defined
in the avalon.py file)
for n in Walk(w): print n
System.Windows.Window
Canvas Canvas Rectangle 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
-
Using Python's list comprehension syntax we can capture all
buttons in the calculator:
[ n for n in Walk(w) if isinstance(n, Button) ]
System.Windows.Controls.Button
The console printed the list of all buttons. To save
the button list object in a variable, use the interpreter's "_" variable, which
always holds the last non-None value result printed by the console:
buttons = _
-
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)
-
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
-
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
-
At this point you can click on the calculator buttons and evaluate
expressions.
-
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 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).
-
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 = ""
-
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 this way with minimal
setup. The advantage is that changes are visible immediately and modifications
are happening to a live system and don't require recompilation.
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
simple interactive application in Windows Forms and created two applications
using Windows Presentation Foundation.
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: 20 minutes
The objective of this tutorial is to explore COM interoperability
from IronPython.
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 the COM object from IronPython.
-
Open the "SDK Command Prompt" or "Visual Studio 2005 Command
Prompt" from the start menu.
-
The .NET Frameworks SDK comes with the "tlbimp" tool which
converts a COM type library into .NET metadata information -- the interop
assembly. The COM object library we want to use from IronPython is msagent,
located in %SystemRoot%\msagent\agentsvr.exe. Use the tlbimp tool to generate
the interop assembly for msagent (tlbimp writes to the current
directory):
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
-
The .NET metadata information was saved in
AgentServerObjects.dll. Use the ildasm tool to explore the generated
DLL:
ildasm AgentServerObjects.dll
-
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.
-
When you are done exploring the AgentServerObjects.dll, close the
ildasm tool. Copy AgentServerObjects.dll to the tutorial directory, and then
close the Windows command prompt.
-
Start the IronPython console from the tutorial directory (see Introduction for details).
-
Using the "AddReferenceToFile" function in the clr module, add a
reference to the AgentServerObjects.dll:
import clr
clr.AddReferenceToFile("AgentServerObjects.dll")
-
Import the contents of the AgentServerObjects namespace and
explore the contents using dir():
from AgentServerObjects import *
dir()
['AgentServer', 'AgentServerClass', 'IAgent',
'IAgentAudioOutputProperties', 'IAgentAudioOutputPropertiesEx', 'IAgentBalloon',
'IAgentBalloonEx', 'IAgentCharacter', 'IAgentCharacterEx', 'IAgentCommand',
'IAgentCommandEx', 'IAgentCommandWindow', 'IAgentCommands', 'IAgentCommandsEx',
'IAgentEx', 'IAgentNotifySink', 'IAgentNotifySinkEx', 'IAgentPropertySheet',
'IAgentSpeechInputProperties', 'IAgentUserInput', '__builtins__', '__doc__',
'__name__', 'clr']
-
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', 'Finalize', 'GetAttrNames',
'GetCharacter', 'GetHashCode', 'GetLifetimeService', 'GetMemberNames',
'GetSuspended', 'GetType', 'InitializeLifetimeService', 'Load',
'MemberwiseClone', 'ReferenceEquals', 'Register', 'ToString', 'Unload',
'Unregister', '__class__', '__delattr__', '__doc__', '__getattribute__',
'__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
'__setattr__', '__str__']
-
Explore the parameters of the Load method on
AgentServerClass:
a.Load.__doc__
>>> a.Load.__doc__
'(int, int) Load(self, object vLoadKey)'
The Load methods have 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 multiple return values.
-
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]
-
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)
103
c.Think("IronPython Tutorial")
104
-
Merlin has a lot of animations he can play. Get list of all the
animations by calling the GetAnimationNames method. Play one of the animations,
"Read", by calling the Play method:
for n in c.GetAnimationNames(): print n
RestPose
Blink
Idle2_2
Idle2_1
Idle1_1
Idle1_3
Idle1_2
Idle1_4
Greet
Idle3_1
GestureUp
GestureDown
GestureLeft
GestureRight
Show
Hide
Hearing_4
Hearing_1
Hearing_2
Hearing_3
Alert
Explain
Processing
Thinking
Searching
Acknowledge
Wave
DontRecognize
Uncertain
Decline
Sad
StopListening
GetAttention
GetAttentionReturn
Idle3_2
Surprised
Congratulate_2
Reading
Announce
Read
ReadReturn
Writing
Write
WriteReturn
Congratulate
Confused
Suggest
MoveRight
MoveLeft
MoveUp
MoveDown
StartListening
WriteContinued
DoMagic1
DoMagic2
LookDown
LookDownBlink
LookDownReturn
LookLeft
LookLeftBlink
LookLeftReturn
LookRight
LookRightBlink
LookRightReturn
LookUp
LookUpBlink
LookUpReturn
ReadContinued
Pleased
GetAttentionContinued
Process
Search
Think
c.Play("Read")
105
-
(Optional) To call a 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)
106
At this point we can have Merlin play animations simply
by calling a function from global namespace:
Congratulate()
-
Stop Merlin's animations, hide him, and exit IronPython
console
c.StopAll(0)
109
c.Hide(0)
In this exercise you will create an instance of Microsoft Word and
use it to check word spellings.
-
Start the IronPython console from the tutorial directory (see Introduction for details).
-
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
-
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()
-
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
-
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
-
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]
-
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()
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.
-
Start the IronPython console from the tutorial directory (see Introduction for details).
-
Import spellcheck.
-
Call spellcheck.correct_word on a correctly spelled word, and
you'll see the function returns the word.
-
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.
-
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 assemblies
generated via tlbimp tool (in the .NET Framework SDK) to control COM objects
from the IronPython environment.
In this tutorial you performed the following exercises.
In this tutorial, you created a COM interop assembly for the
msagent COM object. You used the tlbimp tool which is part of the .NET
Framework SDK. Using the ildasm tool, you explored contents of the interop
assembly.
Then, from IronPython you loaded the interop assembly, created an
instance of the COM object, and instructed Merlin to play different
animations.
You also 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.
This tutorial will walk you through very simple debug session,
debugging a Python script using Microsoft CLR Debugger. See using
Visual Studio if you have a Visual Studio SKU 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.
In this exercise, you will step through a simple Python script in
the Microsoft CLR Debugger.
-
Launch Microsoft CLR Debugger.
-
From the debugger Menu, select Debug / Program to Debug ...
-
For Program, browse to the ipy.exe (located in the installaton
directory)
-
For Arguments, type in debugging.py
-
Change Working directory to the Tutorial directory
-
Click OK
-
From Menu, select File / Open / File. Browse to the Tutorial
directory and select two files to open:
debugging.py
first.py
-
Place breakpoint at the following line of the debugging.py file
(Place cursor on the line and pres F9):
print first.add(1, 2)
-
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
-
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.
-
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.
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.
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, these members get promoted to public class members when imported
into Python. Private class members, however, will be invisible to Python when
imported.
-
Open the "SDK Command Prompt" or "Visual Studio 2005 Command
Prompt" from the start menu.
-
Open the "csextend.cs" file in notepad. The file is initially
empty
notepad csextend.cs
-
Add using clauses at the beginning of the file:
using System;
using System.Collections;
-
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);
}
}
-
Switch back to the SDK Command Prompt window and build the code
using available csx.bat (C# extension):
csx
-
Start the IronPython console from the tutorial directory (see Introduction for details).
-
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', 'Finalize', 'GetHashCode', 'GetType',
'MemberwiseClone', 'ReferenceEquals', 'ToString', '__class__', '__delattr__',
'__doc__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__str__']
-
Create instance of the Simple class:
s = Simple(10)
print s
IronPython will output:
>>> s = Simple(10)
>>> print
s
Simple<10
-
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.
-
In Notepad, add more functionality to the Simple class.
-
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);
}
}
}
-
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__']
['Equals', 'Finalize', 'GetEnumerator', 'GetHashCode',
'GetType', 'MemberwiseClone', 'ReferenceEquals', 'ToString', '__class__',
'__delattr__', '__doc__', '__getattribute__', '__hash__', '__init__',
'__iter__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__',
'__str__']
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)
>>> 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.
-
Exit IronPython and return back to the C# code to add more
functionality.
-
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);
}
}
-
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)
['op_Addition', 'GetEnumerator', '__radd__', '__iter__',
'__add__']
a = Simple(10)
b = Simple(20)
a + b
Simple<30
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)
>>> a =
Simple(10)
>>> b = Simple(20)
>>> a + b
<Simple object at
0x000000000000002B [Simple<30]
-
Exit IronPython and return back to the C# code to add more
functionality.
-
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);
-
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);
}
}
-
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")
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
-
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 a
breakpoint on the X function - you will see mixed call stack between C# and
Python.
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. It’s worth noting that as Python does not
contain protected class members, that these members get promoted to public class
members when imported into Python. Private class members, however, will be
invisible to Python when imported.
-
Open the "SDK Command Prompt" or "Visual Studio 2005 Command
Prompt" from the start menu.
-
Open the "vbextend.vb" file in notepad. The file is initially
empty
notepad vbextend.vb
-
Add Imports clauses at the beginning of the file:
Imports System
Imports System.Collections
-
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
-
Switch back to the SDK Command Prompt window and build the code
using the available vbx.bat (Visual Basic extension):
vbx
-
Start the IronPython console from the tutorial directory (see Introduction for details).
-
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', 'Finalize', 'GetHashCode', 'GetType',
'MemberwiseClone', 'ReferenceEquals', 'ToString', '__class__', '__delattr__',
'__doc__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__str__']
-
Create instance of the Simple class:
s = Simple(10)
print s
IronPython will output:
>>> s = Simple(10)
>>> print
s
Simple<10
-
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.
-
Back in Notepad, add more functionality to the vbextend.vb, Simple
class
-
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
-
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)
>>> 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.
-
Exit IronPython and return back to the Visual Basic code to add
more functionality.
-
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
-
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)
['op_Addition', 'GetEnumerator', '__radd__', '__iter__',
'__add__']
a = Simple(10)
b = Simple(20)
a + b
Simple<30
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)
>>> a =
Simple(10)
>>> b = Simple(20)
>>> a + b
<Simple object at
0x000000000000002B [Simple<30]
-
Exit IronPython and return back to the Visual Basic .NET code to
add more functionality.
-
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
-
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
-
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")
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
-
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 a breakpoint on 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.
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.
There's very basic support for editing .py files in Visual Studio
(no coloring or expression completion), but you get .NET debugging support.
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.
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.
-
Open Visual Studio and close any solution or projects you might
have open.
-
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).
-
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.
-
In the properties dialog, you will fill in two fields, the Command
Arguments and Working Directory. For the Command Arguments, enter "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.
-
Now let's save the solution with the File->Save All ...
command. Save it to the Tutorial directory.
-
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.
-
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.
-
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.