[IPython-dev] PyMAD/IPython which list updated
Fernando.Perez at colorado.edu
Sun Mar 20 05:20:17 CST 2005
Thanks for this update. Below follow a few more comments, it would be great
if you could keep the doc updated as we hash out the issues. I'm thinking a
wiki would be great for this, but for now we don't have one. So while I code,
you can be the FrédéWiki ;-) I actually think most things will be cleared up
in short order, so in reality there's no need to do much.
Frédéric Mantegazza wrote:
> Catch custom /PyMADError/ exceptions (now done with rebinding
> IPython.iplib.InteractiveShell.runcode() method), *with the possibility to
> get the inital text code involved in the exception*. For the moment, in
> the runcode() method, we only get the code object, from which it is
> impossible to retreive the text. Here is the code used:
OK, now, IPython's runsource looks as follows:
# Case 3
# We store the code source and object so that threaded shells and
# custom exception handlers can access all this info if needed.
self.code_to_run_src = source
self.code_to_run = code
# now actually execute the code object
This means that if you modify runcode(), when you catch an exception you can
use the self.code_to_run_src attribute to get the source which was being run,
and do whatever you want with it. Note that the new runcode() at the end has
a bit of flushing to do, which your modified version will also need to do:
if code.softspace(sys.stdout, 0):
# Flush out code object which has been run (and source)
self.code_to_run = None
self.code_to_run_src = ''
I still think you'll have to rebind runcode() to your own method. I don't see
an easy way to make this more flexible, without jumping through convoluted
contortions. It's technically possible to store an arbitrary list of
additional exceptions and handlers. But I think at this point, rather than
trying to parametrize every possible combination in the universe, it's simply
cleaner for you to override one method and do exactly what you want. This
part of the code rarely changes, so I think the risk for you is really minimal.
[... deleted big rant about how hard this was ...]
Scratch the previous paragraph, I just realized this was rather easy to do.
It's funny how python does these things to you. You start arguing how
something is not a good idea, because it's too complicated to do. And when
you make a little test to show why it's so, you simply end up solving the
problem. And it takes longer to argue about why its' hard, than to
actually implement a working solution. No wonder we get hooked on this
language, try doing the same thing in C, Perl, or just about anything else.
In case anyone is curious, here's a simple example (outside of ipython):
print '*** my handler ***'
print 'tb: ',tb
etype,value,tb = sys.exc_info()
# this won't be caught:
You can run the above, even with normal python, to see the effect.
Following this, IPython now has a facility for custom exception hadling.
IPython can take a list of custom exceptions and a handler, and call that
handler if any exception in the list matches. Note that this can only be done
for a single handler, because I can't (short of doing dynamic code generation,
which I don't want to get into) have multiple except clauses. So your one
handler would get the exception info for _any_ of the matched exceptions, and
it would have to deal with them.
Please see the set_custom_exc method for details. You can call it at runtime
anywhere (__IPYTHON__.set_custom_exc(...)), or you can pass the class
constructor the relevant information, via the
parameter. This param has the list of custom exceptions and their handler
(None selects an internal dummy default). Here's what you get with
In : x=99
In : y=0
In : x/y
*** Simple custom exception handler ***
Exception type : exceptions.ZeroDivisionError
Exception value: integer division or modulo by zero
Traceback : <traceback object at 0x40379cfc>
Source code : x/y
In : a=range(3)
In : a
*** Simple custom exception handler ***
Exception type : exceptions.IndexError
Exception value: list index out of range
Traceback : <traceback object at 0x40379e14>
Source code : a
This system allows you to do basically anything you want, as far as exception
reporting and control is concerned. This is in CVS, please let me know how it
> Add some new matchers for completion. As *PyMAD* uses remote objects,
> completion only shows the client Pyro proxy. So we added a new matcher by
> adding a IPython.iplib.MagicCompleter.proxy_matches() method, and insert
> this matcher in ipshell.IP.Completer.matchers list. The new matcher get
> the object (from the text param), call a special method on this object
> which returns all available attributes (in fact, only these we want to
> show to the user). Give the possibility to return all matchers, or only
> the no None first. Here is the code used:
Done. A new rc parameter, readline_merge_completions, controls this (true by
New completers can now be added by using the __IPYTHON__.set_custom_completer
method. Plase see its docstring for details.
> In the same way as matchers, get the docstring from the remote object
> instead of the client one when using 'object?' syntaxe. This could be done
> on the same idea: calling a special method on the object, method returning
> the doc of our remote object).
OK, this is easy. Just make sure that the objects IPython sees have a
.getdoc() method; I've added a call to that into my object introspection code.
Have a look at inspect.getdoc, you can probably have your .getdoc method be
like that, since it does a good job of cleaning up indentation for the
docstring. Your method should conform to the same API as inspect.getdoc:
return None if nothing is found, return a string (formatted to your liking)
In fact, the change is already in CVS, so feel free to play with it. See
OInspect.py's getdoc() function for details.
> New exception handler. Here, the idea is to be able to present different
> kind of exceptions in different ways. Some will only print a simple
> message, some others will print the entire traceback (maybe a modified
Well, there are already three exception handlers in ipython, switchable at
runtime and init time via xmode. If you need more flexibility, the first step
would be to simply subclass AutoFormattedTB from ultraTB.py. This is what
IPython uses for its exception handler:
# TraceBack handlers:
# Need two, one for syntax errors and one for other exceptions.
self.SyntaxTB = ultraTB.ListTB(color_scheme='NoColor')
# This one is initialized with an offset, meaning we always want to
# remove the topmost item in the traceback, which is our own internal
# code. Valid modes: ['Plain','Context','Verbose']
self.InteractiveTB = ultraTB.AutoFormattedTB(mode = 'Plain',
tb_offset = 1)
You could then reassign self.InteractiveTB to be an instance of your modified
one (you may want to also modify the convenience %xmode magic to recognize
your new modes). This should give you full control over exception reporting.
Once you are satisfied with a clean design, we can look into whether some
of this flexibility can be put into a decent API, but for now I think it's
easier if you just subclass and play with whatever you want.
Alternatively, with the custom exception handler mechanism (see above), you
can just catch specifically the exceptions you'd like to treat differently and
deal with them directly. Which of the two approaches you take will depend
exactly on what you want to do.
> Access to the command-line interpreter, to have *IPython* interprets code
> has if it was entered through keyboard (ie make difference between magic
> commands and normal python code).
Why can't you just use the push() method? That's pretty much what it does,
see its docstring. If this is not enough, you'll need to clarify to me
exactly why not, and what else you'd need.
OK, that's quite a lot. All of this is in CVS, please test it extensively and
let me know how it all goes.
More information about the IPython-dev