Computer Programming for Everybody
The on-line Python community is full of little traditions that aren't immediately clear to the outside observer. These can range from oblique whole-sentence-in-one-hyphenated-string-signatures via the occult (“channeling Guido”) to outrageous, e.g., references to the Spanish Inquisition.
One recurring theme is my apparent possession of a time machine. It's often believed this is just a rhetorical device; when someone reports a bug in Python, I will appear to have gone back in time to fix it, so I can answer “that's already fixed in the current working sources.” It would be easy to fake this effect with other means; however, in this article I will show that I actually do have a time machine. I'll change the settings to let me travel to the future instead of the past, and report back with what I have seen. Let the demonstration begin.
A cardinal in a bright red suit appears and accuses me of heresy. His helpers grab me and throw me into their dungeon, where I await execution. Fortunately, I still have my time machine control device with me and quickly press its reset button.
Oops! It seems I had mixed up reverse and forward, and was accidentally thrown back into medieval times. As they say, “nobody expects the Spanish Inquisition.” Cardinal Biggles, I'll come in again.
We're in a middle school, observing a computer programming class. The year is 2002 or perhaps 2003—it's hard to tell. Each pair of students has a computer in front of them. The computers don't look much different from what we're used to in 2000. In fact, they look like they were bought before that year—in the future, educational budgets are obviously not too different from what they are today. Maybe the biggest difference is they are all running Linux—still uncommon in today's classrooms!
On the screen we see the user interface of an interactive Python development environment. The development environment appears to be derived from IDLE, the current Interactive DeveLopment Environment for Python in Python, but it has undergone considerable improvements compared to what's on the web site in early 2000. Let's have a look.
We walk up to two girls in the back of the class who are busy debugging a Barbie dress-up game used as an exercise. Not all groups are using the same examples, by the way. We see kids manipulating characters from a Pokemon-like game, controlling robot fighters, constructing mathematical-looking curves and painting dazzling abstract color patterns on their screens. We even hear some computer-generated music.
The game doesn't seem to be working quite right yet: Barbie is hanging in mid-air in a most uncomfortable pose. The girls are engaged in a mild dispute over the right way to fix it. They decide to run the program in slow motion so they can see exactly where the code goes wrong. One girl presses a pink heart-shaped button labeled “run” (she has customized IDLE's user interface to match her taste...) and a menu with run options appears. Among these is a control to choose the execution speed (from tortoise to hare) and a separate checkbox for stepping. The girl leaves the stepping choice unchecked and clicks on tortoise. The program now starts executing line-for-line, highlighting the current line in the source window and showing the variables in a separate window.
I notice the source window seems to keep the entire current function in view so as to minimize scrolling and maximize context. The background takes on a slightly different shade once a line has been executed, giving us an idea of code coverage. When the program hits an if or while statement, the outcome of the test is indicated by briefly flashing the line in green (true) or red (false). Variables are arranged according to frequency of use; whenever a variable changes, its background lights up. Variables not actually used by the program are grayed out. Whenever a function is called, a new window showing its variables pops up, overlapping the previous function's variable window but slightly offset below, suggesting a stack of variable windows. Clearly it represents the call stack.
After a few minutes of happy slo-mo execution, the program goes off into a lengthy subroutine with which the girls appear to be familiar. It seems to count to 100 doing little interesting work, but taking several program steps for each iteration. The girls quickly press a button labeled “boring” and the session picks up the pace again. The “boring” button appears to have marked the function as permanently uninteresting; later calls to this function are treated as a single step instead of stepping into it.
Whoa! The program, running in slow motion, now appears to have hit the same snag we saw previously. Barbie is uncomfortable again, but the program continues to run—things are getting worse! The girl closest to the mouse quickly presses the stop button, and asks the other what to do now. Her friend says to hit the “reverse step” button a few times. This takes the program back in time and the display unwinds as well, until Barbie is fully unknotted. The girls now carefully take the program forward from this point one step at a time, until the first sign of trouble appears. At each step, they look for suspicious changes in the variables window.
Aha! The bug is found. A method argument had a constant value, but should be a variable instead. The code is changed, the reverse step button pressed, then the run button activated again; Barbie dances around as if she was born to dance. Note that the execution didn't have to be restarted from the beginning. Are dynamic interpreted languages great, or what?!
We leave our two girls alone and join two boys in the next row. They are working on a networked game they just downloaded from the Web. (The girls got the parts for their Barbie program from the teacher.) The boys bring up a program dissection tool from a menu customized in extreme-gothic style. The boys are looking for the low-level network calls: the teacher has suggested that a nice exercise would be to add a network performance monitoring feature to the game, which should show the network activity generated by each player. But first they need to find the networking code!
The boys rotate through a number of structural diagrams of the program, each looking at it from a different point of view: module hierarchy, class inheritance diagram, call graph and so on. None of these seem to give them what they are looking for, so they decide to fire up a search instead. Hey! That doesn't look like the familiar “find string” dialog at all: it looks more like a search engine, maybe Google or Infoseek. Indeed, the results of the search for “network” are sorted by relevancy. Each result is displayed with a little bit of context and several buttons to view the result in the various views.
The boys click on various buttons (they're obviously new at this too!). We notice that the views try to be helpful: they scroll, zoom or distort to show the context of the search result we select. In one of the more detailed views, we see the source code of a method that sends messages to the network.
We now want to know if this is the routine we're looking for. The system can't answer that question directly, but it can answer two others that help us. First, it can find all the call sites for this method. Second, it can look for other “similar” routines. How does it define “similar”? Hard to say exactly, and unnecessary; something like “uses some of the same externalities or instance attributes” might get close. In any case, matches are sorted by relevance again, and the boys deal with it the same way they deal with web search engines: inspect a few items near the top of the list until they're satisfied they have found the right method. The boys find one other method that looks almost identical, and checking for call sites shows that it's never used! Looking more carefully, it seems to be a debugging version of the same routine, left in the code “just in case”; we can safely ignore it.
Another question this system can answer for us: how frequently is this function called? That's important because we want to instrument it. To find out, we run the program with the profiler enabled. Fortunately, there are some other players in the class, so we get to see some action right away: our robotic character runs after others like it through doom-laden corridors, while in a separate window the profiler's statistics light up, showing repetitive patterns as the game goes through its phases.
When we feel we've collected enough data, we abandon the chase and look at the profile data. Opening the source code window for the method we selected earlier, we find that the background has been colorized according to use: the most frequently used lines have the most colorful background. The most obvious conclusion we draw from this is that there's a very short common path through the code and the other paths are rarely taken. A huge piece of exception-handling code has not been touched at all. With a few mouse clicks, we open a window with details about this function: it was called 9,245 times, total execution time was 2.4 seconds, averaging 0.26 msec per call. We also see the callers, sorted by call frequency, and we can hyperlink to the call sites.
The boys have their work cut out for them. It's time to leave them alone with their programming exercise and pay a visit to the teacher. Her computer is networked with all the students' computers, and a status display summarizes what each pair of students is (and has been) doing. One of the summary windows is flashing: the students are asking for help or have handed in their project for review. Since this is an ordinary classroom, the students rarely use this feature to get help, unless the teacher is busy helping someone else; but in a distance learning situation, a teacher can use it to chat with students while looking at their project on her own screen. She can even drive the student's user interface this way, to show them how something's done or to fix an error.
This time, it seems that a student's project is ready for review. The teacher clicks on “download project” in the flashing window, and a local copy of the student's project is created and opened.
The teacher pulls “verify project” from her File menu now, and we see the project verifier churn away on the modules in the project. No errors are found; a few warnings show up, such as unreferenced variables and methods, and some style issues (irregular indentation and a bit of spaghetti-code), but nothing to make the teacher frown. After all, these are 14-year old kids, so we can cut them some slack! When the verifier is done, it displays an overall structural diagram of the program. One nice touch: since the goal of this project was to extend an existing project, it uses a different background color for classes and methods that the students have changed, and a different color again for totally new ones. The teacher samples a few changed and new methods, then decides to run the program.
Our teacher trusts her students and is proud of them—but she wasn't born yesterday, and she knows from experience that student projects can contain pranks or accidental bugs. Therefore, she runs the student program in “restricted execution mode”. This limits the program's ability to access files or programs outside its own project directory and prevents accidental or intentional mishaps on the teacher's system. The student projects are chosen to minimize the needs for such access; the teacher has configured her environment restrictions to allow access to certain trusted applications and the network.
The program runs: it is a simple puzzle game with a 3-D user interface. The teacher interacts with it, looking for ways to break it. The program stands up to scrutiny: it behaves correctly even in the face of incorrect input. The teacher now inspects the profile window for irregularities; in particular, looking for performance snags and code coverage. Everything checks out: the students deserve an A. The teacher opens an annotation window for the project and enters her notes to the students there; a click on the “send feedback” button mails her notes back to the students. She gets no time to relax: two students need help understanding the inheritance diagram of a large application they are just starting to dissect, while another pair have crashed their machine for the third time in a row. Some things never change.
We leave the teacher to her hectic job, and decide we've seen enough for a day; it's time to press the button on our time machine control again.
Phew! Wasn't that exciting? I wish we could've stayed longer, talked some to the students and seen some more software demonstrations. While I was there, I noticed some interesting new things on the Linux desktop that I didn't get to have a good look at. But LJ's editor is waiting for my copy, and it seems I can't use the time machine to extend deadlines!
For some references you can follow without time travel, see Resources.
Guido van Rossum (guido@CNRI.Reston.VA.US) is the creator of Python, one of the major free scripting languages (he prefers to refer to Python as a very high-level language). In 1995, he moved to the U.S. and he now lives and works in Reston, Virginia, for CNRI (the Corporation for National Research Initiatives). At CNRI, he heads a research group working on CP4E (using Python in education) and is technical director of the Python Consortium, an international consortium hosted by CNRI, formed to promote and further the development and use of Python.