How to build an exception that points to a saved python traceback? - python

I'm trying to do something a little weird. I want to save a stack trace in Python, and then when I throw an exception on some later line, I want that exception to have the stack trace that I saved earlier, rather than the default traceback. I'm trying to give the illusion that an error was thrown from a different line than it actually was thrown from.
To make this a bit more concrete, I'd like to be able to do something like
class Pipeline:
def __init__(self):
saved_traceback = None
def m1(self):
# ... Do some work ....
saved_traceback = save_traceback()
def execute(self):
try:
# .... Do Some work
except Exception as e:
raise SpecialError(saved_traceback)
And then I'd run a script like:
foo = Pipeline()
foo.m1()
foo.execute()
And I want to implement execute in such a way that if an error is raised in its execution, the traceback instead points to the call to m1. This is part of a sort of lazy pipeline building domain specific language where no work happens until you call execute, so all error messages would come from execute. Instead, I want users to see an error that points to the method that added the faulty pipeline step.

You can use traceback.format_exc() to save the traceback as a string.
import traceback
try:
print(a)
except:
traceback_str = traceback.format_exc()
print(a) will fail with NameError because a was never defined. In the except block, the most recent traceback will be saved to traceback_str as a string.

Related

Get line number of a python function call in exception

I have a function like this
def try_strip(s):
try:
return s.strip()
except Exception as e:
print(e)
# (I've tried inspect, traceback, logging, sys)
and if I call it somewhere like this
try_strip('could be a string or not')
then the exception line number would be the one in which the try_strip is defined.
Is there a way to get info about where was it called? Thank you in advance.
The Traceback module which is included with Python provides this functionality. According to its documentation it:
provides a standard interface to extract, format and print stack traces of Python programs. It exactly mimics the behaviour of the Python interpreter when it prints a stack trace.
The function traceback.format_stack() will return the stack trace information you need as a list of strings while the function traceback.print_stack() will print the stack trace information to the console. Below I have included some code which shows how you might use this in the example you provided:
import traceback
def try_strip(s):
try:
return s.strip()
except Exception as e:
traceback.print_stack()
stack_trace_info = traceback.format_stack()
# Code that write stack_trace_info to a log could go here
try_strip(5) # This will cause an error at runtime
For additional info on the Traceback module, please see https://docs.python.org/3/library/traceback.html .

Catching Exception in a Property Function with Pytest

I have a pytest function as such:
def test_zork1_serial_number_error(zork1_unicode_error_serial):
"handles a serial code with a unicode error"
with pytest.raises(UnicodeDecodeError) as execinfo:
serial_code = zork1_unicode_error_serial.serial_code
assert serial_code == "XXXXXX"
The code that this hits is:
#property
def serial_code(self) -> str:
code_bytes = bytes(self.view[0x12:0x18])
try:
if code_bytes.count(b"\x00"):
print("111111111111")
return "XXXXXX"
return code_bytes.decode("ascii")
except UnicodeDecodeError:
print("222222222222")
return "XXXXXX"
The print statements were just there for me to validate that the appropriate path was being hit. When I run the test I get this:
zork1_unicode_error_serial = <zmachine.header.Header object at 0x10e320d60>
def test_zork1_serial_number_error(zork1_unicode_error_serial):
"handles a serial code with a unicode error"
with pytest.raises(UnicodeDecodeError) as execinfo:
serial_code = zork1_unicode_error_serial.serial_code
> assert serial_code == "XXXXXX"
E Failed: DID NOT RAISE <class 'UnicodeDecodeError'>
tests/header_test.py:42: Failed
------------------------------------------------------------------------------ Captured stdout setup ------------------------------------------------------------------------------
/Users/jnyman/AppDev/quendor/tests/../zcode/zork1-r15-sXXXXXX.z2
------------------------------------------------------------------------------ Captured stdout call -------------------------------------------------------------------------------
222222222222
Notice how the "222222222222" is captured in the standard output, thus the appropriate path is being hit and thus the exception is also clearly being generated. Yet Pytest is saying that this exception was not raised. (I have also tested this code manually as well to make sure the exception is being generated.)
I've also tried the path of instead "marking" the test as such, like this:
#pytest.mark.xfail(raises=UnicodeDecodeError)
def test_zork1_serial_number_error(zork1_unicode_error_serial):
...
And that passes. However, it also passes regardless of what exception I put in there. For example, if I do #pytest.mark.xfail(raises=IndexError) that also passes even though an IndexError is never raised.
I can't tell if this has something to do with the fact that what I'm testing is a property. Again, as can seen from the captured standard output, the appropriate code path is being executed and the exception is most definitely being raised. But perhaps the fact that my function is a property is causing an issue?
I have read this Python - test a property throws exception but that isn't using Pytest and it's unclear to me how to retrofit the thinking there. I also aware that perhaps throwing an exception in a property is not a good thing (referencing this: By design should a property getter ever throw an exception in python?) so maybe this test problem is pointing to a code smell. But I don't see an immediate to make this better without adding extra complication. And that still wouldn't explain why Pytest is not seeing the exception generated when it clearly is being generated.

How to determine the origin of a Python exception

I'm debugging a complex program that has a block like this:
try:
lots()
of()
deeply()
nested()
code()
except BaseException as e:
log_error(str(e))
The error message that comes out is just Config file missing but that's not much help to me.
I'd really like to see exactly were that message comes from.
(Note that the error string is from an external program, so it's not searchable.)
If I use traceback I only get to see the stack trace after it's been wound back to the handler, which is not useful. I'd like to see the traceback at the source of the exception.
Any ideas?
Thanks!
For debugging purposes you can use traceback module to print stack trace
import traceback
try:
lots()
of()
deeply()
nested()
code()
except BaseException as e:
print traceback.format_exc()
log_error(str(e))

Any way to save a traceback object in Python

I was looking to possibly try and save a traceback object and somehow pickle it to a file that I can access. An example of a use case for this is if I am submitting some python code to a farm computer to run and it fails, it would be nice to be able to open a session and access that traceback to debug the problem rather than just seeing a log of the traceback. I do not know if there is any sort of way to do this but thought it would be worth asking why it couldn't if so.
okay so you can use traceback.print_exception(type, value, traceback[, limit[, file]]) and save it in a text or json or you can refer to docs
if you find it helpful please mark it correct or upvote thanx..:)
Depending on how you've written your code, the try statement is probably your best answer. Since any error is just a class that inherits Python's builtin Exception, you can raise custom errors everywhere you need more information about a thrown error. You just need to rename your errors or pass in an appropriate string as the first argument. If you then try your code and use the except statement except CustomError as e, you can pull all the information you want out of e in the except statement as a regular instance. Example:
Your code would be:
def script():
try: codeblock
except Exception as e: raise Error1('You hit %s error in the first block'% e)
try: codeblock 2
except Exception as e: raise Error2('You hit %s error in the second block' % e)
try: script()
except Exception as e:
with open('path\to\file.txt','w') as outFile:
outFile.write(e)
The last part is really nothing more than creating your own log file, but you have to write it down somewhere, right?
As for using the traceback module mentioned above, you can get error information out of that. Any of the commands here can get you a list of tracebacks:
http://docs.python.org/2/library/traceback.html
On the otherhand, if you're trying to avoid looking at log files, the traceback module is only going to give you the same thing a log file would, in a different format. Adding your own error statements in your code gives you more information than a cryptic ValueError about what actually happened. If you print the traceback to your special error, it might give you still more information on your issue.

Getting more detail from stack traces

Is there a convenient way to get a more detailed stack trace on a Python exception? I'm hoping to find a wrapper utility/module or some other way to get a bit more info from the stack trace without having to actually modify the Python script that generates it. I'd like to be able to use this when running unit tests, or doctests, or when running utilities or inline scripts from the shell.
Specifically I think I'd like to have the values of local variables, or maybe just the values of the arguments passed to the innermost function in the stack trace. Some options to set the detail level would be nifty.
Not specifically related to your problem, but you might find this code useful -- automatically starts up the python debugger when a fatal exception occurs. Good for working with interactive code. It's originally from ActiveState
# code snippet, to be included in 'sitecustomize.py'
import sys
def info(type, value, tb):
if hasattr(sys, 'ps1') or not sys.stderr.isatty():
# we are in interactive mode or we don't have a tty-like
# device, so we call the default hook
sys.__excepthook__(type, value, tb)
else:
import traceback, pdb
# we are NOT in interactive mode, print the exception...
traceback.print_exception(type, value, tb)
print
# ...then start the debugger in post-mortem mode.
pdb.pm()
sys.excepthook = info
Did you have a look at traceback module?
http://docs.python.org/library/traceback.html
Also on SO:
Showing the stack trace from a running Python application
As mentionned by pyfunc, you can use the function in the traceback module but you only get a stacktrace.
If you want to inspect the stack you have to use the sys.exc_info() function and walk the traceback member and dump information from its frame (tb_frame). See the python Reference Manual for further information on these types.
Here is an example:
def killit(a):
a[10000000000000] = 1
def test(a):
killit(a)
def iterate_traceback(tb):
while tb is not None:
yield tb
tb = tb.tb_next
try:
test(tuple())
except Exception as e:
import sys
exception_info = sys.exc_info()
traceback = exception_info[2]
for tb in iterate_traceback(traceback):
print "-" * 10
print tb.tb_frame.f_code
print tb.tb_frame.f_locals
print tb.tb_frame.f_globals

Categories

Resources