Errors happen. Writing scripts that expect and handle errors can save time and frustration. When a tool returns an error message, ArcPy generates a system error or exception. In Python, you can provide a variety of structures and methods that can handle exceptions. Of course, a script can fail for other reasons not related to a geoprocessing tool. These also need to be caught and dealt with in an appropriate manner. The following sections offer a few techniques that introduce the basics of Python exception handling.
When a tool writes an error message, ArcPy generates an arcpy.ExecuteError exception. Python allows you to write a routine that automatically runs when a system error is generated. In this error-handling routine, retrieve the error message from ArcPy and react accordingly. If a script does not have an error-handling routine, it fails immediately, which decreases its robustness. Use error-handling routines to manage errors and improve a script's usability.
try-except statement
A try-except statement can be used to wrap entire programs or just particular portions of code to trap and identify errors. If an error occurs within the try statement, an exception is raised, and the code under the except statement is executed. Using a basic except statement is the most basic form of error handling.
In the following code, Buffer fails because the required buffer_distance_or_field argument has not been provided. Instead of failing without explanation, the except statement is used to trap the error, then fetch and print the error message generated by Buffer. Note that the except block is only executed if Buffer returns an error.
import arcpy
import sys
try:
# Execute the Buffer tool
#
arcpy.Buffer_analysis("c:/transport/roads.shp", "c:/transport/roads_buffer.shp")
except Exception:
e = sys.exc_info()[1]
print(e.args[0])
# If using this code within a script tool, AddError can be used to return messages
# back to a script tool. If not, AddError will have no effect.
arcpy.AddError(e.args[0])
The try statement has an optional finally clause that can be used for tasks that should always be executed, whether an exception occurs or not. In the following example, the ArcGIS 3D Analyst is checked in under a finally clause, ensuring that the extension is always checked in.
class LicenseError(Exception):
pass
import arcpy
try:
if arcpy.CheckExtension("3D") == "Available":
arcpy.CheckOutExtension("3D")
else:
# Raise a custom exception
#
raise LicenseError
arcpy.env.workspace = "D:/GrosMorne"
arcpy.HillShade_3d("WesternBrook", "westbrook_hill", 300)
arcpy.Aspect_3d("WesternBrook", "westbrook_aspect")
except LicenseError:
print "3D Analyst license is unavailable"
except arcpy.ExecuteError:
print(arcpy.GetMessages(2))
finally:
# Check in the 3D Analyst extension
#
arcpy.CheckInExtension("3D")
raise statement
The previous example deals with an exception that occurred in the code. In some cases, it may be necessary to create custom exceptions. A raise statement can be used for this purpose. In the following code, a raise statement is used when an input feature class has been identified as having no features. This is not strictly an error but a condition that the code can be used to guard against.
class NoFeatures(Exception):
pass
import arcpy
import os
import sys
arcpy.env.overwriteOutput = True
fc = arcpy.GetParameterAsText(0)
try:
# Check that the input has features
#
result = arcpy.GetCount_management(fc)
if int(result[0]) > 0:
arcpy.FeatureToPolygon_management(
fc, os.path.join(os.path.dirname(fc), 'out_poly.shp'))
else:
# Raise custom exception
#
raise NoFeatures(result)
except NoFeatures:
# The input has no features
#
print('{} has no features'.format(fc))
except:
# By default any other errors will be caught here
#
e = sys.exc_info()[1]
print(e.args[0])
ExecuteError class
When a geoprocessing tool fails, it throws an arcpy.ExecuteError exception class, which means that you can divide errors into different groups, geoprocessing errors (those that throw the arcpy.ExecuteError exception) and other exception types. You can then handle the errors differently, as demonstrated in the code below:
import arcpy
import sys
try:
result = arcpy.GetCount_management("C:/invalid.shp")
# Return geoprocessing specific errors
#
except arcpy.ExecuteError:
arcpy.AddError(arcpy.GetMessages(2))
# Return any other type of error
except:
# By default any other errors will be caught here
#
e = sys.exc_info()[1]
print(e.args[0])
traceback
In larger, more complex scripts, it can be difficult to determine the precise location of an error. Python's sys and traceback modules can be used together to isolate the exact location and cause of the error, identifying the cause of an error more accurately and saving valuable debugging time.
# Import the required modules
#
import arcpy
import sys
import traceback
arcpy.env.workspace = "C:/Data/myData.gdb"
try:
arcpy.CreateSpatialReference_management()
#--------------------------
# Your code goes here
#
# See the table below for examples
#--------------------------
except arcpy.ExecuteError:
# Get the tool error messages
#
msgs = arcpy.GetMessages(2)
# Return tool error messages for use with a script tool
#
arcpy.AddError(msgs)
# Print tool error messages for use in Python/PythonWin
#
print(msgs)
except:
# Get the traceback object
#
tb = sys.exc_info()[2]
tbinfo = traceback.format_tb(tb)[0]
# Concatenate information together concerning the error into a message string
#
pymsg = "PYTHON ERRORS:\nTraceback info:\n" + tbinfo + "\nError Info:\n" + str(sys.exc_info()[1])
msgs = "ArcPy ERRORS:\n" + arcpy.GetMessages(2) + "\n"
# Return python error messages for use in script tool or Python Window
#
arcpy.AddError(pymsg)
arcpy.AddError(msgs)
# Print Python error messages for use in Python / Python Window
#
print(pymsg)
print(msgs)
If the above code was used and a geoprocessing tool error occurred, such an invalid input, this would raise arcpy.ExecuteError, and the first except statement would be used. This statement would print the error messages using the GetMessages function. If the same code was used, but a different type of error occurred, the second except statement would be used. Instead of printing geoprocessing messages, it gets a traceback object and prints the appropriate system error messages.
The table below shows the expected errors that result from three different lines of codes that could be substituted into the code above. The first is a geoprocessing tool error, which prints out the traceback information and the geoprocessing error messages. The second and third examples are not specifically caught and print only the traceback information.
Your code | Resulting error |
---|---|
arcpy.GetCount_management("") |
|
x = "a" + 1 |
|
float("a text string") |
|
Getting error messages from a result object
A quick word about the Result object, shown below:
result = arcpy.GetCount_management("c:/data/rivers.shp")
If the call to GetCount_management raises an exception, the Result object is not created. This means you cannot retrieve error messages from the Result object.
import arcpy
try:
result = arcpy.GetCount_management("c:/data/rivers.shp")
# Return Geoprocessing specific errors
# (this method is incorrect!)
except arcpy.ExecuteError:
arcpy.AddError(result.getMessages(2))
The above code fails with the message name 'result' is not defined. This is because the Result object could not be created due to the tool's failure. Since the Result object is not created, a Python error is raised when trying to use the getMessages method.