Caged Python

Sunday 10 July 2011This is more than 13 years old. Be careful.

For a presentation, I wanted to produce samples of Python interactive sessions. I could have opened a terminal window and typed my input, and copied the resulting session and pasted it into a text file, but that’s not repeatable, and is labor intensive and error-prone.

I looked for ways people had done this in the past, and didn’t find the thing that I’m sure is out there, but it’s fun to do it yourself anyway. The code module in the standard library provides most of the heavy lifting, but there’s a little input and output grabbing and tweaking to be done. Here’s what I ended up with:

"""A Python prompt in a cage, for producing prompt sessions."""

import code
import cStringIO as StringIO
import sys
import textwrap 

class CagedPrompt(code.InteractiveConsole):
    def __init__(self):
        env = {'__name__': '__main__'}
        code.InteractiveConsole.__init__(self, env)
        self.out = StringIO.StringIO()

    def run(self, input):
        self.inlines = textwrap.dedent(input).splitlines()
        old_stdout = sys.stdout
        sys.stdout = self.out
        self.interact("Python " + sys.version.split("[")[0])
        sys.stdout = old_stdout
        self.output = self.out.getvalue()

    def raw_input(self, prompt):
        try:
            line = self.inlines.pop(0)
        except IndexError:
            raise EOFError
        if line or prompt == sys.ps2:
            self.write("%s%s\n" % (prompt, line))
        else:
            self.write("\n")
        return line

    def write(self, data):
        self.out.write(data)

def prompt_session(input):
    cp = CagedPrompt()
    cp.run(input)
    return cp.output

if __name__ == '__main__':
    TEST_INPUT = """\
        2+2
        import random
        random.random()
        class Foo:
            pass


        f = Foo()
        f
        """

    print prompt_session(TEST_INPUT)

Running it produces:

$ python cagedprompt.py
Python 2.6.6 (r266:84297, Aug 24 2010, 18:13:38)
>>> 2+2
4
>>> import random
>>> random.random()
0.48519166487066712
>>> class Foo:
...     pass
...

>>> f = Foo()
>>> f
<__main__.Foo instance at 0x00000000025B6448>

There’s a few small ways the output differs from a real interactive session: the initial banner is shorter, and a blank line in the input will produce a true blank line in the output. These make the output nicer to use for presentations. Now I can use the prompt_session function to get the textual output of a Python prompt fed with a particular input. Nice.

Comments

[gravatar]
Not sure if this helps, but if you are on a Linux command line, you can also use the script command. This basically logs all information from stdout and stderr to a file.
[gravatar]
Thanks @farzadb82, I hadn't heard of the script command. But that still leaves me entering input by hand, and redoing it by hand if the input needs to be different.
[gravatar]
How about using Crunchy? [ Or perhaps Reinteract (?) - I have only seen a demo of it.]
[gravatar]
I believe player piano may qualify as the "thing you're sure is out there": http://blog.wearpants.org/playerpiano-amaze-your-friends/

It's actually designed so you can bang away at the keyboard as if you were actually typing at the interactive prompt, but I expect you could extract the recorded doctests in other ways easily enough.
[gravatar]
The trouble with Python is that it's often far simpler and more fun to invent your own solution than it is to use someone else's.
[gravatar]
Charles Anderson 11:45 AM on 11 Jul 2011
See also - http://discorporate.us/projects/sliderepl/sliderepl.py. It's very useful for demo'ing code while minimizing typos in front of the audience.

Add a comment:

Ignore this:
Leave this empty:
Name is required. Either email or web are required. Email won't be displayed and I won't spam you. Your web site won't be indexed by search engines.
Don't put anything here:
Leave this empty:
Comment text is Markdown.