Manually raising throwing an exception in Python

0 votes

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

May 11, 2018 in Python by aryya
• 7,460 points
17,551 views

2 answers to this question.

0 votes

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.')


Hope this is helpful!

To know more join Master in Python programming course today.

Thanks!

answered May 11, 2018 by charlie_brown
• 7,720 points
0 votes
Exception handling issues in python can easily be terminated using the rich library function in the IDE.
answered Jan 18, 2019 by nick
• 140 points

Related Questions In Python

0 votes
1 answer

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

It's pretty simple to raise a query  raise ...READ MORE

answered May 29, 2019 in Python by Umesh
987 views
0 votes
1 answer

I am unable to import pyttsx2 in python. Python is throwing exception ModuleNotFoundError.

Hey, @Athrava Gulve, Hey, @Atharva, Which version of Python your ...READ MORE

answered Oct 7, 2020 in Python by Gitika
• 65,770 points
758 views
0 votes
1 answer

How to print an exception in Python?

Hello @kartik, For Python 2.6 and later and ...READ MORE

answered Dec 7, 2020 in Python by Niroj
• 82,840 points
666 views
+4 votes
7 answers
+1 vote
8 answers

Count the frequency of an item in a python list

To count the number of appearances: from collections ...READ MORE

answered Oct 18, 2018 in Python by tinitales
36,075 views
0 votes
1 answer

Size of an object in Python

Use sys.getsizeof() function: >>> import sys >>> s = ...READ MORE

answered May 25, 2018 in Python by Nietzsche's daemon
• 4,260 points
1,210 views
0 votes
1 answer

Create an empty list in python with certain size

Try this instead: lst = [None] * 10 The ...READ MORE

answered Aug 2, 2018 in Python by bug_seeker
• 15,510 points
28,146 views
0 votes
1 answer

Creating an empty list in Python

Here is how you can test which ...READ MORE

answered Aug 17, 2018 in Python by Priyaj
• 58,020 points
1,092 views
+2 votes
3 answers

How can I play an audio file in the background using Python?

down voteacceptedFor windows: you could use  winsound.SND_ASYNC to play them ...READ MORE

answered Apr 4, 2018 in Python by charlie_brown
• 7,720 points
13,591 views
0 votes
1 answer

How can I find out the index of an element from row and column in Python?

You probably want to use np.ravel_multi_index: [code] import numpy ...READ MORE

answered Apr 16, 2018 in Python by charlie_brown
• 7,720 points
2,372 views
webinar REGISTER FOR FREE WEBINAR X
REGISTER NOW
webinar_success Thank you for registering Join Edureka Meetup community for 100+ Free Webinars each month JOIN MEETUP GROUP