Review and test a script

Python scripts can be used to automate tasks in ArcGIS Pro. You can share scripts you have created with others as Python .py files. However, using a script written by someone else requires some knowledge of Python, even if only to change the names of the datasets to be processed. To share a Python script in an easier-to-use form, you can create a Python script tool.

A Python script tool allows you to provide the Python script with a user-friendly interface that looks like the standard geoprocessing tools. You can make your script tool behave like a standard geoprocessing tool by adding code to validate user inputs and to provide messages back to the user. You can also include built-in support documentation to explain how to use the tool and configure its parameters.

Creating script tools makes it easier to share the functionality of a Python script with other users. Python scripts can be embedded in a custom toolbox file (.atbx), which makes sharing straightforward.

Open a project and review the datasets

First, you will download the tutorial data, open an existing project, and review the data that will be processed by the tool.

  1. Download the PythonTool.zip file for this tutorial and locate the file on your computer.

    Note:
    Most web browsers download files to your computer's Downloads folder by default.

  2. Right-click the PythonTool.zip file and extract it to the C:\Tutorials\ folder.

    Note:
    If you do not already have this folder on your computer, you can create it and extract the zip file. You can also extract it to another location, but the instructions and script use the C:\Tutorials path. If you put the PythonTool folder in a different location, you will need to update the script to point to your folder path.

  3. Start ArcGIS Pro and sign in to ArcGIS Online.

    Note:

    If you don't have an organizational account, see options for software access.

  4. In ArcGIS Pro, click Open another project.

    Click Open another project.

  5. In the Open Project window, browse to the PythonTool folder, click Python Tool.aprx, and click OK.

    Open the PythonTool project.

    The project opens.

    DC area map opens.

    The map shows several feature classes for the Washington, D.C., area.

  6. If the Catalog pane is not already visible, on the ribbon, on the View tab, click Catalog Pane.
  7. In the Catalog pane, expand Folders and PythonTool.
  8. Expand the DC.gdb geodatabase.

    Expand Folders, the PythonTool folder, and the DC.gdb geodatabase.

    The geodatabase contains eight feature classes. Some of these are layers on the map.

    The PythonTool folder also contains a shapefile called neighborhood.shp that is also on the map.

    Suppose you work in a city office and regularly receive requests for data extracts for specific neighborhoods in the city.

    To complete this task, you need to clip all the feature classes in the geodatabase to a neighborhood and store the clipped feature classes in a new geodatabase. This new geodatabase should be compressed to a ZIP archive so you can easily share it, or use it for backup purposes.

    This is a relatively common task in many GIS workplaces.

    You can carry out these tasks in ArcGIS Pro by using the Create File Geodatabase tool to make the new geodatabase, then running the Clip tool several times, once for each feature class. You can then create the ZIP archive from the File Explorer, or using a compression utility. This process can be cumbersome and time-consuming, especially if there are many feature classes to process.

    Since this is a regular task, it makes sense to automate it.

    A script to automate the task is included with the project. You will review and test this stand-alone script, and then create a Python script tool based on this script.

Review and test a Python script

First, you’ll open the script and review its content.

  1. In File Explorer, browse to the C:\Tutorials\PythonTool folder.
  2. Right-click the clip_zip.py Python script file, and click Edit with IDLE (ArcGIS Pro).

    Right-click the script and click Edit with IDLE (ArcGIS Pro).

    Note:
    If you also have ArcGIS Desktop 10.x (ArcMap) installed, the context menu will also include the shortcut Edit with IDLE. Do not use this shortcut since this does not open the correct version of Python.

    Note:
    If you are using Windows 11 and the Edit with IDLE (ArcGIS Pro) option is not showing, click Show more options, and click Edit with IDLE (ArcGIS Pro).

    The script opens in IDLE, the default editor for Python.

    The script opens in IDLE.

    You will review this script to understand how it works, and test the script.

    The script starts with the imports:

    import arcpy
    import os
    import zipfile

    These lines import modules that the script will use.

    ArcPy is needed to run geoprocessing tools and related tasks, the os module is needed for path manipulations, and the zipfile module is needed to create a ZIP archive.

    The next part of the script sets up the workspace, and the paths for the input and output datasets. Without the comments, it looks as follows:

    arcpy.env.workspace = "C:/Tutorials/PythonTool/DC.gdb"
    clip_fc = "C:/Tutorials/PythonTool/neighborhood.shp"  
    gdb = "C:/Tutorials/PythonTool/Clip.gdb"
    gdb_path, new_gdb = os.path.split(gdb)
    gdb = "C:/Tutorials/PythonTool/Clip.zip"
    arcpy.env.overwriteOutput = True

    The workspace is set to the DC.gdb geodatabase, which contains the feature classes that need to be clipped. The neighborhood.shp feature class will be used as the clip features. The Clip.gdb geodatabase will be created by the script and will contain the output from the Clip tool. The full path of the geodatabase is split into the folder, C:\Tutorials\PythonTool, and the geodatabase name, Clip.gdb, using os.path.split(). These are needed as separate variables later in the script. The ZIP archive Clip.zip will contain the complete contents of the geodatabase Clip.gdb

    All the datasets reside in the same folder, but that is not necessary for the script to work. For example, the new geodatabase could be created in a separate folder.

    Next in the script is the creation of the new geodatabase. A printed message confirms that the new geodatabase has been created.

    arcpy.CreateFileGDB_management(gdb_path, new_gdb)
    print(f"Output geodatabase {gdb} created")

    The next section is where the Clip tool is run:

    inputs = arcpy.ListFeatureClasses()
    for fc in inputs:
        fc_name = arcpy.da.Describe(fc)["baseName"]
        new_fc = os.path.join(gdb, fc_name)
        arcpy.analysis.Clip(fc, clip_fc, new_fc)
        print(f"Output feature class {new_fc} created")

    The ListFeatureClasses() function returns all the feature classes inside the workspace, which is the DC.gdb geodatabase. Inside the for loop that follows, the base name of the feature class is obtained. While this is not strictly necessary in this example, it makes the script more robust because it removes any file extension, such as .shp when shapefiles are inputs. The full path for the output feature class is created using os.path.join(). This is necessary since you want the output to end up in the Clip.gdb geodatabase, not in the workspace. The Clip tool is run and a message is printed confirming that the new feature class has been created. These steps are repeated for every feature class in the list.

    The final section creates the ZIP archive:

    with zipfile.ZipFile(out_zip, "w") as myzip:
        for f in os.listdir(gdb):
            if not f.endswith(".lock"):
                file_name = os.path.join(gdb, f)
                arc_name = os.path.join(new_gdb, f)
                myzip.write(file_name, arc_name)

    A new empty ZIP archive is created using zipfile.ZipFile(). A with statement is used here to create and open the file, while at the same time ensuring that the file is closed once the processing is complete. The contents of the geodatabase are determined using os.listdir(), which returns a list of all the files inside the geodatabase folder. Any files with the file extension .lock are skipped. The rest of the files are written to the ZIP archive. The variable file_name represents the full path of each file in the original geodatabase. The variable arc_name represents each file to be written to the ZIP archive but only including the name of the geodatabase and the file name. This replicates how you would manually create a ZIP archive in File Explorer.

    Now that you've reviewed the script, you will prepare to test it.

  3. In IDLE, look at the paths used at the beginning of the script.

    If you copied the tutorial folder to a location other than C:/Tutorials/PythonTool/, you will need to get the path to the data using File Explorer.

  4. Edit the four paths in this section:

    arcpy.env.workspace = "C:/Tutorials/PythonTool/DC.gdb"
    clip_fc = "C:/Tutorials/PythonTool/neighborhood.shp"  
    gdb = "C:/Tutorials/PythonTool/Clip.gdb"
    gdb_path, new_gdb = os.path.split(gdb)
    gdb = "C:/Tutorials/PythonTool/Clip.zip"
    arcpy.env.overwriteOutput = True

    Note:
    Observe that the paths in the script use the forward slash character, /, as the path separator. If you copy a path from the File Explorer it will use the default Windows path separator character of \. You will need to change all of the backslash characters to forward slashes in the script.

    The full hard-coded paths are located only in the top section of the script where they are assigned to variables. The remainder of the script uses these variables. The full paths and file names are not repeated beyond this top section. This strategy makes it easier to identify what may need to be changed, and it will be helpful when the script is adapted to be used as a Python script tool later in this tutorial.

  5. In IDLE, click File and click Save to save your changes to the script.

    Save the script.

  6. In IDLE, click Run and click Run Module to run the script.

    Run the script in IDLE.

    The script runs.

    The IDLE Shell window appears, and the script prints the progress messages as it processes the data.

    IDLE Shell window showing output messages.

    Note:
    You may get error messages if you have not modified the paths correctly. For example, if the workspace is set incorrectly, the list called inputs is empty and the script returns the following error:

    TypeError: ‘NoneType’ object is not iterable

    Or, if the path for the geodatabase Clip.gdb is incorrect, the script returns the following error:

    ERROR 000732: File GDB Location: Dataset … does not exist or is not supported

    These errors illustrate the challenge of sharing scripts. Creating a script tool is a good way to prevent these types of errors.

    If the script runs correctly, the messages confirm that the geodatabase and the clipped feature classes were created. You will check this using ArcGIS Pro and File Explorer.

  7. In ArcGIS Pro, in the Catalog pane, right-click the PythonTool folder and click Refresh.

    Refresh the PythonTool folder in the Catalog pane.

  8. In the Catalog pane, in the PythonTool folder, verify that the Clip.gdb geodatabase was created.

    The Clip.gdb geodatabase was created.

  9. Expand the Clip.gdb geodatabase.

    The clipped feature classes were created.

  10. In the Clip.gdb geodatabase, right-click bike_racks and click Add To Current Map.

    Add the bike_racks feature class to the current map.

    The new clipped bike_racks layer is added to the map.

    The clipped bike_racks layer is added to the map.

  11. In File Explorer, browse to the C:\Tutorials\PythonTool folder.

    The Clip.zip file is added to the folder.

    The Clip.zip archive is added to the folder.

The stand-alone script works as intended. If you want to use the script for different datasets, you can open the script and modify the paths. This is not very user-friendly for others who are not as familiar with Python scripting. It is also easy to make mistakes when modifying paths in the script. In the next section, you will develop a Python script tool with a user-friendly interface.


Make a tool and add parameters

The next stage of the process is to create a script tool and configure its parameters. This sets up the user interface for the tool.

Create a toolbox and script tool

Python script tools are created in ArcGIS Pro. They are stored in a custom toolbox. Now you'll create a custom toolbox in your project and add a script tool to it.

  1. In ArcGIS Pro, in the Catalog pane, right-click the PythonTool folder, click New and click Toolbox (.atbx).

    Create a toolbox.

    The toolbox is created.

    The toolbox is created.

  2. For the toolbox name, type Clip_and_ZIP and press Enter.

    Type Clip_and_ZIP.

    The toolbox name is Clip_and_ZIP.

    Toolbox is named.

    Note:
    Older versions of ArcGIS Pro used the .tbx format. You can still use older toolboxes in this format, but when creating toolboxes, you should use the .atbx format starting at ArcGIS Pro 3.0.

  3. Right-click the Clip_and_ZIP toolbox, point to New, and click Script.

    Create script tool in the toolbox.

    The New Script window appears.

    The New Script window appears.

    This window is used to set the general properties of the tool, configure the tool parameters, enter the execution code, and add the validation code. You will complete all these steps in the remainder of this tutorial.

    For now, you will specify the name, label, and description for the script tool.

  4. In the Name box, type ClipandZIP.

    The tool name is the internal, unique identifying name used by the tool. The name can only contain letters and numbers. Spaces and special characters are not allowed.

  5. In the Label box, type Clip and ZIP.

    The tool label is the human-friendly name of the tool in the toolbox, and is displayed when the tool is open on the tool dialog box. The label can have spaces and special characters.

    The next box, Toolbox, is where the script tool resides. The toolbox path and name are displayed based on where the .atbx file is located and cannot be changed in the window. It is important to recognize that the script tool is not a separate file but is part of the .atbx file.

  6. In the Description box, type This script tool allows you to select multiple feature layers or feature classes and clip them using one or more polygon features. The results are stored in a new geodatabase and a ZIP archive of this new geodatabase is created for easy sharing or backup..

    The information you added to the New Script window

  7. Click OK.

    The script tool is created with the general properties you entered.

  8. In the Catalog pane, in the Clip_and_ZIP toolbox, double-click the Clip and ZIP tool.

    Open the Clip and ZIP tool.

    The script tool opens in the Geoprocessing pane. It has the beginnings of the framework of a geoprocessing tool, but it remains mostly empty.

    Clip and ZIP script tool user interface with no parameters

  9. Close the tool.

    Your next step is to configure the tool’s parameters.

Examine the tool properties

Carefully configuring the tool parameters is a critical part of creating a Python script tool. The tool parameters define the tool's user interface, which allows the user of the tool to enter the data and other values that will be processed by the script.

  1. Right-click the Clip and ZIP tool and click Properties.

    Examine the script tool properties.

    The Tool Properties: Clip and ZIP window appears, and your previous entries on the General tab are present.

  2. Click the Parameters tab.

    Click the Parameters tab.

    This is where you will configure the tool parameters. Tool parameters are organized in a table where each row is a parameter, and the columns are the properties of each parameter.

    Tool Parameters tab showing empty table.

    You will use this table to create the following parameters:

    • Input feature layers—A user can select any feature layers in the active map, or browse to any feature classes on disk.
    • Clip polygons—A user can select a polygon feature layer, browse to a polygon feature class on disk, or create a feature set in the active map consisting of polygons.
    • Output geodatabase—A user can specify the path and file name of a new geodatabase to store the results.
    • Output ZIP archive—A user can specify the path and file name of the ZIP archive to be created with the contents of the geodatabase.

Add the input feature layer parameter

You'll add the input feature layer parameter first.

  1. In the Label column, type Input feature layers and press Enter.

    Type the parameter label.

    The parameter label is the human-readable label for the parameter, shown in the tool pane.

    The parameter name is automatically populated from the label, with spaces replaced by underscores. The parameter name is used internally by the script tool, and it can be used when setting the tool parameters in a Python script.

  2. In the Data Type column, click the Change Data Type button.

    Click the button to change the data type for the parameter.

    The Parameter Data Type window appears.

    The Parameter Data Type window appears.

    The default value is String, but you will change it to Feature Layer.

  3. Click the data type drop-down list, scroll to the F section of the list, and click Feature Layer.

    Choose Feature Layer as the data type.

    Setting the data type to Feature Layer allows a user to select feature layers in the active map using a drop-down list on the tool, or to browse to feature classes on disk.

    You want the tool to be able to accept multiple input feature layers.

  4. Check Multiple values.

    Check Multiple values.

    Checking Multiple values means a user can select one or more inputs for this parameter of the tool. These could be a combination of feature layers and feature classes.

  5. Click OK to close the Parameter Data Type window.

    Feature layer input parameter is configured.

  6. For Type, accept the default value, Required.

    Making a parameter Required means that the tool cannot be run without it being set.

  7. For Direction, accept the default value, Input.

    Setting the direction of the parameter to Input indicates to the tool that this is one of the input layers or values that it will process. It also allows the tool to be used in ModelBuilder, where feature layers can be connected to the tool as inputs.

    This completes the first parameter. You can immediately check how the script tool looks with this one parameter.

  8. In the Tool Properties: Clip and ZIP window, click OK.
  9. In the Catalog pane, in the Clip_and_ZIP toolbox, double-click the Clip and ZIP tool.

    The script tool opens in the Geoprocessing pane. The first parameter is now shown. There is a drop-down list to select feature layers from the active map and there is a browse button to browse to feature classes on disk. The red asterisk before the parameter label indicates this is a required parameter.

    The Clip and ZIP tool pane with the first parameter configured

    At this stage, you are configuring the tool parameters and viewing the tool user interface to test that the parameters appear correctly. You cannot yet test whether the tool runs and works correctly because the Python code has not been added.

    Note:
    If you select at least one input feature layer and run the tool, you will get an error message.

    Error message if you attempt to run the tool now

  10. Close the tool.

Add the clip polygons parameter

Next, you will configure the parameter for the clip polygons.

  1. Right-click the Clip and ZIP tool and click Properties.
  2. On the Parameters tab, in the second row of the table, click in the Label column, type Clip Polygons and press Enter.

    The row for the Clip Polygons parameter is partly filled in.

    The Name of the parameter updates to Clip_Polygons.

  3. In the Data Type column, click the Change Data Type button.
  4. For Data Type, click the drop-down list and click Feature Set.

    Set the data type to Feature Set.

    A feature set is a lightweight representation of a feature class. In the context of a geoprocessing tool, a feature set allows the tool to accept interactive input. A user can create features by drawing on the map, like you would create features while editing using the Create Features pane. The user can also select feature layers from the active map, or browse to feature classes on disk.

  5. Click OK to close the Parameter Data Type window.
  6. For Type, accept the default value, Required.
  7. For Direction, accept the default value, Input.

    Next, you'll set a filter on this parameter so the Clip Polygons parameter of the tool will only accept polygon features as input.

  8. Scroll to the right and click in the column for Filter.

    Click in the Filter column.

  9. Click the Filter drop-down list and choose Feature Type.

    Choose Feature Type.

    The Feature Type Filter window appears.

  10. Check Polygon.

    Check Polygon for Feature Type Filter.

    Now, when someone runs the tool, only polygons can be used for the input clipping geometry.

  11. In the Feature Type Filter window, click OK.

    You've configured the Clip Polygons parameter.

Add the output geodatabase parameter

Next, you will configure the parameter for the output geodatabase.

  1. On the Parameters tab, in the third row of the table, click in the Label column, type Output geodatabase and press Enter.
  2. In the Data Type column, click the Change Data Type button.
  3. For Data Type, click the drop-down list, click Workspace, and click OK.

    Set the Output geodatabase parameter data type to Workspace.

  4. For Type, accept the default value, Required.
  5. Click in the Direction column and in the drop-down list, choose Output.

    Set the Direction to Output.

    Setting the Direction to Output tells the tool that this is one of the outputs that it will produce. It also allows the tool to be used in ModelBuilder, where output items will be generated for the tool.

  6. Click in the column for Filter.
  7. Click the Filter drop-down list and choose Workspace.

    Set Filter to Workspace.

  8. In the Workspace Filter window, check Local Database.

    Check Local Database.

    Setting the filter to Local Database means that a user will only be able to specify a file geodatabase as an output, not an enterprise geodatabase or a folder.

  9. Click OK.

    You've configured the Output geodatabase parameter.

Add the output zip archive parameter

Next, you will configure the parameter for the output zip archive.

  1. On the Parameters tab, in the fourth row of the table, click in the Label column, type Output ZIP archive and press Enter.
  2. In the Data Type column, click the Change Data Type button.
  3. For Data Type, click the drop-down list, click File, and click OK.

    Set Data Type to File.

    A zip archive is a file, so this is the correct data type.

  4. Click in the Direction column and in the drop-down list, choose Output.
  5. Click the Filter drop-down list and choose File.

    Set Filter to File.

  6. In the File Filter window, in the Extensions box, type zip.

    Type zip.

    The file extension should just be the letters of the extension, not including the dot file extension separator. Don't type .zip.

    The file extension is not case sensitive.

  7. Click OK.

    You have added all the parameters for the tool.

    All of the parameters have been added.

  8. In the Tool Properties: Clip and ZIP window, click OK.

    The next step is to test the parameters you've configured.

Test the parameters

Once you have created all the tool parameters, you can test the tool pane to ensure the settings for each parameter work as intended.

  1. In the Catalog pane, in the Clip_and_ZIP toolbox, double-click the Clip and ZIP tool.

    The Clip and ZIP tool opens; all parameters have been added to the tool.

    The Clip and ZIP tool opens. All the parameters that you configured in the previous steps now appear in the tool pane.

  2. On the Clip and ZIP tool, click the Input feature layers drop-down list and click bike_racks.

    A second Input feature layers box appears below the one for bike_racks, since this tool parameter allows multiple values.

    The tool with bike_racks layer added

  3. Click the Input feature layers browse button, expand the DC.gdb geodatabase, and click bike_routes, then click OK.

    Browse to the bike_routes feature class in the DC.gdb geodatabase.

    The bike_routes feature class was not on the map, but you added it to the Clip_and_ZIP tool.

    The bike_routes feature class is added to the tool.

    This confirms that a user can select multiple inputs, including feature layers from the active map and feature classes on disk.

  4. Click the Clip Polygons sketch tool.

    Click the Clip Polygons sketch tool.

    The feature set sketch input is limited to polygon features.

    This will prevent someone from trying to clip with point or line features, which would cause an error.

    Now you'll test the outputs.

  5. In the Output geodatabase box, type C:\Tutorials\PythonTool\Clip, then click in the Output ZIP archive text box.

    The path to a folder gets an error marker.

    Entering the path to a folder triggers an error alert icon on the Output geodatabase parameter.

  6. Point to the error alert icon.

    The error message indicates this is the wrong workspace type.

    An error message appears, indicating that there is not a geodatabase at the location you specified, or it is the wrong workspace type.

    In addition, if you enter the wrong file extension, for example, .fdb, this is automatically changed to .gdb.

    Similar validation occurs for the final parameter.

  7. In the Output ZIP archive box, type C:\Tutorials\PythonTool\Clip, then click in the Output geodatabase text box.

    The tool adds the file extension .zip to Clip.

    The zip suffix is added.

  8. In the Output ZIP archive box, type C:\Tutorials\PythonTool\Clip.zipped, then click in the Output geodatabase box.

    Entering a path ending in an incorrect file extension triggers an error alert icon on the Output ZIP archive parameter.

  9. Point to the error alert icon.

    The error message shows that the input is the wrong file type.

    If you point to the icon, an error message appears, indicating that this is the wrong file type.

  10. Close the tool.

    Your testing shows that the filters and other parameter settings that you configured are working. Carefully designing your parameters results in a more robust tool, since it prevents users from running the tool with invalid values.

    You've tested the parameters, but the tool is not yet ready to run, as the code has not been added. You'll do that next.

Edit the execution code

The execution code of a Python script tool is the Python code that is run when a user clicks Run in the Geoprocessing pane. You'll use the stand-alone script you reviewed earlier, but you will need to make some changes to it so the script can receive the parameters entered by a user in the tool pane.

  1. Right-click the Clip and ZIP tool and click Properties.
  2. Click the Execution tab.

    Click the Execution tab to see the Python code template for the tool.

    The tab shows a Python code template for a script tool.

    Python code template.

    The code starts with a multiline comment placeholder for documentation for the script. In Python, three quotation marks start a multiline comment, and three quotation marks end it. Text that you place inside the comment is not run by Python.

    After the comment, the first line of code is import arcpy. Most geoprocessing tool scripts will use ArcPy, so it is included in the template.

    Next, a function called script_tool() is defined. A function is created by using the def keyword followed by the function name and the function arguments. You will change the name of the template function to match your tool.

    The template function specifies two parameters, but your tool takes more; this is another thing that you will change.

    The execution code for the script tool will go inside this function.

    At the end of the function template block is a return statement. This ends the function and returns the results.

    The final section of the code starts with if __name__ == '__main__':. This is a conditional statement and the indented block of code that follows will only run when the condition evaluates to True. The variable __name__, with two underscores on each side, for any script has the value of '__main__'.

    When you run the code as a script, which is the case when you run a script tool, the indented block of code is run. For an imported module, however, the variable is set to the name of the module. In that case, the indented block of code is not run. This code structure makes it possible to distinguish between running the code as a script or importing it into another script as a module. This scenario does not apply here. For purposes of creating the script tool, it is enough to know that the indented block of code runs when the script tool is run. The code block receives parameters using the ArcPy function GetParameterAsText() and then the function ScriptTool() is called on the parameters.

    The template uses two parameters called param0 and param1. These are temporary placeholders; you will edit the parameters to match your script.

    Note:
    In the next steps, you will start modifying the execution code. It is important to understand where the code is stored. By default, the Python code is embedded. This means it is part of the .atbx toolbox file and is not stored separately. However, you can export the code to a .py file for ease of editing, and then later embed the code. You will practice this workflow as you make incremental changes to the code.

    Next, you'll modify the name of the function, script_tool(), to something more meaningful. To start, you will edit the embedded code directly on the tool properties dialog box.

  3. On the Execution tab, in the code box, edit the def script_tool(param0, param1): line to be def ClipZip(param0, param1):

    Change it here:

    Change the function name.

    It should look like this:

    The new function name has been added.

    When you modify the code on the tool properties dialog box, the lines of code that have been modified have a yellow marker placed in front of them.

  4. In the code box, edit the script_tool(param0, param1) line to be ClipZip(param0, param1)

    Edit the name where the function is called.

    Now you've updated the function name both places that it appears in the script tool template.

    While editing code in this manner is convenient and quick, the code box provides limited functionality compared to a typical Python editor. Next, you will see two approaches to edit the execution code in a Python editor.

  5. On the Execution tab, below the code box, click OK.

    Click OK.

    Changes to the embedded code are saved automatically and the yellow markers will disappear when you close the tool properties dialog box.

  6. Right-click the Clip and ZIP tool and click Properties.
  7. At the bottom of the code box, click the Open in Script Editor button.

    Click Open in Script Editor.

    A temporary script file opens in the default Python editor. Unless you have configured another editor, the script opens in IDLE.

    Temporary script file in IDLE

    The edits you made in the script box are present in the script.

    Note:
    You can configure the Python editor from the Project tab. Click the Project tab, click Options, and click Geoprocessing in the left pane. The Script Editor option is blank by default, which means IDLE is used. You can change to your preferred editor by specifying the path. For the purpose of this tutorial, IDLE will suffice.

    The path will be similar to: C:\Users\UserName\AppData\Local\Temp\1\ArcGISProTemp19512\

    The file name consists of the letters tmp followed by an alphanumeric code and the .py file extension. When you make changes to the code and save the .py file, these changes are added to the embedded execution code block. You will practice this workflow.

  8. In IDLE, add a new line of code below import arcpy and add the following code:

    import os
    import zipfile

    The two import lines are added to the temporary script.

    These two modules are used in the original stand-alone script. You can copy and paste from the script or type the code.

  9. In IDLE, click File and click Save.

    Save the temporary Python script.

  10. Close IDLE.

    The code you added in IDLE is now in the code box.

    The code you added in IDLE is now in the code box.

    Next, you will edit the parameters for the script in a stand-alone script file.

Save the script to a separate file

The code you have worked with so far has been embedded in the toolbox. You can also export the code to a .py file for use in your preferred Python editor.

  1. On the Execution tab, above the script box, click the green arrow of the Export script to file button.

    Export to script.

  2. In the Export Script window, verify that the location is the PythonTool folder, and for Name, type clip_zip_tool.py.

    Name the output .py file.

    The original script with most of the code you need to transfer and update is named clip_zip.py, so be sure to use a different name so you do not overwrite the original file.

  3. Click Save.

    Now that you have exported the code from the script box to an external Python script file, the code is no longer stored in the toolbox.

    The top of the box shows the path to the script file.

    The path to the .py script file appears at the top of the code box.

    The benefit of having a separate .py file is that you can now more easily work with the code in your preferred editor. For this tutorial, however, you will continue to use IDLE.

    The code shown in the tool properties window is no longer the embedded code, but the code in the .py file. However, you can continue to edit the code directly in the tool properties window and the .py file will automatically be saved when you close the tool properties window.

    For most of the remainder of this tutorial, you will open the .py file in IDLE instead of editing in the tool properties window.

  4. At the bottom of the code box, click the Open in Script Editor button.

    The .py file opens in IDLE.

    Script opens in IDLE.

    The file name and path are no longer temporary.

    Note:
    It is possible to edit the .py script file code in the code box of the Execution tab and in IDLE at the same time. However, this is not a best practice. You will get a warning message and will have to determine which changes to keep. To avoid this, only edit your script in one application at a time.

    Now that you've exported the code to a .py file, you'll close this window.

  5. In the Properties window, click OK.

    Close the Properties window.

    Next, you'll change the code to match the script tool parameters.

  6. In IDLE, after the if __name__ == "__main__": line, replace the two parameter lines that start with param0 and param1 with the following code:

        inputs = arcpy.GetParameter(0)
        clip_fc = arcpy.GetParameterAsText(1)
        gdb = arcpy.GetParameterAsText(2)
        out_zip = arcpy.GetParameterAsText(3)

    Default template parameters

    The updated code should look like the following:

    Updated parameters

    Note:
    It is important that the indentation of the new lines match the original indentation level of four spaces.

    You set four parameters for the script tool. This is the way the Python script will receive those four parameters.

    Getting the parameters from the script tool can be done using the ArcPy functions GetParameter() and GetParameterAsText(). The tool user interface passes the parameters to the script as a Python tuple, a sequence data type that uses zero-based indexing. The first parameter has an index of zero, the second parameter has an index of one, and so on. These index values are referenced by the numbers in parentheses at the end of each of the new lines. If the order of these numbers does not match the order of the parameters in the tool user interface, the script will get the wrong data values.

    Note:
    Starting with ArcGIS Pro 3.2, you can also refer to parameters by their name. For example, instead of using arcpy.GetParameter(0), you can use arcpy.GetParameter("Input_feature_layers"). This allows the parameters to be entered in any order and you don’t need to know the index of each parameter.

    The first parameter consists of a Python list that may contain multiple input feature class names. The function GetParameter() preserves this format so this function is used for the first parameter. The other parameters are received as text using GetParameterAsText().

    Each parameter value is assigned to a variable. The variables will be passed to the ClipZip function. The variables names were chosen to be consistent with the code in the existing stand-alone script.

  7. Edit the ClipZip(param0, param1) line to replace param0, param1 with the variables that contain the parameter values:

    ClipZip(inputs, clip_fc, gdb, out_zip)

    Update the parameters in the line that calls the ClipZip function.

    The change should look like the following:

    Parameters updated.

    This line calls the ClipZip() function with those parameters.

  8. Delete the arcpy.SetParameterAsText(2, "Result") line.

    Delete the template SetParameterAsText line.

    This script tool does not use this line from the template.

    Next, you'll update the ClipZip() function definition.

  9. Edit the def ClipZip(param0, param1): line to replace param0, param1 with the variables that contain the parameter values:

    def ClipZip(inputs, clip_fc, gdb, out_zip):

    Update the def ClipZip line.

    The change should look like the following:

    Function definition updated to new parameters.

    This defines the ClipZip() function as accepting the four parameters.

    The variables in the function definition are not required to use the same names as the Parameter tab, as long as the order of the variables is the same. The variable names on the function definition line must be consistent with the variables in the block of code that defines what the function does (which has yet to be added).

  10. In IDLE, click File and click Save.

    The changes you've made in IDLE are saved to the script file.

    The script should look like this:

    Script progress so far

    Next, you'll add code from the original script to the code block.

Copy code from the original script

The original script contains the code that you'll add to the code block for your script tool.

  1. In IDLE, click File and click Open.

    Open a file from IDLE.

  2. In the C:\Tutorials\PythonTool folder, click clip_zip.py and click Open.

    Open the clip_zip.py file.

  3. In IDLE, in the clip_zip.py script, click at the beginning of the # Workspace and datasets line and click and drag to the end of the script to select all of the code except for the import lines.

    Click and drag to select most of the script.

  4. Right-click the selected code and click Copy.

    Copy the code.

  5. In IDLE, switch to the clip_zip_tool.py window.
  6. Select the """Script code goes below""" comment.

    Select the comment.

  7. Right-click the selected code and click Paste.

    Paste the code from the original script into the code block.

    The code is pasted into the code block of the ClipZip function.

    The code is pasted into the code block for the function definition.

    You are finished with the original script. Now you can close that window and focus on the clip_zip_tool.py code.

  8. InIDLE, close the clip_zip.py script window.

    The entire block of code you copied into the clip_zip_tool.py script needs to be indented by four spaces to be a part of the function.

    While it is possible to do this manually, by typing four spaces before each line, you can also select the lines that you want to indent and indent them all at once.

    In this case, it is best to do it all at once, because indented whitespace is significant in Python. If you accidentally added three spaces, or five, to a line, the code would not run correctly. Also, since the code has multiple lines and includes indented sections for loops and conditions, adding the indentation manually is likely to introduce errors.

  9. In IDLE, in the clip_zip_tool.py window, click and drag to select the code you pasted that is not yet indented by four spaces to match the code block content.

    The # Workspace and datasets line may or may not be indented, depending on whether the spaces before the """Script code goes below""" comment were selected when you pasted in the code.

    Be sure to stop your selection before the return line.

    Select the lines to indent.

  10. Click Format and click Indent Region.

    Indent the selected region.

    The code lines each get indented by four more spaces.

    The code block is indented now.

  11. Verify that the ClipZip function code block, the lines after the def ClipZip line and up to and including the return line are all indented by four spaces (or more, for lines in loops or conditional statements).
  12. In IDLE, click File and click Save.

Remove hard-coded values

Several of the lines in the original script set variables to specific paths, which is known as hard-coding the paths. This was necessary for the script to be able to locate and process the data. However, in a script tool, the user of the tool specifies the inputs and outputs using the controls of the tool user interface. You have set up the script to use arcpy.GetParameter and arcpy.GetParameterAsText to get this information from the tool user interface. The next step is to remove the hard-coded lines.

  1. Delete each of the lines that specify a hard-coded path.

    These are the lines starting with arcpy.env.workspace = , clip_fc = , gdb =, and out_zip =

    Delete the lines that hard-code paths.

  2. Delete the arcpy.env.overwriteOutput = True line.

    There no need to keep this line because the overwriteOutput property is controlled by ArcGIS Pro when the code is run as a script tool.

  3. Keep the line of code that assigns values to the variables gdb_path and new_gdb by splitting the value of the gdb variable.

    Keep the line that splits the gdb path

  4. Verify that the line is indented by four spaces, the same level as the comment # Workspace and datasets.

    You have now deleted all the lines of code with hard-coded values. The script will get the values from the script tool parameters.

  5. Save the script.

Change print statements to messages

The original script used print statements to print the status to the IDLE Shell window. This only works when you run the script from the script editor. You will edit the print statements to use arcpy.AddMessage to send this information to the geoprocessing messages.

There are three print statements. You will change two of them, and delete the other one.

  1. Edit the print(f"Output geodatabase {gdb} created") line to change print to arcpy.AddMessage.

    The line should now be as follows:

    arcpy.AddMessage(f"Output geodatabase {gdb} created")

    Edit the line to use arcpy.AddMessage instead of print.

    The next print statement in the code prints the list of input feature classes. The inputs variable feature class list is now being set by the script tool, so the line setting inputs using arcpy.ListFeatureClasses is not necessary. You will delete this print statement and the line that makes the list.

  2. In the # Clip each input feature class section, select the inputs = arcpy.ListFeatureClasses() line and the print(inputs) line and delete them.

    Select the two lines.

    The section should look like this:

    After the lines have been deleted, code looks like this.

    Now you'll edit the third print statement.

  3. Edit the print(f"Output feature class {new_fc} created") line to change print to arcpy.AddMessage.

    The line should say the following:

    arcpy.AddMessage(f"Output feature class {new_fc} created")

    The third print statement converted to use arcpy.AddMessage.

  4. In IDLE, click File and click Save.

    The final section of code that creates the ZIP file does not require any changes.

  5. Close IDLE.

Test the script tool

The script tool is now ready to test.

  1. In ArcGIS Pro in the Catalog pane, expand the DC.gdb geodatabase.

    Expand the DC geodatabase.

  2. Right-click bike_routes and click Add To Current Map.
  3. Expand the Clip_and_ZIP.atbx toolbox and double-click the Clip and ZIP tool.

    The Clip and ZIP tool opens.

    The Clip and ZIP tool, ready for testing.

  4. For the Input feature layers parameter, click the Add Many arrow button.

    The Add Many button

  5. On the Add Many box, check bike_racks, bike_routes, and bus_lines and click Add.

    Add multiple input feature layers.

  6. For the Clip Polygons parameter, choose the neighborhood layer.
  7. For the Output geodatabase parameter, type C:\Tutorials\PythonTool\Clip.gdb
  8. For the Output ZIP archive parameter, type C:\Tutorials\PythonTool\Clip.zip

    The tool is ready to test.

  9. Click Run.

    The tool will run. After it finishes, the bottom of the tool will show a green banner indicating that it has completed.

  10. On the banner, click View Details.

    View the details.

    The tool Messages section shows the messages that were generated by the tool, using the code you modified from print statements to arcpy.AddMessage.

  11. Close the Details window.
  12. Click the Catalog tab.

    The Clip.gdb geodatabase was added to the project folder.

    Clip.gdb was added.

    The zip archive does not show in the Catalog pane, but you can use the Windows File Explorer to verify that it was created.

    The Clip.zip file in the Windows File Explorer

You have created a functional Python script tool. The tool has input and output parameters, and has some basic filters for these parameters to prevent some common problems.

The next section shows how you can add more validation code to the script tool to make it more robust.


Improve the tool with validation

Script tools are designed to work with user input. Validation is used to ensure that user inputs are meaningful and don’t result in errors. The first step in validation is to carefully design the tool parameters. For example, the parameter Clip Polygons has a filter to ensure only polygon feature classes are selected. In this section, you will explore other approaches to validation.

Add a check for empty feature classes

Consider a scenario in which one of the output feature classes is empty. This happens when none of the features of an input feature class fall within the boundaries of the clip polygon. In this case, it would be helpful to provide a message that the output is empty.

  1. In the Catalog pane, right-click the Clip and ZIP tool and click Properties.
  2. Click the Execution tab.
  3. In the # Clip each input feature class section, select the arcpy.AddMessage(f"Output feature class {new_fc} created") line.

    Select this line.

  4. Replace the line with the following code:

    count = int(arcpy.management.GetCount(new_fc)[0])
            if count > 0:                                       
                arcpy.AddMessage(f"Output feature class {new_fc} created")
            else:
                arcpy.AddWarning(f"Output feature class {new_fc} created but does not contain any features.")

  5. Verify that the indentation of your code matches this image:

    Check the indentation.

    The new code starts with the line count = int(arcpy.management.GetCount(new_fc)[0]) that gets the number of features in the output feature class. This line should be indented to the same level as the line before it, which starts with arcpy.analysis.Clip.

    The next line uses a conditional if statement to determine whether there are features in the new feature class. If there are features, the conditional block runs and adds the original message. If there are no features, the else block runs and arcpy.AddWarning is used to add a warning message that the feature class was created but is empty. The two conditional lines, after if and else should be indented by four spaces, relative to the previous lines.

  6. Click OK to close the tool Properties window and save your changes
  7. Double-click the Clip and ZIP tool
  8. In the Input feature layers pane, click browse, and in the DC.gdb geodatabase, click the points_outside feature class and click OK.

    The points_outside feature class is selected to add to tool input.

    This feature class contains points features that all occur outside of the polygon of the neighborhoods shapefile. You will use this to test the tool.

  9. For the second of the Input feature layers, choose the bike_routes layer from the map.
  10. For the Clip Polygons parameter, choose the neighborhood feature layer.
  11. For the Output geodatabase parameter, type the following path: C:\Tutorials\PythonTool\Clip_empty.gdb.
  12. For the Output ZIP archive parameter, type the following path: C:\Tutorials\PythonTool\Clip_empty.zip.
  13. Click Run.

    The tool runs. After it finishes, the bottom of the tool will show a yellow banner indicating that it has completed, but with warnings.

  14. On the banner, click View Details.

    View the warnings.

    The tool Messages section shows the messages that were generated by the updated code. A warning message indicates that the points_outside feature class was created but does not contain features.

    Tool messages with warning.

    The other tool messages are also present, as expected.

  15. Close the Clip and ZIP messages and the Clip and ZIP tool.

    This type of validation occurs after the tool has run and the outputs have been created. It is also possible to use validation before the tool is run.

Use validation to check for empty input

Consider a scenario in which the clip polygons selected by a user consist of an empty feature class. This will not cause an error since the inputs are valid. However, the outputs will not be meaningful since all the newly created feature classes will be empty. If there is a way to know in advance that the outputs are not going to be meaningful, ideally the tool should not run.

You will modify the script tool properties and add a check for empty input. This type of validation is not added to the script file, but uses a different type of validation.

  1. In the Catalog pane, right-click the Clip and ZIP tool and click Properties.
  2. Click the Validation tab.

    Go to the Validation tab.

    The Validation tab shows a code box but this is not a script file. The code here only resides in the tool properties and is not stored as a separate .py file. The Validation tab contains template code that you can use as a starting point.

    The ToolValidator template code

    The template code shows a class called ToolValidator. The class includes several methods that are used for different types of validation. Most of this validation occurs before the tool is run.

    You can modify the code directly in the code box or you can open the code as a temporary script file by clicking Open in Script Editor.

  3. Click Open in Script Editor.

    Open the code in the IDLE script editor.

  4. In the def updateMessages section, select the two comment lines.
  5. Select the three lines in the def updateMessages code block.

    The two comments and return line are selected.

  6. Replace the comments and the return statement by copying and pasting the following code:

    # When the clip polgyon feature set, feature layer or
        # feature class does not contain any features, a warning
        # message appears in the tool dialog since this would result
        # in empty feature classes in the new geodatabase.
        if self.params[1].value:
            count = int(arcpy.management.GetCount(self.params[1].value)[0])
            if count < 1:
                self.params[1].setWarningMessage("No clip features. This results in empty outputs.")
        return

    Paste in the code.

    The code is pasted into the method definition. However, there is a problem. The first line of comments is correctly indented, but the lines after it all need to be indented by an additional four spaces.

    Observe that some of the pasted code is not indented correctly.

  7. Select the lines that need to be indented.

    Select the lines that need to be indented.

  8. Click Format and click Indent Region.
  9. Verify that the indentation is now correct.

    Verify the indentation is correct.

  10. In IDLE, click File and click Save.
  11. Close IDLE.

    In ArcGIS Pro, a warning indicating that changes have been made appears.

  12. Click Yes.
  13. Click OK to close the tool properties window.
  14. Double-click the Clip and ZIP tool.
  15. For the Input feature layers, add the bike_racks and bike_routes layers.
  16. For the Clip Polygons parameter, click browse, and in the DC.gdb geodatabase, click the polygon_empty feature class and click OK.

    Choose the polygon_empty feature class.

    The polygon_empty feature class is a polygon feature class without any features.

    A warning icon appears next to the Clip Polygons input box.

  17. Point to the warning icon.

    Point to the warning icon to see the message.

    The tool validation code that you added detects that the polygon feature class is empty and gives a warning message.

    A warning message means that a user can still run the tool, but they know in advance about what the results will be. Alternatively, if you use setErrorMessage instead of setWarningMessage, an error message appears, and this would prevent the tool from running. When designing the tool, you need to carefully consider what type of message is most meaningful.

    Note:
    The validation code used here includes the Get Count tool. This tool is run when a value for the parameter has been selected. In general, you don’t want to use geoprocessing tools in validation code since it could be very time consuming and may cause unforeseen issues. The Get Count tool is an exception, because it is a very simple tool that runs quickly, even for very large datasets.

Add default values

Another type of tool validation is to populate tool parameters with default values. This technique is employed in many geoprocessing tools. For example, when you run the Clip tool, the name of the output feature class defaults to the name of the input feature class followed by _Clip. For the Clip and ZIP tool, there are no defaults for the Output geodatabase and Output ZIP archive parameters. You can add these default values to the validation code.

  1. Close the Clip and ZIP tool.
  2. Open the tool Properties and click the Validation tab.
  3. Click Open in Script Editor.
  4. In IDLE, select the three lines in the def updateParameters code block.

    Select the lines in the def updateParameters code block.

  5. Replace the lines with the following code:

    # The output geodatabase and ZIP archive are given default
        # names of Clip.gdb and Clip.zip, respectively, inside the 
        # workspace of the project. Users can specify a different
        # file name and folder.
        if self.params[0].value and self.params[1].value:
            folder = os.path.split(arcpy.env.workspace)[0]
            self.params[2].value = os.path.join(folder, "Clip.gdb")
            self.params[3].value = os.path.join(folder, "Clip.zip")
        return

  6. Examine the code you pasted.

    The first line is correctly indented, but the others all need to be indented by four more spaces.

    Note the indentation problem.

  7. Select the lines that need to be indented, then click Format and click Indent Region.

    Indent the selected region.

    The indentation problem is fixed.

    Indentation problem solved.

  8. In IDLE, click File and click Save.
  9. Close IDLE.
  10. In ArcGIS Pro, on the warning message, click Yes.
  11. Click OK to close the tool properties window.
  12. Double-click the Clip and ZIP tool.
  13. For the Input feature layers, add the bike_racks and bike_routes layers.
  14. For the Clip Polygons parameter, choose the neighborhood layer.

    As soon as the first two parameters have been given a value, the third and fourth parameters are populated with their default values. A warning message appears since the Clip.gdb and Clip.zip files already exist.

    Tool with default output names populated.

    The tool can be run with these warnings, or you can change the output names and run the tool.

In this tutorial, you learned how to create a script tool that provides a user-friendly interface for Python code. You learned how to configure parameters for the script tool, and how to edit the code to allow it to accept parameters from the script tool. You also learned how to remove hard-coded paths, and add code return messages. Finally, you learned how to add validation to your code to make it more robust.