# Chapter 21: IPython and Leo

To use this plugin, you must run Leo from a console window. When this plugin is enabled, Leo's start-ipython command starts IPython in this console. Leo's ipython plugin provides two-way communication (a bridge) between Leo and IPython: you can run Leo scripts from IPython, and IPython scripts from Leo.

Remarkably, Leo and IPython run simultaneously in the same process, yet their separate event loops do not interfere with each other. Even more remarkably, scripts run from IPython immediately change Leo, exactly as if the script were run from Leo. Conversely, scripts run from Leo immediately affect the IPython interpreter. Care may be needed when running such scripts while lengthy IPython scripts are running. As the result of this truly remarkable connection between Leo and IPython, Leo might plausibly be said to be an IPython Notebook.

The bridge between Leo and IPython is powerful because it is simple. Indeed,

1. You can run any IPython script from Leo. On the Leo side, as single variable:

    ip = IPython.ipapi.get()

assigns ip to IPython's _ip variable. The ip variable allows scripts running in Leo to do anything that an IPython script can do.

2. You can run any Leo script from IPython. The ipython plugin injects a single object named '_leo' into the IPython namespace. IPython scripts access Leo's c and g objects as follows:

    c,g = _leo.c, _leo.g

The c and g variables allow scripts running in IPython to do anything that a Leo script can do.

This is basically everything that is required for IPython-Leo interaction. However, you probably wont use 'c' and 'g' directly, but use a series of convenience wrappers described in this document that make interactive work painless and powerful.

Contents

- Chapter 21: IPython and Leo
  - Introduction
  - Installation
  - Accessing IPython from Leo
    - IPython code
    - Plain Python code
  - Accessing Leo nodes from IPython
    - LeoNode
    - Accessing children with iteration and dict notation
  - @cl definitions
  - Special node types
    - @ipy-startup
    - @ipy-results
    - @ipy-root
    - @a nodes
  - Declaring custom push-to-ipython handlers
  - Example code snippets
  - Magic functions
    - %mb
    - %lee
  - Acknowledgements and history

## 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 and manipulate data in Leo nodes, in addition to mere program code - essentially making ILeo a hierarchical spreadsheet, albeit with non-grid view of the data. The possibilities of this are endless, and the approach can be applied in wide range of problem domains with very little actual coding.

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.8, and IPython 0.8.3

The ILeo concept is still being developed actively, so if you want to get access to latest features you can get IPython from Launchpad by installing bzr and doing:

    bzr branch lp:ipython
    cd ipython
    python eggsetup.py develop

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+shift+i 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.
- Note that you can just press alt-I (push-to-ipython) - it will start IPython if it has not been previously started. However, when you open a new leo document, you have to execute start-ipython (alt+shift+I) again to tell IPython that the new commands should target the new document. IPython session will not be restarted, only the leo commander object is updated in the existing session.

## 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, see ipy_leo.py for examples).

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 (beware of the possible recursion!).

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 metadata. 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' or press alt+I on the node. 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+shift-i). Use it to push your own @cl definitions, import the modules you will be using elsewhere in the document, etc.

The contents of of the node itself will be ignored.

### @ipy-results

If you press alt+I on a node that has @cl, it will be evaluated and the result will be put into this node. Otherwise, it will just be displayed in log tab.

### @ipy-root

You can set up a subportion of the leo document as a "sandbox" for your IPython work. Only the nodes under @ipy-root will be visible through the 'wb' variable.

Also, when you create a new node (wb.foo.v = 'stuff'), the node foo will be created as a child of this 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.

## 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, with 0 being the highest (first one to try) - 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

Create 5 child nodes for the current node (note the use of special _p variable, which means "current node") and moves focus to node number 5:

    for i in range(10):
        _p[i] = 'hello %d' % d
    _p[5].go()

## Magic functions

### %mb

Execute leo minibuffer command. Tab completion works. Example:

    mb open-outline

### %lee

Stands for "LEo Edit". Allows you to open file(s), and even objects in Leo for editing. Examples:

    lee *.txt

Opens all txt files in @auto nodes

    lee MyMacro

Opens the macro MyMacro for editing. Press alt-I to push the edited macro back to IPython.

    s = 'hello word'
    lee s

Opens the variable s for editing. Press alt+I to push the new value to IPython.

    lee hist

Opens IPython interactive history (both input and output) in Leo.

## Acknowledgements and history

This idea got started when I (Ville M. Vainio) 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 promising technologies.

Discussions on SourceForge show how the goal of close cooperation between Leo and IPython went from vague dream to completed reality over the span of about 10 days.