Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>This is really just a matter of convention, exception handling in python is designed to be used heavily (beg forgiveness rather than ask permission). And given that you're working in a different language space, you want to follow those conventions - ie/ you really do want to let developers know where the site of the exception was. But if your really do need to do this ...</p> <p><strong>Using the Inspect Module</strong></p> <p>The <a href="http://docs.python.org/library/inspect.html" rel="nofollow noreferrer">inspect module</a> will do pretty much everything you need to reconstruct a nice version of carp, that works without having to worry about decorators (see below). As per the <a href="https://stackoverflow.com/questions/1095543/get-name-of-calling-functions-module-in-python">comments in this answer</a>, it may be that this approach will break on pythons other than cpython </p> <pre><code># revised carp.py import sys import inspect def carp( msg ): # grab the current call stack, and remove the stuff we don't want stack = inspect.stack() stack = stack[1:] caller_func = stack[0][1] caller_line = stack[0][2] sys.stderr.write('%s at %s line %d\n' % (msg, caller_func, caller_line)) for idx, frame in enumerate(stack[1:]): # The frame, one up from `frame` upframe = stack[idx] upframe_record = upframe[0] upframe_func = upframe[3] upframe_module = inspect.getmodule(upframe_record).__name__ # The stuff we need from the current frame frame_file = frame[1] frame_line = frame[2] sys.stderr.write( '\t%s.%s ' % (upframe_module, upframe_func) ) sys.stderr.write( 'called at %s line %d\n' % (frame_file, frame_line) ) # Exit, circumventing (most) exception handling sys.exit(1) </code></pre> <p>Which for the following example:</p> <pre><code> 1 import carp 2 3 def f(): 4 carp.carp( 'carpmsg' ) 5 6 def g(): 7 f() 8 9 g() </code></pre> <p>Produces the output:</p> <pre><code>msg at main.py line 4 __main__.f called at main.py line 7 __main__.g called at main.py line 9 </code></pre> <p><strong>Using Traceback</strong></p> <p><em>This was the original approach proposed.</em></p> <p>An equivalent to carp could also be written in python by manipulating traceback objects, see the documentation in the <a href="http://docs.python.org/library/traceback.html" rel="nofollow noreferrer">traceback module</a>. The main challenge in doing this turns out to be injecting the exception and traceback print code. It is worth noting that the code in this section is very fragile.</p> <pre><code># carp.py import sys import traceback ''' carp.py - partial emulation of the concept of perls Carp::carp ''' class CarpError(Exception): def __init__(self, value): self.value = value def __str__(self): return repr(self.value) def carpmain( fun ): def impl(): try: fun() except CarpError as ex: _, _, tb = sys.exc_info() items = traceback.extract_tb(tb)[:-1] filename, lineno, funcname, line = items[-1] print '%s at %s line %d' % (ex.value, filename, lineno) for item in items[1:]: filename, lineno, funcname, line = item print '\t%s called at %s line %d' % (funcname, filename, lineno) return impl def carp( value ): raise CarpError( value ) </code></pre> <p>Which can be called using the following basic process:</p> <pre><code>import carp def g(): carp.carp( 'pmsg' ) def f(): g() @carp.carpmain def main(): f() main() </code></pre> <p>The output of which is:</p> <pre><code>msg at foo.py line 4 main called at foo.py line 12 f called at foo.py line 7 g called at foo.py line 4 </code></pre> <p><strong>Perl Reference Example</strong></p> <p>For completeness, both solutions proposed in this answer were debugged by comparing results to this equivalent perl example:</p> <pre><code> 1 use strict; 2 use warnings; 3 use Carp; 4 5 sub f { 6 Carp::carp("msg"); 7 } 8 9 sub g { 10 f(); 11 } 12 13 g(); </code></pre> <p>Which has the output:</p> <pre><code>msg at foo.pl line 6 main::f() called at foo.pl line 10 main::g() called at foo.pl line 13 </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload