diff --git a/IPython/hooks.py b/IPython/hooks.py index a9d50d0..4c28dc3 100644 --- a/IPython/hooks.py +++ b/IPython/hooks.py @@ -32,7 +32,7 @@ ip_set_hook('editor',myiphooks.calljed) The ip_set_hook function is put by IPython into the builtin namespace, so it is always available from all running code. -$Id: hooks.py 988 2006-01-02 21:21:47Z fperez $""" +$Id: hooks.py 1019 2006-01-14 13:02:12Z vivainio $""" #***************************************************************************** # Copyright (C) 2005 Fernando Perez. @@ -46,7 +46,7 @@ __author__ = '%s <%s>' % Release.authors['Fernando'] __license__ = Release.license __version__ = Release.version -import os +import os,bisect # List here all the default hooks. For now it's just the editor functions # but over time we'll move here all the public API for user-accessible things. @@ -93,3 +93,44 @@ def fix_error_editor(self,filename,linenum,column,msg): os.system('vim --cmd "set errorformat=%f:%l:%c:%m" -q ' + t.name) finally: t.close() + +class TryNext(Exception): + pass + +class CommandChainDispatcher: + """ Dispatch calls to a chain of commands until some func can handle it + + Usage: instantiate, execute "add" to add commands (with optional + priority), execute normally via f() calling mechanism. + + """ + def __init__(self,commands=None): + if commands is None: + self.chain = [] + else: + self.chain = commands + + + def __call__(self,*args, **kw): + """ Command chain is called just like normal func. + + This will call all funcs in chain with the same args as were given to this + function, and return the result of first func that didn't raise + TryNext """ + + for prio,cmd in self.chain: + #print "prio",prio,"cmd",cmd #dbg + try: + ret = cmd(*args, **kw) + return ret + except TryNext: + pass + + def __str__(self): + return str(self.chain) + + def add(self, func, priority=0): + """ Add a func to the cmd chain with given priority """ + bisect.insort(self.chain,(priority,func)) + + \ No newline at end of file diff --git a/IPython/ipapi.py b/IPython/ipapi.py index 3e87744..df22a7d 100644 --- a/IPython/ipapi.py +++ b/IPython/ipapi.py @@ -90,8 +90,9 @@ def expose_magic(magicname, func): ''' from IPython import Magic - - setattr(Magic.Magic, "magic_" + magicname, func) + import new + im = new.instancemethod(func,__IP, __IP.__class__) + setattr(__IP, "magic_" + magicname, im) class asmagic: """ Decorator for exposing magics in a friendly 2.4 decorator form @@ -121,11 +122,12 @@ class ashook: """ - def __init__(self,name): + def __init__(self,name,priority=50): self.name = name + self.prio = priority def __call__(self,f): - set_hook(self.name, f) + set_hook(self.name, f, self.prio) return f diff --git a/IPython/iplib.py b/IPython/iplib.py index 15e1d2d..7267b64 100644 --- a/IPython/iplib.py +++ b/IPython/iplib.py @@ -6,7 +6,7 @@ Requires Python 2.3 or newer. This file contains all the classes and helper functions specific to IPython. -$Id: iplib.py 1017 2006-01-14 09:46:45Z vivainio $ +$Id: iplib.py 1019 2006-01-14 13:02:12Z vivainio $ """ #***************************************************************************** @@ -401,7 +401,8 @@ class InteractiveShell(object,Magic): # Set all default hooks, defined in the IPython.hooks module. hooks = IPython.hooks for hook_name in hooks.__all__: - self.set_hook(hook_name,getattr(hooks,hook_name)) + # default hooks have priority 100, i.e. low; user hooks should have 0-100 priority + self.set_hook(hook_name,getattr(hooks,hook_name), 100) # Flag to mark unconditional exit self.exit_now = False @@ -707,17 +708,31 @@ class InteractiveShell(object,Magic): __builtin__.__dict__[biname] = bival self.builtins_added.clear() - def set_hook(self,name,hook): + def set_hook(self,name,hook, priority = 50): """set_hook(name,hook) -> sets an internal IPython hook. IPython exposes some of its internal API as user-modifiable hooks. By - resetting one of these hooks, you can modify IPython's behavior to - call at runtime your own routines.""" + adding your function to one of these hooks, you can modify IPython's + behavior to call at runtime your own routines.""" # At some point in the future, this should validate the hook before it # accepts it. Probably at least check that the hook takes the number # of args it's supposed to. - setattr(self.hooks,name,new.instancemethod(hook,self,self.__class__)) + dp = getattr(self.hooks, name, None) + if not dp: + dp = IPython.hooks.CommandChainDispatcher() + + f = new.instancemethod(hook,self,self.__class__) + try: + dp.add(f,priority) + except AttributeError: + # it was not commandchain, plain old func - replace + dp = f + + setattr(self.hooks,name, dp) + + + #setattr(self.hooks,name,new.instancemethod(hook,self,self.__class__)) def set_custom_exc(self,exc_tuple,handler): """set_custom_exc(exc_tuple,handler) diff --git a/doc/ChangeLog b/doc/ChangeLog index 32aeeef..9e1dd4c 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -9,6 +9,12 @@ backslash (as yielded by tab completer) is still space; "%cd long\ name" works as expected. + * IPython/ipapi.py,hooks.py,iplib.py: Hooks now implemented + as "chain of command", with priority. API stays the same, + TryNext exception raised by a hook function signals that + current hook failed and next hook should try handling it, as + suggested by Walter Dörwald . + 2006-01-13 Ville Vainio