Python raise from usage
The difference is that when you use from
, the __cause__
attribute is set and the message states that the exception was directly caused by. If you omit the from
then no __cause__
is set, but the __context__
attribute may be set as well, and the traceback then shows the context as during handling something else happened.
Setting the __context__
happens if you used raise
in an exception handler; if you used raise
anywhere else no __context__
is set either.
If a __cause__
is set, a __suppress_context__ = True
flag is also set on the exception; when __suppress_context__
is set to True
, the __context__
is ignored when printing a traceback.
When raising from a exception handler where you don't want to show the context (don't want a during handling another exception happened message), then use raise ... from None
to set __suppress_context__
to True
.
In other words, Python sets a context on exceptions so you can introspect where an exception was raised, letting you see if another exception was replaced by it. You can also add a cause to an exception, making the traceback explicit about the other exception (use different wording), and the context is ignored (but can still be introspected when debugging). Using raise ... from None
lets you suppress the context being printed.
See the raise
statement documenation:
The
from
clause is used for exception chaining: if given, the second expression must be another exception class or instance, which will then be attached to the raised exception as the__cause__
attribute (which is writable). If the raised exception is not handled, both exceptions will be printed:>>> try:
... print(1 / 0)
... except Exception as exc:
... raise RuntimeError("Something bad happened") from exc
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
ZeroDivisionError: int division or modulo by zero
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
RuntimeError: Something bad happenedA similar mechanism works implicitly if an exception is raised inside an exception handler or a
finally
clause: the previous exception is then attached as the new exception’s__context__
attribute:>>> try:
... print(1 / 0)
... except:
... raise RuntimeError("Something bad happened")
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
ZeroDivisionError: int division or modulo by zero
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
RuntimeError: Something bad happened
Also see the Built-in Exceptions documentation for details on the context and cause information attached to exceptions.
How to use raise keyword in Python
It has two purposes.
jackcogdill has given the first one:
It's used for raising your own errors.
if something:
raise Exception('My error!')
The second is to reraise the current exception in an exception handler, so that it can be handled further up the call stack.
try:
generate_exception()
except SomeException as e:
if not can_handle(e):
raise
handle_exception(e)
Manually raising (throwing) an exception in Python
How do I manually throw/raise an exception in Python?
Use the most specific Exception constructor that semantically fits your issue.
Be specific in your message, e.g.:
raise ValueError('A very specific bad thing happened.')
Don't raise generic exceptions
Avoid raising a generic Exception
. To catch it, you'll have to catch all other more specific exceptions that subclass it.
Problem 1: Hiding bugs
raise Exception('I know Python!') # Don't! If you catch, likely to hide bugs.
For example:
def demo_bad_catch():
try:
raise ValueError('Represents a hidden bug, do not catch this')
raise Exception('This is the exception you expect to handle')
except Exception as error:
print('Caught this error: ' + repr(error))
>>> demo_bad_catch()
Caught this error: ValueError('Represents a hidden bug, do not catch this',)
Problem 2: Won't catch
And more specific catches won't catch the general exception:
def demo_no_catch():
try:
raise Exception('general exceptions not caught by specific handling')
except ValueError as e:
print('we will not catch exception: Exception')
>>> demo_no_catch()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in demo_no_catch
Exception: general exceptions not caught by specific handling
Best Practices: raise
statement
Instead, use the most specific Exception constructor that semantically fits your issue.
raise ValueError('A very specific bad thing happened')
which also handily allows an arbitrary number of arguments to be passed to the constructor:
raise ValueError('A very specific bad thing happened', 'foo', 'bar', 'baz')
These arguments are accessed by the args
attribute on the Exception
object. For example:
try:
some_code_that_may_raise_our_value_error()
except ValueError as err:
print(err.args)
prints
('message', 'foo', 'bar', 'baz')
In Python 2.5, an actual message
attribute was added to BaseException
in favor of encouraging users to subclass Exceptions and stop using args
, but the introduction of message
and the original deprecation of args has been retracted.
Best Practices: except
clause
When inside an except clause, you might want to, for example, log that a specific type of error happened, and then re-raise. The best way to do this while preserving the stack trace is to use a bare raise statement. For example:
logger = logging.getLogger(__name__)
try:
do_something_in_app_that_breaks_easily()
except AppError as error:
logger.error(error)
raise # just this!
# raise AppError # Don't do this, you'll lose the stack trace!
Don't modify your errors... but if you insist.
You can preserve the stacktrace (and error value) with sys.exc_info()
, but this is way more error prone and has compatibility problems between Python 2 and 3, prefer to use a bare raise
to re-raise.
To explain - the sys.exc_info()
returns the type, value, and traceback.
type, value, traceback = sys.exc_info()
This is the syntax in Python 2 - note this is not compatible with Python 3:
raise AppError, error, sys.exc_info()[2] # avoid this.
# Equivalently, as error *is* the second object:
raise sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]
If you want to, you can modify what happens with your new raise - e.g. setting new args
for the instance:
def error():
raise ValueError('oops!')
def catch_error_modify_message():
try:
error()
except ValueError:
error_type, error_instance, traceback = sys.exc_info()
error_instance.args = (error_instance.args[0] + ' <modification>',)
raise error_type, error_instance, traceback
And we have preserved the whole traceback while modifying the args. Note that this is not a best practice and it is invalid syntax in Python 3 (making keeping compatibility much harder to work around).
>>> catch_error_modify_message()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in catch_error_modify_message
File "<stdin>", line 2, in error
ValueError: oops! <modification>
In Python 3:
raise error.with_traceback(sys.exc_info()[2])
Again: avoid manually manipulating tracebacks. It's less efficient and more error prone. And if you're using threading and sys.exc_info
you may even get the wrong traceback (especially if you're using exception handling for control flow - which I'd personally tend to avoid.)
Python 3, Exception chaining
In Python 3, you can chain Exceptions, which preserve tracebacks:
raise RuntimeError('specific message') from error
Be aware:
- this does allow changing the error type raised, and
- this is not compatible with Python 2.
Deprecated Methods:
These can easily hide and even get into production code. You want to raise an exception, and doing them will raise an exception, but not the one intended!
Valid in Python 2, but not in Python 3 is the following:
raise ValueError, 'message' # Don't do this, it's deprecated!
Only valid in much older versions of Python (2.4 and lower), you may still see people raising strings:
raise 'message' # really really wrong. don't do this.
In all modern versions, this will actually raise a TypeError
, because you're not raising a BaseException
type. If you're not checking for the right exception and don't have a reviewer that's aware of the issue, it could get into production.
Example Usage
I raise Exceptions to warn consumers of my API if they're using it incorrectly:
def api_func(foo):
'''foo should be either 'baz' or 'bar'. returns something very useful.'''
if foo not in _ALLOWED_ARGS:
raise ValueError('{foo} wrong, use "baz" or "bar"'.format(foo=repr(foo)))
Create your own error types when apropos
"I want to make an error on purpose, so that it would go into the except"
You can create your own error types, if you want to indicate something specific is wrong with your application, just subclass the appropriate point in the exception hierarchy:
class MyAppLookupError(LookupError):
'''raise this when there's a lookup error for my app'''
and usage:
if important_key not in resource_dict and not ok_to_be_missing:
raise MyAppLookupError('resource is missing, and that is not ok.')
How to raise exception in except block without original traceback in python 3.5+
Use raise from None
to suppress earlier exceptions:
try:
can_raise_custom_lib_exception()
except custom_lib_exception as e:
cleanup()
raise myOwnException("my own extra text") from None
7.8. The raise statement
[...]
Exception chaining can be explicitly suppressed by specifyingNone
in thefrom
clause:
Get only last traceback without using raise from None?
Use the __suppress_context__
attribute to disable context printing.
According to the docs, using raise MyCustomException(foo) from bar
sets __cause__
to bar
, and __context__
to the original exception (the implicitly chained exception).
An implicitly chained exception in
__context__
is shown only if__cause__
is None and__suppress_context__
is false.
Here's an example:
# Declare an exception that never shows context.
class MyCustomException(Exception):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.__suppress_context__ = True
try:
1/0
except ZeroDivisionError as e:
raise MyCustomException(str(e))
Here's the output I get:
Traceback (most recent call last):
File "/home/don/workspace/scratch/scratch.py", line 12, in <module>
raise MyCustomException(str(e))
MyCustomException: division by zero
Here's the output if I set __suppress_context__
to False
:
Traceback (most recent call last):
File "/home/don/workspace/scratch/scratch.py", line 10, in <module>
1/0
ZeroDivisionError: division by zero
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/don/workspace/scratch/scratch.py", line 12, in <module>
raise MyCustomException(str(e))
MyCustomException: division by zero
Use of except Exception vs. except ... raise in Python
No, your code is not equivalent, for several reasons:
- A blank
except:
catches all exceptions, including those derived fromBaseException
(SystemExit
,KeyboardInterrupt
andGeneratorExit
); catchingException
filters out those exceptions you generally want to avoid catching without a re-raise. In older Python releases, it would also catch string exceptions (no longer permitted). - The
except Exception as e
catches subclasses, but then raises a newException()
instance; the specific type information can't be used anymore in downstreamtry...except
statements. - In Python 3, raising a new exception from an exception handler creates an exception chain (where the original exception is added as the
Exception.__context__
attribute, see Python "raise from" usage) - The message is updated; that's probably the whole point here, is to give the exception a different message.
The code you found is.. rather bad practice. The top-level exception handler should just catch and print a message and perhaps a traceback, rather than re-raise the exception with a new message (and in Python 2 lose all information on the original exception, in Python 3 make it inaccessible to exception matching in later handlers).
Pylint raise-missing-from
The link in the comment on your question above outlines the issue and provides a solution, but for clarity of those landing straight on this page like myself, without having to go off to another thread, read and gain context, here is the answer to your specific problem:
TL;DR;
This is simply solved by aliasing the Exception you are 'excepting' and refering to it in your second raise.
Taking your code snippet above, see the bottom two lines, I've added 'under-carets' to denote what I've added.
class SnippetDetail(APIView):
"""
Retrieve, update or delete a snippet instance.
"""
def get_object(self, pk):
try:
return Snippet.objects.get(pk=pk)
except Snippet.DoesNotExist as snip_no_exist:
# ^^^^^^^^^^^^^^^^
raise Http404 from snip_no_exist
# ^^^^^^^^^^^^^^^^^^
Note: The alias can be any well formed string.
Related Topics
Installing Scipy in Python 3.5 on 32-Bit Windows 7 MAChine
How to Take the Nth Digit of a Number in Python
Class Inheritance in Python 3.7 Dataclasses
How to Invoke Pandas.Rolling.Apply with Parameters from Multiple Column
How to Remove the Top and Right Axis in Matplotlib
Using a Dictionary to Select Function to Execute
Check If String Is in a Pandas Dataframe
Get the Position of the Largest Value in a Multi-Dimensional Numpy Array
Making an Executable in Cython
Appending Item to Lists Within a List Comprehension
Inserting a Table Name into a Query Gives SQLite3.Operationalerror: Near "": Syntax Error
How Could I Use Batch Normalization in Tensorflow
Pythonic Way to Create Union of All Values Contained in Multiple Lists
Wordnet Lemmatization and Pos Tagging in Python