''' IPython customization API 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): print "Ankka",self,"says uppercase:",arg.upper() ip.expose_magic("ankka",ankka_f) ip.magic('alias sayhi echo "Testing, hi ok"') ip.magic('alias helloworld echo "Hello world"') ip.system('pwd') ip.ex('import re') ip.ex(""" def funcci(a,b): print a+b print funcci(3,4) """) ip.ex("funcci(348,9)") def jed_editor(self,filename, linenum=None): print "Calling my own editor, jed ... via hook!" import os if linenum is None: linenum = 0 os.system('jed +%d %s' % (linenum, filename)) print "exiting jed" ip.set_hook('editor',jed_editor) o = ip.options o.autocall = 2 # FULL autocall mode print "done!" ''' # stdlib imports import __builtin__ import sys try: # Python 2.3 compatibility set except NameError: import sets set = sets.Set # our own #from IPython.genutils import warn,error 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 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. """ 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 # contains the most recently instantiated IPApi 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 "" __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 _recent = None 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. """ global _recent if allow_dummy and not _recent: _recent = IPythonNotRunning(dummy_warn) return _recent class IPApi: """ 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): # 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.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) global _recent _recent = self # 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 # is in fact wanted (e.g. when exposing new options), do allow_new_attr(True) # for the received rc struct. 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): ''' Expose own function as magic function for ipython def foo_impl(self,parameter_s=''): """My very own magic!. (Use docstrings, IPython reads them).""" print 'Magic function. Passed parameter is between < >: <'+parameter_s+'>' print 'The self object is:',self ipapi.expose_magic("foo",foo_impl) ''' import new im = new.instancemethod(func,self.IP, self.IP.__class__) old = getattr(self.IP, "magic_" + magicname, None) if old: self.dbg.debug_stack("Magic redefinition '%s', old %s" % (magicname, old)) 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. """ if isinstance(lines,basestring): self.IP.runlines(lines) else: self.IP.runlines('\n'.join(lines)) 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: self.dbg.debug_stack("Alias redefinition: '%s' => '%s' (old '%s')" % (name, cmd, self.IP.alias_table[name])) 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: raise Exception('The %s and %l specifiers are mutually exclusive ' 'in alias definitions.') 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) """ 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) def launch_new_instance(user_ns = None): """ 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. """ ses = make_session(user_ns) 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. """ if user_ns is None: # 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__) return user_ns 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.""" if ns is None: ns = {} return ns def make_session(user_ns = None): """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 return IPython.Shell.start(user_ns)