Interactive Python Scripting Example


For my first blog entry I wanted to write an article explaining what a dynamic language is. Since I prefer to think concretely about things, I’m going to start with an example of using a dynamic language (Python) to play with an interesting API. For now, my working definition of a dynamic language is any language which could be easily used for the following example.

I’m going to use Windows.Forms as a standard API for this demo. (Note: This probably won’t work on Mono-1.0, but it shouldn’t be hard to port this example to GTK#. Here’s an example of using IronPython with GTK# for those who are interested.) If you want to follow along, you can download IronPython-0.6. Remember that IronPython is at a pre-alpha 0.6 release, so don’t be surprised if it has bugs.

Formatting note: I’m sorry if these examples are a little hard to read.  I’m having trouble talking my blog editor into showing PRE blocks correctly.  The extra lines are an artifact of the blog editor and are not intentional.

The first step is to bring up the interactive interpreter. This should be simple. See README.html in the IronPython zip file for a few more hints on different platforms.

C:\IronPython\bin> IronPythonConsole 
>>>

Whenever I bring up a new interactive interpreter I feel compelled to get it to do some tricky math to let me know that it’s really working.

>>> 2+2 
4

Next we’ll create a new Form and show the window.

>>> from System.Windows.Forms import * 
>>> from System.Drawing import * 
>>> f = Form(Text="Experiment #1") 
>>> f.ShowDialog()

You should see a nice little window popup now. However, there’s a big problem. f.ShowDialog has taken over the main thread and it’s impossible to type anything more at the interpreter prompt. Let’s fix that by first closing the window (click on the ‘X’). You should then see:

Cancel 
>>> 

Now let’s start the ShowDialog method in its own thread so that it doesn’t block the interpreter

>>> from System.Threading import * 
>>> Thread(f.ShowDialog).Start() 
>>> 

Now we have our window showing and the interpreter is still “live” for us to keep typing at. We can change different properties of the form like this:

>>> f.MinimizeBox = False 
>>> f.Size = Size(200,200) 
>>> f.Text = "New Title"

It’s fun playing with the form, but we’d like to add a button to give it some more functionality.

>>> b = Button(Text="Push Me") 
>>> f.Controls.Add(b) 
System.ArgumentException: Controls created on one thread cannot be parented to a contro <snip>

Oops! That didn’t work as well as we might have liked. This is our second experience of the interactions between the threading model in Windows.Forms and the interactive interpreter. There are a number of creative solutions to this problem (see …) To keep things simple for this blog, we’re going to just close the window again, add the control, and then restart the main event thread. Don’t forget to close the window before proceeding with the next two lines.

>>> f.Controls.Add(b) 
>>> Thread(f.ShowDialog).Start()

Now we should see the form appear again with a button on it. We can play with the button to make it bigger and brighter.

>>> b.Size = f.ClientSize 
>>> b.BackColor = Color.Yellow

Let’s make the button do something interesting. In preparation for this we’re going to create a list of the named colors in the Color class. This is the one piece of code that includes the most Python specific code. If you’re unfamiliar with Python you should just treat most of this code as magic. Dive Into Python is a good first resource for an experienced programmer who wants to become familiar with Python. It also has a very short section on indentation of blocks in Python. To make this example work, the one thing you should be aware of is that for each line which begins with “…” you will need to type a TAB to create the correct indentation and get your program to run correctly.

>>> colors = [] 
>>> for name in dir(Color): 
...    c = getattr(Color, name) # Use TAB to indent this line and below 
...    if type(c) == Color: colors.append(c) 
... 
>>> colors 
[Color [AliceBlue], Color [AntiqueWhite], Color [Aqua], Color [Aquamarine], Colo 
<snip> 
ke], Color [Yellow], Color [YellowGreen]]

Now that we have this list of colors, we can set the background color of the button to these colors in sequence. This requires creating a function to do the work and then setting that function to be run on the Click event. (Note: def is used in Python to define a new function, and just like above you should use TAB to indent each line in the body of the function.)

>>> index = 0 
>>> def push(b, e): 
...    global index # Use TAB to indent this line and below 
...    b.BackColor = colors[index] 
...    b.Text = colors[index].Name 
...    index += 1 
... 
>>> b.Click += push 
>>> 

Click on the button to cycle through all of the named colors. Finally, we need to exit the interpreter. This is done with ‘Ctrl-Z’.

>>> ^Z 
C:\IronPython\bin>

Now that we’re done playing with the interactive shell, we can put the results of our experiment into a file for running stand-alone. Now that we’re done experimenting, we can simplify the code a little bit by moving some of the property settings into the Form and Button constructors. We can also leave out all of the separate Thread nonsense and just call f.ShowDialog at the end of setting everything up.

------ colors.py ------ 
from System.Windows.Forms import * 
from System.Drawing import * 
f = Form(Text="Playing with colors", MinimizeBox=False, Size=Size(200,200)) 
b = Button(Text="Push Me", Size=f.ClientSize) 
f.Controls.Add(b) 
colors = [] 
for name in dir(Color): 
    c = getattr(Color, name) 
    if type(c) == Color: colors.append(c) 
 
index = 0 
def push(b, e): 
    global index 
    b.BackColor = colors[index] 
    b.Text = colors[index].Name 
    index += 1 
 
b.Click += push 
f.ShowDialog() 
------

Now you can run this as a stand-alone program:

C:\IronPython\bin> IronPythonConsole colors.py

I hope that this has helpful to give newcomers a feel for what IronPython feels like to code in and to give even experienced Python programmers a few more concrete examples of how to work with the CLR. As one last recommendation to anyone new to IronPython, I’d like to pass on this advice overheard on the ironpython users mailing list. IronPython will sometimes look more like VB than C# in the way that it uses CLR libraries and you should look at both C# and VB examples if you’re confused about how to call something in IronPython.


Comments (36)

  1. Looks like a lot of newlines got whacked 🙂

  2. Bart Jacobs says:

    A quick comment concerning the threading problems: the "fix" used in this article is, of course, both simple and wrong, in the sense that it does not comply with the Windows Forms threading model. Specifically, only the thread that creates a control is allowed to access (methods or properties of) that control. Therefore, the f.ShowDialog calls are non-compliant.

    Note: Once all of the separate Thread nonsense is left out, the code is compliant.

  3. Your newline in the console session seem to be MIA.

  4. JosephCooney says:

    Jim Hugunin has a weblog

  5. Jim Hugunin says:

    Thanks for the formatting comments. I’ve at least partly fixed the issue and added comment to the start of the blog.

    The threading issue is a more serious one, and not one that I can fix with a couple of edits. I’ll still defend this example as showing a good model for the process of interactively exploring an API with an interactive interpreter. Notice that the end-result stand-alone program doesn’t violate the threading model.

    I know that GTK has similar complicated threading issues that make this kind of interactive use dificult in similar ways. I’m curious to play with the new Avalon GUI framework to see what its threading model is like.

  6. Andrew says:

    Thanks for this great intro 🙂

  7. Swaroop C H says:

    I’ve been following IronPython for quite some time now. Personally, I think combining Python and the CLR is like a dream 🙂

    So, Jim, what other dynamic languages will you be working on? How much of a focus is Python in the Microsoft radar??

    – Swaroop

    http://www.python.g2swaroop.net

    "A Byte of Python" – The Beginner’s book on Python

  8. IM says:

    This is very cool. If somebody were to add intellisense to the interpreter, this would be very very cool. 🙂

  9. Eric Newton says:

    I always thought one thread in a WinForms app couldnt modify controls’ properties on the main UI thread, and obviously you cant have a control collection that spans threads

    Anyways, IronPython looks cool for debugging Xml and DataSet navigation until C# E&C is done.

  10. Andy Todd says:

    Very interesting post, thanks.

    Just a very minor stylistic quibble, the ‘from module import *’ convention is generally frowned upon as it pollutes the current namespace. The more usually preferred style is ‘import module’ and then any subsequent references are prefaced with the module name, making it explicit where they are from.

    For instance, in your example above it’s not necessarily clear where the Form class comes from. Theoretically it could be either of the packages you import and reading this code for the first time I would have to check the API documentation to be absolutely sure.

  11. Jim Hugunin says:

    The ‘from module import *’ style question is interesting. I REALLY like to use this style when working at an interactive interpreter. If I’m confused about where a type came from it’s trivial to find out, i.e.

    >>> Form

    <type ‘System.Windows.Forms.Form’>

    The best style for code in files is less obvious to me. A big part of what I like about Python is its lightweight feel, and using significantly more verbose conventions doesn’t really appeal to me. In the Java world, there appears to be growing consensus that the best style is the equivalent of Python’s

    from System.Windows.Forms import Form, Button

    This makes the explicit imports very visible to someone reading the file, but keeps the references in the code itself clean.

    Personally, I think that this is an area where IDE support can make all the difference. If it’s easy for my editor to let me jump to the referenced type, like ‘Go to definition’ in VS.Net, then I care a lot less about how explicit the actual ASCII code is.

  12. I love the IronPython command line. It is great for testing out VG.net features we are developing. That library has a declarative-style interface, similar to windows forms.

    The think I miss is the intellisense — is anyone working on that?

  13. Chris Sells says:

    Why not just use the modeless Form.Show instead of the modal Form.ShowDialog?

  14. I’ve been playing with IronPython for a few months now and I must say, I love the rapid throw-together feel of IronPython. Looking forward to seeing what you come up with Jim.

  15. Jim Hugunin says:

    I tried using Form.Show, but then I couldn’t get the button to show up when I added it to the form. I’m guessing there’s a message pump somewhere that I’m supposed to worry about, but I haven’t taken the time to delve in. I welcome anyone who can show me the "correct" way to do something like the interactive example above.

  16. Jim Arnold says:

    ShowDialog() starts its own message pump, which is why your updates work. For the same to happen with Show(), you’ll need to call Application.DoEvents() after each update.

    Jim

  17. kojo adams says:

    hi Jim,

    IronPython does not implement PEP 227 yet. http://www.python.org/peps/pep-0227.html

    Kojo

  18. PN Devlog says:

    Jim Hugunin, the inventor of IronPython (Python for .NET) has got a blog and for his first post gives examples of using IronPython as an interactive scripting environment for .NET. I’d never really thought before about just how useful the…

  19. PN Devlog says:

    Jim Hugunin, the inventor of IronPython (Python for .NET) has got a blog and for his first post gives examples of using IronPython as an interactive scripting environment for .NET. I’d never really thought before about just how useful the…

  20. Looks great,But i need something different in our application. What i Really need is, on Click of that button, i need to run python command, for example ‘print "hello"’.

    Actually There is a

    a) Textbox to type the command

    b) Button to run the command(given in textbox)

    c) Textarea to display the output of the result.

    Please help me over it.

    Thanks

    Senthil Kumar

  21. Jim Hugunin says:

    Thanks for the note. IronPython is still in an early stage and has a number of features left to be implemented before it will be fully compatible with Python 2.3. Nested scopes will obviously be implemented before a 1.0 release. There is no technical reason that nested scopes would be hard to implement on the CLR; however, there are a number of different possible designs to choose from and I’d like to have some time to experiment before settling on one.

  22. Jim Hugunin says:

    It shouldn’t be that hard to write a little C# code around the existing IronPython code to enable this. Take a look at these pictures for what I think is a really cool version of what you’re asking for, but using a tablet PC – http://www.knowing.net/2004/08/30.aspx#a818

  23. Paul says:

    I fail to see how the example demonstrates the properties of a dynamic language. A lot of languages that are not considered dynamic could be used for the example.

  24. Hank Fay says:

    Jim, while you’re working with other dynamic languages, you might want to speak to the Visual FoxPro team (klevy@microsoft.com).

  25. Jim Hugunin says:

    To Paul:

    I realize that a LOT of languages could be used to write the final stand-alone program shown here. However, I’m not aware of very many languages that could be used to build this gui in the interactive way described in the blog. Could you give me an example of a non-dynamic language that could do this?

  26. anonymous says:

    I think that "boo"(http://boo.codehaus.org) will soon have an interactive mode.

  27. Michael says:

    Dear Jim:

    I am a fresh man to IronPython.The sample in this post is so amazing. Wish IronPython better and better!

    When I experience IronPython, I tried these code as below(under the interpreter):

    >>> class FantasySoft:

    … def hello(self):

    … print "Hello,World!"



    Interpreter throws System.Reflection.TargetException: Non-static field requires a target.

    And these three lines code is OK under Python Interpreter.Why the exception is thrown? Whether I made some mistake? Thanks!

  28. Jim Hugunin says:

    You’ve found one of many bugs in this early release of IronPython. classes don’t work at the interactive interpreter, but only work in source files. If you put your code into a .py file and run it like the colors.py example above then this should work. There’s no good reason for this limitation, it’s just a bug. FYI – Another feature that fails badly at the interactive interpreter is multi-line strings using """. I personally don’t use """ strings or class defs at the interactive prompt, so these areas just weren’t tested before the public release.