I have some code thus:
try:
subprocess.check_call(
"mysqldump {} {}".format(mysql_args, cmd), shell=True
)
except Exception as e:
raise Exception("Command failed")
The problem is that Exception logging code elsewhere in the application catches this exception and helpfully prints it out - which in this case looks like this:
Traceback (most recent call last):
File "/apps/django/myapp/tasks.py", line 336, in _safe_mysqldump
"mysqldump {} {}".format(mysql_args, cmd), shell=True
File "/usr/lib/python3.6/subprocess.py", line 291, in check_call
raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError:
Command 'mysqldump -ufoo -pMYSECRETPASSWORD myappdb t1 t2 t3...' returned non-zero exit status 6.
During handling of the above exception, another exception occurred:
etc.
The critical thing being it printed out the mysql connection string. How can I prevent it from doing this?
Use the syntax:
raise Exception("Command failed") from None
See PEP 409 Suppressing exception context):
>>> try:
... raise TypeError('a')
... except TypeError:
... raise ValueError('b') from None
...
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
ValueError: b
Versus the default behaviour which you saw:
>>> try:
... raise TypeError('a')
... except TypeError:
... raise ValueError('b')
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
TypeError: a
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
ValueError: b
As an alternative specific to your use case: do not use check_call which raises an exception but use subprocess.run
instead:
if subprocess.run(<command>).returncode != 0:
raise Exception("Command failed")
or subprocess.call
if subprocess.call(<command>) != 0:
raise Exception("Command failed")
You may change the output like this:
failed = False
try:
...
except Exception:
failed = True
finally:
if failed:
raise Exception("Command failed")
Related
I have defined a custom Exception object and would like to get the line number of the exception.
class FlowException(Exception):
pass
def something():
print 2/3
print 1/2
print 2/0
try:
something()
except Exception as e:
raise FlowException("Process Exception", e)
Now, if there is a exception in something() it throws the FlowException but it does not give me the exact line number, How can I get the line number from FlowException(ie; it failed when it executed 2/0)?
Here is the output:--
raise FlowException("Process Exception", e)
__main__.FlowException: ('Process Exception', ZeroDivisionError('integer division or modulo by zero',))
[Finished in 0.4s with exit code 1]
The traceback object holds that info in the tb_lineno attribute:
import sys
# ...
except Exception as e:
trace_back = sys.exc_info()[2]
line = trace_back.tb_lineno
raise FlowException("Process Exception in line {}".format(line), e)
Tested on Python 3.6
class FlowException(Exception):
pass
def something():
raise ValueError
try:
something()
except Exception as e:
raise FlowException("Process Exception", e)
Output has line numbers:
Traceback (most recent call last):
File "/Users/diman/PycharmProjects/invites/test.py", line 8, in <module>
something()
File "/Users/diman/PycharmProjects/invites/test.py", line 5, in something
raise ValueError
ValueError
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/diman/PycharmProjects/invites/test.py", line 10, in <module>
raise FlowException("Process Exception", e)
__main__.FlowException: ('Process Exception', ValueError())
For Python 2 try using internal logger ".exception()" method instead of using monstrous "sys" module.
import logging
logger = logging.getLogger()
try:
something()
except Exception as e:
logger.exception(e)
raise FlowException("Process Exception", e)
Consider the simple example:
def f():
try:
raise TypeError
except TypeError:
raise ValueError
f()
I want to catch TypeError object when ValueError is thrown after f() execution. Is it possible to do it?
If I execute function f() then python3 print to stderr all raised exceptions of exception chain (PEP-3134) like
Traceback (most recent call last):
File "...", line 6, in f
raise TypeError
TypeError
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "...", line 11, in <module>
f()
File "...", line 8, in f
raise ValueError
ValueError
So I would get the list of all exceptions of exception chain or check if exception of some type (TypeError in the above example) exists in exception chain.
Python 3 has a beautiful syntactic enhancement on exceptions handling. Instead of plainly raising ValueError, you should raise it from a caught exception, i.e.:
try:
raise TypeError('Something awful has happened')
except TypeError as e:
raise ValueError('There was a bad value') from e
Notice the difference between the tracebacks. This one uses raise from version:
Traceback (most recent call last):
File "/home/user/tmp.py", line 2, in <module>
raise TypeError('Something awful has happened')
TypeError: Something awful has happened
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/home/user/tmp.py", line 4, in <module>
raise ValueError('There was a bad value') from e
ValueError: There was a bad value
Though the result may seem similar, in fact it is rather different! raise from saves the context of the original exception and allows one to trace all the exceptions chain back - which is impossible with simple raise.
To get the original exception, you simply have to refer to new exception's __context__ attribute, i.e.
try:
try:
raise TypeError('Something awful has happened')
except TypeError as e:
raise ValueError('There was a bad value') from e
except ValueError as e:
print(e.__context__)
>>> Something awful has happened
Hopefully that is the solution you were looking for.
For more details, see PEP 3134 -- Exception Chaining and Embedded Tracebacks
If I raise an Exception in Python, here's what I get:
raise Exception("Hello world")
Traceback (most recent call last):
File "<ipython-input-24-dd3f3f45afbe>", line 1, in <module>
raise Exception("Hello world")
Exception: Hello world
Note the last line that says Exception: Hello world. Given an Exception (foo = Exception("Hello world")), how can I produce text like this? None of the following work:
str(foo)
Out[27]: 'Hello world'
repr(foo)
Out[28]: "Exception('Hello world',)"
"{}".format(foo)
Out[29]: 'Hello world'
"{}: {}".format(type(foo), foo)
Out[30]: "<type 'exceptions.Exception'>: Hello world"
If your exception object is exc, then:
The part before the colon is type(exc).__name__.
The part after the colon is str(exc).
So you can just do this:
print('{}: {}'.format(type(exc).__name__, exc))
Making tdelaney's answer formal and demonstrating the difference...
Strings
#test.py
import traceback
try :
raise TypeError("Wrong Type baby!")
except Exception as e:
print( "EXCEPTION FORMAT PRINT:\n{}".format( e ) )
print( "EXCEPTION TRACE PRINT:\n{}".format( "".join(traceback.format_exception(type(e), e, e.__traceback__))
Resulting console output
EXCEPTION FORMAT PRINT:
Wrong Type baby!
EXCEPTION TRACE PRINT:
Traceback (most recent call last):
File "test.py", line 4, in <module>
raise TypeError("Wrong Type baby!")
TypeError: Wrong Type baby!
Logging
If you're in the context of logging there's also the exception method and exc_info kwarg that will do the formatting for you. We should note that the debug and info level log messages are ignored out on account of the root logger holding warning as its default log level.
# logTest.py
import logging
try :
raise ValueError("my bad value")
except Exception as e :
logging.exception( e )
logging.debug("\n{}\nDEBUG LEVEL EXAMPLE".format('-'*30), exc_info=e)
logging.info("\n{}\nINFO LEVEL EXAMPLE".format('-'*30), exc_info=e)
logging.warning("\n{}\nWARNING LEVEL EXAMPLE".format('-'*30), exc_info=e)
logging.error("\n{}\nERROR LEVEL EXAMPLE".format('-'*30), exc_info=e)
with the resulting console output...
ERROR:root:my bad value
Traceback (most recent call last):
File "/Users/me/logTest.py", line 5, in <module>
raise ValueError("my bad value")
ValueError: my bad value
WARNING:root:
------------------------------
WARNING LEVEL EXAMPLE
Traceback (most recent call last):
File "/Users/me/logTest.py", line 5, in <module>
raise ValueError("my bad value")
ValueError: my bad value
ERROR:root:
------------------------------
ERROR LEVEL EXAMPLE
Traceback (most recent call last):
File "/Users/me/logTest.py", line 5, in <module>
raise ValueError("my bad value")
ValueError: my bad value
Consider the following code and traceback:
>>> try:
... raise KeyboardInterrupt
... except KeyboardInterrupt:
... raise Exception
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
KeyboardInterrupt
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
Exception
>>>
I'd like to print only the most recent traceback (the one in which Exception was raised).
How can this be achieved?
From the above example, I'd like to print the following, as if raise Exception had been called outside the except clause.
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
Exception
The perfect question for me.
You can suppress the exception context, that is the first part of the traceback, by explicitly raising the exception from None:
>>> try:
raise KeyboardInterrupt
except:
raise Exception from None
Traceback (most recent call last):
File "<pyshell#4>", line 4, in <module>
raise Exception from None
Exception
This was formalized in PEP 409 and further improved in PEP 415. The original bug request for this was filed by myself btw.
Note that suppressing the context will not actually remove the context from the new exception. So you can still access the original exception:
try:
try:
raise Exception('inner')
except:
raise Exception('outer') from None
except Exception as e:
print(e.__context__) # inner
Consider the following code:
try:
raise Exception("a")
except:
try:
raise Exception("b")
finally:
raise
This will raise Exception: a. I expected it to raise Exception: b (need I explain why?). Why does the final raise raise the original exception rather than (what I thought) was the last exception raised?
Raise is re-raising the last exception you caught, not the last exception you raised
(reposted from comments for clarity)
On python2.6
I guess, you are expecting the finally block to be tied with the "try" block where you raise the exception "B". The finally block is attached to the first "try" block.
If you added an except block in the inner try block, then the finally block will raise exception B.
try:
raise Exception("a")
except:
try:
raise Exception("b")
except:
pass
finally:
raise
Output:
Traceback (most recent call last):
File "test.py", line 5, in <module>
raise Exception("b")
Exception: b
Another variation that explains whats happening here
try:
raise Exception("a")
except:
try:
raise Exception("b")
except:
raise
Output:
Traceback (most recent call last):
File "test.py", line 7, in <module>
raise Exception("b")
Exception: b
If you see here, replacing the finally block with except does raise the exception B.