If I write something in Python and things go awry, I automatically get a traceback.
For example:
#!/usr/bin/env python
print("this will raise a division by zero exception")
print(2/0)
It automatically throws a traceback with the exception
>>> %Run test.py
this will raise a division by zero exception
Traceback (most recent call last):
File "/home/pi/Desktop/test.py", line 4, in <module>
print(2/0)
ZeroDivisionError: division by zero
>>>
Now, let's assume that I don't want the program to crash-and-burn, but I want to handle the exception gracefully - say I need to close files or whatever.
Viz.:
#!/usr/bin/env python
import sys
try:
print("Assume I'm doing something with a resource, like a file")
print(". . . and something goes wrong\n")
print("(This will raise a division by zero exception when I try to \"print(2/0)\")\n")
print(2/0)
except BaseException as e:
print(". . . and I want to handle the exception gracefully:\n")
print("Oops! Something happened! (",e, ")\n")
sys.exit(0)
Which gets me the expected:
>>> %Run test.py
Assume I'm doing something with a resource, like a file
. . . and something goes wrong
(This will raise a division by zero exception when I try to "print(2/0)")
. . . and I want to handle the exception gracefully:
Oops! Something happened! ( division by zero )
─────────────────────────────────────────────────────────────────────────────────────────────────
Python 3.7.3 (/usr/bin/python3)
>>>
However, if I try to handle the exception manually, I am told that in order to get the system provided traceback, (the call stack and location of the error), I have to import traceback.
When I do that, I can get all the information I want.
#!/usr/bin/env python
import sys
import traceback
try:
print("Assume I'm doing something with a resource, like a file")
print(". . . and something goes wrong\n")
print("(This will raise a division by zero exception when I try to \"print(2/0)\")\n")
print(2/0)
except BaseException as e:
print(". . . and I want to handle the exception gracefully:\n")
print("Oops! Something happened! (",e, ")\n")
traceback.print_exc()
sys.exit(0)
And the expected result:
────────────────────────────────────────────────────────────────────────────────────────────────
Python 3.7.3 (/usr/bin/python3)
>>> %Run test.py
Assume I'm doing something with a resource, like a file
. . . and something goes wrong
(This will raise a division by zero exception when I try to "print(2/0)")
. . . and I want to handle the exception gracefully:
Oops! Something happened! ( division by zero )
Traceback (most recent call last):
File "/home/pi/Desktop/test.py", line 9, in <module>
print(2/0)
ZeroDivisionError: division by zero
─────────────────────────────────────────────────────────────────────────────────────────────────
Python 3.7.3 (/usr/bin/python3)
>>>
"Traceback" appears to already exist as the traceback is automatic on unhandled exceptions
Why do I have to import traceback if I use a try/except block, but don't if I don't?
The traceback reflects the state of the Python Interpreter at a given point during the execution of some code. The Python Interpreter obviously has access to its own state, and shares it with the user, by printing that error message, when an exception is thrown and not handled. It also gives the code access to it – via the traceback module, which is why you need to import it.
The default behavior of Python when an exception is raised that isn't caught in a try...except is to print a traceback and exit. If you do catch it, then the default doesn't happen. You have to decide what to do with it. If you decide to print the traceback you now have to explicitly do it by importing the traceback module and using the methods there. However, that's not strictly necessary as you could just inspect the current traceback object (from sys.exc_info()), and write your own formatter.
"Traceback" appears to already exist as the traceback is automatic on unhandled exceptions
What you're seeing is the default behavior of sys.excepthook, which is to print a traceback out to stderr anyway, just before the program exits from an unhandled exception.
See for yourself with:
# example.py
import sys
sys.excepthook = lambda *args: None
print("before")
1/0
print("after")
The script above will exit non-zero, but no traceback will be printed since the hook was replaced with a no-op. And "after" will not get printed, because the process will have already exited. So, it is not quite that a traceback is "automatic" on unhandled exceptions, but rather that is the default configuration.
By calling sys.exit(0) you're intentionally suppressing the ZeroDivisionError exception. The interpreter will exit with a zero return code, and as far as the except hook is concerned you have "handled" the ZeroDivisionError and are opting out of the traceback printing (src).
The traceback exists, and the interpreter can use it (e.g. print it) , but you can only use it if you import it. If you do not use a try/except block you do not have a chance to import it.
Related
I'm setting up a logger in my script like shown at the bottom. This works fine for my purposes and logs my __main__ log messages and those of any modules I use to stdout and a log file.
During program execution a module call that I'm using xarray.open_dataset(file, engine="cfgrib") raises an Error in some conditions and produces the following log output:
2023-02-18 10:02:06,731 cfgrib.dataset ERROR skipping variable: paramId==228029 shortName='i10fg'
Traceback (most recent call last):
...
How can I access this output during program execution?
The raised error in the cfgrib module is handled there gracefully and program execution can continue, but the logic of my program requires that I access the error message, in particular the part saying shortName='i10fg' in order to handle the error exhaustively.
Here is how my logger is set up:
def init_log():
"""initialize logging
returns logger using log settings from the config file (settings.toml)
"""
# all settings from a settings file with reasonable defaults
lg.basicConfig(
level=settings.logging.log_level,
format=settings.logging.format,
filemode=settings.logging.logfile_mode,
filename=settings.logging.filename,
)
mylogger = lg.getLogger(__name__)
stream = lg.StreamHandler()
mylogger.addHandler(stream)
clg.install(
level=settings.logging.log_level,
logger=mylogger,
fmt="%(asctime)s %(levelname)s:\t%(message)s",
)
return mylogger
# main
log = init_log()
log.info('...reading files...')
I went through the python logging documentation and cookbook. While this contains ample examples on how to modify logging for various purposes, I could not find an example for accessing and reacting to a log message during program execution.
The Exception in my logs look this:
2023-02-20 12:22:37,209 cfgrib.dataset ERROR skipping variable: paramId==228029 shortName='i10fg'
Traceback (most recent call last):
File "/home/foo/projects/windgrabber/.venv/lib/python3.10/site-packages/cfgrib/dataset.py", line 660, in build_dataset_components
dict_merge(variables, coord_vars)
File "/home/foo/projects/windgrabber/.venv/lib/python3.10/site-packages/cfgrib/dataset.py", line 591, in dict_merge
raise DatasetBuildError(
cfgrib.dataset.DatasetBuildError: key present and new value is different: key='time' value=Variable(dimensions=('time',), data=array([1640995200, 1640998800, 1641002400, ..., 1672520400, 1672524000,
1672527600])) new_value=Variable(dimensions=('time',), data=array([1640973600, 1641016800, 1641060000, 1641103200, 1641146400,
1641189600, 1641232800, 1641276000, 1641319200, 1641362400,
I cannot catch the Exception directly for some reason:
...
import sys
from cfgrib.dataset import DatasetBuildError
...
try:
df = xr.open_dataset(file, engine="cfgrib").to_dataframe()
# triggering error manually like with the two lines below works as expected
# raise Exception()
# raise DatasetBuildError()
except Exception as e:
print('got an Exception')
print(e)
print(e.args)
except BaseException as e:
print('got a BaseException')
print(e.args)
except DatasetBuildError as e:
print(e)
except:
print('got any and all exception')
type, value, traceback = sys.exc_info()
print(type)
print(value)
print(traceback)
Unless I uncomment the two lines where I raise the exception manually, the except clauses are never triggered, event though I can see the DatabaseBuildError in my logs.
Not sure if this has any bearing, but while I can see the Exception as quoted above in my file log, it is not printed to stdout.
If an exception is raised I'd like to analyse the stack trace in python that tells about where exactly the problem is in the source code file.
Of course for that purpose the module traceback exists. And that works fine for regular exceptions. But how do you deal with this situation if nested exceptions occur?
Consider this example:
def test():
try:
a = 0
b = 5 / a
except Exception as ee1:
assert False
test()
This example prints two exceptions:
Traceback (most recent call last):
File "./test4d.py", line 12, in test
b = 5 / a
ZeroDivisionError: division by zero
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "./test4d.py", line 18, in <module>
test()
File "./test4d.py", line 14, in test
assert False
AssertionError
So information about both exceptions is known to the interpreter. I therefore like to retrieve these two pieces of information from Python: The stack trace of the assert statement (used as an example here to cause an exception) and the stack trace of the division by zero exception (used as an example here to cause an exception). How can I do that?
And the second part of the question: How can I do that in a structured way? The module traceback can be used to get more information about an existing exception. But I do not want to print the exception, I want to store it. Therefore I'd like to get the stack trace as a tuple of instances of some class where each instance represents the data within each stack frame. How can I do that?
There is a variable named __context__ associated with an exception. This variable can be used to access nested exceptions. See this example:
import traceback
def test():
try:
a = 0
b = 5 / a
except Exception as ee1:
assert False
try:
test()
except Exception as ee:
print(repr(ee))
stackTraceList = traceback.extract_stack(ee.__traceback__.tb_frame)
del stackTraceList[0]
for frame in stackTraceList:
print("\t", frame)
if ee.__context__:
print(repr(ee.__context__))
stackTraceList = traceback.extract_stack(ee.__context__.__traceback__.tb_frame)
del stackTraceList[0]
for frame in stackTraceList:
print("\t", frame)
This will output the following text:
AssertionError()
ZeroDivisionError('division by zero',)
<FrameSummary file ./example.py, line 8 in test>
That indicates that both exceptions can be identified and their stack traces can be iterated through.
For convenience I implemented a simple helper module to process exceptions and stack traces named jk_exceptionhelper. You can install this module using pip. For details have a look at the GIT repository: https://github.com/jkpubsrc/python-module-jk-exceptionhelper
I'm using Fabric to automate, including the task of creating a directory. Here is my fabfile.py:
#!/usr/bin/env python
from fabric.api import *
def init():
try:
local('mkdir ./www')
except ##what exception?##:
#print exception name to put in above
Run fab fabfile.py and f I already have ./www created an error is raised, but I don't know what kind, so I don't know how to handle the error yet. Fabric only prints out the following:
mkdir: cannot create directory ‘./www’: File exists
Fatal error: local() encountered an error (return code 1) while executing 'mkdir ./www'
Aborting.
What I want to do is be able to find out the error type so that I can except my errors properly without blanket statements. It would be really helpful if an answer does not just tell me how to handle a mkdir exception, but print (or otherwise find the name to) any exception I may run into down the line (mkdir is just an example).
Thank you!
The issue is that fabric uses subprocess for doing these sorts of things. If you look at the source code for local you can see it doesn't actually raise an exception. It calls suprocess.Popen and uses communicate() to read stdout and stderr. If there is a non-zero return code then it returns a call to either warn or abort. The default is abort. So, to do what you want, try this:
def init():
with settings(warn_only=True):
local('mkdir ./www')
If you look at the source for abort, it looks like this:
10 def abort(msg):
21 from fabric.state import output
22 if output.aborts:
23 sys.stderr.write("\nFatal error: %s\n" % str(msg))
24 sys.stderr.write("\nAborting.\n")
25 sys.exit(1)
So, the exception would be a SystemExit exception. While you could catch this, the proper way to do it is outlined above using settings.
It is nothing to handle with exception, it is from the fabric api
try to set the entire script's warn_only setting to be true with
env.warn_only = True
Normally, when you get an uncaught exception, Python will print the exception type along with the error message:
>>> raise IOError("Error message.")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IOError: Error message.
If that's not happening, you're probably not getting an exception at all.
If you really want to catch an arbitrary exception and print it, you want to catch Exception or BaseException. BaseException will include even things like KeyboardInterrupt, though, so be careful with that.
def init():
try:
local('mkdir ./www')
except BaseException as e:
print "local() threw a", type(e).__name__
raise # Reraise the exception
In general:
try:
some_code()
except Exception, e:
print 'Hit An Exception', e
raise
Will tell you what the exception was but if you are not planning on actually handling some of the exceptions then simply getting rid of the try: except: lines will have exactly the same effect.
Also if you run your code under a debugger then you can look at the exception(s) that you hit in more detail.
def init():
try:
local('mkdir ./www')
except Exception as e:
print e.__class__.__name__
That's all there is to it!
edit: Just re-read your question and realized that my code would only print "Fatal" in your case. It looks like fabric is throwing an error and returning their own error code so you would have to look at the documentation. I don't have any experience with fabric so I'd suggest to look here if you haven't already. Sorry if this isn't helpful!
I've seen similar questions to this one but none of them really address the trackback.
If I have a class like so
class Stop_if_no_then():
def __init__(self, value one, operator, value_two, then, line_or_label, line_number):
self._firstvalue = value_one
self._secondvalue = value_two
self._operator = operator
self._gohere = line_or_label
self._then = then
self._line_number = line_number
def execute(self, OtherClass):
"code comparing the first two values and making changes etc"
What I want my execute method to be able to do is if self._then is not equal to the string "THEN" (in allcaps) then I want it to raise a custom error message and terminate the whole program while also not showing a traceback.
If the error is encountered the only thing that should print out would look something like (I'm using 3 as an example, formatting is not a problem) this.
`Syntax Error (Line 3): No -THEN- present in the statement.`
I'm not very picky about it actually being an exception class object, so there's no issue in that aspect. Since I will be using this in a while loop, simple if, elif just repeats the message over and over (because obviously I am not closing the loop). I have seen sys.exit() but that also prints out a giant block of red text, unless I am not using it correctly. I don't want to catch the exception in my loop because there are other classes in the same module in which I need to implement something like this.
You can turn off the traceback by limiting its depth.
Python 2.x
import sys
sys.tracebacklimit = 0
Python 3.x
In Python 3.5.2 and 3.6.1, setting tracebacklimit to 0 does not seem to have the intended effect. This is a known bug. Note that -1 doesn't work either. Setting it to None does however seem to work, at least for now.
In Python 3.6.2 and above you should set tracebacklimit to 0 or -1, as setting it to None does not disable the traceback output.
Python 3.6.1 and bellow results:
>>> import sys
>>> sys.tracebacklimit = 0
>>> raise Exception
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
Exception
>>> sys.tracebacklimit = -1
>>> raise Exception
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
Exception
>>> sys.tracebacklimit = None
>>> raise Exception
Exception
Python 3.6.2 and above results:
>>> import sys
>>> sys.tracebacklimit = 0
>>> raise Exception
Exception
>>> sys.tracebacklimit = -1
>>> raise Exception
Exception
>>> sys.tracebacklimit = None
>>> raise Exception
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
Exception
Nevertheless, for better or worse, if multiple exceptions are raised, they can all still be printed. For example:
socket.gaierror: [Errno -2] Name or service not known
During handling of the above exception, another exception occurred:
urllib.error.URLError: <urlopen error [Errno -2] Name or service not known>
You can use SystemExit exception:
except Exception as err:
raise SystemExit(err)
https://docs.python.org/3/library/exceptions.html
You can use a try: and then except Exception as inst:
What that will do is give you your error message in a variable named inst and you can print out the arguments on the error with inst.args. Try printing it out and seeing what happens, and is any item in inst.args is the one you are looking for.
EDIT Here is an example I tried with pythons IDLE:
>>> try:
open("epik.sjj")
except Exception as inst:
d = inst
>>> d
FileNotFoundError(2, 'No such file or directory')
>>> d.args
(2, 'No such file or directory')
>>> d.args[1]
'No such file or directory'
>>>
EDIT 2: as for closing the program you can always raise and error or you can use sys.exit()
The cleanest way that I know is to use sys.excepthook.
You implement a three argument function that accepts type, value, and traceback and does whatever you like (say, only prints the value) and assign that function to sys.excepthook.
Here is an example:
import sys
def excepthook(type, value, traceback):
print(value)
sys.excepthook = excepthook
raise ValueError('hello')
This is available in both python 2 and python 3.
If you want to get rid of any traceback for customs exceptions and have line number,
you can do this trick
Python 3
import sys
import inspect
class NoTraceBackWithLineNumber(Exception):
def __init__(self, msg):
try:
ln = sys.exc_info()[-1].tb_lineno
except AttributeError:
ln = inspect.currentframe().f_back.f_lineno
self.args = "{0.__name__} (line {1}): {2}".format(type(self), ln, msg),
sys.exit(self)
class MyNewError(NoTraceBackWithLineNumber):
pass
raise MyNewError("Now TraceBack Is Gone")
Will give this output, and make the raise keyword useless
MyNewError (line 16): Now TraceBack Is Gone
"Exception chaining can be disabled by using from None " - Python docs
>>> try:
... open('database.sqlite')
... except IOError:
... raise RuntimeError from None
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
In general, if you want to catch any exception except SystemExit, and exit with the exception's message without the traceback, define your main function as below:
>>> import sys
>>> def main():
... try:
... # Run your program from here.
... raise RandomException # For testing
... except (Exception, KeyboardInterrupt) as exc:
... sys.exit(exc)
...
>>> main()
name 'RandomException' is not defined
$ echo $?
1
Note that in the case of multiple exceptions being raised, only one message is printed.
This answer is meant to improve upon the one by The-IT.
I am doing this:
try: self.failUnless(sel.is_text_present("F!")) #sel.is_text_present("F!") is false
except AssertionError, e:
print("x"+e+"y")
sys.exit()
it is printing nothing except xy. no class name or anything else. what does the error in AssertionError normally contain?
edit: apparently the user provides its own message. selenium generated many of these:
except AssertionError, e: self.verificationErrors.append(str(e))
without sending in a message at all, so it appends a bunch of empty strings to verificationErrors.
Don't catch the errors from assertions. All the assertions in the unittest module take a final parameter, msg, which is the message to be raised if the assertion fails. Put your debugging there if necessary.
Standard assert statement doesn't put anything into the AssertionError, it's the traceback that matters. There is a assert expr, msg variant that sets the error message, if you're using unittest, then the second argument of assertTrue (failUnless is deprecated) will do it.
Sounds like you want to use your assertions as debug statements in the event of a failure. This should help...
import traceback
try:
assert 1 == 2
except AssertionError:
traceback.print_exc()
This prints:
Traceback (most recent call last):
File "./foo.py", line 4, in <module>
assert 1 == 2