ipapi.py
647 lines
| 20.4 KiB
| text/x-python
|
PythonLexer
/ IPython / ipapi.py
Fernando Perez
|
r1414 | """IPython customization API | ||
ville
|
r988 | |||
Your one-stop module for configuring & extending ipython | ||||
The API will probably break when ipython 1.0 is released, but so | ||||
will the other configuration method (rc files). | ||||
All names prefixed by underscores are for internal use, not part | ||||
of the public api. | ||||
Below is an example that you can just put to a module and import from ipython. | ||||
A good practice is to install the config script below as e.g. | ||||
~/.ipython/my_private_conf.py | ||||
And do | ||||
import_mod my_private_conf | ||||
in ~/.ipython/ipythonrc | ||||
That way the module is imported at startup and you can have all your | ||||
personal configuration (as opposed to boilerplate ipythonrc-PROFILENAME | ||||
stuff) in there. | ||||
----------------------------------------------- | ||||
import IPython.ipapi | ||||
ip = IPython.ipapi.get() | ||||
def ankka_f(self, arg): | ||||
Fernando Perez
|
r1416 | print 'Ankka',self,'says uppercase:',arg.upper() | ||
ville
|
r988 | |||
Fernando Perez
|
r1416 | ip.expose_magic('ankka',ankka_f) | ||
ville
|
r988 | |||
ip.magic('alias sayhi echo "Testing, hi ok"') | ||||
ip.magic('alias helloworld echo "Hello world"') | ||||
ip.system('pwd') | ||||
ip.ex('import re') | ||||
Fernando Perez
|
r1416 | ip.ex(''' | ||
ville
|
r988 | def funcci(a,b): | ||
print a+b | ||||
print funcci(3,4) | ||||
Fernando Perez
|
r1416 | ''') | ||
ip.ex('funcci(348,9)') | ||||
ville
|
r988 | |||
def jed_editor(self,filename, linenum=None): | ||||
Fernando Perez
|
r1416 | print 'Calling my own editor, jed ... via hook!' | ||
ville
|
r988 | import os | ||
if linenum is None: linenum = 0 | ||||
os.system('jed +%d %s' % (linenum, filename)) | ||||
Fernando Perez
|
r1416 | print 'exiting jed' | ||
ville
|
r988 | |||
ip.set_hook('editor',jed_editor) | ||||
o = ip.options | ||||
o.autocall = 2 # FULL autocall mode | ||||
Fernando Perez
|
r1416 | print 'done!' | ||
Fernando Perez
|
r1414 | """ | ||
#----------------------------------------------------------------------------- | ||||
# Modules and globals | ||||
ville
|
r988 | |||
# stdlib imports | ||||
import __builtin__ | ||||
import sys | ||||
Fernando Perez
|
r1414 | # contains the most recently instantiated IPApi | ||
_RECENT_IP = None | ||||
#----------------------------------------------------------------------------- | ||||
# Code begins | ||||
ville
|
r988 | |||
class TryNext(Exception): | ||||
"""Try next hook exception. | ||||
Raise this in your hook function to indicate that the next hook handler | ||||
should be used to handle the operation. If you pass arguments to the | ||||
constructor those arguments will be used by the next hook instead of the | ||||
original ones. | ||||
""" | ||||
def __init__(self, *args, **kwargs): | ||||
self.args = args | ||||
self.kwargs = kwargs | ||||
Fernando Perez
|
r1416 | |||
ville
|
r988 | class UsageError(Exception): | ||
""" Error in magic function arguments, etc. | ||||
Something that probably won't warrant a full traceback, but should | ||||
nevertheless interrupt a macro / batch file. | ||||
""" | ||||
Fernando Perez
|
r1416 | |||
ville
|
r988 | class IPyAutocall: | ||
""" Instances of this class are always autocalled | ||||
This happens regardless of 'autocall' variable state. Use this to | ||||
develop macro-like mechanisms. | ||||
""" | ||||
def set_ip(self,ip): | ||||
""" Will be used to set _ip point to current ipython instance b/f call | ||||
Override this method if you don't want this to happen. | ||||
""" | ||||
self._ip = ip | ||||
class IPythonNotRunning: | ||||
"""Dummy do-nothing class. | ||||
Instances of this class return a dummy attribute on all accesses, which | ||||
can be called and warns. This makes it easier to write scripts which use | ||||
the ipapi.get() object for informational purposes to operate both with and | ||||
without ipython. Obviously code which uses the ipython object for | ||||
computations will not work, but this allows a wider range of code to | ||||
transparently work whether ipython is being used or not.""" | ||||
def __init__(self,warn=True): | ||||
if warn: | ||||
self.dummy = self._dummy_warn | ||||
else: | ||||
self.dummy = self._dummy_silent | ||||
def __str__(self): | ||||
return "<IPythonNotRunning>" | ||||
__repr__ = __str__ | ||||
def __getattr__(self,name): | ||||
return self.dummy | ||||
def _dummy_warn(self,*args,**kw): | ||||
"""Dummy function, which doesn't do anything but warn.""" | ||||
print ("IPython is not running, this is a dummy no-op function") | ||||
def _dummy_silent(self,*args,**kw): | ||||
"""Dummy function, which doesn't do anything and emits no warnings.""" | ||||
pass | ||||
def get(allow_dummy=False,dummy_warn=True): | ||||
"""Get an IPApi object. | ||||
If allow_dummy is true, returns an instance of IPythonNotRunning | ||||
instead of None if not running under IPython. | ||||
If dummy_warn is false, the dummy instance will be completely silent. | ||||
Running this should be the first thing you do when writing extensions that | ||||
can be imported as normal modules. You can then direct all the | ||||
configuration operations against the returned object. | ||||
""" | ||||
Fernando Perez
|
r1414 | global _RECENT_IP | ||
if allow_dummy and not _RECENT_IP: | ||||
_RECENT_IP = IPythonNotRunning(dummy_warn) | ||||
return _RECENT_IP | ||||
ville
|
r988 | |||
Fernando Perez
|
r1416 | class IPApi(object): | ||
ville
|
r988 | """ The actual API class for configuring IPython | ||
You should do all of the IPython configuration by getting an IPApi object | ||||
with IPython.ipapi.get() and using the attributes and methods of the | ||||
returned object.""" | ||||
def __init__(self,ip): | ||||
Fernando Perez
|
r1414 | global _RECENT_IP | ||
ville
|
r988 | # All attributes exposed here are considered to be the public API of | ||
# IPython. As needs dictate, some of these may be wrapped as | ||||
# properties. | ||||
self.magic = ip.ipmagic | ||||
self.system = ip.system | ||||
self.set_hook = ip.set_hook | ||||
self.set_custom_exc = ip.set_custom_exc | ||||
self.user_ns = ip.user_ns | ||||
self.user_ns['_ip'] = self | ||||
self.set_crash_handler = ip.set_crash_handler | ||||
# Session-specific data store, which can be used to store | ||||
# data that should persist through the ipython session. | ||||
self.meta = ip.meta | ||||
# The ipython instance provided | ||||
self.IP = ip | ||||
self.extensions = {} | ||||
self.dbg = DebugTools(self) | ||||
Fernando Perez
|
r1414 | _RECENT_IP = self | ||
ville
|
r988 | |||
# Use a property for some things which are added to the instance very | ||||
# late. I don't have time right now to disentangle the initialization | ||||
# order issues, so a property lets us delay item extraction while | ||||
# providing a normal attribute API. | ||||
def get_db(self): | ||||
"""A handle to persistent dict-like database (a PickleShareDB object)""" | ||||
return self.IP.db | ||||
db = property(get_db,None,None,get_db.__doc__) | ||||
def get_options(self): | ||||
"""All configurable variables.""" | ||||
# catch typos by disabling new attribute creation. If new attr creation | ||||
Fernando Perez
|
r1414 | # is in fact wanted (e.g. when exposing new options), do | ||
# allow_new_attr(True) for the received rc struct. | ||||
ville
|
r988 | |||
self.IP.rc.allow_new_attr(False) | ||||
return self.IP.rc | ||||
options = property(get_options,None,None,get_options.__doc__) | ||||
def expose_magic(self,magicname, func): | ||||
Fernando Perez
|
r1416 | """Expose own function as magic function for ipython | ||
ville
|
r988 | |||
def foo_impl(self,parameter_s=''): | ||||
Fernando Perez
|
r1416 | 'My very own magic!. (Use docstrings, IPython reads them).' | ||
print 'Magic function. Passed parameter is between < >:' | ||||
print '<%s>' % parameter_s | ||||
ville
|
r988 | print 'The self object is:',self | ||
Fernando Perez
|
r1416 | ipapi.expose_magic('foo',foo_impl) | ||
""" | ||||
ville
|
r988 | import new | ||
im = new.instancemethod(func,self.IP, self.IP.__class__) | ||||
old = getattr(self.IP, "magic_" + magicname, None) | ||||
if old: | ||||
Fernando Perez
|
r1416 | self.dbg.debug_stack("Magic redefinition '%s', old %s" % | ||
(magicname,old) ) | ||||
ville
|
r988 | |||
setattr(self.IP, "magic_" + magicname, im) | ||||
def ex(self,cmd): | ||||
""" Execute a normal python statement in user namespace """ | ||||
exec cmd in self.user_ns | ||||
def ev(self,expr): | ||||
""" Evaluate python expression expr in user namespace | ||||
Returns the result of evaluation""" | ||||
return eval(expr,self.user_ns) | ||||
def runlines(self,lines): | ||||
""" Run the specified lines in interpreter, honoring ipython directives. | ||||
This allows %magic and !shell escape notations. | ||||
Takes either all lines in one string or list of lines. | ||||
""" | ||||
def cleanup_ipy_script(script): | ||||
""" Make a script safe for _ip.runlines() | ||||
Fernando Perez
|
r1414 | - Removes empty lines Suffixes all indented blocks that end with | ||
- unindented lines with empty lines | ||||
ville
|
r988 | """ | ||
Fernando Perez
|
r1414 | |||
ville
|
r988 | res = [] | ||
lines = script.splitlines() | ||||
Ville M. Vainio
|
r1115 | |||
ville
|
r988 | level = 0 | ||
for l in lines: | ||||
Ville M. Vainio
|
r1070 | lstripped = l.lstrip() | ||
Ville M. Vainio
|
r1115 | stripped = l.strip() | ||
Ville M. Vainio
|
r1070 | if not stripped: | ||
ville
|
r988 | continue | ||
Ville M. Vainio
|
r1070 | newlevel = len(l) - len(lstripped) | ||
Ville M. Vainio
|
r1115 | def is_secondary_block_start(s): | ||
if not s.endswith(':'): | ||||
return False | ||||
if (s.startswith('elif') or | ||||
s.startswith('else') or | ||||
s.startswith('except') or | ||||
s.startswith('finally')): | ||||
return True | ||||
Fernando Perez
|
r1414 | if level > 0 and newlevel == 0 and \ | ||
not is_secondary_block_start(stripped): | ||||
ville
|
r988 | # add empty line | ||
res.append('') | ||||
Ville M. Vainio
|
r1115 | |||
ville
|
r988 | res.append(l) | ||
level = newlevel | ||||
return '\n'.join(res) + '\n' | ||||
if isinstance(lines,basestring): | ||||
script = lines | ||||
else: | ||||
script = '\n'.join(lines) | ||||
clean=cleanup_ipy_script(script) | ||||
Fernando Perez
|
r1414 | # print "_ip.runlines() script:\n",clean # dbg | ||
ville
|
r988 | self.IP.runlines(clean) | ||
Fernando Perez
|
r1414 | |||
ville
|
r988 | def to_user_ns(self,vars, interactive = True): | ||
"""Inject a group of variables into the IPython user namespace. | ||||
Inputs: | ||||
- vars: string with variable names separated by whitespace, or a | ||||
dict with name/value pairs. | ||||
- interactive: if True (default), the var will be listed with | ||||
%whos et. al. | ||||
This utility routine is meant to ease interactive debugging work, | ||||
where you want to easily propagate some internal variable in your code | ||||
up to the interactive namespace for further exploration. | ||||
When you run code via %run, globals in your script become visible at | ||||
the interactive prompt, but this doesn't happen for locals inside your | ||||
own functions and methods. Yet when debugging, it is common to want | ||||
to explore some internal variables further at the interactive propmt. | ||||
Examples: | ||||
To use this, you first must obtain a handle on the ipython object as | ||||
indicated above, via: | ||||
import IPython.ipapi | ||||
ip = IPython.ipapi.get() | ||||
Once this is done, inside a routine foo() where you want to expose | ||||
variables x and y, you do the following: | ||||
def foo(): | ||||
... | ||||
x = your_computation() | ||||
y = something_else() | ||||
# This pushes x and y to the interactive prompt immediately, even | ||||
# if this routine crashes on the next line after: | ||||
ip.to_user_ns('x y') | ||||
... | ||||
# To expose *ALL* the local variables from the function, use: | ||||
ip.to_user_ns(locals()) | ||||
... | ||||
# return | ||||
If you need to rename variables, the dict input makes it easy. For | ||||
example, this call exposes variables 'foo' as 'x' and 'bar' as 'y' | ||||
in IPython user namespace: | ||||
ip.to_user_ns(dict(x=foo,y=bar)) | ||||
""" | ||||
# print 'vars given:',vars # dbg | ||||
# We need a dict of name/value pairs to do namespace updates. | ||||
if isinstance(vars,dict): | ||||
# If a dict was given, no need to change anything. | ||||
vdict = vars | ||||
elif isinstance(vars,basestring): | ||||
# If a string with names was given, get the caller's frame to | ||||
# evaluate the given names in | ||||
cf = sys._getframe(1) | ||||
vdict = {} | ||||
for name in vars.split(): | ||||
try: | ||||
vdict[name] = eval(name,cf.f_globals,cf.f_locals) | ||||
except: | ||||
print ('could not get var. %s from %s' % | ||||
(name,cf.f_code.co_name)) | ||||
else: | ||||
raise ValueError('vars must be a string or a dict') | ||||
# Propagate variables to user namespace | ||||
self.user_ns.update(vdict) | ||||
# And configure interactive visibility | ||||
config_ns = self.IP.user_config_ns | ||||
if interactive: | ||||
for name,val in vdict.iteritems(): | ||||
config_ns.pop(name,None) | ||||
else: | ||||
for name,val in vdict.iteritems(): | ||||
config_ns[name] = val | ||||
def expand_alias(self,line): | ||||
""" Expand an alias in the command line | ||||
Returns the provided command line, possibly with the first word | ||||
(command) translated according to alias expansion rules. | ||||
[ipython]|16> _ip.expand_aliases("np myfile.txt") | ||||
<16> 'q:/opt/np/notepad++.exe myfile.txt' | ||||
""" | ||||
pre,fn,rest = self.IP.split_user_input(line) | ||||
res = pre + self.IP.expand_aliases(fn,rest) | ||||
return res | ||||
def itpl(self, s, depth = 1): | ||||
""" Expand Itpl format string s. | ||||
Only callable from command line (i.e. prefilter results); | ||||
If you use in your scripts, you need to use a bigger depth! | ||||
""" | ||||
return self.IP.var_expand(s, depth) | ||||
def defalias(self, name, cmd): | ||||
""" Define a new alias | ||||
_ip.defalias('bb','bldmake bldfiles') | ||||
Creates a new alias named 'bb' in ipython user namespace | ||||
""" | ||||
self.dbg.check_hotname(name) | ||||
if name in self.IP.alias_table: | ||||
Fernando Perez
|
r1414 | self.dbg.debug_stack("Alias redefinition: '%s' => '%s' (old '%s')" | ||
% (name, cmd, self.IP.alias_table[name])) | ||||
ville
|
r988 | |||
if callable(cmd): | ||||
self.IP.alias_table[name] = cmd | ||||
import IPython.shadowns | ||||
setattr(IPython.shadowns, name,cmd) | ||||
return | ||||
if isinstance(cmd,basestring): | ||||
nargs = cmd.count('%s') | ||||
if nargs>0 and cmd.find('%l')>=0: | ||||
Fernando Perez
|
r1414 | raise Exception('The %s and %l specifiers are mutually ' | ||
'exclusive in alias definitions.') | ||||
ville
|
r988 | |||
self.IP.alias_table[name] = (nargs,cmd) | ||||
return | ||||
# just put it in - it's probably (0,'foo') | ||||
self.IP.alias_table[name] = cmd | ||||
def defmacro(self, *args): | ||||
""" Define a new macro | ||||
2 forms of calling: | ||||
mac = _ip.defmacro('print "hello"\nprint "world"') | ||||
(doesn't put the created macro on user namespace) | ||||
_ip.defmacro('build', 'bldmake bldfiles\nabld build winscw udeb') | ||||
(creates a macro named 'build' in user namespace) | ||||
""" | ||||
import IPython.macro | ||||
if len(args) == 1: | ||||
return IPython.macro.Macro(args[0]) | ||||
elif len(args) == 2: | ||||
self.user_ns[args[0]] = IPython.macro.Macro(args[1]) | ||||
else: | ||||
return Exception("_ip.defmacro must be called with 1 or 2 arguments") | ||||
def set_next_input(self, s): | ||||
""" Sets the 'default' input string for the next command line. | ||||
Requires readline. | ||||
Example: | ||||
[D:\ipython]|1> _ip.set_next_input("Hello Word") | ||||
[D:\ipython]|2> Hello Word_ # cursor is here | ||||
""" | ||||
self.IP.rl_next_input = s | ||||
def load(self, mod): | ||||
""" Load an extension. | ||||
Some modules should (or must) be 'load()':ed, rather than just imported. | ||||
Loading will do: | ||||
- run init_ipython(ip) | ||||
- run ipython_firstrun(ip) | ||||
""" | ||||
Fernando Perez
|
r1414 | |||
ville
|
r988 | if mod in self.extensions: | ||
# just to make sure we don't init it twice | ||||
# note that if you 'load' a module that has already been | ||||
# imported, init_ipython gets run anyway | ||||
return self.extensions[mod] | ||||
__import__(mod) | ||||
m = sys.modules[mod] | ||||
if hasattr(m,'init_ipython'): | ||||
m.init_ipython(self) | ||||
if hasattr(m,'ipython_firstrun'): | ||||
already_loaded = self.db.get('firstrun_done', set()) | ||||
if mod not in already_loaded: | ||||
m.ipython_firstrun(self) | ||||
already_loaded.add(mod) | ||||
self.db['firstrun_done'] = already_loaded | ||||
self.extensions[mod] = m | ||||
return m | ||||
class DebugTools: | ||||
""" Used for debugging mishaps in api usage | ||||
So far, tracing redefinitions is supported. | ||||
""" | ||||
def __init__(self, ip): | ||||
self.ip = ip | ||||
self.debugmode = False | ||||
self.hotnames = set() | ||||
def hotname(self, name_to_catch): | ||||
self.hotnames.add(name_to_catch) | ||||
def debug_stack(self, msg = None): | ||||
if not self.debugmode: | ||||
return | ||||
import traceback | ||||
if msg is not None: | ||||
print '====== %s ========' % msg | ||||
traceback.print_stack() | ||||
def check_hotname(self,name): | ||||
if name in self.hotnames: | ||||
self.debug_stack( "HotName '%s' caught" % name) | ||||
Fernando Perez
|
r1414 | |||
Ville M. Vainio
|
r1060 | def launch_new_instance(user_ns = None,shellclass = None): | ||
ville
|
r988 | """ Make and start a new ipython instance. | ||
This can be called even without having an already initialized | ||||
ipython session running. | ||||
This is also used as the egg entry point for the 'ipython' script. | ||||
""" | ||||
Ville M. Vainio
|
r1060 | ses = make_session(user_ns,shellclass) | ||
ville
|
r988 | ses.mainloop() | ||
def make_user_ns(user_ns = None): | ||||
"""Return a valid user interactive namespace. | ||||
This builds a dict with the minimal information needed to operate as a | ||||
valid IPython user namespace, which you can pass to the various embedding | ||||
classes in ipython. | ||||
""" | ||||
Robert Kern
|
r1419 | raise NotImplementedError | ||
ville
|
r988 | |||
def make_user_global_ns(ns = None): | ||||
"""Return a valid user global namespace. | ||||
Similar to make_user_ns(), but global namespaces are really only needed in | ||||
embedded applications, where there is a distinction between the user's | ||||
interactive namespace and the global one where ipython is running.""" | ||||
Robert Kern
|
r1419 | raise NotImplementedError | ||
# Record the true objects in order to be able to test if the user has overridden | ||||
# these API functions. | ||||
_make_user_ns = make_user_ns | ||||
_make_user_global_ns = make_user_global_ns | ||||
def make_user_namespaces(user_ns = None,user_global_ns = None): | ||||
"""Return a valid local and global user interactive namespaces. | ||||
This builds a dict with the minimal information needed to operate as a | ||||
valid IPython user namespace, which you can pass to the various embedding | ||||
classes in ipython. The default implementation returns the same dict for | ||||
both the locals and the globals to allow functions to refer to variables in | ||||
the namespace. Customized implementations can return different dicts. The | ||||
locals dictionary can actually be anything following the basic mapping | ||||
protocol of a dict, but the globals dict must be a true dict, not even | ||||
a subclass. It is recommended that any custom object for the locals | ||||
namespace synchronize with the globals dict somehow. | ||||
Raises TypeError if the provided globals namespace is not a true dict. | ||||
""" | ||||
if user_ns is None: | ||||
if make_user_ns is not _make_user_ns: | ||||
# Old API overridden. | ||||
# FIXME: Issue DeprecationWarning, or just let the old API live on? | ||||
user_ns = make_user_ns(user_ns) | ||||
else: | ||||
# Set __name__ to __main__ to better match the behavior of the | ||||
# normal interpreter. | ||||
user_ns = {'__name__' :'__main__', | ||||
'__builtins__' : __builtin__, | ||||
} | ||||
else: | ||||
user_ns.setdefault('__name__','__main__') | ||||
user_ns.setdefault('__builtins__',__builtin__) | ||||
if user_global_ns is None: | ||||
if make_user_global_ns is not _make_user_global_ns: | ||||
# Old API overridden. | ||||
user_global_ns = make_user_global_ns(user_global_ns) | ||||
else: | ||||
user_global_ns = user_ns | ||||
if type(user_global_ns) is not dict: | ||||
raise TypeError("user_global_ns must be a true dict; got %r" | ||||
% type(user_global_ns)) | ||||
return user_ns, user_global_ns | ||||
ville
|
r988 | |||
Ville M. Vainio
|
r1060 | def make_session(user_ns = None, shellclass = None): | ||
ville
|
r988 | """Makes, but does not launch an IPython session. | ||
Later on you can call obj.mainloop() on the returned object. | ||||
Inputs: | ||||
- user_ns(None): a dict to be used as the user's namespace with initial | ||||
data. | ||||
WARNING: This should *not* be run when a session exists already.""" | ||||
import IPython.Shell | ||||
Ville M. Vainio
|
r1060 | if shellclass is None: | ||
return IPython.Shell.start(user_ns) | ||||
return shellclass(user_ns = user_ns) | ||||