Python Raise an Exception
Syntax errors
Perhaps the most frequent type of Python-related complaint is syntax mistakes, also referred to as parsing errors:
while True print('Hello world')
File "<stdin>", line 1
while True print('Hello world')
SyntaxError: invalid syntax
The problematic line is repeated by the parser, which also shows a tiny 'arrow' indicating to the line's initial spot where the mistake was found. The token before the arrow is where the error occurs, or at the very least, where it is noticed. In the example, the method print() is where the error is noticed because a colon (':') is absent.
If the data originated from a script, the file name and line number are printed so you know where to search.
Exceptions
Even if a sentence or formula is syntactically accurate, trying to implement it might result in an error. You will shortly learn how to manage exceptions, which are errors discovered during program execution but are not always fatal. The majority of exceptions, however, are not managed by programs and produce the following error messages:
10 * (1/0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
4 + spam*3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'spam' is not defined
'2' + 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can only concatenate str (not "int") to str
What occurred is described in the error message's final sentence. The kinds of exceptions in the sample are ZeroDivisionError, NameError, and TypeError, and the type is displayed along with the message.
The identity of the occurrence of the built-in exception is contained in the string displayed as the exception type. Although it need not apply to user-defined exceptions, this is true for all built-in exceptions (although it is a useful convention). Integrated IDs are standard exception names (not reserved keywords).
Based on the sort of exception and what triggered it, the remainder of the line gives specific information.
In the form of a stack traceback, the error message's previous section demonstrates the environment in which the exception happened. Generally speaking, it includes a stack traceback showing the source lines; however, lines received from standard input are not displayed.
The built-in exclusions and their descriptions are listed in Built-in Exceptions.
The BaseException class must be a child of ExceptionType(). It typically belongs to the exception class as a subtype. It should be noted that the ExceptionType class does not necessarily have to be explicitly shared by it. It might tangentially descended from a subtype of the exception class.
The __init__ function of the BaseException class takes a *args parameter. It implies that when creating an exception, you can send any number of arguments to the exception object.
The raise line is used to report a ValueError exception in the example that follows. It gives the ValueError __init__ function three arguments:
Dealing with Exceptions
Programs that manage particular errors can be written. Consider the example below, which prompts for input until a legitimate integer is entered, but enables the user to halt the program using Control-C or any other method that the operating system supports. Take notice that a user-generated interruption is indicated by raising the KeyboardInterrupt exception.
while True:
try:
x = int(input("Please enter a number: "))
break
except ValueError:
print("Oops! That was no valid number. Try again...")
This is how the try sentence functions.
The statements between the try and except clauses, known as the try clause, are first performed.
If there is no exception, the except clause is omitted, and the try statement's processing is complete.
The remainder of the sentence is ignored if an exception arises while the try clause is being executed. The except clause is then performed, and processing moves on from the try/except block if its type fits the exception specified after the except keyword.
If an exception that isn't covered by the except clause occurs, it's passed to the outer try lines; if no controller is found, it's an unhandled exception, and execution halts with the notification shown above.
To define handlers for various exceptions, a try sentence may contain more than one except clause. There will only ever be one manager run. In contrast to other handlers of the same try line, handlers only deal with exceptions that appear in the associated try clause. An except clause may enumerate multiple instances as a parenthesized tuple, as in the accompanying illustration:
Exceptions include RuntimeError, TypeError, and NameError:
... pass
The same class or subclass of the class listed in the except clause is consistent with the exception; the other way around, a derived class listed in the except clause is incompatible with a parent class. For instance, the code below prints B, C, and D in that order:
class B(Exception):
pass
class C(B):
pass
class D(C):
pass
for cls in [B, C, D]:
try:
raise cls()
except D:
print("D")
except C:
print("C")
except B:
print("B")
You should be aware that if the except clauses are inverted, with except B appearing first, the first matching except clause is triggered.
An exception may have accompanying values, also referred to as the exception's inputs, when it happens. Depending on the exception type, the arguments' presence and types may vary.
After the exception name in the except clause, a variable may be specified. The args property, which is usually present on exception instances and saves the arguments, is bound to the variable.
Built-in exception types include a function called __str__() that allows printing of all parameters without directly accessing the file.args.
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 be printed directly,
# but may be overridden in exception subclasses
x, y = inst.args # unpack args
print('x =', x)
print('y =', y)
<class 'Exception'>
('spam', 'eggs')
('spam', 'eggs')
x = spam
y = eggs
For unhandled exceptions, the final section of the message (the "detail") is displayed with the result of the __str__() function.
The basic class shared by all exceptions is called BaseException. The parent class for all non-fatal exceptions is one of its subtypes, Exception. Because they are used to signal that the program should end, exceptions that are not instances of Exception are usually not addressed.
They include KeyboardInterrupt, which is raised when a user wants to pause the application, and SystemExit, which is handled by sys.exit().
As a variable that captures (almost) everything, exception can be used. Nevertheless, it is best practice to be as precise as possible when describing the exception types we plan to manage and to permit any unexpected exceptions to continue to occur.
import sys
try:
f = open('myfile.txt')
s = f.readline()
i = int(s.strip())
except OSError as err:
print("OS error:", err)
except ValueError:
print("Could not convert data to an integer.")
except Exception as err:
print(f"Unexpected {err=}, {type(err)=}")
raise
An optional else clause that must come after all except clauses is contained in the attempt... except sentence. It is beneficial for code that needs to run if the attempt phrase doesn't throw an exception. For instance:
for arg in sys.argv[1:]:
try:
f = open(arg, 'r')
except OSError:
print('cannot open', arg)
else:
print(arg, 'has', len(f.readlines()), 'lines')
f.close()
Because it prevents unintentionally catching an exception that wasn't generated by the code being secured by the try... except line, using the else clause is preferable to adding more code to the try clause.
Exception handlers deal with all exceptions, including those that arise inside functions that are called (even tangentially) in the try clause, not just those that appear explicitly in the try clause. For instance:
def this_fails():
x = 1/0
try:
this_fails()
except ZeroDivisionError as err:
print('Handling run-time error:', err)
Handling run-time error: division by zero
Raising Exceptions
The raise line enables the coder to compel the occurrence of a specific exception. As an illustration, report NameError('HiThere')
Traceback (last contact is the most recent):
Line 1 of file "stdin" in module
NameError: HiThere
The only defense that should be made identifies the defense of the exception. Either an exception object or an exception class must be present here (a class that derives from BaseException, such as Exception or one of its subclasses).
By calling the constructor of the exception class with no parameters, it will be automatically created if an exception class is passed:
alias for "raise ValueError()" is raise ValueError.
A more straightforward version of the raise line enables you to re-raise the exception if you need to ascertain whether an exception was raised but don't want to manage it:
>>>
try:
raise NameError('HiThere')
except NameError:
print('An exception flew by!')
raise
An exception flew by!
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
NameError: HiThere
Exception Chaining
The exception that is being managed will be attached to the unhandled exception and included in the error message if it happens inside an except section:
>>>
try:
open("database.sqlite")
except OSError:
raise RuntimeError("unable to handle error")
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
FileNotFoundError: [Errno 2] No such file or directory: 'database.sqlite'
Another exception happened while the previous exception was being handled:
Traceback (last contact is the most recent):
Line 4 of file "stdin" in module
Unable to resolve error: RuntimeError
The raise statement enables an optional from phrase to specify whether one exception is a direct result of another: # exc must be exception instance or None.
exc report RuntimeError
def func():
raise ConnectionError
try:
func()
except ConnectionError as exc:
raise RuntimeError('Failed to open database') from exc
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
File "<stdin>", line 2, in func
ConnectionError
The prior exception served as a clear trigger for the subsequent exception:
Traceback (last contact is the most recent):
Line 4 of file "stdin" in module
RuntimeError: db opening failed
Additionally, automatic exception chaining may be stopped using the from None idiom:
try:
open('database.sqlite')
except OSError:
raise RuntimeError from None
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
RuntimeError
Individual Exclusions
By creating a new exception class, programs are able to create their own exceptions (see Classes for more about Python classes). Typically, exceptions should originate from the Exception class, either directly or tangentially.
However, exception classes are typically kept basic, frequently only providing a few attributes that enable information about the mistake to be extracted by handlers for the exception. It is possible to build exception classes that can perform any action that a class can.
Similar to how the names of the standard exceptions are named, the majority of exceptions are specified with names that finish in "Error".
Many common modules define their own exceptions to signify potential flaws in the methods they describe.
Specifying Cleanup Procedures
Another optional phrase in the try statement is designed to specify cleanup procedures that must always be followedTry this, for instance: raise KeyboardInterrupt
In conclusion, put "Goodbye, universe!"
Hello, universe!
KeyboardInterrupt Traceback (last instruction is the most recent):
Line 2 of file "stdin" in module
If there is a finally clause, that task must be completed before the try sentence is complete.
Whether or not the try line results in an exception, the finally section is executed. The situations where an exception happens are covered in more detail in the following points:
An except clause may be used to manage an exception that happens while the try clause is being executed. After the finally clause has been performed, the exception is raised again if it is not managed by an except clause.
When an except or else phrase is being used, an exception could happen. Once more, after the eventually clause has been used, the exception is triggered.
Exceptions are not raised again if the finally sentence performs a break, continue, or return line.
The finally sentence will run just before the break, continue, or return line is executed if the try statement encounters one of those statements.
If there is a return statement in an eventually clause, that value will be returned rather than the value from the return statement in the attempt clause.
For instance:
def bool_return():
try:
return True
finally:
return False
bool_return()
False
A more complicated example:
>>>
def divide(x, y):
try:
result = x / y
except ZeroDivisionError:
print("division by zero!")
else:
print("result is", result)
finally:
print("executing finally clause")
divide(2, 1)
result is 2.0
putting ultimately phrase to use
Traceback (last contact is the most recent):
Line 1 of file "stdin" in module
TypeError: invalid argument type(s) for /:'str' and'str' in file "stdin>", line 3
You can see that the final adage is consistently used. Because the except clause does not address the TypeError caused by dividing two strings, it is raised again after the eventually clause has been performed.
The ultimately phrase is useful in real-world applications for giving up external resources, regardless of whether the use of the resource was successful (such as files or network links).
Standard Cleansing Techniques
When an object is no longer needed, some objects define standard cleanup procedures to be followed, regardless of whether the process using the object was successful or unsuccessful.
Consider the example that attempts to open a file and print its data to the screen in the next section.
for line in open("myfile.txt"):
print(line, end="")
The issue with this code is that it keeps the file open after this section of the code has completed running for an illogically long period of time. Simple scripts don't have this difficulty, but more complex apps might. Using things like files in a way that ensures prompt and thorough cleanup is possible thanks to the with expression.
with line in f: and open("myfile.txt")
line; end; display
The file f is always closed after the command has been executed, even if there was a problem processing the lines.
Objects that offer preset clean-up steps, like files, will make this clear in their literature.
Creating and Managing a Number of Separate Cases
Certain circumstances call for the reporting of multiple exceptions that have occurred. There are other use cases where it is preferable to continue processing and gather multiple failures rather than report the first exception, as is frequently the case in concurrency frameworks when multiple tasks may have failed in tandem.
A collection of exception objects is wrapped by the built-in ExceptionGroup so they can be raised collectively. It can be captured like any other exception because it is an exception in and of itself.
def f():
excs = [OSError('error 1'), SystemError('error 2')]
raise ExceptionGroup('there were problems', excs)
f()
+ Exception Group Traceback (most recent call last):
| File "<stdin>", line 1, in <module>
| File "<stdin>", line 3, in f
| ExceptionGroup: there were problems
+-+---------------- 1 ----------------
| OSError: error 1
+---------------- 2 ----------------
| SystemError: error 2
+------------------------------------
try:
f()
except Exception as e:
print(f'caught {type(e)}: e')
caught <class 'ExceptionGroup'>: e
Instead of using except, we can manage only the exceptions in the group that fit a specific type by using except*. Each except* clause in the example that follows, which illustrates a stacked exception group, separates out from the group exceptions of a particular class while allowing all other exceptions to spread to other clauses and ultimately be raised again.
def f():
raise ExceptionGroup("group1",
[OSError(1),
SystemError(2),
ExceptionGroup("group2",
[OSError(3), RecursionError(4)])])
try:
f()
except* OSError as e:
print("There were OSErrors")
except* SystemError as e:
print("There were SystemErrors")
There were OSErrors
There were SystemErrors
+ Exception Group Traceback (most recent call last):
| File "<stdin>", line 2, in <module>
| File "<stdin>", line 2, in f
| ExceptionGroup: group1
+-+---------------- 1 ----------------
| ExceptionGroup: group2
+-+---------------- 1 ----------------
| RecursionError: 4
+------------------------------------
Keep in mind that cases, not classes, must make up an exception group. This is due to the fact that in reality, the application would usually only raise and catch exceptions that fit the following pattern
excs = []
for test in tests:
try:
test.run()
except Exception as e:
excs.append(e)
if excs:
raise ExceptionGroup("Test Failures", excs)
Adding Comments to Exception
Typically, information describing the mistake that has happened is initialized when an exception is generated in order to be raised. In some circumstances, adding details after the exception was detected is helpful.
The add note(note) method on exceptions adds a string to the array of notes for the exception. All comments, in the order they were made, are included in the usual traceback depiction after the exception.
try:
raise TypeError('bad type')
except Exception as e:
e.add_note('Add some information')
e.add_note('Add some more information')
raise
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
TypeError: bad type
>>>
For instance, when categorizing exceptions into exception groups, we might want to include context information for the particular errors. The notes that follow each exception in the group include the time this mistake happened.
def f():
raise OSError('operation failed')
excs = []
for i in range(3):
try:
f()
except Exception as e:
e.add_note(f'Happened in Iteration {i+1}')
excs.append(e)
raise ExceptionGroup('We have some problems', excs)
+ Exception Group Traceback (most recent call last):
| File "<stdin>", line 1, in <module>
| ExceptionGroup: We have some problems (3 sub-exceptions)
+-+---------------- 1 ----------------
| Traceback (most recent call last):
| File "<stdin>", line 3, in <module>
| File "<stdin>", line 2, in f
| OSError: operation failed
| Happened in Iteration 1
+---------------- 2 ----------------
| Traceback (most recent call last):
| File "<stdin>", line 3, in <module>
| File "<stdin>", line 2, in f
| OSError: operation failed
| Happened in Iteration 2
+---------------- 3 ----------------
| Traceback (most recent call last):
| File "<stdin>", line 3, in <module>
| File "<stdin>", line 2, in f
| OSError: operation failed
| Happened in Iteration 3
+------------------------------------