Why doesn't sys.excepthook work? - python

Why isn't the sys.excepthook function called if I try to execute this code?
import sys;
def MyExcepthook(ex_cls, ex, tb):
print("Oops! There's an Error.\n");
a=open("./ERR.txt","w"); #Fixed as suggested by unutbu BUT the problem is the same!
a.write("Oops! There's an Error.\n");
a.close();
sys.excepthook = MyExcepthook;
def main():
print(1/0);
if (__name__=="__main__"):
main();
Output:
Traceback (most recent call last):
File "C:\Users\Path\to\my\python\file.py", line 13, in <module>
main();
File "C:\Users\Path\to\my\python\file.py", line 10, in main
print(1/0);
ZeroDivisionError: division by zero
Expected Output (by print):
Oops! There's an Error.
and a new file (Err.txt) should be created (by open)
The print function doesn't show the text and the file is not created because the sys.excepthook function is not called - why?
-->EDIT
My Problem is caused by a bug in idle-python 3.4 because now i tried to run the code by interpreter python (command line) and it works! this makes my Question useless if not to warn about this bug in idle-python 3.4 i'm sorry and thanks for your help!
[SOLUTION] if someone has my same problem => Try to Run your code by command line! and not from IDE.

Your custom excepthook must not itself raise an exception:
a=open("./ERR.txt") # opens the file in read mode
should be
a=open("./ERR.txt", 'w') # open the file in write mode.
When the custom excepthook raises an exception, you should see
something like
Oops! There's an Error.
Error in sys.excepthook:
...
IOError: [Errno 2] No such file or directory: './ERR.txt'
Original exception was:
...
ZeroDivisionError: integer division or modulo by zero
PS. Don't forget to delete all those unnecessary semicolons!
import sys
def my_excepthook(ex_cls, ex, tb):
msg = "Oops! There's an Error.\n"
print(msg)
with open("./ERR.txt", 'w') as a:
a.write(msg)
sys.excepthook = my_excepthook
def main():
print(1/0)
if __name__=="__main__":
main()

Related

Is it possible to continue execution after error in Jupyter? [duplicate]

How do I print the error/exception in the except: block?
try:
...
except:
print(exception)
For Python 2.6 and later and Python 3.x:
except Exception as e: print(e)
For Python 2.5 and earlier, use:
except Exception,e: print str(e)
The traceback module provides methods for formatting and printing exceptions and their tracebacks, e.g. this would print exception like the default handler does:
import traceback
try:
1/0
except Exception:
traceback.print_exc()
Output:
Traceback (most recent call last):
File "C:\scripts\divide_by_zero.py", line 4, in <module>
1/0
ZeroDivisionError: division by zero
In Python 2.6 or greater it's a bit cleaner:
except Exception as e: print(e)
In older versions it's still quite readable:
except Exception, e: print e
Python 3: logging
Instead of using the basic print() function, the more flexible logging module can be used to log the exception. The logging module offers a lot extra functionality, for example, logging messages...
into a given log file, or
with timestamps and additional information about where the logging happened.
For more information check out the official documentation.
Usage
Logging an exception can be done with the module-level function logging.exception() like so:
import logging
try:
1/0
except BaseException:
logging.exception("An exception was thrown!")
Output
ERROR:root:An exception was thrown!
Traceback (most recent call last):
File ".../Desktop/test.py", line 4, in <module>
1/0
ZeroDivisionError: division by zero
Notes
the function logging.exception() should only be called from an exception handler
the logging module should not be used inside a logging handler to avoid a RecursionError (thanks #PrakharPandey)
Alternative log-levels
It's also possible to log the exception with another log level but still show the exception details by using the keyword argument exc_info=True, like so:
logging.critical("An exception was thrown!", exc_info=True)
logging.error ("An exception was thrown!", exc_info=True)
logging.warning ("An exception was thrown!", exc_info=True)
logging.info ("An exception was thrown!", exc_info=True)
logging.debug ("An exception was thrown!", exc_info=True)
# or the general form
logging.log(level, "An exception was thrown!", exc_info=True)
Name and description only
Of course, if you don't want the whole traceback but only some specific information (e.g., exception name and description), you can still use the logging module like so:
try:
1/0
except BaseException as exception:
logging.warning(f"Exception Name: {type(exception).__name__}")
logging.warning(f"Exception Desc: {exception}")
Output
WARNING:root:Exception Name: ZeroDivisionError
WARNING:root:Exception Desc: division by zero
(I was going to leave this as a comment on #jldupont's answer, but I don't have enough reputation.)
I've seen answers like #jldupont's answer in other places as well. FWIW, I think it's important to note that this:
except Exception as e:
print(e)
will print the error output to sys.stdout by default. A more appropriate approach to error handling in general would be:
except Exception as e:
print(e, file=sys.stderr)
(Note that you have to import sys for this to work.) This way, the error is printed to STDERR instead of STDOUT, which allows for the proper output parsing/redirection/etc. I understand that the question was strictly about 'printing an error', but it seems important to point out the best practice here rather than leave out this detail that could lead to non-standard code for anyone who doesn't eventually learn better.
I haven't used the traceback module as in Cat Plus Plus's answer, and maybe that's the best way, but I thought I'd throw this out there.
In case you want to pass error strings, here is an example from Errors and Exceptions (Python 2.6)
>>> try:
... raise Exception('spam', 'eggs')
... except Exception as inst:
... print type(inst) # the exception instance
... print inst.args # arguments stored in .args
... print inst # __str__ allows args to printed directly
... x, y = inst # __getitem__ allows args to be unpacked directly
... print 'x =', x
... print 'y =', y
...
<type 'exceptions.Exception'>
('spam', 'eggs')
('spam', 'eggs')
x = spam
y = eggs
One has pretty much control on which information from the traceback to be displayed/logged when catching exceptions.
The code
with open("not_existing_file.txt", 'r') as text:
pass
would produce the following traceback:
Traceback (most recent call last):
File "exception_checks.py", line 19, in <module>
with open("not_existing_file.txt", 'r') as text:
FileNotFoundError: [Errno 2] No such file or directory: 'not_existing_file.txt'
Print/Log the full traceback
As others already mentioned, you can catch the whole traceback by using the traceback module:
import traceback
try:
with open("not_existing_file.txt", 'r') as text:
pass
except Exception as exception:
traceback.print_exc()
This will produce the following output:
Traceback (most recent call last):
File "exception_checks.py", line 19, in <module>
with open("not_existing_file.txt", 'r') as text:
FileNotFoundError: [Errno 2] No such file or directory: 'not_existing_file.txt'
You can achieve the same by using logging:
try:
with open("not_existing_file.txt", 'r') as text:
pass
except Exception as exception:
logger.error(exception, exc_info=True)
Output:
__main__: 2020-05-27 12:10:47-ERROR- [Errno 2] No such file or directory: 'not_existing_file.txt'
Traceback (most recent call last):
File "exception_checks.py", line 27, in <module>
with open("not_existing_file.txt", 'r') as text:
FileNotFoundError: [Errno 2] No such file or directory: 'not_existing_file.txt'
Print/log error name/message only
You might not be interested in the whole traceback, but only in the most important information, such as Exception name and Exception message, use:
try:
with open("not_existing_file.txt", 'r') as text:
pass
except Exception as exception:
print("Exception: {}".format(type(exception).__name__))
print("Exception message: {}".format(exception))
Output:
Exception: FileNotFoundError
Exception message: [Errno 2] No such file or directory: 'not_existing_file.txt'
Expanding off of the "except Exception as e:" solution here is a nice one liner which includes some additional info like the type of error and where it occurred.
try:
1/0
except Exception as e:
print(f"{type(e).__name__} at line {e.__traceback__.tb_lineno} of {__file__}: {e}")
Output:
ZeroDivisionError at line 48 of /Users/.../script.py: division by zero
Try this
try:
print("Hare Krishna!")
except Exception as er:
print(er)
One liner error raising can be done with assert statements if that's what you want to do. This will help you write statically fixable code and check errors early.
assert type(A) is type(""), "requires a string"
I would recommend using a try-except statement. Also, rather than using a print statement, a logging exception logs a message with level ERROR on the logger, which I find is more effective than a print output. This method should only be called from an exception handler, as it is here:
import logging
try:
*code goes here*
except BaseException:
logging.exception("*Error goes here*")
There's good documentation on this python page if you want to learn more about logging and debugging.

python: how to get line text where exception occur, not line number

I am trying to use sys.excepthook
With below hook
def foo(type, value, traceback):
# how to print the line that except occurs
sys.excepthook = foo
and use like below
$ python3
>>> text that cause error
How to define foo such that text that cause error being printed?
EDIT
Let me add the full story, to make it clear. (deserve downvotes? -) )
What I want is not print the line, is
get the line
if the line match some criterion, modify then exec
eg,
if type
>>> import requests
>>> edit requests
get the line that exception occurs, i.e, edit requests
then exec edit(find_file(request))
where edit() use subprocess.call(), find_file use inspect to find the file where an object being defined.
Yes, I know ipython magics, and use it regularlly. this time I am ask to how to define it.
You can use the modul traceback and the passed traceback object's tb_lineno attribute:
import sys
import traceback as tb
def foo(type, value, traceback):
# how to print the line that except occurs
print("The line where the exception occurs: {}".format(tb.linecache.getline(tb.extract_tb(sys.last_traceback)[0].filename, traceback.tb_lineno)))
sys.excepthook = foo
int("text")
Out:
The line where the exception occurs: int("text")
as the the line int("text") line raised an exeption.
We can use the traceback module to help us.
def foo(type, value, trace):
print(trace.msg)
The traceback object will have lots of information about where the error occurred.

Setting exit code in atexit callback

Is there any way to set exit code in the function registered in atexit module and called on exit? The call to sys.exit(code) produces an error and does not set exit code to the desired value.
d:\>python atexit_test.py
Error in atexit._run_exitfuncs:
Traceback (most recent call last):
File "atexit_test.py", line 3, in myexit
sys.exit(2)
SystemExit: 2
d:\>echo %ERRORLEVEL%
0
The contents of atexit_test.py:
def myexit():
import sys
sys.exit(2)
import atexit
atexit.register(myexit)
I can prove the test code works in Python 2.7.x (2.7.6 in my case), as reported by #Łukasz Rogalski. So I’ve assumed this to be a bug of Python 3.x and filed a bug report.

Python: capturing both of sys.stdout and sys.stderr as a log file

Roughly speaking, I want to port this to pure Python:
#!/bin/bash
{
python test.py
} &> /tmp/test.log
This didn't work for some unknown reasons:
import os.path, sys
import tempfile
with open(os.path.join(tempfile.gettempdir(), "test.log"), "a") as fp:
sys.stdout = sys.stderr = fp
raise Exception("I'm dying")
The resulting test.log was empty (and I didn't see anything on my console,) when I tested it with Python 2.6.6, Python 2.7.8 and Python 3.4.2 on CentOS x86_64.
But Ideally I'd like a solution for Python 2.6.
(For now, it's tolerable to clutter the log file with intermixed output from stdout and stderr or multithreading, as long as any data won't simply disappear into a blackhole.)
Show me a concise and portable solution which is confirmed to work with an exception stack trace on sys.stderr. (Preferably something other than os.dup2)
Remember that file objects are closed after with blocks :)
Use simply this:
sys.stdout = sys.stderr = open("test.log","w")
raise Exception("Dead")
Content of test.log after exit:
Traceback (most recent call last):
File "test.py", line 5, in <module>
raise Exception("Dead")
Exception: Dead
You can use a method like this one:
import traceback
import sys
from contextlib import contextmanager
#contextmanager
def output_to_file(filepath, write_mode='w'):
stdout_orig = None
stderr_orig = None
stdout_orig = sys.stdout
stderr_orig = sys.stderr
f = open(filepath, write_mode)
sys.stdout = f
sys.stderr = f
try:
yield
except:
info = sys.exc_info()
f.write('\n'.join(traceback.format_exception(*info)))
f.close()
sys.stdout = stdout_orig
sys.stderr = stderr_orig
And the the usage is:
with output_to_file('test.log'):
print('hello')
raise Exception('I am dying')
And the cat test.log produces:
hello
Traceback (most recent call last):
File "<ipython-input-3-a3b702c7b741>", line 20, in outputi_to_file
yield
File "<ipython-input-4-f879d82580b2>", line 3, in <module>
raise Exception('I am dying')
Exception: I am dying
This works for me:
#!/usr/bin/env python
from __future__ import print_function
import os, os.path, sys, tempfile
old_out = os.dup(sys.stdout.fileno())
old_err = os.dup(sys.stderr.fileno())
with open(os.path.join(tempfile.gettempdir(), "test.log"), "a") as fp:
fd = fp.fileno()
os.dup2(fd, sys.stdout.fileno())
os.dup2(fd, sys.stderr.fileno())
print("Testing")
print('testing errs', file=sys.stderr)
raise Exception("I'm dying")
The future is just for cleaner handling of Python2 or Python3 with the same example. (I've also changed the raise statement to instantiate an exception, strings as exceptions have been deprecated for a long time and they're not properly supported under Python3).
The old_* values are just if we wanted to restore our original stdout and/or stderr after using our redirected file.

e.printStackTrace equivalent in python

I know that print(e) (where e is an Exception) prints the occurred exception
but, I was trying to find the python equivalent of Java's e.printStackTrace() that exactly traces the exception to what line it occurred and prints the entire trace of it.
Could anyone please tell me the equivalent of e.printStackTrace() in Python?
import traceback
traceback.print_exc()
When doing this inside an except ...: block it will automatically use the current exception. See http://docs.python.org/library/traceback.html for more information.
There is also logging.exception.
import logging
...
try:
g()
except Exception as ex:
logging.exception("Something awful happened!")
# will print this message followed by traceback
Output:
ERROR 2007-09-18 23:30:19,913 error 1294 Something awful happened!
Traceback (most recent call last):
File "b.py", line 22, in f
g()
File "b.py", line 14, in g
1/0
ZeroDivisionError: integer division or modulo by zero
(From http://blog.tplus1.com/index.php/2007/09/28/the-python-logging-module-is-much-better-than-print-statements/ via How to print the full traceback without halting the program?)
e.printStackTrace equivalent in python
In Java, this does the following (docs):
public void printStackTrace()
Prints this throwable and its backtrace to the standard error stream...
This is used like this:
try
{
// code that may raise an error
}
catch (IOException e)
{
// exception handling
e.printStackTrace();
}
In Java, the Standard Error stream is unbuffered so that output arrives immediately.
The same semantics in Python 2 are:
import traceback
import sys
try: # code that may raise an error
pass
except IOError as e: # exception handling
# in Python 2, stderr is also unbuffered
print >> sys.stderr, traceback.format_exc()
# in Python 2, you can also from __future__ import print_function
print(traceback.format_exc(), file=sys.stderr)
# or as the top answer here demonstrates, use:
traceback.print_exc()
# which also uses stderr.
Python 3
In Python 3, we can get the traceback directly from the exception object (which likely behaves better for threaded code).
Also, stderr is line-buffered, but the print function gets
a flush argument, so this would be immediately printed to stderr:
print(traceback.format_exception(None, # <- type(e) by docs, but ignored
e, e.__traceback__),
file=sys.stderr, flush=True)
Conclusion:
In Python 3, therefore, traceback.print_exc(), although it uses sys.stderr by default, would buffer the output, and you may possibly lose it. So to get as equivalent semantics as possible, in Python 3, use print with flush=True.
Adding to the other great answers, we can use the Python logging library's debug(), info(), warning(), error(), and critical() methods. Quoting from the docs for Python 3.7.4,
There are three keyword arguments in kwargs which are inspected: exc_info which, if it does not evaluate as false, causes exception information to be added to the logging message.
What this means is, you can use the Python logging library to output a debug(), or other type of message, and the logging library will include the stack trace in its output. With this in mind, we can do the following:
import logging
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
def f():
a = { 'foo': None }
# the following line will raise KeyError
b = a['bar']
def g():
f()
try:
g()
except Exception as e:
logger.error(str(e), exc_info=True)
And it will output:
'bar'
Traceback (most recent call last):
File "<ipython-input-2-8ae09e08766b>", line 18, in <module>
g()
File "<ipython-input-2-8ae09e08766b>", line 14, in g
f()
File "<ipython-input-2-8ae09e08766b>", line 10, in f
b = a['bar']
KeyError: 'bar'

Categories