Reinteract

Don't just record history, change it!

Reinteract is a system for interactive experimentation with python. You enter Python code and expressions and immediately see the results. What distinguishes Reinteract from a shell (such as IPython or the builtin interactive mode) is that you can go back and edit expressions you entered earlier and the results will flow through the part of the worksheet after the changed portion.

Obviously, going back and changing previously edited expressions works better for some things than others. If your code already wrote something to a file, it can't be unwritten. Things that Reinteract works well for include numerical experimentation, lightweight signal processing and data analysis, and just plain playing around with the Python language.

Reinteract has a fair bit of intelligence for editing Python code; it does syntax highlighting, audo-indentation, and highlighting of matching parentheses. I'd like to extend it in the future to do completion as well.

Reinteract supports custom formatting of results. If an expression returns a subclass of reinteract.CustomResult, then the result gets to control how it will be displayed in the worksheet. Custom result types currently in reinteract are line graphs and images via matplotlib, and a "play" result which displays as a button that can be clicked to play an array of data as a sound.

Project Site

What it looks like


Screencast (5 minutes)

Trying it out

Information about checking it out from source control and trying it out can be found on the project site.

Dependencies

pygtk
Replot is written the pygtk bindings to the GTK+ user interface toolkit. These bindings should be present on most current Linux distributions. GTK+ and pygtk also work well on Windows, and it's probably even possible to get them to work on OS X. But I haven't done any testing other than on Linux. I've been using GTK+-2.10 and GTK+-2.12, though reinteract might work with older versions. There is one bug fix scheduled for 2.12.2 that fixes a misbehavior when deleting multiple statements at once.
numpy
The examples and replot/replay modules use numpy, though the core should work without it.
matplotlib
The plot() and imshow() commands in the replot module are based on matplotlib.
sox
The play() command in the replay module uses sox for output. sox should be present on just about every Linux system. (It would be really simple to use a more inherently portable Python module like pymedia if someone was interested in getting it to work on Windows.)

Ideas for future development

How it works

The basic idea of reinteract is that reinteract saves a copy of the scope dictionary after every statement. If it needs to recompile starting from a particular statement, it then has the right scope to execute that statement in.

	# scope: { a: 1, l: [1,2,3,4] }
	b = a + 1
	# scope: { a: 1, b: 2, l: [1,2,3,4] }

This is pretty efficient because variables that stay the same can be shared between successive scopes, so the only memory used is for the scope dictionaries themselves. However, if reinteract detects that a statement modifies a variable, then it makes a shallow copy of the variable using copy.copy()

	# scope: { a: 1, l: [1,2,3,4] }
	# reinteract does scope['l'] = copy.copy(scope['l'])
	l[0] = 5
	# scope: { a: 1, l: [5,2,3,4] }

Some types of modification are easy to detect, like the slice assignment above or assigning to an attribute. However, some modifications are harder to detect. Compare:

	l.append(2)    # modifies l
	l.count(2)     # no modification

The rule that reinteract uses is that a bare method call like the ones is probably a modification, and it conservatively makes a copy in that case. You can avoid a copy by using a temporary variable or explicitly printing the result.

	print l.count(2)    # no copy
	c = l.count(2); c   # no copy