Use web tools in Python scripts

You can write a Python script to run and use a web tool in multiple ways. The primary way to run a script is to use ArcPy. ArcPy has built-in methods to connect to, run, and handle the result from the service. Alternatively, if you access the service from the ArcGIS REST API, you can use built-in Python modules to make REST calls using a JSON structure to transfer results. You must build a client from scratch with Python code to use this. The majority of scripts will connect to and use geoprocessing services through ArcPy.

Note:

The examples below consume a geoprocessing service on a federated ArcGIS Server. You do not consume the corresponding web tool item from the portal.

UseArcPy

A web tool can be accessed through the Python window in ArcGIS Pro, a script tool, or a stand-alone script. The service URL is used to connect to and use a web tool.

Connect to a service using ImportToolbox or AddToolbox .

# arcpy.ImportToolbox("https://machine.domain.com/webadaptor/services;<optional folder>/<service name>;{username};{password}")
arcpy.ImportToolbox("https://machine.domain.com/webadaptor/services;GPFolder/BufferService;serverusername;serverpassword")

The path used to import the tool is the URL of the web tool. ImportToolbox and AddToolbox accept the information of the server, which includes four parts separated by a semicolon (;). The first part is the URL (or link) to the service end point, the second is the service name (optionally, a folder name precedes the service name), the third is the optional server username, and the last is the optional server password. Provide the username and password if you do not have the permission to access the web tool.

A web tool can run either synchronously or asynchronously. As a Python scripter, you must understand how the service runs in order to use it. The IsSynchronous property can be used to determine whether the service runs synchronously or asynchronously. When a service runs synchronously, the results are automatically returned, but no action can be taken until it has completed. When a service runs asynchronously, the current status must be queried periodically. Once the service has finished running, you can access the result.

The following Python code shows how to connect to an asynchronous geoprocessing service, and use ArcPy functions to run it, get the result, and process it further. By setting a result variable when running the task, a while loop can be used to check the status. The task is finished once a status code of 4 (succeeded) or higher is returned.

Use an asynchronous service to create a buffer and save the result locally.

import arcpy
import time

arcpy.ImportToolbox("http://machine.domain.com/webadaptor/services;Elevation/viewshedAlias;serverusername;serverpassword")

result = arcpy.Viewshed_viewshedAlias(r'c:\data\inputPoint.shp', "10000 Kilometers")

while result.status < 4:
	   print(result.status)
	   time.sleep(0.2)
print("Execution Finished")

print(result.getMessages())
arcpy.CopyFeatures_management(result[0], 'localResult.shp')

More information regarding the Result object and status codes, as well as creating raster and map image output, is available in the Use tools in Python help topic.

Use REST

An alternate (but less common) way to use a web tool is by writing a script that makes REST calls, using JSON as a data exchange format. This method requires you to write code to both, send the request, and handle the response.

Sending and receiving REST messages is more involved, as you must handle all aspects of the inputs and outputs syntax yourself. When sending and receiving REST messages, they are returned in a consistent manner. A request can be sent through an HTTP GET or HTTP POST method, and a response can come back structured as JSON. The core Python libraries support both sending a request and sending functions, which make reading and parsing JSON messages straightforward.

The following example demonstrates how you would connect to the service, send a request, and handle a response.

Submit the request

For example, a geoprocessing service to create viewsheds is accessible from https://machine.domain.com/webadaptor/rest/services/Elevation. This service takes a point and distance as input and returns a feature set. The following inputs can be used to better understand the service:

Parameter nameInput value

Input Observation Point (GPFeatureRecordSetLayer)

{"geometryType" : "esriGeometryPoint",
"spatialReference" : {"wkid" : 54003},
'features':[{'geometry':{'x': -13308192.1956127,
'y':  4221903.58555983}}]}

Viewshed Distance (GPLinearUnit)

{ 'distance' : 8.5, 'units' : 'esriMiles' }

Format (format of the output)

JSON

This request returns the JSON of the visible locations from the service. The full URL used to generate the request can be used in the address bar of a web browser.

https://machine.domain.com/webadaptor/rest/services/Elevation/ESRI_Elevation_World/GPServer/Viewshed/execute?Input_Observation_Point={%22features%22%3A[{%22geometry%22%3A{%22x%22%3A-13308192.1956127%2C%22y%22%3A4221903.58555983}}]}&Viewshed_Distance={+%27distance%27+%3A+8.5%2C+%27units%27+%3A+%27esriMiles%27+}&env%3AoutSR=&env%3AprocessSR=&f=pjson

The URL can be submitted using the Python module urllib or urllib2. The following Python code runs the above request in a similar way to using the Service Directory or copying the link into the address bar of a web browser:

# Requires Python 3+
import urllib.request as urlopen
import urllib.parse as urlencode
import urllib.request as request
import json

inPts = {"geometryType" : "esriGeometryPoint",
         "spatialReference" : {"wkid" : 54003},
         'features':[{'geometry': {'x': -13308192.1956127, 'y': 4221903.58555983}}]}
dist = {'distance':8.5,'units':'esriMiles'}

data = {'Input_Observation_Point': inPts,
        'Viewshed_Distance': dist,
        'f': 'pjson'}

URL = 'https://machine.domain.com/webadaptor/rest/services/Elevation/ESRI_Elevation_World/GPServer/Viewshed/execute'

req = request.Request(URL, urlencode.urlencode(data).encode('UTF-8'))
response = urlopen.urlopen(req)
response_bytes = response.read()
print(json.loads(response_bytes.decode('UTF-8')))

The Result object is returned as a string. A variety of methods can be used to make a request and parse a result; the example above is just one method. The JSON returned from a web tool can be placed into a dictionary using json.loads() as shown in the previous example. Depending on the output of the web tool, this may be the best technique, or you may need to explore other options to handle the output when consumed from Python through REST.

Note:

When working with result features, ensure that using the actual x,y pairs makes sense for your workflow, as you won't receive the actual features in a format that supports geometry in ArcGIS.

A service that runs asynchronously requires that you periodically check the status of a task to see if it has finished, similar to the example above using ArcPy. Your Python code could search for the phrase esriJobSucceeded or esriJobFailed in the jobStatus status message, which is returned when checking the job status. The following code sample demonstrates one technique for working with an asynchronous web tool (using submitJob):

import urllib.request as urlopen
import urllib.parse as urlencode
import urllib.request as request
import json
import time

def sendReq(URL, data=None):

    req = request.Request(URL, urlencode.urlencode(data).encode('UTF-8'))
    response = urlopen.urlopen(req)
    response_bytes = response.read()
    return json.loads(response_bytes.decode('UTF-8'))

inPts = {"geometryType": "esriGeometryPoint",
         "spatialReference": {"wkid" : 54003},
         'features': [{'geometry': {'x': -13308192.1956127, 'y': 4221903.58555983}}]}
dist = {'distance': 8.5, 'units': 'esriMiles'}

data = {'Input_Observation_Point': inPts,
        'Viewshed_Distance': dist,
        'f': 'pjson'}

taskUrl = "https://machine.domain.com/webadaptor/rest/services/Elevation/GPServer/viewshed"
submitUrl = taskUrl + "/submitJob"

submitJson = sendReq(submitUrl, data)

if 'jobId' in submitJson:
    jobID = submitJson['jobId']
    status = submitJson['jobStatus']
    jobUrl = taskUrl + "/jobs/" + jobID

    while status == "esriJobSubmitted" or status == "esriJobExecuting":
        print("checking to see if job is completed...")
        time.sleep(1)

        jobJson = sendReq(jobUrl, {"f": "json"})

        if 'jobStatus' in jobJson.keys():
            status = jobJson['jobStatus']

            if status == "esriJobSucceeded":
                if 'results' in jobJson:
                    resultsJson = jobJson['results']
                    for paramName in resultsJson.keys():
                        resultsUrl = jobUrl + "/" + resultsJson[paramName]['paramUrl']
                        resultJson = sendReq(resultsUrl, {"f": "json"})
                        print(resultJson)

            if status == "esriJobFailed":
                if 'messages' in jobJson.keys():
                    print(jobJson['messages'])

else:
    print("no jobId found in the response")

Related topics


In this topic
  1. UseArcPy
  2. Use REST