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.
Solved! Go to Solution.
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)
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)
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
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...
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?)
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
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.
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...
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:
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"