Caged python

Sunday 10 July 2011

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.

tagged: » 6 reactions

Comments

[gravatar]
farzadb82 10:43 AM on 10 Jul 2011

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]
Ned Batchelder 10:55 AM on 10 Jul 2011

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]
André Roberge 11:17 AM on 10 Jul 2011

How about using Crunchy? [ Or perhaps Reinteract (?) - I have only seen a demo of it.]

[gravatar]
Nick Coghlan 6:49 PM on 10 Jul 2011

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]
Simon Brunning 1:48 AM on 11 Jul 2011

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:

name
email
Ignore this:
not displayed and no spam.
Leave this empty:
www
not searched.
 
Name and either email or www are required.
Don't put anything here:
Leave this empty:
URLs auto-link and some tags are allowed: <a><b><i><p><br><pre>.