Error handling with Python
Errors happen. Writing scripts that expect and handle errors can save you a lot of 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 many other reasons that are not specifically related to a geoprocessing tool; these too 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 a system error, or an exception. Python allows you to write a routine that is automatically run whenever a system error is generated. Within this error-handling routine, you can 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.
Geoprocessing tool error messages are accompanied by a six-digit code. These ID codes have been documented to provide additional information on their cause and how they can be dealt with.
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 then executed. Using a simple except statement is the most basic form of error handling.
In the following code, Buffer fails because the required Distance parameter 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
try:
# Execute the Buffer tool
#
arcpy.Buffer_analysis("c:/transport/roads.shp", "c:/transport/roads_buffer.shp")
except Exception as e:
print e.message
# 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.message)
The try statement has an optional finally clause that can be used for tasks that should be always be executed, whether an exception has occurred or not. In the following example, the ArcGIS 3D Analyst extension is checked back in under a finally clause, ensuring that the extension is always checked back in.
class LicenseError(Exception):
pass
import arcpy
from arcpy import env
try:
if arcpy.CheckExtension("3D") == "Available":
arcpy.CheckOutExtension("3D")
else:
# Raise a custom exception
#
raise LicenseError
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:
print arcpy.GetMessages(2)
finally:
# Check in the 3D Analyst extension
#
arcpy.CheckInExtension("3D")
raise statement
The previous example dealt with handling 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
arcpy.env.overwriteOutput = 1
fc = arcpy.GetParameterAsText(0)
try:
# Check that the input has features
#
result = arcpy.GetCount_management(fc)
if int(result.getOutput(0)) > 0:
arcpy.FeatureToPolygon_management(fc, os.path.dirname(fc) + os.sep + "out_poly.shp")
else:
# Raise custom exception
#
raise NoFeatures(result)
except NoFeatures:
# The input has no features
#
print fc + " has no features."
except:
# By default any other errors will be caught here
#
print arcpy.GetMessages(2)
ExecuteError class
When a geoprocessing tool fails, it throws an ExecuteError exception class. What this means is that you can divide errors into two groups, geoprocessing errors (those that throw the ExecuteError exception) and everything else. You can then handle the errors differently, as demonstrated in the code below:
import arcpy
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:
arcpy.AddError("Non-tool error occurred")
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 you 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 + "\n"
print msgs
If the above code was used and a geoprocessing tool error occurred, such an invalid input, this would raise ExecuteError, and the first except statement would be used. This statement would print out 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 would get a traceback object and print out 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 out 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 raises an exception, the result object is null. 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.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.
A result object created by calling a geoprocessing service on ArcGIS for Server is created even with a tool failure. A result object only fails to be created when a tool is run locally and it raises an error. For more information about using the result object, see Getting results from a geoprocessing tool.