Best practice arcpy command line and toolbox

8496
9
Jump to solution
08-05-2014 07:32 AM
AlexanderGray
Occasional Contributor III

I have some scripts I want to run both command line (schedule task) and in the desktop toolbox.  I use argparse for command line arguments but toolbox uses arcpy.getParameters...  Is there a best practice to have the same script be run in multiple ways?  I specifically don't want two scripts since the toolbox one is used to make sure the output of the scheduled one will be correct.

1 Solution

Accepted Solutions
ShaunWalbridge
Esri Regular Contributor

I've found that this pattern works pretty well that I use for Python toolboxes: place your code into a function with the named parameters, and then depending on how it's called, either take the command line arguments, or the parameters from ArcPy. You should be able to use this same pattern by detecting if you have any input command-line parameters (e.g. len(sys.argv)), and if so, use those, if not, default to the getParameters arguments. Something like this:

import sys

import arcpy

def main(input_fc=None, output_fc=None):

     # your main script body goes here

# executed as a script

if __name__ == '__main__':

     if len(sys.argv) == 3:

          # we were passed command line parameters, execute in a script context

          input_fc=sys.argv[1]

          output_fc=sys.argv[2]

     else:

          # if we don't have args passed on the command line, assume a toolbox context

          input_fc=arcpy.GetParametersAsText(0)

          output_fc=arcpy.GetParametersAsText(1)

     # call the main function with our parameters

     main(input_fc, output_fc)

View solution in original post

9 Replies
ShaunWalbridge
Esri Regular Contributor

I've found that this pattern works pretty well that I use for Python toolboxes: place your code into a function with the named parameters, and then depending on how it's called, either take the command line arguments, or the parameters from ArcPy. You should be able to use this same pattern by detecting if you have any input command-line parameters (e.g. len(sys.argv)), and if so, use those, if not, default to the getParameters arguments. Something like this:

import sys

import arcpy

def main(input_fc=None, output_fc=None):

     # your main script body goes here

# executed as a script

if __name__ == '__main__':

     if len(sys.argv) == 3:

          # we were passed command line parameters, execute in a script context

          input_fc=sys.argv[1]

          output_fc=sys.argv[2]

     else:

          # if we don't have args passed on the command line, assume a toolbox context

          input_fc=arcpy.GetParametersAsText(0)

          output_fc=arcpy.GetParametersAsText(1)

     # call the main function with our parameters

     main(input_fc, output_fc)

DanPatterson_Retired
MVP Emeritus

As suggested, use sys.argv  that way there there is no script editing when you need to ascribe scripts to a tool.  The only thing to remember that sys.argv[1] is = GetParameterAsText(0) since Arcmap starts parameters at 0 and sys.argv[0] is reserved for the running script name...which can be useful

AlexanderGray
Occasional Contributor III

I like this solution but checking the number of paramters from arcpy would be good too since it might be invoked incorrectly from command line without the arguments, the absence of args could be either user error or toolbox...

0 Kudos
MattWilkie3
Occasional Contributor II

This is useful Shaun, thanks. Do you have or are you aware of a location where useful arcpy patterns like this are collected? (hopefully curated?)

0 Kudos
ShaunWalbridge
Esri Regular Contributor

Hi Matt,

I don't think there's any single location for tips like this. I agree it'd be nice to have, an "ArcPy patterns" resource which collected together these kinds of approaches for making Python development simpler. I'll ask around.

Cheers, Shaun

JamesCrandall
MVP Frequent Contributor

As an alternative you could also trap the sys.executable source to check where to expect parameters to come from.  I think it would something like this (using Shaun's code exampe):

import arcpy

import sys

def main(input_fc=None, output_fc=None):

     # your main script body goes here

# executed as a script

if __name__ == '__main__':

     if not "ArcMap" in sys.executable:

          # we were passed command line parameters, execute in a script context

          input_fc=sys.argv[1]

          output_fc=sys.argv[2]

     else:

          # if we don't have args passed on the command line, assume a toolbox context

          input_fc=arcpy.GetParametersAsText(0)

          output_fc=arcpy.GetParametersAsText(1)

       

     # call the main function with our parameters

     main(input_fc, output_fc)

If you were to run this from an Geoprocessing Toolbox it would evaluate as 'else' and ust arcpy.GetParameter but would evaluate as "if" from another source.

Edit: I don't see any option to format the code anywhere in this ridiculous updated "forum".

Edit 2: I had to hop over tags, blogs, rss feeds, streams, and a bunch of other things that aboslutely should not be found in a tech forum and located a "how to" on formatting code blocks.  The level of obscurity built into this "forum" is simply absurd.

AlexanderGray
Occasional Contributor III

Thanks James,  This solution is pretty elegant but I gave the answer to Shaun because although satisfies the initial requirements doesn't account for the possibility that the script could be called by other esri applications such as ArcCatalog, etc.  It is possible to trap for each one but then esri can come out with new applications such pro...

0 Kudos
JamesCrandall
MVP Frequent Contributor

I only tested from the embedded ArcCatalog (is there still a standalone version?), so you may be correct.  Although you could also just shorten the string to evaluate to just "arc" and it would pickup both "ArcMap" or "ArcCatalog" I guess.

if not "Arc" in sys.executable:

0 Kudos
JeromeSeigneuret
New Contributor

You don't need to check environnement. Running script in toolbox or autonome script return the same result when you use GetParameterAsText

This simple code return what you need:

my-script.py

import arcpy

def main(a=None, b=None, c=None):
    print "param1: {}".format(a)
    print "param2: {}".format(b)
    print "param3: {}".format(c)
    
# executed as a script  
if __name__ == '__main__':  

    a =  arcpy.GetParameterAsText(0)
    b = arcpy.GetParameterAsText(1)
    c = arcpy.GetParameterAsText(2)
    # run
    main (a,b,c)

and call with:

python my-script.py "param 1" "param 2" "param 3"