# Chapter 19: Embedding Leo with the leoBridge module

The leoBridge module allows complete access to all aspects of Leo from programs running independently of Leo. Let us call such a program a host program. Using the leoBridge module, host programs can get access to:

- all of Leo's source code,
- the contents of any .leo file,
- the commander of any .leo file.

Contents

- Chapter 19: Embedding Leo with the leoBridge module
  - The basics
  - Running leoBridge from within Leo

## The basics

Host programs use the leoBridge module as follows:

    import leoBridge
    controller = leoBridge.controller(gui='nullGui')
    g = controller.globals()
    c = controller.openLeoFile(path)

Let us look at these statements in detail. The statements:

    import leoBridge
    controller = leoBridge.controller(gui='nullGui')

import the leoBridge module and create a bridge controller. In effect, these statements embed an invisible copy Leo into the host program. This embedded copy of Leo uses a null gui, which simulates all aspects of Leo's normal gui code without creating any screen objects.

The statement:

    g = controller.globals()

provides access to Leo's leoGlobals module, and properly inits globals such as g.app, g.app.gui, etc. Host programs should not import leoGlobals directly, because doing so would not init the g.app object properly.

The statement:

    c = controller.openLeoFile(path)

invisibly opens the .leo file given by the path argument. This call returns a completely standard Leo commander, properly inited. This is the big payoff from the leoBridge module: the host program gets instant access to c.config.getBool, etc. Do you see how sweet this is?

For example, the following script runs leo/test/leoBridgeTest.py outside of Leo. leoBridgeTest.py uses the leoBridge module to run all unit tests in leo/test/unitTest.leo:

    import os,sys

    path = g.os_path_abspath(
        g.os_path_join(
            g.app.loadDir,'..','test','leoBridgeTest.py'))

    os.system('%s %s' % (sys.executable,path))

The file leo/test/test.leo contains the source code for leoBridgeTest.py. Here it is, stripped of its sentinel lines:

    '''A program to run unit tests with the leoBridge module.'''

    import leoBridge
    import leoTest

    def main ():
        tag = 'leoTestBridge'

        # Setting verbose=True prints messages that would be sent to the log pane.
        bridge = leoBridge.controller(gui='nullGui',verbose=False)
        if bridge.isOpen():
            g = bridge.globals()
            path = g.os_path_abspath(g.os_path_join(
                g.app.loadDir,'..','test','unitTest.leo'))
            c = bridge.openLeoFile(path)
            g.es('%s %s' % (tag,c.shortFileName()))
            runUnitTests(c,g)

        print tag,'done'

    def runUnitTests (c,g):
        nodeName = 'All unit tests' # The tests to run.
        try:
            u = leoTest.testUtils(c)
            p = u.findNodeAnywhere(nodeName)
            if p:
                g.es('running unit tests in %s...' % nodeName)
                c.selectPosition(p)
                c.debugCommands.runUnitTests()
                g.es('unit tests complete')
            else:
                g.es('node not found:' % nodeName)
        except Exception:
            g.es('unexpected exception')
            g.es_exception()
            raise

    if __name__ == '__main__':
        main()

## Running leoBridge from within Leo

This following is adapted from Terry Brown's entry in Leo's wiki.

You can not just run leoBridge from Leo, because the leoBridge module is design to run a separate copy of Leo. However, it is possible to run leoBridge from a subprocess. That turned out to be more, um, interesting than anticipated, so I'm recording the results here.

The idea is that script A running in Leo (i.e. in a regular GUI Leo session) calls script B through subprocess.Popen(), script B uses LeoBridge to do something (parse unloaded Leo files), and returns the result to script A. Passing the result back via the clipboard seemed like a possibility, but XWindows / tcl/tk clipboard madness being what it is, that didn't seem to work.

First trick, calling script B from script A:

    import subprocess
    p = subprocess.Popen(('python',
        path_to_script_B,
        parameter_for_script_B,),
        stdout=subprocess.PIPE,
        env={'PYTHONPATH': g.app.loadDir,'USER': g.app.leoID},
    )
    p.wait()

Setting PYTHONPATH in the environment seemed like the easiest way to let script B find leoBridge.py (which it needs to import). But by setting the env parameter you limit script B's environment to be only PYTHONPATH, which causes leoBridge to fail because, in unix at least, it depends on USER in the environment. So you need to pass that through too.

Now, because passing stuff back on the clipboard seems unreliable, at least in XWindows, script B passes results back to script A via stdout (print), but there's some Leo initialization chatter you want to avoid. So put a sentinel, 'START_CLIPBOARD', in the output, and collect it like this:

    response = p.stdout.readlines()
    while response and 'START_CLIPBOARD' not in response[0]:
        del response[0]
    del response[0]  # delete the sentinel as well
    response = ''.join(response)

This is the basic mechanism. What I actually wanted to do was have script B generate a branch of nodes and pass that back to script A for insertion in the tree script A is running in. That's relatively easy if you use:

    c.setCurrentPosition(pos_of_branch_to_return)
    c.copyOutline()
    print '<!-- START_CLIPBOARD -->'
    print g.app.gui.getTextFromClipboard()
    print '<!-- END_CLIPBOARD -->'

at the end of script B, and back in script A, after you've rebuilt response as shown above, just:

    g.app.gui.replaceClipboardWith(response)
    c.pasteOutline()