Manually raising (throwing) an exception in Python
Asked Answered
P

11

3233

How do I raise an exception in Python so that it can later be caught via an except block?

Pulsometer answered 12/1, 2010 at 21:7 Comment(0)
G
4218

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.')
Gean answered 5/6, 2014 at 16:30 Comment(8)
try: raise ValueError('error') except ValueError as e: print('we will catch exception: Exception',str(e)) #is the best solutionDrinker
@SharathBJ you're raising a ValueError and reporting it as the type, Exception, and that's an unnecessary loss of precision. repr(e) will at least report the type for you.Gean
Where's the official documentation that shows you can pass in a message when raising an exception? ex: raise TypeError("my message")Truda
@GabrielStaples here's the docs on instantiation args: docs.python.org/3/library/exceptions.html#BaseException.argsGean
@AaronHall, ah, I see. I didn't realize the exception type, such as TypeError, was a call to a class constructor! Makes sense now. The message is therefore a common constructor arg for error-type classes.Truda
@AaronHall This is a great answer! Can you please add some notes for Py2 & 3 about re-raise the same exception? What is the difference between raise and raise ex? I think: raise will preserve the original stack trace, but raise ex will reset/rewrite the stack trace. I don't know if there is diff behaviour between Py2 & 3.Puddling
@RussiaMustRemovePutin Yes, and in 2022 this is more true than ever.Raker
you can also chain the previous error with raise Exception("Error happened", exp)Beeswing
D
579

Don't do this. Raising a bare Exception is absolutely not the right thing to do; see Aaron Hall's excellent answer instead.

It can't get much more Pythonic than this:

raise Exception("I know Python!")

Replace Exception with the specific type of exception you want to throw.

See the raise statement documentation for Python if you'd like more information.

Duty answered 12/1, 2010 at 21:8 Comment(15)
No please! This removes the potential to be specific about what you catch. It is ENTIRELY the wrong way to do it. Take a look at Aaron Hall's excellent answer instead of this one. It's times like this I wish I could give more than one downvote per answer.Debbi
@PeterR It's equally terrible that it has so few downvotes. To ANYBODY reading this answer, DO NOT DO THIS EVER! The correct answer is Aaron Hall's one.Debbi
I think there should be a more detailed explanation on why this is wrong or so bad.Kahl
@CharlieParker There is. It's the first part of Aaron Hall's answer.Grindlay
Why can't this answer be flagged for deletion? It has got 93 downvotes already!Forego
@Forego maybe it's because it's answering the question.Dormancy
This answer is still here because many experienced developers disagree with use of specialized exception classes. I wish I could downvote comments.Dirac
This is Python. A lot of times you are writing a 20 line script for some quick work that can only go wrong one way and raising a generic exception with an informative message is the best tool for the job.Issuant
When I only do raise Exception("I know python!") I cannot print its traceAsteroid
I agree with @Dirac here. In most cases, when you catch an exception is already too late to recover it in a proper way (unless you catch it very close from where it actually happened). In most cases what you really want is a detailed description of the error. A generic exception is fine here (or some especialized exception for these cases in which you just want to show a message, with the stacktrace). Rarely I catch an exception to just log and do nothing else like in the case Aaron posted as a bad practice of Exception (and in these cases, I probably want to catch all exceptions).Nowadays
Why the fury against using a bare exception? It is useful to have custom exception classes, but it isn't that big of a deal for basic/small projects or scripts. It's not like a code vulnerability...Seigel
There's a list of more specific exception types at: docs.python.org/3/library/exceptions.html#exception-hierarchyIdolah
The answer answers the question. Period. People are confused about "how to use the language" and "best practices".Scolecite
As someone just experimenting in Jupyter, this is good enough. In this setting, it wouldn't be worth the time to google all the possible exceptions and see which category the one I'm trying to catch falls into.Klaxon
Wow... "ENTIRELY the wrong way"... "DO NOT DO THIS EVER!"... Kids these days.Chrysa
A
102

In Python 3 there are four different syntaxes for raising exceptions:

  1. raise exception
  2. raise exception (args)
  3. raise
  4. raise exception (args) from original_exception

1. Raise exception vs. 2. raise exception (args)

If you use raise exception (args) to raise an exception then the args will be printed when you print the exception object - as shown in the example below.

# Raise exception (args)
try:
    raise ValueError("I have raised an Exception")
except ValueError as exp:
    print("Error", exp)     # Output -> Error I have raised an Exception


# Raise exception
try:
    raise ValueError
except ValueError as exp:
    print("Error", exp)     # Output -> Error

3. Statement raise

The raise statement without any arguments re-raises the last exception.

This is useful if you need to perform some actions after catching the exception and then want to re-raise it. But if there wasn't any exception before, the raise statement raises a TypeError Exception.

def somefunction():
    print("some cleaning")

a = 10
b = 0
result = None

try:
    result = a / b
    print(result)

except Exception:            # Output ->
    somefunction()           # Some cleaning
    raise                    # Traceback (most recent call last):
                             # File "python", line 9, in <module>
                             # ZeroDivisionError: division by zero

4. Raise exception (args) from original_exception

This statement is used to create exception chaining in which an exception that is raised in response to another exception can contain the details of the original exception - as shown in the example below.

class MyCustomException(Exception):
    pass

a = 10
b = 0
reuslt = None
try:
    try:
        result = a / b

    except ZeroDivisionError as exp:
        print("ZeroDivisionError -- ",exp)
        raise MyCustomException("Zero Division ") from exp

except MyCustomException as exp:
    print("MyException",exp)
    print(exp.__cause__)

Output:

ZeroDivisionError --  division by zero
MyException Zero Division
division by zero
Aurist answered 8/11, 2016 at 17:54 Comment(2)
Please note that PEP8 prefers exception(args) over exception (args)Poling
There is also raise exception(args) from None to say that the currently active exception was handled and is no longer of interest. Otherwise if you raise an exception inside an except block and it isn't handled, tracebacks for both exceptions will be shown separated by the message “During handling of the above exception, another exception occurred”Benjaminbenji
E
45

For the common case where you need to throw an exception in response to some unexpected conditions, and that you never intend to catch, but simply to fail fast to enable you to debug from there if it ever happens — the most logical one seems to be AssertionError:

if 0 < distance <= RADIUS:
    #Do something.
elif RADIUS < distance:
    #Do something.
else:
    raise AssertionError("Unexpected value of 'distance'!", distance)
Endosmosis answered 19/5, 2015 at 4:55 Comment(7)
This is a better case for ValueError than AssertionError because there's no problem with an assertion (because none is being made here) -- the problem is with a value. If you really want an AssertionError in this case, write assert distance > 0, 'Distance must be positive'. But you shouldn't error check that way because assertions can be turned off (python -O).Eweneck
@Two-BitAlchemist Good point. The idea was lost in simplification, when I wrote the simple example above. In many similar cases it's a condition that isn't associated with a particular value. Rather, the meaning is "control flow should never get here".Endosmosis
@Two-BitAlchemist Assertions can be turned off, yes, but then you shouldn't use them to error check at all?Endosmosis
Well it depends. I wouldn't let that be my only error checking in a program I intended to distribute. On the other hand, I could make a program just for my co-workers and tell them they use it at their own risk if they run it with -O.Eweneck
@Two-BitAlchemist For me the role of assertions isn't error-checking per se (which is what testing is for), but they set up fences within the code that certain bugs can't get through. So it becomes easier to track down and isolate the bugs, which will inevitably occur. This is just good habits that take little effort, while testing takes a lot of effort and a lot of time.Endosmosis
"testing takes a lot of effort and a lot of time" - true, but not as much as the time and effort it saves.Lorie
The error could be reproduced if pseudo-const RADIUS is somehow set to negative too.Gleeful
D
22

Read the existing answers first, this is just an addendum.

Notice that you can raise exceptions with or without arguments.

Example:

raise SystemExit

exits the program, but you might want to know what happened. So you can use this.

raise SystemExit("program exited")

This will print "program exited" to standard error before closing the program.

Drastic answered 29/3, 2017 at 11:59 Comment(2)
Isn't this against the OOP paradigm? I assume, the first case throws the class reference and the second one an instance of SystemExit. Wouldn't raise SystemExit() be the better choice? Why does the first one even work?Geanine
python.org/dev/peps/pep-3109 discusses thisPancratium
A
5

Just to note: there are times when you do want to handle generic exceptions. If you're processing a bunch of files and logging your errors, you might want to catch any error that occurs for a file, log it, and continue processing the rest of the files. In that case, a

try:
    foo()
except Exception as e:
    print(e) # Print out handled error

block is a good way to do it. You'll still want to raise specific exceptions so you know what they mean, though.

Acapulco answered 3/12, 2018 at 16:13 Comment(0)
B
4

Another way to throw an exception is using assert. You can use assert to verify a condition is being fulfilled. If not, then it will raise AssertionError. For more details have a look here.

def avg(marks):
    assert len(marks) != 0, "List is empty."
    return sum(marks)/len(marks)

mark2 = [55,88,78,90,79]
print("Average of mark2:", avg(mark2))

mark1 = []
print("Average of mark1:", avg(mark1))
Burglarious answered 27/2, 2019 at 9:42 Comment(2)
not foolproof since asserts in CPython are ignored when the interpreter is inovked with optimizations (-O) flag; if you want to really control program flow "hey this condition shouldn't happen but abend if it is true", manually raise AssertionError()Vagarious
Asserts are for debugging. If it is a possible user input, raise a ValueError.Uncouth
F
2

You might also want to raise custom exceptions. For example, if you're writing a library, it's a very good practice to make a base exception class for your module, and then have custom sub-exceptions to be more specific.

You can achieve that like this:

class MyModuleBaseClass(Exception):
    pass

class MoreSpecificException(MyModuleBaseClass):
    pass


# To raise custom exceptions, you can just
# use the raise keyword
raise MoreSpecificException
raise MoreSpecificException('message')

If you're not interested in having a custom base class, you can just inherit your custom exception classes from an ordinary exception class like Exception, TypeError, ValueError, etc.

Fanchet answered 8/6, 2021 at 16:20 Comment(0)
L
0

You should learn the raise statement of Python for that.

It should be kept inside the try block.

Example -

try:
    raise TypeError            # Replace TypeError by any other error if you want
except TypeError:
    print('TypeError raised')
Lontson answered 31/3, 2020 at 8:37 Comment(2)
Can you give a reason for why your example is good? I've read all the answers to this one, and I'm genuinely curious.Handal
It's not good to blanket try/except an error unless you have a good reason to.Uncouth
S
0

If you don't care about which error to raise, you could use assert to raise an AssertionError:

>>> assert False, "Manually raised error"
Traceback (most recent call last):
  File "<pyshell#24>", line 1, in <module>
    assert False, "Manually raised error"
AssertionError: Manually raised error
>>> 

The assert keyword raises an AssertionError if the condition is False. In this case, we specified False directly, so it raises the error, but to have it have a text we want it to raise to, we add a comma and specify the error text we want. In this case, I wrote Manually raised error and this raises it with that text.

Speechless answered 12/9, 2021 at 1:59 Comment(1)
asserts will be disabled when the interpreter is run with optimizations, so should not be used for control flow. see comments on Rehan Haider's answerUncouth
L
-3

If you don’t care about the raised exception, do:

def crash(): return 0/0

It does not allow you to throw a specific message to your user but will crash the python interpriter.

For the more advanced python users who have used python may think that the expression will be evaluated at compile time (python is compiled) but the python compiler wont evaluate the expression at run time. If we look at what tthe dis dissasembely module for python shows, we can see the bytecode.

              2 LOAD_CONST               1 (0)
              4 LOAD_CONST               1 (0)
              6 BINARY_OP               11 (/)

So essentialy python will push the constant 0 onto the stack and another 0 then run a binary operator, to divide.

Although this function is quite useless, and should never be used in production code, it can still crash the python interpriter.

Lipman answered 8/12, 2021 at 16:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.