##// END OF EJS Templates
ipy_leo: minor doc adjustment
ipy_leo: minor doc adjustment

File last commit:

r1010:8c8d1a36
r1013:6d58bcd4
Show More
ipy_leo.py
409 lines | 11.5 KiB | text/x-python | PythonLexer
Ville M. Vainio
deprecated @ipy header prefix, use @cl alone as first body line instead...
r996 """ ILeo - Leo plugin for IPython
ville
initialization (no svn history)
r988
"""
import IPython.ipapi
import IPython.genutils
import IPython.generics
Ville M. Vainio
Can register custom push-to-ipython handlers, e.g. 'expose_ileo_push(push_plain_python, 100)'...
r1006 from IPython.hooks import CommandChainDispatcher
ville
initialization (no svn history)
r988 import re
Ville M. Vainio
deprecated @ipy header prefix, use @cl alone as first body line instead...
r996 import UserDict
Ville M. Vainio
Can register custom push-to-ipython handlers, e.g. 'expose_ileo_push(push_plain_python, 100)'...
r1006 from IPython.ipapi import TryNext
ville
initialization (no svn history)
r988
ip = IPython.ipapi.get()
leo = ip.user_ns['leox']
c,g = leo.c, leo.g
# will probably be overwritten by user, but handy for experimentation early on
ip.user_ns['c'] = c
ip.user_ns['g'] = g
from IPython.external.simplegeneric import generic
import pprint
Ville M. Vainio
add leo_bridge_demo example, add @cl (data class) support (e.g. @cl csvdata, @cl tmpfile, @cl rfile)
r994 def es(s):
g.es(s, tabName = 'IPython')
pass
ville
initialization (no svn history)
r988 @generic
def format_for_leo(obj):
""" Convert obj to string representiation (for editing in Leo)"""
return pprint.pformat(obj)
@format_for_leo.when_type(list)
def format_list(obj):
return "\n".join(str(s) for s in obj)
Ville M. Vainio
deprecated @ipy header prefix, use @cl alone as first body line instead...
r996 attribute_re = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*$')
def valid_attribute(s):
return attribute_re.match(s)
ville
initialization (no svn history)
r988
def all_cells():
d = {}
for p in c.allNodes_iter():
h = p.headString()
Ville M. Vainio
ipy_leo: @a anchors, @ipy-results is back (for collecting new nodes, if it exists)
r1004 if h.startswith('@a '):
d[h.lstrip('@a ').strip()] = p.parent().copy()
elif not valid_attribute(h):
Ville M. Vainio
deprecated @ipy header prefix, use @cl alone as first body line instead...
r996 continue
d[h] = p.copy()
ville
initialization (no svn history)
r988 return d
Ville M. Vainio
add leo_bridge_demo example, add @cl (data class) support (e.g. @cl csvdata, @cl tmpfile, @cl rfile)
r994 def eval_node(n):
body = n.b
if not body.startswith('@cl'):
# plain python repr node, just eval it
return ip.ev(n.b)
# @cl nodes deserve special treatment - first eval the first line (minus cl), then use it to call the rest of body
first, rest = body.split('\n',1)
Ville M. Vainio
deprecated @ipy header prefix, use @cl alone as first body line instead...
r996 tup = first.split(None, 1)
# @cl alone SPECIAL USE-> dump var to user_ns
if len(tup) == 1:
val = ip.ev(rest)
ip.user_ns[n.h] = val
es("%s = %s" % (n.h, repr(val)[:20] ))
return val
cl, hd = tup
Ville M. Vainio
add leo_bridge_demo example, add @cl (data class) support (e.g. @cl csvdata, @cl tmpfile, @cl rfile)
r994 xformer = ip.ev(hd.strip())
es('Transform w/ %s' % repr(xformer))
Ville M. Vainio
ipy_leo: @cl transform takes node ref in addition to body text
r998 return xformer(rest, n)
ville
initialization (no svn history)
r988
Ville M. Vainio
deprecated @ipy header prefix, use @cl alone as first body line instead...
r996 class LeoNode(object, UserDict.DictMixin):
Ville M. Vainio
Can register custom push-to-ipython handlers, e.g. 'expose_ileo_push(push_plain_python, 100)'...
r1006 """ Node in Leo outline
Most important attributes (getters/setters available:
.v - evaluate node, can also be alligned
.b, .h - body string, headline string
.l - value as string list
Also supports iteration,
setitem / getitem (indexing):
wb.foo['key'] = 12
assert wb.foo['key'].v == 12
Note the asymmetry on setitem and getitem! Also other
dict methods are available.
.ipush() - run push-to-ipython
"""
ville
initialization (no svn history)
r988 def __init__(self,p):
self.p = p.copy()
Ville M. Vainio
ipy_leo: LeoNode.uA for unknownAttributes
r1009 def __get_h(self): return self.p.headString()
def __set_h(self,val):
ville
ipy_leo improvements (%leo opens a file as @auto file)
r990 print "set head",val
c.beginUpdate()
try:
c.setHeadString(self.p,val)
finally:
c.endUpdate()
ville
initialization (no svn history)
r988
Ville M. Vainio
ipy_leo: LeoNode.uA for unknownAttributes
r1009 h = property( __get_h, __set_h, doc = "Node headline string")
ville
initialization (no svn history)
r988
Ville M. Vainio
ipy_leo: LeoNode.uA for unknownAttributes
r1009 def __get_b(self): return self.p.bodyString()
def __set_b(self,val):
ville
ipy_leo improvements (%leo opens a file as @auto file)
r990 print "set body",val
c.beginUpdate()
try:
c.setBodyString(self.p, val)
finally:
c.endUpdate()
ville
initialization (no svn history)
r988
Ville M. Vainio
ipy_leo: LeoNode.uA for unknownAttributes
r1009 b = property(__get_b, __set_b, doc = "Nody body string")
ville
initialization (no svn history)
r988
Ville M. Vainio
ipy_leo: LeoNode.uA for unknownAttributes
r1009 def __set_val(self, val):
Ville M. Vainio
ipy_leo: remove more @ipy stuff, use format_for_leo generic for LeoNode.v
r997 self.b = format_for_leo(val)
ville
initialization (no svn history)
r988
Ville M. Vainio
ipy_leo: LeoNode.uA for unknownAttributes
r1009 v = property(lambda self: eval_node(self), __set_val, doc = "Node evaluated value")
ville
initialization (no svn history)
r988
Ville M. Vainio
ipy_leo: LeoNode.uA for unknownAttributes
r1009 def __set_l(self,val):
ville
initialization (no svn history)
r988 self.b = '\n'.join(val )
l = property(lambda self : IPython.genutils.SList(self.b.splitlines()),
Ville M. Vainio
ipy_leo: LeoNode.uA for unknownAttributes
r1009 __set_l, doc = "Node value as string list")
ville
initialization (no svn history)
r988
def __iter__(self):
Ville M. Vainio
Can register custom push-to-ipython handlers, e.g. 'expose_ileo_push(push_plain_python, 100)'...
r1006 """ Iterate through nodes direct children """
ville
initialization (no svn history)
r988 return (LeoNode(p) for p in self.p.children_iter())
Ville M. Vainio
deprecated @ipy header prefix, use @cl alone as first body line instead...
r996
Ville M. Vainio
ipy_leo: LeoNode.uA for unknownAttributes
r1009 def __children(self):
Ville M. Vainio
deprecated @ipy header prefix, use @cl alone as first body line instead...
r996 d = {}
for child in self:
head = child.h
tup = head.split(None,1)
if len(tup) > 1 and tup[0] == '@k':
d[tup[1]] = child
continue
if not valid_attribute(head):
d[head] = child
continue
return d
def keys(self):
Ville M. Vainio
ipy_leo: LeoNode.uA for unknownAttributes
r1009 d = self.__children()
Ville M. Vainio
deprecated @ipy header prefix, use @cl alone as first body line instead...
r996 return d.keys()
def __getitem__(self, key):
Ville M. Vainio
Can register custom push-to-ipython handlers, e.g. 'expose_ileo_push(push_plain_python, 100)'...
r1006 """ wb.foo['Some stuff'] Return a child node with headline 'Some stuff'
If key is a valid python name (e.g. 'foo'), look for headline '@k foo' as well
"""
Ville M. Vainio
deprecated @ipy header prefix, use @cl alone as first body line instead...
r996 key = str(key)
Ville M. Vainio
ipy_leo: LeoNode.uA for unknownAttributes
r1009 d = self.__children()
Ville M. Vainio
deprecated @ipy header prefix, use @cl alone as first body line instead...
r996 return d[key]
def __setitem__(self, key, val):
Ville M. Vainio
Can register custom push-to-ipython handlers, e.g. 'expose_ileo_push(push_plain_python, 100)'...
r1006 """ You can do wb.foo['My Stuff'] = 12 to create children
This will create 'My Stuff' as a child of foo (if it does not exist), and
do .v = 12 assignment.
Exception:
wb.foo['bar'] = 12
will create a child with headline '@k bar', because bar is a valid python name
and we don't want to crowd the WorkBook namespace with (possibly numerous) entries
"""
Ville M. Vainio
deprecated @ipy header prefix, use @cl alone as first body line instead...
r996 key = str(key)
Ville M. Vainio
ipy_leo: LeoNode.uA for unknownAttributes
r1009 d = self.__children()
Ville M. Vainio
deprecated @ipy header prefix, use @cl alone as first body line instead...
r996 if key in d:
d[key].v = val
return
if not valid_attribute(key):
head = key
else:
head = '@k ' + key
p = c.createLastChildNode(self.p, head, '')
LeoNode(p).v = val
def __delitem__(self,key):
pass
Ville M. Vainio
Can register custom push-to-ipython handlers, e.g. 'expose_ileo_push(push_plain_python, 100)'...
r1006 def ipush(self):
""" Does push-to-ipython on the node """
push_from_leo(self)
Ville M. Vainio
ipy_leo: LeoNode.go(), __iter__ for WorkBook
r1008 def go(self):
""" Set node as current node (to quickly see it in Outline) """
c.beginUpdate()
try:
c.setCurrentPosition(self.p)
finally:
Ville M. Vainio
ipy_leo: LeoNode.uA for unknownAttributes
r1009 c.endUpdate()
Ville M. Vainio
Can register custom push-to-ipython handlers, e.g. 'expose_ileo_push(push_plain_python, 100)'...
r1006
Ville M. Vainio
ipy_leo: LeoNode.uA for unknownAttributes
r1009 def __get_uA(self):
p = self.p
# Create the uA if necessary.
if not hasattr(p.v.t,'unknownAttributes'):
p.v.t.unknownAttributes = {}
Ville M. Vainio
Can register custom push-to-ipython handlers, e.g. 'expose_ileo_push(push_plain_python, 100)'...
r1006
Ville M. Vainio
ipy_leo: LeoNode.uA for unknownAttributes
r1009 d = p.v.t.unknownAttributes.setdefault('ipython', {})
return d
Ville M. Vainio
ipy_leo: doc improvements
r1010 uA = property(__get_uA, doc = "Access persistent unknownAttributes of node")
Ville M. Vainio
deprecated @ipy header prefix, use @cl alone as first body line instead...
r996
ville
initialization (no svn history)
r988
class LeoWorkbook:
Ville M. Vainio
ipy_leo: LeoNode.go(), __iter__ for WorkBook
r1008 """ class for 'advanced' node access
Has attributes for all "discoverable" nodes. Node is discoverable if it
either
- has a valid python name (Foo, bar_12)
- is a parent of an anchor node (if it has a child '@a foo', it is visible as foo)
"""
ville
initialization (no svn history)
r988 def __getattr__(self, key):
Ville M. Vainio
deprecated @ipy header prefix, use @cl alone as first body line instead...
r996 if key.startswith('_') or key == 'trait_names' or not valid_attribute(key):
ville
initialization (no svn history)
r988 raise AttributeError
cells = all_cells()
p = cells.get(key, None)
if p is None:
Ville M. Vainio
ipy_leo: @a anchors, @ipy-results is back (for collecting new nodes, if it exists)
r1004 p = add_var(key)
ville
initialization (no svn history)
r988
return LeoNode(p)
def __str__(self):
return "<LeoWorkbook>"
Ville M. Vainio
deprecated @ipy header prefix, use @cl alone as first body line instead...
r996 def __setattr__(self,key, val):
raise AttributeError("Direct assignment to workbook denied, try wb.%s.v = %s" % (key,val))
ville
initialization (no svn history)
r988 __repr__ = __str__
Ville M. Vainio
ipy_leo: LeoNode.go(), __iter__ for WorkBook
r1008
def __iter__(self):
""" Iterate all (even non-exposed) nodes """
cells = all_cells()
return (LeoNode(p) for p in c.allNodes_iter())
ville
initialization (no svn history)
r988 ip.user_ns['wb'] = LeoWorkbook()
Ville M. Vainio
ipy_leo: @a anchors, @ipy-results is back (for collecting new nodes, if it exists)
r1004
ville
initialization (no svn history)
r988 @IPython.generics.complete_object.when_type(LeoWorkbook)
def workbook_complete(obj, prev):
return all_cells().keys()
Ville M. Vainio
ipy_leo: @a anchors, @ipy-results is back (for collecting new nodes, if it exists)
r1004 def add_var(varname):
ville
ipy_leo improvements (%leo opens a file as @auto file)
r990 c.beginUpdate()
try:
Ville M. Vainio
ipy_leo: remove more @ipy stuff, use format_for_leo generic for LeoNode.v
r997 p2 = g.findNodeAnywhere(c,varname)
Ville M. Vainio
ipy_leo: @a anchors, @ipy-results is back (for collecting new nodes, if it exists)
r1004 if p2:
return
rootpos = g.findNodeAnywhere(c,'@ipy-results')
if not rootpos:
rootpos = c.currentPosition()
p2 = rootpos.insertAsLastChild()
c.setHeadString(p2,varname)
ville
ipy_leo improvements (%leo opens a file as @auto file)
r990 return p2
finally:
c.endUpdate()
ville
initialization (no svn history)
r988
def add_file(self,fname):
p2 = c.currentPosition().insertAfter()
Ville M. Vainio
Can register custom push-to-ipython handlers, e.g. 'expose_ileo_push(push_plain_python, 100)'...
r1006 push_from_leo = CommandChainDispatcher()
def expose_ileo_push(f, prio = 0):
push_from_leo.add(f, prio)
def push_ipython_script(node):
""" Execute the node body in IPython, as if it was entered in interactive prompt """
ville
ipy_leo improvements (%leo opens a file as @auto file)
r990 c.beginUpdate()
try:
ohist = ip.IP.output_hist
hstart = len(ip.IP.input_hist)
Ville M. Vainio
Can register custom push-to-ipython handlers, e.g. 'expose_ileo_push(push_plain_python, 100)'...
r1006 script = g.getScript(c,node.p,useSelectedText=False,forcePythonSentinels=False,useSentinels=False)
ville
ipy_leo improvements (%leo opens a file as @auto file)
r990
script = g.splitLines(script + '\n')
ip.runlines(script)
has_output = False
for idx in range(hstart,len(ip.IP.input_hist)):
val = ohist.get(idx,None)
if val is None:
continue
has_output = True
inp = ip.IP.input_hist[idx]
if inp.strip():
Ville M. Vainio
add leo_bridge_demo example, add @cl (data class) support (e.g. @cl csvdata, @cl tmpfile, @cl rfile)
r994 es('In: %s' % (inp[:40], ))
ville
ipy_leo improvements (%leo opens a file as @auto file)
r990
Ville M. Vainio
add leo_bridge_demo example, add @cl (data class) support (e.g. @cl csvdata, @cl tmpfile, @cl rfile)
r994 es('<%d> %s' % (idx, pprint.pformat(ohist[idx],width = 40)))
ville
ipy_leo improvements (%leo opens a file as @auto file)
r990
if not has_output:
Ville M. Vainio
Can register custom push-to-ipython handlers, e.g. 'expose_ileo_push(push_plain_python, 100)'...
r1006 es('ipy run: %s (%d LL)' %( node.h,len(script)))
ville
ipy_leo improvements (%leo opens a file as @auto file)
r990 finally:
c.endUpdate()
Ville M. Vainio
Can register custom push-to-ipython handlers, e.g. 'expose_ileo_push(push_plain_python, 100)'...
r1006
# this should be the LAST one that will be executed, and it will never raise TryNext
expose_ileo_push(push_ipython_script, 1000)
ville
initialization (no svn history)
r988
def eval_body(body):
try:
val = ip.ev(body)
except:
# just use stringlist if it's not completely legal python expression
val = IPython.genutils.SList(body.splitlines())
return val
Ville M. Vainio
Can register custom push-to-ipython handlers, e.g. 'expose_ileo_push(push_plain_python, 100)'...
r1006 def push_plain_python(node):
if not node.h.endswith('P'):
raise TryNext
script = g.getScript(c,node.p,useSelectedText=False,forcePythonSentinels=False,useSentinels=False)
Ville M. Vainio
ipy_leo: @a anchors, @ipy-results is back (for collecting new nodes, if it exists)
r1004 lines = script.count('\n')
Ville M. Vainio
ipy_leo: @ipy-startup children are run in sequence, not ipy_startup itself
r999 try:
exec script in ip.user_ns
except:
print " -- Exception in script:\n"+script + "\n --"
raise
Ville M. Vainio
Can register custom push-to-ipython handlers, e.g. 'expose_ileo_push(push_plain_python, 100)'...
r1006 es('ipy plain: %s (%d LL)' % (node.h,lines))
Ville M. Vainio
add leo_bridge_demo example, add @cl (data class) support (e.g. @cl csvdata, @cl tmpfile, @cl rfile)
r994
Ville M. Vainio
Can register custom push-to-ipython handlers, e.g. 'expose_ileo_push(push_plain_python, 100)'...
r1006 expose_ileo_push(push_plain_python, 100)
def push_cl_node(node):
""" If node starts with @cl, eval it
ville
ipy_leo improvements (%leo opens a file as @auto file)
r990
Ville M. Vainio
Can register custom push-to-ipython handlers, e.g. 'expose_ileo_push(push_plain_python, 100)'...
r1006 The result is put to root @ipy-results node
"""
if not node.b.startswith('@cl'):
raise TryNext
p2 = g.findNodeAnywhere(c,'@ipy-results')
val = node.v
if p2:
es("=> @ipy-results")
LeoNode(p2).v = val
es(val)
expose_ileo_push(push_cl_node,100)
def push_position_from_leo(p):
push_from_leo(LeoNode(p))
ville
initialization (no svn history)
r988
Ville M. Vainio
Can register custom push-to-ipython handlers, e.g. 'expose_ileo_push(push_plain_python, 100)'...
r1006 ip.user_ns['leox'].push = push_position_from_leo
ville
initialization (no svn history)
r988
def leo_f(self,s):
ville
ipy_leo improvements (%leo opens a file as @auto file)
r990 """ open file(s) in Leo
Takes an mglob pattern, e.g. '%leo *.cpp' or %leo 'rec:*.cpp'
"""
import os
from IPython.external import mglob
files = mglob.expand(s)
c.beginUpdate()
try:
for fname in files:
p = g.findNodeAnywhere(c,'@auto ' + fname)
if not p:
p = c.currentPosition().insertAfter()
p.setHeadString('@auto ' + fname)
if os.path.isfile(fname):
c.setBodyString(p,open(fname).read())
c.selectPosition(p)
finally:
c.endUpdate()
ville
initialization (no svn history)
r988
ip.expose_magic('leo',leo_f)
Ville M. Vainio
ipy_leo: lock term title (makes the right ipython session easy to find) + brief %leoref
r992
def leoref_f(self,s):
Ville M. Vainio
ipy_leo: @a anchors, @ipy-results is back (for collecting new nodes, if it exists)
r1004 """ Quick reference for ILeo """
Ville M. Vainio
ipy_leo: lock term title (makes the right ipython session easy to find) + brief %leoref
r992 import textwrap
print textwrap.dedent("""\
%leo file - open file in leo
wb.foo.v - eval node foo (i.e. headstring is 'foo' or '@ipy foo')
wb.foo.v = 12 - assign to body of node foo
wb.foo.b - read or write the body of node foo
wb.foo.l - body of node foo as string list
for el in wb.foo:
print el.v
Ville M. Vainio
ipy_leo: @ipy-startup
r993
Ville M. Vainio
ipy_leo: lock term title (makes the right ipython session easy to find) + brief %leoref
r992 """
)
ip.expose_magic('leoref',leoref_f)
def show_welcome():
print "------------------"
print "Welcome to Leo-enabled IPython session!"
print "Try %leoref for quick reference."
import IPython.platutils
Ville M. Vainio
add leo_bridge_demo example, add @cl (data class) support (e.g. @cl csvdata, @cl tmpfile, @cl rfile)
r994 IPython.platutils.set_term_title('ILeo')
Ville M. Vainio
ipy_leo: lock term title (makes the right ipython session easy to find) + brief %leoref
r992 IPython.platutils.freeze_term_title()
Ville M. Vainio
ipy_leo: @ipy-startup
r993
def run_leo_startup_node():
p = g.findNodeAnywhere(c,'@ipy-startup')
if p:
Ville M. Vainio
ipy_leo: @ipy-startup children are run in sequence, not ipy_startup itself
r999 print "Running @ipy-startup nodes"
for n in LeoNode(p):
Ville M. Vainio
Can register custom push-to-ipython handlers, e.g. 'expose_ileo_push(push_plain_python, 100)'...
r1006 push_from_leo(n)
Ville M. Vainio
ipy_leo: @ipy-startup
r993
run_leo_startup_node()
Ville M. Vainio
ipy_leo: lock term title (makes the right ipython session easy to find) + brief %leoref
r992 show_welcome()
Ville M. Vainio
ipy_leo: @ipy-startup
r993