From 8ed1c30cbdf5ca22c948edc5f43deedcec992a6e 2013-08-02 08:19:23 From: Paul Ivanov Date: 2013-08-02 08:19:23 Subject: [PATCH] leo bridge provided two-way communication in 2008 this was before the ZeroMQ transition, so by now I would consider it obsolete. --- diff --git a/examples/core/leo_bridge_demo.leo b/examples/core/leo_bridge_demo.leo deleted file mode 100644 index 052a930..0000000 --- a/examples/core/leo_bridge_demo.leo +++ /dev/null @@ -1,552 +0,0 @@ - - - - - - - - - - - -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) - -