Documentation
@nosent ILeo_doc.txt
Documentation
Introduction
Installation
Accessing IPython from Leo
Accessing Leo nodes from IPython
Cl definitions
Special node types
Custom push
Code snippets
Acknowledgements and history
@chapters
@settings
@@string ipython_argv = ipython -pylab
@enabled-plugins
@ipy-startup
b
Some classes P
File-like access
csv data
String list
slist to leo
Class tests
csvr
tempfile
rfile
strlist
IPython script push tests
Direct variables
bar
pylab tests
Generate testarr
testarr
Call plotter on testarr
test stuff
@ipy-results
foo
spam
?
?Direct children of this node will be pushed at ipython bridge startup

This node itself will *not* be pushed
print "world"
def rfile(body,n): """ @cl rfile

produces a StringIO (file like obj of the rest of the body) """

import StringIO return StringIO.StringIO(body)

def tmpfile(body,n): """ @cl tmpfile

Produces a temporary file, with node body as contents

""" import tempfile h, fname = tempfile.mkstemp() f = open(fname,'w') f.write(body) f.close() return fname
@cl tmpfile

Hello
?
@cl rfile These lines should be readable
@others
def csvdata(body,n): import csv d = csv.Sniffer().sniff(body) reader = csv.reader(body.splitlines(), dialect = d) return reader
@cl csvdata

a,b,b 1,2,2
@cl "hello world"
import IPython.genutils def slist(body,n): return IPython.genutils.SList(body.splitlines())
@cl slist hello world on many lines
import ipy_leo @ipy_leo.format_for_leo.when_type(IPython.genutils.SList) def format_slist(obj): return "@cl slist\n" + obj.n
?
@wrap @nocolor
# test ipython script 'cleanup' with complex blocks 1+2 print "hello" 3+4

def f(x): return x.upper()

if 0: print "foo" else: print "bar"

def g(): pass

g()

if 1: if 1: print "hello"

print "world"

if 1: print "hello"

print "word" else: print "foo"

print "bar" print "baz"

try: raise Exception except: print "exc ok"
@cl rfile hello world and whatever
@others
ipython.py
Introduction ============

The purpose of ILeo, or leo-ipython bridge, is being a two-way communication channel between Leo and IPython. The level of integration is much deeper than conventional integration in IDEs; most notably, you are able to store *data* in Leo nodes, in addition to mere program code. The possibilities of this are endless, and this degree of integration has not been seen previously in the python world.

IPython users are accustomed to using things like %edit to produce non-trivial functions/classes (i.e. something that they don't want to enter directly on the interactive prompt, but creating a proper script/module involves too much overhead). In ILeo, this task consists just going to the Leo window, creating a node and writing the code there, and pressing alt+I (push-to-ipython).

Obviously, you can save the Leo document as usual - this is a great advantage of ILeo over using %edit, you can save your experimental scripts all at one time, without having to organize them into script/module files (before you really want to, of course!)
Installation ============

You need at least Leo 4.4.7, and the development version of IPython (ILeo will be incorporated to IPython 0.8.3).

You can get IPython from Launchpad by installing bzr and doing

bzr branch lp:ipython

and running "setup.py install".

You need to enable the 'ipython.py' plugin in Leo:

- Help -> Open LeoSettings.leo

- Edit @settings-->Plugins-->@enabled-plugins, add/uncomment 'ipython.py'

- Alternatively, you can add @settings-->@enabled-plugins with body ipython.py to your leo document.

- Restart Leo. Be sure that you have the console window open (start leo.py from console, or double-click leo.py on windows)

- Press alt+5 OR alt-x start-ipython to launch IPython in the console that started leo. You can start entering IPython commands normally, and Leo will keep running at the same time.
Accessing IPython from Leo ==========================

IPython code ------------

Just enter IPython commands on a Leo node and press alt-I to execute push-to-ipython in order to execute the script in IPython. 'commands' is interpreted loosely here - you can enter function and class definitions, in addition to the things you would usually enter at IPython prompt - calculations, system commands etc.

Everything that would be legal to enter on IPython prompt is legal to execute from ILeo.

Results will be shows in Leo log window for convenience, in addition to the console.

Suppose that a node had the following contents: {{{ 1+2 print "hello" 3+4

def f(x): return x.upper()

f('hello world') }}}

If you press alt+I on that node, you will see the following in Leo log window (IPython tab):

{{{ In: 1+2 <2> 3 In: 3+4 <4> 7 In: f('hello world') <6> 'HELLO WORLD' }}}

(numbers like <6> mean IPython output history indices; the actual object can be referenced with _6 as usual in IPython).

Plain Python code -----------------

If the headline of the node ends with capital P, alt-I will not run the code through IPython translation mechanism but use the direct python 'exec' statement (in IPython user namespace) to execute the code. It wont be shown in IPython history, and sometimes it is safer (and more efficient) to execute things as plain Python statements. Large class definitions are good candidates for P nodes.
Accessing Leo nodes from IPython ================================

The real fun starts when you start entering text to leo nodes, and are using that as data (input/output) for your IPython work.

Accessing Leo nodes happens through the variable 'wb' (short for "WorkBook") that exist in the IPython user namespace. Nodes that are directly accessible are the ones that have simple names which could also be Python variable names; 'foo_1' will be accessible directly from IPython, whereas 'my scripts' will not. If you want to access a node with arbitrary headline, add a child node '@a foo' (@a stands for 'anchor'). Then, the parent of '@a foo' is accessible through 'wb.foo'.

You can see what nodes are accessible be entering (in IPython) wb.<TAB>. Example:

[C:leo/src]|12> wb. wb.b wb.tempfile wb.rfile wb.NewHeadline wb.bar wb.Docs wb.strlist wb.csvr [C:leo/src]|12> wb.tempfile <12> <ipy_leo.LeoNode object at 0x044B6D90>

So here, we meet the 'LeoNode' class that is your key to manipulating Leo content from IPython!

LeoNode -------

Suppose that we had a node with headline 'spam' and body:

['12',2222+32]

we can access it from IPython (or from scripts entered into other Leo nodes!) by doing:

C:leo/src]|19> wb.spam.v <19> ['12', 2254]

'v' attribute stands for 'value', which means the node contents will be run through 'eval' and everything you would be able to enter into IPython prompt will be converted to objects. This mechanism can be extended far beyond direct evaluation (see '@cl definitions').

'v' attribute also has a setter, i.e. you can do:

wb.spam.v = "mystring"

Which will result in the node 'spam' having the following text:

'mystring'

What assignment to 'v' does can be configured through generic functions ('simplegeneric' module, will be explained later).

Besides v, you can set the body text directly through

wb.spam.b = "some\nstring",

headline by

wb.spam.h = 'new_headline'

(obviously you must access the node through wb.new_headline from that point onwards), and access the contents as string list (IPython SList) through 'wb.spam.l'.

If you do 'wb.foo.v = 12' when node named 'foo' does not exist, the node titled 'foo' will be automatically created and assigned body 12.

LeoNode also supports go() that focuses the node in the Leo window, and ipush() that simulates pressing alt+I on the node.

You can access unknownAttributes by .uA property dictionary. Unknown attributes allow you to store arbitrary (pickleable) python objects in the Leo nodes; the attributes are stored when you save the .leo document, and recreated when you open the document again. The attributes are not visible anywhere, but can be used for domain-specific metatada. Example:

[C:leo/src]|12> wb.spam.uA['coords'] = (12,222) [C:leo/src]|13> wb.spam.uA <13> {'coords': (12, 222)}

Accessing children with iteration and dict notation ---------------------------------------------------

Sometimes, you may want to treat a node as a 'database', where the nodes children represent elements in the database. You can create a new child node for node 'spam', with headline 'foo bar' like this:

wb.spam['foo bar'] = "Hello"

And assign a new value for it by doing

wb.spam['foo bar'].v = "Hello again"

Note how you can't use .v when you first create the node - i.e. the node needs to be initialized by simple assignment, that will be interpreted as assignment to '.v'. This is a conscious design choice.

If you try to do wb.spam['bar'] = 'Hello', ILeo will assign '@k bar' as the headline for the child instead, because 'bar' is a legal python name (and as such would be incorporated in the workbook namespace). This is done to avoid crowding the workbook namespace with extraneous items. The item will still be accessible as wb.spam['bar']

LeoNodes are iterable, so to see the headlines of all the children of 'spam' do:

for n in wb.spam: print n.h
@cl definitions ===============

If the first line in the body text is of the form '@cl sometext', IPython will evaluate 'sometext' and call the result with the rest of the body when you do 'wb.foo.v'. An example is in place here. Suppose that we have defined a class (I use the term class in a non-python sense here)

{{{ def rfile(body,node): """ @cl rfile

produces a StringIO (file like obj) of the rest of the body """

import StringIO return StringIO.StringIO(body) }}}

(note that node is ignored here - but it could be used to access headline, children etc.),

Now, let's say you have node 'spam' with text

{{{ @cl rfile hello world and whatever }}}

Now, in IPython, we can do this:

{{{ [C:leo/src]|22> f = wb.spam.v [C:leo/src]|23> f <23> <StringIO.StringIO instance at 0x04E7E490> [C:leo/src]|24> f.readline() <24> u'hello\n' [C:leo/src]|25> f.readline() <25> u'world\n' [C:leo/src]|26> f.readline() <26> u'and whatever' [C:leo/src]|27> f.readline() <27> u'' }}}

You should declare new @cl types to make ILeo as convenient your problem domain as possible. For example, a "@cl etree" could return the elementtree object for xml content.
Special node types ==================

@ipy-startup ------------

If this node exist, the *direct children* of this will be pushed to IPython when ILeo is started (you press alt+5). Use it to push your own @cl definitions etc. The contents of of the node itself will be ignored.

@ipy-results ------------

When you create a new node (wb.foo.v = 'stuff'), the node foo will be created as a child of this node. If @ipy-results does not exist, the new node will be created after the currently selected node.

@a nodes --------

You can attach these as children of existing nodes to provide a way to access nodes with arbitrary headlines, or to provide aliases to other nodes. If multiple @a nodes are attached as children of a node, all the names can be used to access the same object.
Acknowledgements & History ==========================

This idea got started when I (Ville) saw this post by Edward Ream (the author of Leo) on IPython developer mailing list:

http://lists.ipython.scipy.org/pipermail/ipython-dev/2008-January/003551.html

I was using FreeMind as mind mapping software, and so I had an immediate use case for Leo (which, incidentally, is superior to FreeMind as mind mapper). The wheels started rolling, I got obsessed with the power of this concept (everything clicked together), and Edwards excitement paralleled mine. Everything was mind-bogglingly easy/trivial, something that is typical of all revolutionary technologies (think Python here).

The discussion that "built" ILeo is here: http://sourceforge.net/forum/forum.php?thread_id=1911662&forum_id=10226

?
Declaring custom push-to-ipython handlers =========================================

Sometimes, you might want to configure what alt+I on a node does. You can do that by creating your own push function and expose it using ipy_leo.expose_ileo_push(f, priority). The function should check whether the node should by handled by the function and raise IPython.ipapi.TryNext if it will not do the handling, giving the next function in the chain a chance to see whether it should handle the push.

This example would print an uppercase version of node body if the node headline ends with U (yes, this is completely useless!):

{{{ def push_upcase(node): if not node.h.endswith('U'): raise TryNext print node.b.upper()

ipy_leo.expose_ileo_push(push_upcase, 12) }}}

(the priority should be between 0-100 - typically, you don't need to care about it and can usually omit the argument altogether)
Example code snippets =====================

Get list of all headlines of all the nodes in leo:

[node.h for node in wb]

Create node with headline 'baz', empty body: wb.baz

Create 10 child nodes for baz, where i is headline and 'Hello ' + i is body:

for i in range(10): wb.baz[i] = 'Hello %d' % i

12
array([[ 0, 1, 2], [ 3, 4, 5], [ 6, 7, 8], [ 9, 10, 11]])
# press alt+i here to plot testarr

plot(wb.testarr.v)
Quickstart: easy_install numpy easy_install matplotlib

Make sure you have '@string ipython-argv = ipython -pylab' in @settings. We currently recommend using TkAgg as the backend (it's also the default)
#press alt+i here to generate an array for plotter

wb.testarr.v = arange(12).reshape(4,3)