I've been home sick for a few days.  Between sleeps, generally pinned down by a cat, I've been fairly productive.  I bought a couple of books:  "Dive into Python" by Mark Pilgrim, and "Programming Python" by Mark Lutz, and installed ActivePython and IronPython, the new Python for .NET being implemented as an open source project by Jim Hugunin and a cast of thousands (okay, one or two) at Microsoft. 

IronPython is a ton of fun.  I read through the code Thursday night and played with the Console.  Friday morning I woke up, started coding (laptop displaced cat to a position under the knees) and an hour later I was running Python code inside the large Avalon application I've been working on.  An hour after that, I was automating said application using Python code. 

IronPython has a command-line Console that lets you type in Python interactively.  It is built on a class called PythonEngine that reads and writes from a simple wrapper called IConsole (IConsole has methods for reading and writing lines of text).  The biggest hurdle I had was that the PythonEngine expects to be in charge, running the main loop.  I considered writing a Console with a ReadLine method that blocked and got characters from an Avalon TextBox...but I choose to reverse control and wrote a little PythonProcessor class that you can pass lines of code to for execution.  I'll post the code, and then explain:

 
namespace AddInConsole
{
	using System;
	using System.Collections;
	using System.IO;
	using System.Text;
	using System.Reflection;
	using IronPython.AST;
	using IronPython.Modules;
	using IronPython.Objects;
	public class PythonProcessor
	{
		private module module = null;
		private Frame frame = null;
		private StringBuilder inputBuffer = new StringBuilder();
		private bool isAccumulating = false;
		private StreamWriter outputWriter;
		public PythonProcessor(Stream outputStream)
		{
			object o = SnippetMaker.snippetAssembly; // Ensure SnippetMaker static ctor gets called ???
			this.module = new module();
			this.module.__name__ = "__main__";
			this.frame = new Frame(this.module);
			// Only way to set stdout is to do it globally
			sys.stdout = new PythonFile(outputStream, "w", false);
			this.outputWriter = new StreamWriter(outputStream);
		}
		private void ReportException(Exception e)
		{
			this.outputWriter.WriteLine(e.ToString());
		}
		public module Module
		{
			get
			{
				return this.module;
			}
		}
		public Frame Frame
		{
			get
			{
				return this.frame;
			}
		}
		private bool IsAccumulating
		{
			get
			{
				return this.isAccumulating;
			}
			set
			{
				if (value == false)
				{
					this.inputBuffer = new StringBuilder();
				}
				this.isAccumulating = value;
			}
		}
		private void AppendLine(string text)
		{
			this.inputBuffer.Append(text);
			this.inputBuffer.Append("\n");
		}
		private string CurrentExpression
		{
			get
			{
				return this.inputBuffer.ToString();
			}
		}
		private Stmt Parse(string text, bool endOfInput)
		{
			Parser p = Parser.fromString(text);
			try
			{
				return p.parseStmt();
			}
			catch (PythonSyntaxError se)
			{
				if (endOfInput)
				{
					throw se;
				}
				if (se.Message.IndexOf("<eof>") > 0) return null;
				if (se.Message.IndexOf("<newline>") > 0) return null;
				if (se.Message.IndexOf("<dedent>") > 0) return null;
				throw se;
			}
		}
		public bool EvaluateLine(string line)
		{
			if (line == null)
				return false;
			this.AppendLine(line);
			Stmt statement = null;
			try
			{
				statement = this.Parse(this.CurrentExpression, line.Length == 0);
			}
			catch (Exception e)
			{
				this.ReportException(e);
				this.IsAccumulating = false;
				return true;
			}
			if (!this.IsAccumulating || 
				(line.Length == 0))
			{
				this.IsAccumulating = true;
				if (statement != null)
				{
					this.IsAccumulating = false;
					this.ExecuteStatement(statement);
					return true;
				}
			}
			return false;
		}
		public void ExecuteStatement(Stmt statement)
		{
			try
			{
				FrameCode frameCode = SnippetMaker.generate(statement, "input", true);
				frameCode.Run(this.Frame);
			}
			catch (Exception e)
			{
				this.ReportException(e);
			}
		}
		public void SetVariable(string name, object val)
		{
			Ops.SetAttr(this.Module, name, val);
		}
	}
}